feat: add YouTubeAdapter with injectable client seam
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
from typing import Callable, Protocol
|
||||
|
||||
from lyra_worker.types import Candidate, DownloadResult, MBTarget, Quality
|
||||
|
||||
_YT_QUALITY = Quality(fmt="OPUS", lossless=False, bitrate_kbps=160)
|
||||
|
||||
|
||||
class YtClient(Protocol):
|
||||
def search_album(self, artist: str, album: str) -> list[dict]:
|
||||
"""Return album matches: dicts with keys source_ref, title, artist, track_count."""
|
||||
...
|
||||
|
||||
def download(self, source_ref: str, dest: str, on_progress: Callable[[float], None]) -> dict:
|
||||
"""Download into dest; return {track_count, path}. Raise on failure."""
|
||||
...
|
||||
|
||||
|
||||
class YouTubeAdapter:
|
||||
name = "youtube"
|
||||
tier = 2
|
||||
|
||||
def __init__(self, client: YtClient):
|
||||
self._client = client
|
||||
|
||||
def health(self) -> bool:
|
||||
return True
|
||||
|
||||
def search(self, target: MBTarget) -> list[Candidate]:
|
||||
candidates: list[Candidate] = []
|
||||
for r in self._client.search_album(target.artist, target.album):
|
||||
candidates.append(
|
||||
Candidate(
|
||||
source="youtube",
|
||||
source_ref=r["source_ref"],
|
||||
matched_artist=r.get("artist", ""),
|
||||
matched_album=r.get("title", ""),
|
||||
quality=_YT_QUALITY,
|
||||
track_count=r.get("track_count", 1) or 1,
|
||||
source_tier=self.tier,
|
||||
)
|
||||
)
|
||||
return candidates
|
||||
|
||||
def download(
|
||||
self, candidate: Candidate, dest: str, on_progress: Callable[[float], None]
|
||||
) -> DownloadResult:
|
||||
try:
|
||||
info = self._client.download(candidate.source_ref, dest, on_progress)
|
||||
except Exception as e: # network/extraction failures -> fall-through in the pipeline
|
||||
return DownloadResult(ok=False, error=str(e))
|
||||
return DownloadResult(
|
||||
ok=True,
|
||||
path=info.get("path", dest),
|
||||
track_count=info.get("track_count", candidate.track_count),
|
||||
)
|
||||
Reference in New Issue
Block a user