Make the UI's Download button download the episode you clicked

POST /api/enclosures/{id}/download requeued the row and asked for a
normal scan, but a scan takes the lowest-id pending rows up to
max_new_per_check. With a large backlog and a small cap the requested
row was never a candidate, so other episodes downloaded while it stayed
pending.

A queue expresses what is outstanding, not what was asked for. Download
is now its own command that fetches one specific enclosure immediately,
ignoring queue order and the per-scan cap, still via the single worker.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RPyeapneuXrCdojsaiXGbe
This commit is contained in:
2026-09-10 01:11:12 +00:00
parent 74ec6e9281
commit 93b4815d84
4 changed files with 100 additions and 5 deletions

View File

@@ -56,6 +56,30 @@ and until now nothing set them.
---
## 2026-09-10 — Fixed: the UI's Download button downloaded the wrong episodes
Reported from the running instance: clicking Download on *Music from a Darkened Room | Session Zero*
showed progress, then left the episode at `pending`.
**Cause, and it was a design error in step 11, not a glitch.** `POST /api/enclosures/{id}/download`
requeued the row to `pending` and asked for an ordinary scan, justified at the time as "needed no
new machinery — the queue is the table". But a scan means *take the lowest-id pending rows, up to
`max_new_per_check`*. Against a 125-item backlog with a cap of 2, that is ids 7 and 8; the requested
row 11 was never a candidate. The progress on screen was two other episodes downloading past it.
The queue can express what is outstanding but not what was *asked for*. `Command::Download {
enclosure }` is now its own command: it fetches that one row immediately, ignoring both queue order
and the per-scan cap, still through the single worker so it cannot overlap a scan. Torrent
enclosures route to `torrent_one` the same way a scan would.
Verified live: enclosure 11 went `pending` -> `done`, 90.3 MB on disk, and nothing else was pulled
in its place. `cargo test` 30/30, with the new command's wire form pinned.
Worth remembering: **the daemon scans immediately on its first tick**, so every restart costs
`max_new_per_check` episodes on the normal path.
---
## 2026-09-10 — Phase 2: web front end (steps 9-13)
axum in the daemon process, plain HTML/JS in `web/index.html` (embedded with `include_str!`), no