From 9769d8be192df278221f44b78be3db8fcc87bf1c Mon Sep 17 00:00:00 2001 From: Broque Thomas <26755000+Nezreka@users.noreply.github.com> Date: Sun, 24 May 2026 01:17:38 -0700 Subject: [PATCH] Fetch all Qobuz favorite tracks for discovery Remove the implicit 500-track cap from Qobuz Favorite Tracks so the Sync page discovers the same number of tracks shown on the playlist card. Keep an explicit limit parameter for callers that want a capped fetch. Add tests covering the default full-pagination behavior and explicit limit handling. --- core/qobuz_client.py | 16 ++++++++--- tests/test_qobuz_playlists.py | 54 +++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 4 deletions(-) diff --git a/core/qobuz_client.py b/core/qobuz_client.py index c2cf4e13..cf4fbf9b 100644 --- a/core/qobuz_client.py +++ b/core/qobuz_client.py @@ -887,11 +887,14 @@ class QobuzClient(DownloadSourcePlugin): logger.info(f"Retrieved Qobuz playlist '{normalized['name']}' with {len(tracks)} tracks") return normalized - def get_user_favorite_tracks(self, limit: int = 500) -> List[Dict[str, Any]]: + def get_user_favorite_tracks(self, limit: Optional[int] = None) -> List[Dict[str, Any]]: """Fetch the authenticated user's favorited tracks. Mirrors ``TidalClient.get_collection_tracks`` — the Sync page's - Favorite Tracks card pulls from here on click. + Favorite Tracks card pulls from here on click. By default this + fetches the full favorites collection so the card count and the + discovered track list cannot silently diverge. Pass ``limit`` for + explicit capped callers. """ if not self.is_authenticated(): logger.warning("Qobuz not authenticated — cannot list favorite tracks") @@ -899,8 +902,11 @@ class QobuzClient(DownloadSourcePlugin): tracks: List[Dict[str, Any]] = [] offset = 0 - while len(tracks) < limit: - page_size = min(self._PLAYLIST_PAGE_SIZE, limit - len(tracks)) + while True: + page_size = self._PLAYLIST_PAGE_SIZE if limit is None else min(self._PLAYLIST_PAGE_SIZE, limit - len(tracks)) + if page_size <= 0: + break + data = self._api_request('favorite/getUserFavorites', { 'type': 'tracks', 'limit': page_size, @@ -924,6 +930,8 @@ class QobuzClient(DownloadSourcePlugin): offset += len(items) if offset >= total or len(items) < page_size: break + if limit is not None and len(tracks) >= limit: + break logger.info(f"Retrieved {len(tracks)} Qobuz favorite tracks") return tracks diff --git a/tests/test_qobuz_playlists.py b/tests/test_qobuz_playlists.py index 3a3a9bea..828a9f97 100644 --- a/tests/test_qobuz_playlists.py +++ b/tests/test_qobuz_playlists.py @@ -331,6 +331,60 @@ def test_get_user_favorite_tracks_paginates(authed_client): assert tracks[-1]['name'] == 'F129' +def test_get_user_favorite_tracks_fetches_all_by_default(authed_client): + def make_items(start, count): + return [ + {'id': start + i, 'title': f'F{start + i}', 'duration': 200, + 'performer': {'name': 'Fav Artist'}, + 'album': {'title': 'Fav Album', 'image': {}}} + for i in range(count) + ] + + offsets: List[int] = [] + + def responder(endpoint, params=None): + assert endpoint == 'favorite/getUserFavorites' + offsets.append(params['offset']) + start = params['offset'] + remaining = max(0, 625 - start) + return {'tracks': {'items': make_items(start, min(params['limit'], remaining)), 'total': 625}} + + _install_api_responder(authed_client, responder) + tracks = authed_client.get_user_favorite_tracks() + + assert len(tracks) == 625 + assert offsets == [0, 100, 200, 300, 400, 500, 600] + assert tracks[-1]['name'] == 'F624' + + +def test_get_user_favorite_tracks_honors_explicit_limit(authed_client): + def make_items(start, count): + return [ + {'id': start + i, 'title': f'F{start + i}', 'duration': 200, + 'performer': {'name': 'Fav Artist'}, + 'album': {'title': 'Fav Album', 'image': {}}} + for i in range(count) + ] + + requests: List[Dict[str, Any]] = [] + + def responder(endpoint, params=None): + assert endpoint == 'favorite/getUserFavorites' + requests.append(dict(params)) + start = params['offset'] + return {'tracks': {'items': make_items(start, params['limit']), 'total': 625}} + + _install_api_responder(authed_client, responder) + tracks = authed_client.get_user_favorite_tracks(limit=150) + + assert len(tracks) == 150 + assert requests == [ + {'type': 'tracks', 'limit': 100, 'offset': 0}, + {'type': 'tracks', 'limit': 50, 'offset': 100}, + ] + assert tracks[-1]['name'] == 'F149' + + def test_get_user_favorite_tracks_count_uses_cheap_call(authed_client): captured: Dict[str, Any] = {}