- Changed API key validation from if not _key in self.constants.API_KEYS to if _key not in self.constants.API_KEYS for better readability.

Enhance RadioUtil playlist handling and deduplication

- Added checks to ensure playlists are initialized and not empty.
- Improved deduplication logic to prevent modifying the original playlist during iteration.
- Added logging for duplicate removal and playlist population.

Add cover art handling in rip_background.py

- Implemented functionality to attach album art if provided in metadata.
- Added error handling for cover art download failures.

Introduce unique filename handling in rip_background.py

- Added `ensure_unique_filename_in_dir` function to prevent overwriting files with the same name.

Refactor SRUtil for improved error handling and metadata fetching

- Introduced `MetadataFetchError` for better error management during metadata retrieval.
- Implemented `_safe_api_call` for resilient API calls with retry logic.
- Enhanced `get_artists_by_name` to optionally group results by artist name.
- Updated various methods to utilize the new error handling and retry mechanisms.
This commit is contained in:
2025-09-22 11:08:48 -04:00
parent e1194475b3
commit c2044711fb
9 changed files with 1466 additions and 354 deletions

View File

@@ -22,7 +22,7 @@ from fastapi import (
HTTPException,
Depends)
from fastapi_throttle import RateLimiter
from fastapi.responses import RedirectResponse, JSONResponse
from fastapi.responses import RedirectResponse, JSONResponse, FileResponse
class Radio(FastAPI):
"""Radio Endpoints"""
@@ -273,10 +273,9 @@ class Radio(FastAPI):
album_art: Optional[bytes] = self.radio_util.get_album_art(
track_id=track_id
)
if not album_art:
return RedirectResponse(
url="https://codey.lol/images/radio_art_default.jpg",
status_code=302,
if not album_art:
return FileResponse(
path="/var/www/codey.lol/new/public/images/radio_art_default.jpg",
)
return Response(content=album_art, media_type="image/png")
except Exception as e:

View File

@@ -80,6 +80,9 @@ class RIP(FastAPI):
tracks_in = job.meta.get("tracks_in")
tracks_out = len(job.meta.get("tracks", []))
# `utils/rip_background.py` sets per-track status to 'Success' or 'Failed'
# so check for 'success' case-insensitively and count matches.
succeeded_tracks = len([t for t in job.meta.get("tracks", []) if str(t.get("status", "")).lower() == "success"])
return {
"id": job.id,
@@ -90,7 +93,7 @@ class RIP(FastAPI):
"started_at": job.started_at,
"ended_at": job.ended_at,
"progress": progress,
"tracks": f"{tracks_out} / {tracks_in}"
"tracks": f"{succeeded_tracks} / {tracks_in}"
if isinstance(tracks_in, int)
else tracks_out,
"target": job.meta.get("target"),
@@ -101,7 +104,10 @@ class RIP(FastAPI):
self, artist: str, request: Request, user=Depends(get_current_user)
) -> Response:
"""Get artists by name"""
artists = await self.trip_util.get_artists_by_name(artist)
# support optional grouping to return one primary per display name
# with `alternatives` for disambiguation (use ?group=true)
group = bool(request.query_params.get("group", False))
artists = await self.trip_util.get_artists_by_name(artist, group=group)
if not artists:
return Response(status_code=404, content="Not found")
return JSONResponse(content=artists)
@@ -176,7 +182,7 @@ class RIP(FastAPI):
job_timeout=14400,
failure_ttl=86400,
result_ttl=-1,
retry=Retry(max=1, interval=[30]),
# retry=Retry(max=1, interval=[30]),
meta={
"progress": 0,
"status": "Queued",