fix(worker): retrieve Soulseek downloads from slskd's dir into staging
SlskdClient enqueued the transfer and polled to Completed but never moved any
files into `dest` — slskd saves them to its OWN downloads dir, so staging stayed
empty and every Soulseek job failed "incomplete download". Qobuz/YouTube write
straight into staging and were unaffected.
- SlskdClient._retrieve() moves the finished files out of slskd's downloads dir
into staging after completion, matching tolerantly by basename and preferring
a parent-folder + size match (slskd's on-disk layout varies by version).
- download() returns the moved count as track_count so the pipeline's
completeness check reflects what actually landed; raises loudly if nothing was
found (misconfigured/unmounted downloads dir).
- Downloads dir resolves slskd.downloadsDir Config > SLSKD_DOWNLOADS_ROOT env >
/slskd-downloads default; docker-compose mounts ${SLSKD_DOWNLOADS_DIR} there.
- .env.example documents SLSKD_DOWNLOADS_DIR; retrieval unit-tested offline.
slskd runs as an external daemon, so the shared dir must be a host path both
slskd and the worker mount — set once Lyra runs on the slskd host. Not yet
verified live end-to-end (no slskd access from this VM).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
"""Offline tests for SlskdClient._retrieve — the step that moves slskd's finished
|
||||
downloads out of its own dir into the pipeline's staging dir. The search/download REST
|
||||
flow stays live-only (test_soulseek_live.py); retrieval is pure filesystem, so it is
|
||||
unit-testable here."""
|
||||
import os
|
||||
|
||||
from lyra_worker.adapters._slskd import SlskdClient
|
||||
|
||||
|
||||
def _client(downloads_dir: str) -> SlskdClient:
|
||||
return SlskdClient({"slskd.url": "http://x", "slskd.api_key": "k",
|
||||
"slskd.downloadsDir": downloads_dir})
|
||||
|
||||
|
||||
def _write(path: str, content: bytes = b"audio") -> None:
|
||||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||
with open(path, "wb") as fh:
|
||||
fh.write(content)
|
||||
|
||||
|
||||
def test_retrieve_moves_files_from_slskd_dir(tmp_path):
|
||||
downloads = tmp_path / "slskd"
|
||||
dest = tmp_path / "staging"
|
||||
# slskd's typical layout: <downloads>/<remote parent folder>/<file>
|
||||
_write(str(downloads / "Artist - Album" / "01 One.flac"))
|
||||
_write(str(downloads / "Artist - Album" / "02 Two.flac"))
|
||||
files = [
|
||||
{"filename": r"@@abc\music\Artist - Album\01 One.flac", "size": 5},
|
||||
{"filename": r"@@abc\music\Artist - Album\02 Two.flac", "size": 5},
|
||||
]
|
||||
|
||||
moved = _client(str(downloads))._retrieve(files, str(dest))
|
||||
|
||||
assert moved == 2
|
||||
assert sorted(os.listdir(dest)) == ["01 One.flac", "02 Two.flac"]
|
||||
# the source files were moved, not copied
|
||||
assert os.listdir(downloads / "Artist - Album") == []
|
||||
|
||||
|
||||
def test_retrieve_disambiguates_by_parent_and_size(tmp_path):
|
||||
downloads = tmp_path / "slskd"
|
||||
dest = tmp_path / "staging"
|
||||
# a decoy with the same basename in a different folder + wrong size
|
||||
_write(str(downloads / "Other Album" / "01 One.flac"), b"wrongsize!!")
|
||||
_write(str(downloads / "Artist - Album" / "01 One.flac"), b"right")
|
||||
files = [{"filename": r"x\Artist - Album\01 One.flac", "size": len(b"right")}]
|
||||
|
||||
moved = _client(str(downloads))._retrieve(files, str(dest))
|
||||
|
||||
assert moved == 1
|
||||
with open(dest / "01 One.flac", "rb") as fh:
|
||||
assert fh.read() == b"right" # picked the parent+size match, not the decoy
|
||||
|
||||
|
||||
def test_retrieve_returns_zero_when_nothing_matches(tmp_path):
|
||||
downloads = tmp_path / "slskd"
|
||||
downloads.mkdir()
|
||||
dest = tmp_path / "staging"
|
||||
files = [{"filename": r"x\Album\missing.flac", "size": 1}]
|
||||
|
||||
assert _client(str(downloads))._retrieve(files, str(dest)) == 0
|
||||
|
||||
|
||||
def test_retrieve_raises_when_downloads_dir_absent(tmp_path):
|
||||
dest = tmp_path / "staging"
|
||||
client = _client(str(tmp_path / "does-not-exist"))
|
||||
files = [{"filename": r"x\Album\a.flac", "size": 1}]
|
||||
try:
|
||||
client._retrieve(files, str(dest))
|
||||
except RuntimeError as e:
|
||||
assert "downloads dir not found" in str(e)
|
||||
else:
|
||||
raise AssertionError("expected RuntimeError for a missing downloads dir")
|
||||
Reference in New Issue
Block a user