feat(m4): persist the download queue across restarts

The queue/scheduler lived only in renderer memory, so saved/scheduled and other
pending downloads were silently lost on quit (audit M4). Persist them:

- main: queue.ts store (proven cached-atomic createJsonStore, queue.json) +
  queue:list / queue:save IPC; QUEUE_MAX cap.
- shared: PersistedQueueItem = the durable subset of DownloadItem (runtime-only
  progress/speed/eta/sizeLabel/finishing dropped).
- renderer: mirror the persistable items on every queue change (a signature over
  the subset means progress ticks don't re-save; main's jsonStore coalesces the
  writes) and rehydrate on launch via an App useEffect. Pure mappers extracted
  to store/queuePersist.ts and unit-tested.

Restore semantics: downloading -> queued (yt-dlp continues the .part), saved +
scheduledFor survives so the promoter fires when due, paused/error return
actionable; completed/canceled are never persisted. A queueHydrated gate stops a
launch-time store change from wiping queue.json before it's read.

Reconciles ROADMAP.md. typecheck + 268 tests + eslint + prettier green.
(OS-level quit/relaunch cycle is worth a manual smoke test.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-01 09:34:55 -04:00
parent 63687aaec3
commit 0a681f864f
13 changed files with 320 additions and 10 deletions
+4 -2
View File
@@ -342,8 +342,10 @@ Today `src/renderer/src/store/downloads.ts` has cancel + retry only.
`datetime-local` input in `DownloadBar.tsx` (future time parks the new download as
scheduled), plus per-row **Save for later** / **Add to queue** actions in `QueueItem.tsx`;
saved items survive "Clear finished". Distinct from the Phase J `--sync` schedule (which is
for *sources*). **Limitation: the queue isn't persisted, so a schedule only fires while
AeroFetch is running** — noted in the UI hint and the code.*
for *sources*). The queue is now persisted across restarts (audit M4 — `queue.json` via
`src/main/queue.ts`), so a parked/scheduled item survives a quit and its schedule fires on the
next launch once due. (The app must still be running at the scheduled moment for an immediate
fire; a schedule that came due while closed fires at the next launch.)*
## Phase N — Power-user surface ✅ COMPLETE