feat: add Soulseek to the config-aware registry

This commit is contained in:
Jonathan
2026-07-10 22:53:25 +02:00
parent ec1fc568ba
commit 6b09047f64
2 changed files with 22 additions and 20 deletions
+5 -2
View File
@@ -1,18 +1,21 @@
from lyra_worker.adapters._slskd import SlskdClient
from lyra_worker.adapters._streamrip import StreamripClient from lyra_worker.adapters._streamrip import StreamripClient
from lyra_worker.adapters._ytdlp import YtDlpClient from lyra_worker.adapters._ytdlp import YtDlpClient
from lyra_worker.adapters.base import SourceAdapter from lyra_worker.adapters.base import SourceAdapter
from lyra_worker.adapters.qobuz import QobuzAdapter from lyra_worker.adapters.qobuz import QobuzAdapter
from lyra_worker.adapters.soulseek import SoulseekAdapter
from lyra_worker.adapters.youtube import YouTubeAdapter from lyra_worker.adapters.youtube import YouTubeAdapter
def build_adapters(config: dict) -> list[SourceAdapter]: def build_adapters(config: dict) -> list[SourceAdapter]:
"""Real source adapters, best-tier first, filtered to those that are configured. """Real source adapters, best-tier first, filtered to those that are configured.
An adapter is included only if its health() is true (e.g. Qobuz requires An adapter is included only if its health() is true (Qobuz needs credentials,
credentials in config). Plan 3d adds Soulseek (slskd) the same way. Soulseek needs an slskd URL + API key). YouTube needs no config.
""" """
candidates: list[SourceAdapter] = [ candidates: list[SourceAdapter] = [
QobuzAdapter(StreamripClient(config)), QobuzAdapter(StreamripClient(config)),
SoulseekAdapter(SlskdClient(config)),
YouTubeAdapter(YtDlpClient()), YouTubeAdapter(YtDlpClient()),
] ]
return [a for a in candidates if a.health()] return [a for a in candidates if a.health()]
+17 -18
View File
@@ -1,30 +1,29 @@
from lyra_worker.adapters.qobuz import QobuzAdapter from lyra_worker.adapters.qobuz import QobuzAdapter
from lyra_worker.adapters.soulseek import SoulseekAdapter
from lyra_worker.adapters.youtube import YouTubeAdapter from lyra_worker.adapters.youtube import YouTubeAdapter
from lyra_worker.registry import build_adapters from lyra_worker.registry import build_adapters
QOBUZ_CFG = {"qobuz.email": "me@example.com", "qobuz.password": "secret"} QOBUZ_CFG = {"qobuz.email": "me@example.com", "qobuz.password": "secret"}
SLSKD_CFG = {"slskd.url": "http://slskd:5030", "slskd.api_key": "key"}
def test_youtube_always_present_qobuz_skipped_without_config(): def test_only_youtube_without_any_config():
adapters = build_adapters({}) names = [a.name for a in build_adapters({})]
assert names == ["youtube"] # qobuz + soulseek both gated off
def test_soulseek_included_when_configured():
adapters = build_adapters(SLSKD_CFG)
names = [a.name for a in adapters] names = [a.name for a in adapters]
assert "youtube" in names assert "soulseek" in names and "youtube" in names
assert "qobuz" not in names # no creds -> health() false -> excluded assert "qobuz" not in names
assert isinstance(next(a for a in adapters if a.name == "soulseek"), SoulseekAdapter)
def test_qobuz_included_when_configured(): def test_all_three_configured_ordered_by_tier():
adapters = build_adapters(QOBUZ_CFG) adapters = build_adapters({**QOBUZ_CFG, **SLSKD_CFG})
names = [a.name for a in adapters]
assert "qobuz" in names
assert "youtube" in names
qz = next(a for a in adapters if a.name == "qobuz")
yt = next(a for a in adapters if a.name == "youtube")
assert isinstance(qz, QobuzAdapter)
assert isinstance(yt, YouTubeAdapter)
def test_adapter_names_unique_and_qobuz_first():
adapters = build_adapters(QOBUZ_CFG)
names = [a.name for a in adapters] names = [a.name for a in adapters]
assert names == ["qobuz", "soulseek", "youtube"] # tiers 0,1,2
assert len(names) == len(set(names)) assert len(names) == len(set(names))
assert names[0] == "qobuz" # tier 0 listed first assert isinstance(next(a for a in adapters if a.name == "qobuz"), QobuzAdapter)
assert isinstance(next(a for a in adapters if a.name == "youtube"), YouTubeAdapter)