From 0edd8f5c81b9d07c265b08b1da6f849812d145c6 Mon Sep 17 00:00:00 2001 From: Broque Thomas <26755000+Nezreka@users.noreply.github.com> Date: Sun, 12 Apr 2026 21:46:04 -0700 Subject: [PATCH] Raise artist discography limit from 50 to 200 with Deezer pagination Deezer and iTunes defaulted to 50 albums max, silently truncating large discographies. Deezer now paginates (100 per page) up to 200. iTunes raised to 200 (single call). All callers in web_server.py updated to use the new defaults instead of hardcoding limit=50. Also adds diagnostic logging for allow_duplicates album comparison to help debug inconsistent singles behavior. --- core/deezer_client.py | 39 +++++++++++++++++++++++++-------------- core/itunes_client.py | 2 +- core/watchlist_scanner.py | 5 ++++- web_server.py | 18 +++++++++--------- 4 files changed, 39 insertions(+), 25 deletions(-) diff --git a/core/deezer_client.py b/core/deezer_client.py index e94ebbc3..cf7a6186 100644 --- a/core/deezer_client.py +++ b/core/deezer_client.py @@ -635,26 +635,37 @@ class DeezerClient: '_raw_data': artist_data } - def get_artist_albums_list(self, artist_id: str, album_type: str = 'album,single', limit: int = 50) -> List[Album]: + def get_artist_albums_list(self, artist_id: str, album_type: str = 'album,single', limit: int = 200) -> List[Album]: """Get albums by artist ID — returns Album dataclass list (metadata source interface). - Matches iTunesClient.get_artist_albums() interface.""" - data = self._api_get(f'artist/{artist_id}/albums', {'limit': min(limit, 100)}) - if not data or 'data' not in data: - return [] - + Matches iTunesClient.get_artist_albums() interface. + Paginates through all results up to the requested limit.""" albums = [] + all_raw = [] requested_types = [t.strip() for t in album_type.split(',')] + offset = 0 + page_size = 100 # Deezer API max per request - for album_data in data['data']: - album = Album.from_deezer_album(album_data) + while offset < limit: + fetch_limit = min(page_size, limit - offset) + data = self._api_get(f'artist/{artist_id}/albums', {'limit': fetch_limit, 'index': offset}) + if not data or 'data' not in data or len(data['data']) == 0: + break - if album_type != 'album,single': - if album.album_type not in requested_types: - if not (album.album_type == 'ep' and 'single' in requested_types): - continue + for album_data in data['data']: + all_raw.append(album_data) + album = Album.from_deezer_album(album_data) - albums.append(album) + if album_type != 'album,single': + if album.album_type not in requested_types: + if not (album.album_type == 'ep' and 'single' in requested_types): + continue + + albums.append(album) + + if len(data['data']) < fetch_limit: + break # Last page + offset += len(data['data']) cache = get_metadata_cache() # Deezer's /artist/{id}/albums endpoint doesn't include artist info on each album. @@ -663,7 +674,7 @@ class DeezerClient: if albums and albums[0].artists: artist_stub = {'id': int(artist_id) if artist_id.isdigit() else 0, 'name': albums[0].artists[0]} entries = [] - for ad in data['data']: + for ad in all_raw: if ad.get('id'): if artist_stub and not ad.get('artist'): ad['artist'] = artist_stub diff --git a/core/itunes_client.py b/core/itunes_client.py index 828088f6..8cd4d774 100644 --- a/core/itunes_client.py +++ b/core/itunes_client.py @@ -991,7 +991,7 @@ class iTunesClient: return None - def get_artist_albums(self, artist_id: str, album_type: str = 'album,single', limit: int = 50) -> List[Album]: + def get_artist_albums(self, artist_id: str, album_type: str = 'album,single', limit: int = 200) -> List[Album]: """ Get albums by artist ID diff --git a/core/watchlist_scanner.py b/core/watchlist_scanner.py index dedc93f2..f21c5654 100644 --- a/core/watchlist_scanner.py +++ b/core/watchlist_scanner.py @@ -1542,10 +1542,13 @@ class WatchlistScanner: from difflib import SequenceMatcher album_sim = SequenceMatcher(None, album_name.lower(), lib_album.lower()).ratio() if album_sim < 0.85: - logger.debug(f"Track found but different album (allow_duplicates=True): '{original_title}' — library: '{lib_album}', wanted: '{album_name}'") + logger.info(f"[AllowDup] Different album — allowing: '{original_title}' (wanted: '{album_name}', library: '{lib_album}', sim: {album_sim:.2f})") continue # Different album — allow it + else: + logger.info(f"[AllowDup] Same album — skipping: '{original_title}' (wanted: '{album_name}', library: '{lib_album}', sim: {album_sim:.2f})") else: # No album info in library — can't compare, allow it + logger.info(f"[AllowDup] No album info in library — allowing: '{original_title}'") continue logger.debug(f"Track found in library: '{original_title}' by '{artist_name}' (confidence: {confidence:.2f})") return False # Track exists in library diff --git a/web_server.py b/web_server.py index ebaa5083..68f277cf 100644 --- a/web_server.py +++ b/web_server.py @@ -10458,17 +10458,17 @@ def get_artist_discography(artist_id): active_source = 'spotify' elif source_override == 'itunes': itunes_cl = _get_itunes_client() - albums = itunes_cl.get_artist_albums(artist_id, album_type='album,single', limit=50) + albums = itunes_cl.get_artist_albums(artist_id, album_type='album,single') if albums: active_source = 'itunes' elif source_override == 'deezer': deezer_cl = _get_deezer_client() - albums = deezer_cl.get_artist_albums(artist_id, album_type='album,single', limit=50) + albums = deezer_cl.get_artist_albums(artist_id, album_type='album,single') if albums: active_source = 'deezer' elif source_override == 'discogs': discogs_cl = _get_discogs_client() - albums = discogs_cl.get_artist_albums(artist_id, album_type='album,single', limit=50) + albums = discogs_cl.get_artist_albums(artist_id, album_type='album,single') if albums: active_source = 'discogs' elif source_override == 'hydrabase': @@ -10479,7 +10479,7 @@ def get_artist_discography(artist_id): hb_cl = _get_itunes_client() else: hb_cl = spotify_client - albums = hb_cl.get_artist_albums(artist_id, album_type='album,single', limit=50) + albums = hb_cl.get_artist_albums(artist_id, album_type='album,single') if albums: active_source = plugin or 'hydrabase' @@ -10506,7 +10506,7 @@ def get_artist_discography(artist_id): search_artists = cl.search_artists(artist_name, limit=5) if search_artists: best = next((a for a in search_artists if a.name.lower() == artist_name.lower()), search_artists[0]) - albums = cl.get_artist_albums(best.id, album_type='album,single', limit=50) + albums = cl.get_artist_albums(best.id, album_type='album,single') if albums: active_source = source_override @@ -10544,7 +10544,7 @@ def get_artist_discography(artist_id): try: if is_numeric_id: # It's a numeric ID (iTunes/Deezer), use directly - albums = fallback_client.get_artist_albums(artist_id, album_type='album,single', limit=50) + albums = fallback_client.get_artist_albums(artist_id, album_type='album,single') if albums: active_source = fallback_source print(f"Got {len(albums)} albums from {fallback_source} (direct ID)") @@ -10563,7 +10563,7 @@ def get_artist_discography(artist_id): best_match = fallback_artists[0] print(f"Found {fallback_source} artist: {best_match.name} (ID: {best_match.id})") - albums = fallback_client.get_artist_albums(best_match.id, album_type='album,single', limit=50) + albums = fallback_client.get_artist_albums(best_match.id, album_type='album,single') if albums: active_source = fallback_source print(f"Got {len(albums)} albums from {fallback_source} (name search)") @@ -48064,7 +48064,7 @@ def playlist_explorer_build_tree(): try: # skip_cache only supported by spotify_client — other clients don't cache this call _skip = {'skip_cache': True} if hasattr(active_client, 'sp') else {} - all_albums = active_client.get_artist_albums(artist_id, album_type='album,single', limit=50, **_skip) + all_albums = active_client.get_artist_albums(artist_id, album_type='album,single', **_skip) except Exception as e: return {'success': False, 'error': f'Album fetch failed: {e}'} @@ -49269,7 +49269,7 @@ def get_spotify_artist_discography(artist_name): print(f"Found Spotify artist: {artist.name} (ID: {spotify_artist_id}, confidence: {highest_score:.3f})") # Get all albums (albums, singles, and compilations) - all_albums = spotify_client.get_artist_albums(spotify_artist_id, album_type='album,single,compilation', limit=50) + all_albums = spotify_client.get_artist_albums(spotify_artist_id, album_type='album,single,compilation') if not all_albums: return {