Fix MP3 metadata being wiped by intermediate save after tag clear

This commit is contained in:
Broque Thomas 2026-02-17 09:12:29 -08:00
parent 71f362b79a
commit 335c9dc977

View file

@ -8143,6 +8143,11 @@ def _enhance_file_metadata(file_path: str, context: dict, artist: dict, album_in
""" """
Core function to enhance audio file metadata using Spotify data. Core function to enhance audio file metadata using Spotify data.
Thread-safe with per-file locking to prevent concurrent metadata writes. Thread-safe with per-file locking to prevent concurrent metadata writes.
Opens the file once in non-easy mode, clears all tags in memory, writes
new tags using format-specific frames/keys, embeds album art and source
IDs, then saves once. This avoids the old clearsavereopen pattern
which stripped the ID3v2 header from MP3 files, leaving them tagless.
""" """
if not config_manager.get('metadata_enhancement.enabled', True): if not config_manager.get('metadata_enhancement.enabled', True):
print("🎵 Metadata enhancement disabled in config.") print("🎵 Metadata enhancement disabled in config.")
@ -8153,62 +8158,97 @@ def _enhance_file_metadata(file_path: str, context: dict, artist: dict, album_in
with file_lock: with file_lock:
print(f"🎵 Enhancing metadata for: {os.path.basename(file_path)}") print(f"🎵 Enhancing metadata for: {os.path.basename(file_path)}")
try: try:
# Wipe ALL existing tags first — files from Soulseek carry random metadata audio_file = MutagenFile(file_path)
# (wrong comments, encoder info, ReplayGain, old album art, random TXXX frames) if audio_file is None:
audio_raw = MutagenFile(file_path)
if audio_raw is None:
print(f"❌ Could not load audio file with Mutagen: {file_path}") print(f"❌ Could not load audio file with Mutagen: {file_path}")
return False return False
if hasattr(audio_raw, 'clear_pictures'): # ── Wipe ALL existing tags in memory (NO save yet) ──
audio_raw.clear_pictures() # Files from Soulseek carry random metadata (wrong comments,
# encoder info, ReplayGain, old album art, random TXXX frames).
if hasattr(audio_file, 'clear_pictures'):
audio_file.clear_pictures()
if audio_raw.tags is not None: if audio_file.tags is not None:
audio_raw.tags.clear() audio_file.tags.clear()
audio_raw.save() else:
# Re-open in easy mode for writing standard tags
audio_file = MutagenFile(file_path, easy=True)
if audio_file is None:
print(f"❌ Could not reopen audio file in easy mode: {file_path}")
return False
if audio_file.tags is None:
audio_file.add_tags() audio_file.add_tags()
metadata = _extract_spotify_metadata(context, artist, album_info) metadata = _extract_spotify_metadata(context, artist, album_info)
if not metadata: if not metadata:
print("⚠️ Could not extract Spotify metadata, preserving original tags.") print("⚠️ Could not extract Spotify metadata, saving with cleared tags.")
audio_file.save()
return True return True
# Use 'easy' tags for broad compatibility first # ── Write standard tags using format-specific API ──
audio_file['title'] = metadata.get('title', '')
audio_file['artist'] = metadata.get('artist', '')
audio_file['albumartist'] = metadata.get('album_artist', '')
audio_file['album'] = metadata.get('album', '')
if metadata.get('date'):
audio_file['date'] = metadata['date']
if metadata.get('genre'):
audio_file['genre'] = metadata['genre']
track_num_str = f"{metadata.get('track_number', 1)}/{metadata.get('total_tracks', 1)}" track_num_str = f"{metadata.get('track_number', 1)}/{metadata.get('total_tracks', 1)}"
audio_file['tracknumber'] = track_num_str
if metadata.get('disc_number'): if isinstance(audio_file.tags, ID3):
audio_file['discnumber'] = str(metadata.get('disc_number')) # MP3: write ID3 frames directly
if metadata.get('title'):
audio_file.tags.add(TIT2(encoding=3, text=[metadata['title']]))
if metadata.get('artist'):
audio_file.tags.add(TPE1(encoding=3, text=[metadata['artist']]))
if metadata.get('album_artist'):
audio_file.tags.add(TPE2(encoding=3, text=[metadata['album_artist']]))
if metadata.get('album'):
audio_file.tags.add(TALB(encoding=3, text=[metadata['album']]))
if metadata.get('date'):
audio_file.tags.add(TDRC(encoding=3, text=[metadata['date']]))
if metadata.get('genre'):
audio_file.tags.add(TCON(encoding=3, text=[metadata['genre']]))
audio_file.tags.add(TRCK(encoding=3, text=[track_num_str]))
if metadata.get('disc_number'):
audio_file.tags.add(TPOS(encoding=3, text=[str(metadata['disc_number'])]))
elif isinstance(audio_file, (FLAC, OggVorbis)):
# FLAC / OGG Vorbis: dict-style VorbisComment tags
if metadata.get('title'):
audio_file['title'] = [metadata['title']]
if metadata.get('artist'):
audio_file['artist'] = [metadata['artist']]
if metadata.get('album_artist'):
audio_file['albumartist'] = [metadata['album_artist']]
if metadata.get('album'):
audio_file['album'] = [metadata['album']]
if metadata.get('date'):
audio_file['date'] = [metadata['date']]
if metadata.get('genre'):
audio_file['genre'] = [metadata['genre']]
audio_file['tracknumber'] = [track_num_str]
if metadata.get('disc_number'):
audio_file['discnumber'] = [str(metadata['disc_number'])]
elif isinstance(audio_file, MP4):
# MP4 / M4A: Apple-style tag keys
if metadata.get('title'):
audio_file['\xa9nam'] = [metadata['title']]
if metadata.get('artist'):
audio_file['\xa9ART'] = [metadata['artist']]
if metadata.get('album_artist'):
audio_file['aART'] = [metadata['album_artist']]
if metadata.get('album'):
audio_file['\xa9alb'] = [metadata['album']]
if metadata.get('date'):
audio_file['\xa9day'] = [metadata['date']]
if metadata.get('genre'):
audio_file['\xa9gen'] = [metadata['genre']]
track_num = metadata.get('track_number', 1)
total_tracks = metadata.get('total_tracks', 1)
audio_file['trkn'] = [(track_num, total_tracks)]
if metadata.get('disc_number'):
audio_file['disk'] = [(metadata['disc_number'], 0)]
# ── Embed album art on the same object ──
if config_manager.get('metadata_enhancement.embed_album_art', True):
_embed_album_art_metadata(audio_file, metadata)
# ── Embed source IDs (Spotify, MusicBrainz, etc.) on the same object ──
_embed_source_ids(audio_file, metadata)
# ── Single save for everything ──
audio_file.save() audio_file.save()
# Re-open in non-easy mode for album art and source ID embedding
audio_file_raw = MutagenFile(file_path)
if audio_file_raw is not None:
if config_manager.get('metadata_enhancement.embed_album_art', True):
_embed_album_art_metadata(audio_file_raw, metadata)
_embed_source_ids(audio_file_raw, metadata)
audio_file_raw.save()
print("✅ Metadata enhanced successfully.") print("✅ Metadata enhanced successfully.")
return True return True
except Exception as e: except Exception as e: