feat(transcode): optimize + on-the-fly quality + HLS streaming (§6.6)
POST /tracks/{id}/optimize enqueues a transcode; GET /stream/{id}?quality=
serves a cached Opus rendition (miss → master + background warm); GET
/stream/{id}/hls/{playlist.m3u8,segment} serves the worker-generated HLS
rendition (AAC-in-TS), with ?token= propagated onto segment URLs for players
that can't set headers. Hexagonal: Transcoder port, FfmpegTranscoder adapter,
TranscodeService + cache-path helpers, transcode_track worker (idempotent),
schemas + deps wiring. ffmpeg already in the image.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -76,6 +76,13 @@ class StorageError(DomainError):
|
||||
code = "storage_error"
|
||||
|
||||
|
||||
class TranscodeError(DomainError):
|
||||
"""Transcoding (ffmpeg) failed. Raised in the worker; a play falls back to
|
||||
the original file rather than surfacing this."""
|
||||
|
||||
code = "transcode_error"
|
||||
|
||||
|
||||
class RangeNotSatisfiableError(DomainError):
|
||||
"""Requested byte range cannot be satisfied."""
|
||||
|
||||
|
||||
@@ -497,6 +497,16 @@ class CoverArtProvider(Protocol):
|
||||
async def fetch_release_group(self, release_group_mbid: str) -> CoverArt | None: ...
|
||||
|
||||
|
||||
class Transcoder(Protocol):
|
||||
"""Transcodes an audio file with ffmpeg (plan §6.6 / Group B). ``to_opus``
|
||||
writes a single Opus rendition; ``to_hls`` writes an HLS playlist + segments
|
||||
(AAC-in-TS) into ``out_dir``. Both raise ``TranscodeError`` on failure; heavy
|
||||
work always runs in a worker, never the request cycle."""
|
||||
|
||||
async def to_opus(self, src: Path, dest: Path, *, bitrate_kbps: int) -> None: ...
|
||||
async def to_hls(self, src: Path, out_dir: Path, *, bitrate_kbps: int) -> None: ...
|
||||
|
||||
|
||||
class LyricsProvider(Protocol):
|
||||
"""Fetches lyrics from an external database (LRCLIB) by artist/title/album/
|
||||
duration. Returns a hit or ``None`` (no match / service down), never raising."""
|
||||
|
||||
Reference in New Issue
Block a user