Allow duplicate tracks across albums with settings toggle

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.
pull/253/head
Broque Thomas 1 month ago
parent 2f4ff8213f
commit 0bc6abd683

@ -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)

@ -4660,6 +4660,16 @@
Full artist list is always preserved in file metadata tags.</small>
</div>
<div class="form-group">
<label class="checkbox-label">
<input type="checkbox" id="allow-duplicate-tracks" checked>
Allow duplicate tracks across albums
</label>
<small class="settings-hint">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.</small>
</div>
<div class="form-group">
<button class="test-button" onclick="resetFileOrganizationTemplates()"
style="background: #666;">

@ -5832,6 +5832,7 @@ async function loadSettingsData() {
document.getElementById('template-playlist-path').value = settings.file_organization?.templates?.playlist_path || '$playlist/$artist - $title';
document.getElementById('disc-label').value = settings.file_organization?.disc_label || 'Disc';
document.getElementById('collab-artist-mode').value = settings.file_organization?.collab_artist_mode || 'first';
document.getElementById('allow-duplicate-tracks').checked = settings.wishlist?.allow_duplicate_tracks !== false;
// Populate Playlist Sync settings
document.getElementById('create-backup').checked = settings.playlist_sync?.create_backup !== false;
@ -6860,6 +6861,9 @@ async function saveSettings(quiet = false) {
playlist_path: document.getElementById('template-playlist-path').value
}
},
wishlist: {
allow_duplicate_tracks: document.getElementById('allow-duplicate-tracks').checked
},
playlist_sync: {
create_backup: document.getElementById('create-backup').checked
},

Loading…
Cancel
Save