feat(lyrics): LRCLIB provider + cached lyrics endpoints (§6.7)
GET /tracks/{id}/lyrics (get-or-fetch, caches found/not_found with a 7-day
miss TTL) and POST /tracks/{id}/lyrics/refetch (force). Hexagonal wiring:
LyricsProvider/LyricsRepository ports, LrclibHttpClient adapter (keyless,
degrades to not_found on error), SqlAlchemyLyricsRepository (upsert on the
existing lyrics table), LyricsService, LyricsOut schema, deps.py factory.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
"""Lyrics response schema (§6.7 / Now Playing lyrics panel).
|
||||
|
||||
Returns the raw LRC (``synced``) and/or ``plain`` text; the client parses LRC
|
||||
timestamps for synced highlighting. A miss is a normal 200 with
|
||||
``status="not_found"`` and null text — not an error — so the panel can render a
|
||||
"no lyrics" state.
|
||||
"""
|
||||
|
||||
import uuid
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
from app.domain.entities.lyrics import Lyrics
|
||||
|
||||
|
||||
class LyricsOut(BaseModel):
|
||||
track_id: uuid.UUID
|
||||
status: str
|
||||
source: str | None
|
||||
synced: str | None
|
||||
plain: str | None
|
||||
synced_available: bool
|
||||
|
||||
@classmethod
|
||||
def from_entity(cls, lyrics: Lyrics) -> LyricsOut:
|
||||
return cls(
|
||||
track_id=lyrics.track_id,
|
||||
status=lyrics.status,
|
||||
source=lyrics.source,
|
||||
synced=lyrics.synced,
|
||||
plain=lyrics.plain,
|
||||
synced_available=lyrics.synced is not None,
|
||||
)
|
||||
Reference in New Issue
Block a user