From 67efdedb51e6a59dd7baa7ac4c1983900776ee36 Mon Sep 17 00:00:00 2001 From: Jonathan Date: Tue, 21 Jul 2026 23:47:35 +0200 Subject: [PATCH] =?UTF-8?q?WIP:=20slskd=20download=20resume=20(idempotent?= =?UTF-8?q?=20enqueue)=20=E2=80=94=20INCOMPLETE,=20do=20not=20deploy?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the _needs_enqueue() helper (a file is (re)enqueued only if it has no transfer yet or its last one ended Errored/Cancelled/Rejected). This is step 1 of making SlskdClient._download idempotent so a retry/hard-kill resumes instead of re-downloading the whole album from scratch. REMAINING (continue here): 1. Add SlskdClient._existing_states(username) -> {filename: state} (GET /api/v0/transfers/downloads/{username}, best-effort, {} on error). 2. In _download(): before the POST, compute existing = self._existing_states(username) to_enqueue = [f for f in files if _needs_enqueue(existing.get(f["filename"], ""))] POST only to_enqueue (skip the POST entirely when empty); poll loop unchanged. Effect: a completed prior copy is retrieved immediately; a partial one resumes. 3. Tests in test_slskd_cancel.py: resume-skips-completed (no POST, immediate retrieve) + resume-only-enqueues-missing (partial). Traced: the 3 existing download tests still pass (the extra pre-loop GET consumes poll0 and degrades gracefully) — but RUN the suite to confirm. 4. Merge to main, build+push worker image, deploy — but only AFTER the in-flight Drake "Scorpion" job (soulseekboy43) has imported, so the deploy's worker recreate doesn't interrupt it. Deferred follow-up (bigger, needs candidate reconstruction): pipeline peer-stickiness (reuse the persisted chosen candidate on retry instead of re-searching) + cancel a reclaimed job's orphaned transfers at startup. That is what fully stops the cross-peer duplicate the user saw. Co-Authored-By: Claude Opus 4.8 (1M context) --- worker/lyra_worker/adapters/_slskd.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/worker/lyra_worker/adapters/_slskd.py b/worker/lyra_worker/adapters/_slskd.py index 8766db4..6e10d9e 100644 --- a/worker/lyra_worker/adapters/_slskd.py +++ b/worker/lyra_worker/adapters/_slskd.py @@ -33,6 +33,15 @@ def _max_polls(total_bytes: int) -> int: return max(_MIN_XFER_POLLS, min(budget, _MAX_XFER_POLLS)) +def _needs_enqueue(state: str) -> bool: + """Whether a file must be (re)enqueued on slskd. True if it has no transfer yet or its last + transfer ended badly (Errored/Cancelled/Rejected); a file already Completed/InProgress/Queued is + left alone so a resumed download continues it instead of restarting from zero. Pure — unit-tested.""" + if not state: + return True + return any(bad in state for bad in ("Errored", "Cancelled", "Rejected")) + + def _basename(path: str) -> str: return path.rsplit("\\", 1)[-1].rsplit("/", 1)[-1]