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.
This commit is contained in:
parent
3c0b6c6204
commit
9769d8be19
2 changed files with 66 additions and 4 deletions
|
|
@ -887,11 +887,14 @@ class QobuzClient(DownloadSourcePlugin):
|
||||||
logger.info(f"Retrieved Qobuz playlist '{normalized['name']}' with {len(tracks)} tracks")
|
logger.info(f"Retrieved Qobuz playlist '{normalized['name']}' with {len(tracks)} tracks")
|
||||||
return normalized
|
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.
|
"""Fetch the authenticated user's favorited tracks.
|
||||||
|
|
||||||
Mirrors ``TidalClient.get_collection_tracks`` — the Sync page's
|
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():
|
if not self.is_authenticated():
|
||||||
logger.warning("Qobuz not authenticated — cannot list favorite tracks")
|
logger.warning("Qobuz not authenticated — cannot list favorite tracks")
|
||||||
|
|
@ -899,8 +902,11 @@ class QobuzClient(DownloadSourcePlugin):
|
||||||
|
|
||||||
tracks: List[Dict[str, Any]] = []
|
tracks: List[Dict[str, Any]] = []
|
||||||
offset = 0
|
offset = 0
|
||||||
while len(tracks) < limit:
|
while True:
|
||||||
page_size = min(self._PLAYLIST_PAGE_SIZE, limit - len(tracks))
|
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', {
|
data = self._api_request('favorite/getUserFavorites', {
|
||||||
'type': 'tracks',
|
'type': 'tracks',
|
||||||
'limit': page_size,
|
'limit': page_size,
|
||||||
|
|
@ -924,6 +930,8 @@ class QobuzClient(DownloadSourcePlugin):
|
||||||
offset += len(items)
|
offset += len(items)
|
||||||
if offset >= total or len(items) < page_size:
|
if offset >= total or len(items) < page_size:
|
||||||
break
|
break
|
||||||
|
if limit is not None and len(tracks) >= limit:
|
||||||
|
break
|
||||||
|
|
||||||
logger.info(f"Retrieved {len(tracks)} Qobuz favorite tracks")
|
logger.info(f"Retrieved {len(tracks)} Qobuz favorite tracks")
|
||||||
return tracks
|
return tracks
|
||||||
|
|
|
||||||
|
|
@ -331,6 +331,60 @@ def test_get_user_favorite_tracks_paginates(authed_client):
|
||||||
assert tracks[-1]['name'] == 'F129'
|
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):
|
def test_get_user_favorite_tracks_count_uses_cheap_call(authed_client):
|
||||||
captured: Dict[str, Any] = {}
|
captured: Dict[str, Any] = {}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue