AdityaAdaki commited on
Commit
9313d0f
·
1 Parent(s): 9ecce35
Files changed (1) hide show
  1. app.py +45 -138
app.py CHANGED
@@ -15,6 +15,8 @@ import mutagen.oggvorbis
15
  from werkzeug.utils import secure_filename
16
  import logging
17
  import base64
 
 
18
 
19
  app = Flask(__name__, static_folder='static')
20
  # Create a temporary directory for uploads
@@ -33,6 +35,8 @@ file_timestamps = {}
33
  logging.basicConfig(level=logging.INFO)
34
  logger = logging.getLogger(__name__)
35
 
 
 
36
  def allowed_file(filename):
37
  return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
38
 
@@ -43,119 +47,36 @@ def extract_metadata(filepath):
43
  if audio is None:
44
  return {}
45
 
 
46
  metadata = {
47
- 'duration': round(audio.info.length) if hasattr(audio.info, 'length') else None,
48
- 'bitrate': round(audio.info.bitrate / 1000) if hasattr(audio.info, 'bitrate') else None,
49
- 'sample_rate': audio.info.sample_rate if hasattr(audio.info, 'sample_rate') else None,
50
- 'channels': audio.info.channels if hasattr(audio.info, 'channels') else None,
51
  'title': None,
52
  'artist': 'Unknown Artist',
53
- 'album': None,
54
- 'genre': None,
55
- 'date': None,
56
- 'artwork': None
57
  }
58
 
59
- # Get the original filename without timestamp prefix and extension
60
  original_filename = os.path.splitext(os.path.basename(filepath))[0]
61
  if '_' in original_filename:
62
  original_filename = '_'.join(original_filename.split('_')[2:])
63
-
64
  try:
65
- # Handle MP3 files
66
- if isinstance(audio, mutagen.mp3.MP3):
67
- try:
68
- # Try to get ID3 tags first
69
- if audio.tags:
70
- id3 = audio.tags
71
- else:
72
- # If no tags exist, try to create them
73
- id3 = mutagen.id3.ID3(filepath)
74
-
75
- # Get basic metadata
76
- metadata.update({
77
- 'title': str(id3.get('TIT2', [''])[0]) if 'TIT2' in id3 else '',
78
- 'artist': str(id3.get('TPE1', ['Unknown Artist'])[0]) if 'TPE1' in id3 else 'Unknown Artist',
79
- 'album': str(id3.get('TALB', [''])[0]) if 'TALB' in id3 else '',
80
- 'genre': str(id3.get('TCON', [''])[0]) if 'TCON' in id3 else '',
81
- 'date': str(id3.get('TDRC', [''])[0]) if 'TDRC' in id3 else ''
82
- })
83
-
84
- # Extract artwork - try different APIC frame keys
85
- apic_keys = ['APIC:', 'APIC:Cover', 'APIC:Front Cover', 'APIC']
86
- for key in apic_keys:
87
- if key in id3:
88
- artwork = id3[key]
89
- if artwork and artwork.data:
90
- logger.info(f"Found artwork in {key} frame")
91
- metadata['artwork'] = {
92
- 'mime': artwork.mime,
93
- 'data': base64.b64encode(artwork.data).decode('utf-8')
94
- }
95
- break
96
-
97
- if not metadata['artwork']:
98
- logger.info("No artwork found in ID3 tags")
99
-
100
- except Exception as e:
101
- logger.error(f"Error reading ID3 tags: {str(e)}")
102
- # Fallback to EasyID3 if ID3 fails
103
- try:
104
- easy_id3 = EasyID3(filepath)
105
- metadata.update({
106
- 'title': easy_id3.get('title', [''])[0],
107
- 'artist': easy_id3.get('artist', ['Unknown Artist'])[0],
108
- 'album': easy_id3.get('album', [''])[0],
109
- 'genre': easy_id3.get('genre', [''])[0],
110
- 'date': easy_id3.get('date', [''])[0]
111
- })
112
- except:
113
- metadata['title'] = original_filename
114
 
115
- # Handle FLAC files
116
- elif isinstance(audio, mutagen.flac.FLAC):
117
  metadata.update({
118
- 'title': audio.tags.get('TITLE', [''])[0] if audio.tags and 'TITLE' in audio.tags else None,
119
- 'artist': audio.tags.get('ARTIST', ['Unknown Artist'])[0] if audio.tags and 'ARTIST' in audio.tags else 'Unknown Artist',
120
- 'album': audio.tags.get('ALBUM', [''])[0] if audio.tags and 'ALBUM' in audio.tags else None,
121
- 'genre': audio.tags.get('GENRE', [''])[0] if audio.tags and 'GENRE' in audio.tags else None,
122
- 'date': audio.tags.get('DATE', [''])[0] if audio.tags and 'DATE' in audio.tags else None
123
  })
