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:
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user