Spaces:
Running
Running
AdityaAdaki
commited on
Commit
·
63fc423
1
Parent(s):
c071803
bug fixes1
Browse files- app.py +75 -8
- static/js/main.js +29 -6
app.py
CHANGED
|
@@ -20,6 +20,7 @@ import asyncio
|
|
| 20 |
from flask_compress import Compress
|
| 21 |
from flask_caching import Cache
|
| 22 |
import hashlib
|
|
|
|
| 23 |
|
| 24 |
# Create uploads directory in the static folder instead of using tempfile
|
| 25 |
UPLOAD_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'static', 'uploads')
|
|
@@ -56,6 +57,16 @@ logger = logging.getLogger(__name__)
|
|
| 56 |
|
| 57 |
THREAD_POOL = ThreadPoolExecutor(max_workers=8)
|
| 58 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 59 |
def allowed_file(filename):
|
| 60 |
return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
|
| 61 |
|
|
@@ -110,17 +121,19 @@ def index():
|
|
| 110 |
return render_template('index.html', title="Soundscape - 3D Music Visualizer")
|
| 111 |
|
| 112 |
def cleanup_old_files():
|
|
|
|
| 113 |
while True:
|
| 114 |
current_time = datetime.now()
|
| 115 |
files_to_delete = []
|
| 116 |
|
| 117 |
-
#
|
| 118 |
for filename, timestamp in file_timestamps.items():
|
| 119 |
if current_time - timestamp > FILE_LIFETIME:
|
| 120 |
filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
|
| 121 |
try:
|
| 122 |
if os.path.exists(filepath):
|
| 123 |
os.remove(filepath)
|
|
|
|
| 124 |
files_to_delete.append(filename)
|
| 125 |
except Exception as e:
|
| 126 |
logger.error(f"Error deleting file {filename}: {str(e)}")
|
|
@@ -128,6 +141,28 @@ def cleanup_old_files():
|
|
| 128 |
# Remove deleted files from timestamps
|
| 129 |
for filename in files_to_delete:
|
| 130 |
file_timestamps.pop(filename, None)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 131 |
|
| 132 |
time.sleep(300) # Check every 5 minutes
|
| 133 |
|
|
@@ -160,6 +195,17 @@ def serve_static(filename):
|
|
| 160 |
def upload_file():
|
| 161 |
logger.info('Upload request received')
|
| 162 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 163 |
if 'files[]' not in request.files:
|
| 164 |
logger.warning('No files in request')
|
| 165 |
return jsonify({'success': False, 'error': 'No files uploaded'}), 400
|
|
@@ -184,27 +230,26 @@ def upload_file():
|
|
| 184 |
|
| 185 |
filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
|
| 186 |
|
| 187 |
-
# Use buffered write for faster file saving
|
| 188 |
with open(filepath, 'wb') as f:
|
| 189 |
while True:
|
| 190 |
-
chunk = file.read(8192)
|
| 191 |
if not chunk:
|
| 192 |
break
|
| 193 |
f.write(chunk)
|
| 194 |
|
| 195 |
file_timestamps[filename] = datetime.now()
|
|
|
|
|
|
|
| 196 |
|
| 197 |
-
# Process metadata in parallel
|
| 198 |
metadata = extract_metadata(filepath)
|
| 199 |
-
|
| 200 |
-
# Invalidate cache if needed
|
| 201 |
invalidate_metadata_cache(filepath)
|
| 202 |
|
| 203 |
return {
|
| 204 |
'filename': file.filename,
|
| 205 |
'success': True,
|
| 206 |
'filepath': f'/static/uploads/{filename}',
|
| 207 |
-
'metadata': metadata
|
|
|
|
| 208 |
}
|
| 209 |
return {
|
| 210 |
'filename': file.filename,
|
|
@@ -225,7 +270,8 @@ def upload_file():
|
|
| 225 |
|
| 226 |
return jsonify({
|
| 227 |
'success': True,
|
| 228 |
-
'files': results
|
|
|
|
| 229 |
})
|
| 230 |
|
| 231 |
@app.route('/static/uploads/<filename>')
|
|
@@ -252,5 +298,26 @@ def add_header(response):
|
|
| 252 |
def invalidate_metadata_cache(filepath):
|
| 253 |
cache.delete_memoized(extract_metadata, filepath)
|
| 254 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 255 |
if __name__ == '__main__':
|
| 256 |
app.run(host='0.0.0.0', port=7860)
|
|
|
|
| 20 |
from flask_compress import Compress
|
| 21 |
from flask_caching import Cache
|
| 22 |
import hashlib
|
| 23 |
+
from uuid import uuid4
|
| 24 |
|
| 25 |
# Create uploads directory in the static folder instead of using tempfile
|
| 26 |
UPLOAD_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'static', 'uploads')
|
|
|
|
| 57 |
|
| 58 |
THREAD_POOL = ThreadPoolExecutor(max_workers=8)
|
| 59 |
|
| 60 |
+
# Track files by session
|
| 61 |
+
session_files = {}
|
| 62 |
+
INACTIVE_SESSION_TIMEOUT = 3600 # 1 hour in seconds
|
| 63 |
+
|
| 64 |
+
def get_session_id():
|
| 65 |
+
"""Generate or retrieve session ID from request"""
|
| 66 |
+
if 'X-Session-ID' in request.headers:
|
| 67 |
+
return request.headers['X-Session-ID']
|
| 68 |
+
return str(uuid4())
|
| 69 |
+
|
| 70 |
def allowed_file(filename):
|
| 71 |
return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
|
| 72 |
|
|
|
|
| 121 |
return render_template('index.html', title="Soundscape - 3D Music Visualizer")
|
| 122 |
|
| 123 |
def cleanup_old_files():
|
| 124 |
+
"""Enhanced cleanup that handles both time-based and session-based cleanup"""
|
| 125 |
while True:
|
| 126 |
current_time = datetime.now()
|
| 127 |
files_to_delete = []
|
| 128 |
|
| 129 |
+
# Cleanup files by age
|
| 130 |
for filename, timestamp in file_timestamps.items():
|
| 131 |
if current_time - timestamp > FILE_LIFETIME:
|
| 132 |
filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
|
| 133 |
try:
|
| 134 |
if os.path.exists(filepath):
|
| 135 |
os.remove(filepath)
|
| 136 |
+
logger.info(f"Deleted old file: {filename}")
|
| 137 |
files_to_delete.append(filename)
|
| 138 |
except Exception as e:
|
| 139 |
logger.error(f"Error deleting file {filename}: {str(e)}")
|
|
|
|
| 141 |
# Remove deleted files from timestamps
|
| 142 |
for filename in files_to_delete:
|
| 143 |
file_timestamps.pop(filename, None)
|
| 144 |
+
|
| 145 |
+
# Cleanup files by session
|
| 146 |
+
inactive_sessions = []
|
| 147 |
+
current_timestamp = time.time()
|
| 148 |
+
|
| 149 |
+
for session_id, session_data in session_files.items():
|
| 150 |
+
last_access = session_data.get('last_access', 0)
|
| 151 |
+
if current_timestamp - last_access > INACTIVE_SESSION_TIMEOUT:
|
| 152 |
+
# Delete all files for this session
|
| 153 |
+
for filename in session_data.get('files', []):
|
| 154 |
+
filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
|
| 155 |
+
try:
|
| 156 |
+
if os.path.exists(filepath):
|
| 157 |
+
os.remove(filepath)
|
| 158 |
+
logger.info(f"Deleted session file: {filename}")
|
| 159 |
+
except Exception as e:
|
| 160 |
+
logger.error(f"Error deleting session file {filename}: {str(e)}")
|
| 161 |
+
inactive_sessions.append(session_id)
|
| 162 |
+
|
| 163 |
+
# Remove inactive sessions
|
| 164 |
+
for session_id in inactive_sessions:
|
| 165 |
+
session_files.pop(session_id, None)
|
| 166 |
|
| 167 |
time.sleep(300) # Check every 5 minutes
|
| 168 |
|
|
|
|
| 195 |
def upload_file():
|
| 196 |
logger.info('Upload request received')
|
| 197 |
|
| 198 |
+
session_id = get_session_id()
|
| 199 |
+
|
| 200 |
+
if session_id not in session_files:
|
| 201 |
+
session_files[session_id] = {
|
| 202 |
+
'files': [],
|
| 203 |
+
'last_access': time.time()
|
| 204 |
+
}
|
| 205 |
+
|
| 206 |
+
# Update last access time
|
| 207 |
+
session_files[session_id]['last_access'] = time.time()
|
| 208 |
+
|
| 209 |
if 'files[]' not in request.files:
|
| 210 |
logger.warning('No files in request')
|
| 211 |
return jsonify({'success': False, 'error': 'No files uploaded'}), 400
|
|
|
|
| 230 |
|
| 231 |
filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
|
| 232 |
|
|
|
|
| 233 |
with open(filepath, 'wb') as f:
|
| 234 |
while True:
|
| 235 |
+
chunk = file.read(8192)
|
| 236 |
if not chunk:
|
| 237 |
break
|
| 238 |
f.write(chunk)
|
| 239 |
|
| 240 |
file_timestamps[filename] = datetime.now()
|
| 241 |
+
# Track file with session
|
| 242 |
+
session_files[session_id]['files'].append(filename)
|
| 243 |
|
|
|
|
| 244 |
metadata = extract_metadata(filepath)
|
|
|
|
|
|
|
| 245 |
invalidate_metadata_cache(filepath)
|
| 246 |
|
| 247 |
return {
|
| 248 |
'filename': file.filename,
|
| 249 |
'success': True,
|
| 250 |
'filepath': f'/static/uploads/{filename}',
|
| 251 |
+
'metadata': metadata,
|
| 252 |
+
'session_id': session_id # Return session ID to client
|
| 253 |
}
|
| 254 |
return {
|
| 255 |
'filename': file.filename,
|
|
|
|
| 270 |
|
| 271 |
return jsonify({
|
| 272 |
'success': True,
|
| 273 |
+
'files': results,
|
| 274 |
+
'session_id': session_id
|
| 275 |
})
|
| 276 |
|
| 277 |
@app.route('/static/uploads/<filename>')
|
|
|
|
| 298 |
def invalidate_metadata_cache(filepath):
|
| 299 |
cache.delete_memoized(extract_metadata, filepath)
|
| 300 |
|
| 301 |
+
# Add endpoint to explicitly end session
|
| 302 |
+
@app.route('/end-session', methods=['POST'])
|
| 303 |
+
def end_session():
|
| 304 |
+
session_id = request.headers.get('X-Session-ID')
|
| 305 |
+
if session_id and session_id in session_files:
|
| 306 |
+
# Delete all files for this session
|
| 307 |
+
for filename in session_files[session_id].get('files', []):
|
| 308 |
+
filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
|
| 309 |
+
try:
|
| 310 |
+
if os.path.exists(filepath):
|
| 311 |
+
os.remove(filepath)
|
| 312 |
+
logger.info(f"Deleted session file: {filename}")
|
| 313 |
+
except Exception as e:
|
| 314 |
+
logger.error(f"Error deleting session file {filename}: {str(e)}")
|
| 315 |
+
|
| 316 |
+
# Remove session data
|
| 317 |
+
session_files.pop(session_id, None)
|
| 318 |
+
return jsonify({'success': True, 'message': 'Session ended and files cleaned up'})
|
| 319 |
+
|
| 320 |
+
return jsonify({'success': False, 'error': 'Session not found'}), 404
|
| 321 |
+
|
| 322 |
if __name__ == '__main__':
|
| 323 |
app.run(host='0.0.0.0', port=7860)
|
static/js/main.js
CHANGED
|
@@ -1310,12 +1310,9 @@ async function handleFiles(files) {
|
|
| 1310 |
const response = await fetch('/upload', {
|
| 1311 |
method: 'POST',
|
| 1312 |
body: formData,
|
| 1313 |
-
|
| 1314 |
-
|
| 1315 |
-
|
| 1316 |
-
if (progressFill) progressFill.style.width = percentComplete + '%';
|
| 1317 |
-
if (progressText) progressText.textContent = Math.round(percentComplete) + '%';
|
| 1318 |
-
}
|
| 1319 |
});
|
| 1320 |
|
| 1321 |
if (!response.ok) {
|
|
@@ -1372,6 +1369,12 @@ async function handleFiles(files) {
|
|
| 1372 |
item.classList.toggle('active', i === currentTrackIndex);
|
| 1373 |
});
|
| 1374 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1375 |
} else {
|
| 1376 |
console.error('Upload failed:', data.error);
|
| 1377 |
showError(data.error || 'Upload failed');
|
|
@@ -1455,4 +1458,24 @@ function setupVolumeControl() {
|
|
| 1455 |
document.addEventListener('DOMContentLoaded', () => {
|
| 1456 |
// ... other initialization code ...
|
| 1457 |
setupVolumeControl();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1458 |
});
|
|
|
|
| 1310 |
const response = await fetch('/upload', {
|
| 1311 |
method: 'POST',
|
| 1312 |
body: formData,
|
| 1313 |
+
headers: sessionId ? {
|
| 1314 |
+
'X-Session-ID': sessionId
|
| 1315 |
+
} : {}
|
|
|
|
|
|
|
|
|
|
| 1316 |
});
|
| 1317 |
|
| 1318 |
if (!response.ok) {
|
|
|
|
| 1369 |
item.classList.toggle('active', i === currentTrackIndex);
|
| 1370 |
});
|
| 1371 |
}
|
| 1372 |
+
|
| 1373 |
+
// Store session ID if we got one
|
| 1374 |
+
if (data.session_id) {
|
| 1375 |
+
sessionId = data.session_id;
|
| 1376 |
+
localStorage.setItem('audioSessionId', sessionId);
|
| 1377 |
+
}
|
| 1378 |
} else {
|
| 1379 |
console.error('Upload failed:', data.error);
|
| 1380 |
showError(data.error || 'Upload failed');
|
|
|
|
| 1458 |
document.addEventListener('DOMContentLoaded', () => {
|
| 1459 |
// ... other initialization code ...
|
| 1460 |
setupVolumeControl();
|
| 1461 |
+
});
|
| 1462 |
+
|
| 1463 |
+
// Add session management
|
| 1464 |
+
let sessionId = localStorage.getItem('audioSessionId');
|
| 1465 |
+
|
| 1466 |
+
// Add cleanup on page unload
|
| 1467 |
+
window.addEventListener('beforeunload', async () => {
|
| 1468 |
+
if (sessionId) {
|
| 1469 |
+
try {
|
| 1470 |
+
await fetch('/end-session', {
|
| 1471 |
+
method: 'POST',
|
| 1472 |
+
headers: {
|
| 1473 |
+
'X-Session-ID': sessionId
|
| 1474 |
+
}
|
| 1475 |
+
});
|
| 1476 |
+
localStorage.removeItem('audioSessionId');
|
| 1477 |
+
} catch (error) {
|
| 1478 |
+
console.error('Error ending session:', error);
|
| 1479 |
+
}
|
| 1480 |
+
}
|
| 1481 |
});
|