124
-
125
- # Extract artwork from pictures
126
- if audio.pictures:
127
- picture = audio.pictures[0]
128
- metadata['artwork'] = {
129
- 'mime': picture.mime,
130
- 'data': base64.b64encode(picture.data).decode('utf-8')
131
- }
132
-
133
- # Handle OGG files
134
- elif isinstance(audio, mutagen.oggvorbis.OggVorbis):
135
- metadata.update({
136
- 'title': audio.get('title', [''])[0] if 'title' in audio else None,
137
- 'artist': audio.get('artist', ['Unknown Artist'])[0] if 'artist' in audio else 'Unknown Artist',
138
- 'album': audio.get('album', [''])[0] if 'album' in audio else None,
139
- 'genre': audio.get('genre', [''])[0] if 'genre' in audio else None,
140
- 'date': audio.get('date', [''])[0] if 'date' in audio else None
141
- })
142
-
143
- # Extract artwork if available (some OGG files store artwork in metadata)
144
- if 'METADATA_BLOCK_PICTURE' in audio:
145
- try:
146
- picture_data = base64.b64decode(audio['METADATA_BLOCK_PICTURE'][0])
147
- picture = mutagen.flac.Picture(picture_data)
148
- metadata['artwork'] = {
149
- 'mime': picture.mime,
150
- 'data': base64.b64encode(picture.data).decode('utf-8')
151
- }
152
- except:
153
- pass
154
-
155
- # If title is not found or empty, use original filename
156
- if not metadata['title']:
157
- metadata['title'] = original_filename
158
-
159
  except Exception as e:
160
  logger.error(f"Error reading tags: {str(e)}")
161
  metadata['title'] = original_filename
@@ -164,7 +85,7 @@ def extract_metadata(filepath):
164
  except Exception as e:
165
  logger.error(f"Error extracting metadata: {str(e)}")
166
  return {
167
- 'title': os.path.splitext(os.path.basename(filepath))[0].split('_', 2)[-1],
168
  'artist': 'Unknown Artist'
169
  }
170
 
@@ -220,66 +141,52 @@ def upload_file():
220
  files = request.files.getlist('files[]')
221
  logger.info(f'Received {len(files)} files')
222
 
223
- if not files or all(file.filename == '' for file in files):
224
- logger.warning('No selected files')
225
- return jsonify({'success': False, 'error': 'No selected files'}), 400
226
-
227
  if len(files) > app.config['MAX_FILES']:
228
- logger.warning(f'Too many files: {len(files)}')
229
  return jsonify({
230
  'success': False,
231
  'error': f"Maximum {app.config['MAX_FILES']} files can be uploaded at once"
232
  }), 400
233
 
234
  results = []
235
- for file in files:
236
- logger.info(f'Processing file: {file.filename}')
237
- if file and allowed_file(file.filename):
238
- try:
 
239
  filename = secure_filename(file.filename)
240
  timestamp = datetime.now().strftime('%Y%m%d_%H%M%S_')
241
  filename = timestamp + filename
242
 
243
  filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
244
  file.save(filepath)
245
- logger.info(f'File saved: {filepath}')
246
 
247
  file_timestamps[filename] = datetime.now()
248
-
249
- if not os.path.exists(filepath):
250
- logger.error(f'Failed to save file: {filepath}')
251
- results.append({
252
- 'filename': file.filename,
253
- 'success': False,
254
- 'error': 'Failed to save file'
255
- })
256
- continue
257
-
258
  metadata = extract_metadata(filepath)
259
- logger.info(f'Metadata extracted: {metadata}')
260
-
261
- results.append({
262
  'filename': file.filename,
263
  'success': True,
264
  'filepath': f'/static/uploads/{filename}',
265
  'metadata': metadata
266
- })
267
- except Exception as e:
268
- logger.error(f'Upload error for {file.filename}: {str(e)}')
269
- results.append({
270
- 'filename': file.filename,
271
- 'success': False,
272
- 'error': 'Server error during upload'
273
- })
274
- else:
275
- logger.warning(f'Invalid file type: {file.filename}')
276
- results.append({
277
  'filename': file.filename,
278
  'success': False,
279
  'error': 'Invalid file type'
280
- })
 
 
 
 
 
 
 
 
 
 
 
 
281
 
