From 0bc6abd6839fdb2a9886eccb8e0cffcba1be617b Mon Sep 17 00:00:00 2001 From: Broque Thomas <26755000+Nezreka@users.noreply.github.com> Date: Tue, 31 Mar 2026 12:16:57 -0700 Subject: [PATCH] Allow duplicate tracks across albums with settings toggle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Same song from different albums was blocked from entering the wishlist by a name+artist dedup check. Added toggle in Settings → Library → File Organization: "Allow duplicate tracks across albums" (on by default). When enabled, the dedup is skipped — different album versions of the same song can coexist in the wishlist for complete discography downloads. The UNIQUE constraint on track ID still prevents the exact same track from being added twice. --- database/music_database.py | 87 ++++++++++++++++++++------------------ webui/index.html | 10 +++++ webui/static/script.js | 4 ++ 3 files changed, 59 insertions(+), 42 deletions(-) diff --git a/database/music_database.py b/database/music_database.py index 9f32aa05..26cc4f74 100644 --- a/database/music_database.py +++ b/database/music_database.py @@ -6058,52 +6058,55 @@ class MusicDatabase: logger.info(f"Wishlist add: missing album name for '{track_name}', using track name as fallback") # Check for duplicates by track name + artist (not just Spotify ID) - # This prevents adding the same track multiple times with different IDs or edge cases - cursor.execute(""" - SELECT id, spotify_track_id, spotify_data FROM wishlist_tracks - WHERE profile_id = ? - """, (profile_id,)) + # When allow_duplicates is True (default), same song from different albums can coexist + allow_duplicates = config_manager.get('wishlist.allow_duplicate_tracks', True) + + if not allow_duplicates: + cursor.execute(""" + SELECT id, spotify_track_id, spotify_data FROM wishlist_tracks + WHERE profile_id = ? + """, (profile_id,)) - existing_tracks = cursor.fetchall() + existing_tracks = cursor.fetchall() - # Check if any existing track has matching name AND artist - for existing in existing_tracks: - try: - existing_data = json.loads(existing['spotify_data']) - existing_name = existing_data.get('name', '') - existing_artists = existing_data.get('artists', []) - if existing_artists: - existing_first = existing_artists[0] - if isinstance(existing_first, str): - existing_artist = existing_first - elif isinstance(existing_first, dict): - existing_artist = existing_first.get('name', '') + # Check if any existing track has matching name AND artist + for existing in existing_tracks: + try: + existing_data = json.loads(existing['spotify_data']) + existing_name = existing_data.get('name', '') + existing_artists = existing_data.get('artists', []) + if existing_artists: + existing_first = existing_artists[0] + if isinstance(existing_first, str): + existing_artist = existing_first + elif isinstance(existing_first, dict): + existing_artist = existing_first.get('name', '') + else: + existing_artist = '' else: existing_artist = '' - else: - existing_artist = '' - - # Case-insensitive comparison of track name and primary artist - if (existing_name.lower() == track_name.lower() and - existing_artist.lower() == artist_name.lower()): - # Enhance mode: upsert existing entry with enhance bypass context - if source_type == 'enhance': - source_json = json.dumps(source_info or {}) - cursor.execute(""" - UPDATE wishlist_tracks - SET source_type = ?, source_info = ?, failure_reason = ?, - spotify_data = ?, spotify_track_id = ? - WHERE id = ? - """, (source_type, source_json, failure_reason, - json.dumps(spotify_track_data), track_id, existing['id'])) - conn.commit() - logger.info(f"Upserted wishlist entry to enhance mode: '{track_name}' by {artist_name}") - return True - logger.info(f"Skipping duplicate wishlist entry: '{track_name}' by {artist_name} (already exists as ID: {existing['id']})") - return False # Already exists, don't add duplicate - except Exception as parse_error: - logger.warning(f"Error parsing existing wishlist track data: {parse_error}") - continue + + # Case-insensitive comparison of track name and primary artist + if (existing_name.lower() == track_name.lower() and + existing_artist.lower() == artist_name.lower()): + # Enhance mode: upsert existing entry with enhance bypass context + if source_type == 'enhance': + source_json = json.dumps(source_info or {}) + cursor.execute(""" + UPDATE wishlist_tracks + SET source_type = ?, source_info = ?, failure_reason = ?, + spotify_data = ?, spotify_track_id = ? + WHERE id = ? + """, (source_type, source_json, failure_reason, + json.dumps(spotify_track_data), track_id, existing['id'])) + conn.commit() + logger.info(f"Upserted wishlist entry to enhance mode: '{track_name}' by {artist_name}") + return True + logger.info(f"Skipping duplicate wishlist entry: '{track_name}' by {artist_name} (already exists as ID: {existing['id']})") + return False # Already exists, don't add duplicate + except Exception as parse_error: + logger.warning(f"Error parsing existing wishlist track data: {parse_error}") + continue # Convert data to JSON strings spotify_json = json.dumps(spotify_track_data) diff --git a/webui/index.html b/webui/index.html index ad3a9179..8ba8c0ae 100644 --- a/webui/index.html +++ b/webui/index.html @@ -4660,6 +4660,16 @@ Full artist list is always preserved in file metadata tags. +
+ + When enabled, the same song can be added to the wishlist from + different albums (e.g., completing a discography where albums share tracks). + When disabled, tracks with the same name and artist are skipped. +
+