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 {