feat: add SoulseekAdapter with injectable client seam

This commit is contained in:
Jonathan
2026-07-10 22:40:53 +02:00
parent 9804f3bb90
commit b5931a7b66
2 changed files with 130 additions and 0 deletions
+62
View File
@@ -0,0 +1,62 @@
from typing import Callable, Protocol
from lyra_worker.types import Candidate, DownloadResult, MBTarget, Quality
class SoulseekClient(Protocol):
def is_configured(self) -> bool:
...
def search_album(self, artist: str, album: str) -> list[dict]:
"""Album matches: dicts with source_ref, title, artist, track_count, format, bitrate."""
...
def download(self, source_ref: str, dest: str, on_progress: Callable[[float], None]) -> dict:
"""Download into dest; return {track_count, path}. Raise on failure."""
...
def _quality(fmt: str, bitrate: int | None) -> Quality:
if fmt.upper() == "FLAC":
return Quality(fmt="FLAC", lossless=True, bit_depth=16, sample_rate=44100)
return Quality(fmt="MP3", lossless=False, bitrate_kbps=bitrate or 320)
class SoulseekAdapter:
name = "soulseek"
tier = 1
def __init__(self, client: SoulseekClient):
self._client = client
def health(self) -> bool:
return self._client.is_configured()
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="soulseek",
source_ref=r["source_ref"],
matched_artist=r.get("artist", ""),
matched_album=r.get("title", ""),
quality=_quality(r.get("format", "MP3"), r.get("bitrate")),
track_count=r.get("track_count", 0) or 0,
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:
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),
)