WIP: slskd download resume (idempotent enqueue) — INCOMPLETE, do not deploy
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) <noreply@anthropic.com>
This commit is contained in:
@@ -33,6 +33,15 @@ def _max_polls(total_bytes: int) -> int:
|
|||||||
return max(_MIN_XFER_POLLS, min(budget, _MAX_XFER_POLLS))
|
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:
|
def _basename(path: str) -> str:
|
||||||
return path.rsplit("\\", 1)[-1].rsplit("/", 1)[-1]
|
return path.rsplit("\\", 1)[-1].rsplit("/", 1)[-1]
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user