mirror of
https://github.com/oskvr37/tiddl.git
synced 2026-06-13 04:05:08 +03:00
🐛 Other API exceptions are now handled (#212)
* add retry logic for `res.json()` * add wrapper for handling resources * lower api limits
This commit is contained in:
@@ -9,6 +9,7 @@ from rich.live import Live
|
||||
from typing_extensions import Annotated
|
||||
|
||||
from tiddl.core.metadata import add_track_metadata, add_video_metadata, Cover
|
||||
from tiddl.core.api import ApiError
|
||||
from tiddl.core.api.models import Album, Track, Video, AlbumItemsCredits
|
||||
from tiddl.core.utils.format import format_template
|
||||
from tiddl.core.utils.m3u import save_tracks_to_m3u
|
||||
@@ -492,7 +493,16 @@ def download_callback(
|
||||
console=ctx.obj.console,
|
||||
transient=True,
|
||||
):
|
||||
await asyncio.gather(*(handle_resource(r) for r in ctx.obj.resources))
|
||||
|
||||
async def wrapper(r: TidalResource):
|
||||
try:
|
||||
await handle_resource(r)
|
||||
except ApiError as e:
|
||||
ctx.obj.console.print(f"[red]API Error:[/] {e} at {r}")
|
||||
except Exception as e:
|
||||
ctx.obj.console.print(f"[red]Error:[/] {e} at {r}")
|
||||
|
||||
await asyncio.gather(*(wrapper(r) for r in ctx.obj.resources))
|
||||
|
||||
rich_output.show_stats()
|
||||
|
||||
|
||||
@@ -34,20 +34,20 @@ ID: TypeAlias = str | int
|
||||
class Limits:
|
||||
# TODO test every max limit
|
||||
|
||||
ARTIST_ALBUMS = 50
|
||||
ARTIST_ALBUMS_MAX = 200
|
||||
ARTIST_ALBUMS = 10
|
||||
ARTIST_ALBUMS_MAX = 100
|
||||
|
||||
ARTIST_VIDEOS = 50
|
||||
ARTIST_VIDEOS_MAX = 200
|
||||
ARTIST_VIDEOS = 10
|
||||
ARTIST_VIDEOS_MAX = 100
|
||||
|
||||
ALBUM_ITEMS = 100
|
||||
ALBUM_ITEMS = 20
|
||||
ALBUM_ITEMS_MAX = 100
|
||||
|
||||
PLAYLIST_ITEMS = 50
|
||||
PLAYLIST_ITEMS_MAX = 200
|
||||
PLAYLIST_ITEMS = 20
|
||||
PLAYLIST_ITEMS_MAX = 100
|
||||
|
||||
MIX_ITEMS = 100
|
||||
MIX_ITEMS_MAX = 200
|
||||
MIX_ITEMS = 20
|
||||
MIX_ITEMS_MAX = 100
|
||||
|
||||
|
||||
class TidalAPI:
|
||||
|
||||
@@ -4,6 +4,9 @@ from pathlib import Path
|
||||
from typing import Any, Type, TypeVar
|
||||
|
||||
from pydantic import BaseModel
|
||||
from time import sleep
|
||||
|
||||
from requests.exceptions import JSONDecodeError
|
||||
from requests_cache import (
|
||||
CachedSession,
|
||||
StrOrPath,
|
||||
@@ -15,6 +18,8 @@ from .exceptions import ApiError
|
||||
T = TypeVar("T", bound=BaseModel)
|
||||
|
||||
API_URL = "https://api.tidal.com/v1"
|
||||
MAX_RETRIES = 5
|
||||
RETRY_DELAY = 2
|
||||
|
||||
log = getLogger(__name__)
|
||||
|
||||
@@ -48,6 +53,7 @@ class TidalClient:
|
||||
endpoint: str,
|
||||
params: dict[str, Any] = {},
|
||||
expire_after: int = NEVER_EXPIRE,
|
||||
_attempt: int = 1,
|
||||
) -> T:
|
||||
"""
|
||||
Fetch data from the API endpoint
|
||||
@@ -62,7 +68,27 @@ class TidalClient:
|
||||
f"{endpoint} {params} '{'HIT' if res.from_cache else 'MISS'}' [{res.status_code}]",
|
||||
)
|
||||
|
||||
data = res.json()
|
||||
try:
|
||||
data = res.json()
|
||||
except JSONDecodeError as e:
|
||||
if _attempt >= MAX_RETRIES:
|
||||
log.error(f"JSON decode failed after {MAX_RETRIES} attempts: {e}")
|
||||
raise ApiError(
|
||||
status=res.status_code,
|
||||
subStatus="0",
|
||||
userMessage="Response body does not contain valid json.",
|
||||
)
|
||||
|
||||
log.warning(f"JSON decode error, retrying {_attempt}/{MAX_RETRIES}")
|
||||
sleep(RETRY_DELAY)
|
||||
|
||||
return self.fetch(
|
||||
model=model,
|
||||
endpoint=endpoint,
|
||||
params=params,
|
||||
expire_after=expire_after,
|
||||
_attempt=_attempt + 1,
|
||||
)
|
||||
|
||||
if self.debug_path:
|
||||
file = self.debug_path / f"{endpoint}.json"
|
||||
|
||||
Reference in New Issue
Block a user