feat: Add real_time_multiplier. This param speeds up the download time by X when real_time_dl is set to True

This commit is contained in:
Xoconoch
2025-08-19 21:17:57 -06:00
parent cd16cbabf3
commit 8b40f5194e
6 changed files with 144 additions and 26 deletions

View File

@@ -72,16 +72,55 @@ class Spo:
@classmethod
def __lazy(cls, results, api=None):
"""Process paginated results"""
"""Process paginated results and extend the initial page's items in-place."""
api = api or cls.__api
albums = results['items']
if not results or 'items' not in results:
return results
items_ref = results['items']
while results['next']:
while results.get('next'):
results = api.next(results)
albums.extend(results['items'])
if results and 'items' in results:
items_ref.extend(results['items'])
else:
break
return results
@classmethod
def __fetch_all_album_tracks(cls, album_id: str, api: Spotify) -> dict:
"""
Fetch all tracks for an album using album_tracks pagination.
Returns a dict shaped like Spotify's 'tracks' object with all items merged.
"""
all_items = []
limit = 50
offset = 0
first_page = None
while True:
page = api.album_tracks(album_id, limit=limit, offset=offset)
if first_page is None:
first_page = dict(page) if page is not None else None
items = page.get('items', []) if page else []
if not items:
break
all_items.extend(items)
offset += len(items)
if page.get('next') is None:
break
if first_page is None:
return {'items': [], 'total': 0, 'limit': limit, 'offset': 0}
# Build a consolidated tracks object
total_val = first_page.get('total', len(all_items))
return {
'items': all_items,
'total': total_val,
'limit': limit,
'offset': 0,
'next': None,
'previous': None
}
@classmethod
def get_track(cls, ids, client_id=None, client_secret=None):
"""
@@ -108,6 +147,7 @@ class Spo:
def get_tracks(cls, ids: list, market: str = None, client_id=None, client_secret=None):
"""
Get information for multiple tracks by a list of IDs.
Handles chunking by 50 IDs per request and merges results while preserving order.
Args:
ids (list): A list of Spotify track IDs.
@@ -116,23 +156,35 @@ class Spo:
client_secret (str, optional): Optional custom Spotify client secret.
Returns:
dict: A dictionary containing a list of track information.
dict: A dictionary containing a list of track information under key 'tracks'.
"""
if not ids:
return {'tracks': []}
api = cls.__get_api(client_id, client_secret)
all_tracks = []
chunk_size = 50
try:
tracks_json = api.tracks(ids, market=market)
for i in range(0, len(ids), chunk_size):
chunk = ids[i:i + chunk_size]
resp = api.tracks(chunk, market=market) if market else api.tracks(chunk)
# Spotify returns {'tracks': [...]} for each chunk
chunk_tracks = resp.get('tracks', []) if resp else []
all_tracks.extend(chunk_tracks)
except SpotifyException as error:
if error.http_status in cls.__error_codes:
# Create a string of the first few IDs for the error message
ids_preview = ', '.join(ids[:3]) + ('...' if len(ids) > 3 else '')
raise InvalidLink(f"one or more IDs in the list: [{ids_preview}]")
else:
raise
return tracks_json
return {'tracks': all_tracks}
@classmethod
def get_album(cls, ids, client_id=None, client_secret=None):
"""
Get album information by ID.
Get album information by ID and include all tracks (paged if needed).
Args:
ids (str): Spotify album ID
@@ -140,7 +192,7 @@ class Spo:
client_secret (str, optional): Optional custom Spotify client secret
Returns:
dict: Album information
dict: Album information with full 'tracks.items'
"""
api = cls.__get_api(client_id, client_secret)
try:
@@ -148,9 +200,22 @@ class Spo:
except SpotifyException as error:
if error.http_status in cls.__error_codes:
raise InvalidLink(ids)
else:
raise
tracks = album_json['tracks']
cls.__lazy(tracks, api)
# Replace/ensure tracks contains all items via dedicated pagination endpoint
try:
full_tracks_obj = cls.__fetch_all_album_tracks(ids, api)
if isinstance(album_json, dict):
album_json['tracks'] = full_tracks_obj
except Exception:
# Fallback to lazy-paging over embedded 'tracks' if available
try:
tracks = album_json.get('tracks') if isinstance(album_json, dict) else None
if tracks:
cls.__lazy(tracks, api)
except Exception:
pass
return album_json