Save cleared tags to disk before metadata enhancement

Previously, tags.clear() only cleared in memory — if any later step
threw (metadata extraction, API calls, album art download), the file
was moved with its original Soulseek source tags intact. This caused
album fragmentation in media servers when some tracks had MusicBrainz
IDs and others didn't.

Now the cleared tags are saved to disk immediately after wiping. If
enhancement succeeds, the file is saved again with full metadata
(identical to before). If it fails, the file has clean empty tags
instead of inconsistent junk — media servers group by folder structure
which is always correct.
pull/253/head
Broque Thomas 2 months ago
parent 7a2bc49458
commit c0bb1a4f34

@ -15690,14 +15690,16 @@ def _enhance_file_metadata(file_path: str, context: dict, artist: dict, album_in
print(f"❌ Could not load audio file with Mutagen: {file_path}")
return False
# ── Wipe ALL existing tags in memory (NO save yet) ──
# ── Wipe ALL existing tags and save immediately ──
# Files from Soulseek carry random metadata (wrong comments,
# encoder info, ReplayGain, old album art, random TXXX frames).
# Save the cleared state FIRST so that if anything below throws,
# the file at least has clean (empty) tags instead of junk that
# causes album fragmentation in media servers.
if hasattr(audio_file, 'clear_pictures'):
audio_file.clear_pictures()
if audio_file.tags is not None:
# Log what's being cleared for debugging
if len(audio_file.tags) > 0:
tag_keys = list(audio_file.tags.keys())[:15]
print(f"🧹 Clearing {len(audio_file.tags)} existing tags: "
@ -15706,6 +15708,14 @@ def _enhance_file_metadata(file_path: str, context: dict, artist: dict, album_in
else:
audio_file.add_tags()
# Persist the wipe — guarantees junk tags are gone even if later steps fail
if isinstance(audio_file.tags, ID3):
audio_file.save(v1=0, v2_version=4)
elif isinstance(audio_file, FLAC):
audio_file.save(deleteid3=True)
else:
audio_file.save()
metadata = _extract_spotify_metadata(context, artist, album_info)
if not metadata:
print("⚠️ Could not extract Spotify metadata, saving with cleared tags.")
@ -18999,6 +19009,16 @@ def get_version_info():
"title": "What's New in SoulSync",
"subtitle": f"Version {SOULSYNC_VERSION} — Latest Changes",
"sections": [
{
"title": "🔧 Fix Soulseek Junk Tags Surviving Post-Processing",
"description": "Tags from Soulseek source files are now wiped to disk immediately, before metadata enhancement",
"features": [
"• Clears and saves tags before any API calls or metadata extraction",
"• If enhancement fails, file has clean empty tags instead of inconsistent junk",
"• Fixes album fragmentation in Navidrome/Jellyfin/Plex caused by partial MusicBrainz data",
"• Happy path unchanged — full metadata still written on success"
]
},
{
"title": "👁️ Watch All Unwatched Preview Modal",
"description": "The Watch All Unwatched button now opens a modal showing exactly which artists will be added",

@ -3403,6 +3403,7 @@ function closeHelperSearch() {
const WHATS_NEW = {
'2.1': [
// Newest features first
{ title: 'Fix Junk Tags Surviving', desc: 'Soulseek source tags are now wiped to disk immediately — no more album fragmentation from partial metadata' },
{ title: 'Watch All Preview Modal', desc: 'Watch All Unwatched now opens a modal showing which artists will be added before confirming', page: 'library', selector: '#library-watchlist-all-btn' },
{ title: 'Fix Watch All Unwatched', desc: 'Watch All Unwatched now works for Deezer users — was silently skipping artists with only Deezer IDs' },
{ title: 'Fix Path Mismatch Fixes', desc: 'Library Maintenance path fixes now use fresh config and show error reasons in the toast' },

Loading…
Cancel
Save