282
- logger.info(f'Upload complete. Results: {results}')
283
  return jsonify({
284
  'success': True,
285
  'files': results
 
15
  from werkzeug.utils import secure_filename
16
  import logging
17
  import base64
18
+ from concurrent.futures import ThreadPoolExecutor
19
+ import asyncio
20
 
21
  app = Flask(__name__, static_folder='static')
22
  # Create a temporary directory for uploads
 
35
  logging.basicConfig(level=logging.INFO)
36
  logger = logging.getLogger(__name__)
37
 
38
+ THREAD_POOL = ThreadPoolExecutor(max_workers=4) # Adjust based on your server capacity
39
+
40
  def allowed_file(filename):
41
  return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
42
 
 
47
  if audio is None:
48
  return {}
49
 
50
+ # Basic metadata that we absolutely need
51
  metadata = {
 
 
 
 
52
  'title': None,
53
  'artist': 'Unknown Artist',
54
+ 'duration': None
 
 
 
55
  }
56
 
57
+ # Get filename as fallback
58
  original_filename = os.path.splitext(os.path.basename(filepath))[0]
59
  if '_' in original_filename:
60
  original_filename = '_'.join(original_filename.split('_')[2:])
61
+
62
  try:
63
+ # Add basic audio info
64
+ if hasattr(audio.info, 'length'):
65
+ metadata['duration'] = round(audio.info.length)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
 
67
+ # Simplified tag extraction
68
+ if isinstance(audio, (mutagen.flac.FLAC, mutagen.oggvorbis.OggVorbis)):
69
  metadata.update({
70
+ 'title': audio.tags.get('TITLE', [original_filename])[0] if audio.tags else original_filename,
71
+ 'artist': audio.tags.get('ARTIST', ['Unknown Artist'])[0] if audio.tags else 'Unknown Artist',
 
 
 
72
  })
73
+ elif isinstance(audio, mutagen.mp3.MP3):
74
+ if audio.tags:
75
+ metadata.update({
76
+ 'title': str(audio.tags.get('TIT2', original_filename)),
77
+ 'artist': str(audio.tags.get('TPE1', 'Unknown Artist')),
78
+ })
79
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
  except Exception as e:
81
  logger.error(f"Error reading tags: {str(e)}")
82
  metadata['title'] = original_filename
 
85
  except Exception as e:
86
  logger.error(f"Error extracting metadata: {str(e)}")
87
  return {
88
+ 'title': original_filename,
89
  'artist': 'Unknown Artist'
90
  }
91
 
 
141
  files = request.files.getlist('files[]')
142
  logger.info(f'Received {len(files)} files')
143
 
 
 
 
 
144
  if len(files) > app.config['MAX_FILES']:
 
145
  return jsonify({
146
  'success': False,
147
  'error': f"Maximum {app.config['MAX_FILES']} files can be uploaded at once"
148
  }), 400
149
 
150
  results = []
151
+ futures = []
152
+
153
+ def process_file(file):
154
+ try:
155
+ if file and allowed_file(file.filename):
156
  filename = secure_filename(file.filename)
157
  timestamp = datetime.now().strftime('%Y%m%d_%H%M%S_')
158
  filename = timestamp + filename
159
 
160
  filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
161
  file.save(filepath)
 
162
 
163
  file_timestamps[filename] = datetime.now()
 
 
 
 
 
 
 
 
 
 
164
  metadata = extract_metadata(filepath)
165
+
166
+ return {
 
167
  'filename': file.filename,
168
  'success': True,
169
  'filepath': f'/static/uploads/{filename}',
170
  'metadata': metadata
171
+ }
172
+ return {
 
 
 
 
 
 
 
 
 
173
  'filename': file.filename,
174
  'success': False,
175
  'error': 'Invalid file type'
176
+ }
177
+ except Exception as e:
178
+ logger.error(f'Upload error for {file.filename}: {str(e)}')
179
+ return {
180
+ 'filename': file.filename,
181
+ 'success': False,
182
+ 'error': 'Server error during upload'
183
+ }
184
+
185
+ # Process files in parallel
186
+ with THREAD_POOL:
187
+ futures = [THREAD_POOL.submit(process_file, file) for file in files]
188
+ results = [future.result() for future in futures]
189
 
 
190
  return jsonify({
191
  'success': True,
192
  'files': results