Fix allow_duplicate_tracks setting not saving and wishlist dropping cross-album tracks

Two bugs: (1) 'wishlist' was missing from the settings save whitelist,
so the toggle silently reset to ON on every page reload. (2) The
wishlist cleanup function unconditionally removed tracks sharing the
same name+artist regardless of album, ignoring the allow_duplicates
setting. Now when allow_duplicates is on, the dedup key includes the
album name so same song from different albums can coexist.
pull/304/head
Broque Thomas 4 weeks ago
parent 6677807b2a
commit 41b5cd1f34

@ -6682,10 +6682,16 @@ class MusicDatabase:
return False
def remove_wishlist_duplicates(self, profile_id: int = 1) -> int:
"""Remove duplicate tracks from wishlist based on track name + artist.
"""Remove duplicate tracks from wishlist.
When allow_duplicate_tracks is True, only removes exact duplicates
(same name + artist + album). When False, removes any track with the
same name + artist regardless of album.
Keeps the oldest entry (by date_added) for each duplicate set.
Returns the number of duplicates removed."""
try:
from config.settings import config_manager
allow_duplicates = config_manager.get('wishlist.allow_duplicate_tracks', True)
with self._get_connection() as conn:
cursor = conn.cursor()
@ -6699,7 +6705,7 @@ class MusicDatabase:
all_tracks = cursor.fetchall()
# Track seen tracks and duplicates to remove
seen_tracks = {} # Key: (track_name, artist_name), Value: track_id to keep
seen_tracks = {} # Value: track row id to keep
duplicates_to_remove = []
for track in all_tracks:
@ -6714,7 +6720,13 @@ class MusicDatabase:
else:
artist_name = 'unknown'
key = (track_name, artist_name)
if allow_duplicates:
# Include album in the key so same song from different albums survives
album = track_data.get('album', {})
album_name = (album.get('name', '') if isinstance(album, dict) else str(album)).lower()
key = (track_name, artist_name, album_name)
else:
key = (track_name, artist_name)
if key in seen_tracks:
# Duplicate found - mark for removal
@ -6735,7 +6747,8 @@ class MusicDatabase:
removed_count += 1
conn.commit()
logger.info(f"Removed {removed_count} duplicate tracks from wishlist")
if removed_count > 0:
logger.info(f"Removed {removed_count} duplicate tracks from wishlist (allow_duplicates={allow_duplicates})")
return removed_count
except Exception as e:

@ -5541,7 +5541,7 @@ def handle_settings():
if 'active_media_server' in new_settings:
config_manager.set_active_media_server(new_settings['active_media_server'])
for service in ['spotify', 'plex', 'jellyfin', 'navidrome', 'soulseek', 'download_source', 'settings', 'database', 'metadata_enhancement', 'file_organization', 'playlist_sync', 'tidal', 'tidal_download', 'qobuz', 'hifi_download', 'deezer_download', 'lidarr_download', 'listenbrainz', 'acoustid', 'lastfm', 'genius', 'import', 'lossy_copy', 'listening_stats', 'ui_appearance', 'youtube', 'content_filter', 'itunes', 'm3u_export', 'musicbrainz', 'deezer', 'audiodb', 'metadata', 'hydrabase', 'security', 'discogs', 'library', 'discover']:
for service in ['spotify', 'plex', 'jellyfin', 'navidrome', 'soulseek', 'download_source', 'settings', 'database', 'metadata_enhancement', 'file_organization', 'playlist_sync', 'tidal', 'tidal_download', 'qobuz', 'hifi_download', 'deezer_download', 'lidarr_download', 'listenbrainz', 'acoustid', 'lastfm', 'genius', 'import', 'lossy_copy', 'listening_stats', 'ui_appearance', 'youtube', 'content_filter', 'itunes', 'm3u_export', 'musicbrainz', 'deezer', 'audiodb', 'metadata', 'hydrabase', 'security', 'discogs', 'library', 'discover', 'wishlist']:
if service in new_settings:
for key, value in new_settings[service].items():
config_manager.set(f'{service}.{key}', value)

@ -3605,6 +3605,8 @@ const WHATS_NEW = {
{ title: 'Fix Spotify API Leaking When Deezer/iTunes is Primary', desc: 'Spotify was being called for watchlist album scanning, similar artist discovery, repair jobs, and the Artists page search even when another source was set as primary. All data-fetching now respects the configured primary source. Spotify playlist sync is unaffected' },
{ title: 'Fix OAuth Callback Port Hardcoding', desc: 'Custom callback ports (SOULSYNC_SPOTIFY_CALLBACK_PORT / SOULSYNC_TIDAL_CALLBACK_PORT) are now respected in auth instruction pages and log messages instead of always showing 8888. Added startup diagnostics logging for callback port binding' },
{ title: 'Fix Wishlist Button on Non-Dashboard Pages', desc: 'Wishlist button click handler moved to global init so it works from any page, not just the dashboard' },
{ title: 'Fix Allow Duplicates Setting Not Saving', desc: 'The "Allow duplicate tracks across albums" toggle was never persisted — it silently reset to ON on every page reload. Now saves correctly' },
{ title: 'Fix Wishlist Dropping Cross-Album Tracks', desc: 'Wishlist cleanup was removing same-titled tracks from different albums even when Allow Duplicates was enabled. Cleanup now respects the setting — same song from different albums can coexist in the wishlist' },
// --- April 14, 2026 ---
{ date: 'April 14, 2026' },

Loading…
Cancel
Save