diff --git a/.changes/downloads-season-queue.md b/.changes/downloads-season-queue.md new file mode 100644 index 000000000..b6658cd92 --- /dev/null +++ b/.changes/downloads-season-queue.md @@ -0,0 +1,6 @@ +--- +type: feature +area: downloads +--- + +Series downloads now let you queue several episodes in a row or add every available episode from the selected season. Already queued, paused, or downloaded episodes are skipped without duplicates, and the app reports the batch result. diff --git a/CLAUDE.md b/CLAUDE.md index 120fb100e..e2a0ee099 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -915,6 +915,21 @@ engine` (restart required) or Xtream-scoped, and Stalker-scoped routes. Completed movie and grouped-series cards use the global Small/Medium/Large cover-grid tokens; missing completed files move to Needs attention instead of remaining in Ready to watch. +- Series details route individual and selected-season episode downloads through + the provider-neutral `SeasonDownloadCoordinator`. It reserves per-episode + pending identities synchronously, submits season candidates sequentially and + best-effort through the existing `DOWNLOADS_START` path, performs one final + authoritative refresh after accepted submissions, and reports added, + skipped, and failed counts. Xtream and Stalker adapters remain responsible + for provider URLs, headers, and metadata; the backend still runs one active + transfer with a FIFO queue. +- Episode ownership uses normalized `episode.id` as the canonical `xtreamId` + for both providers; Stalker playback identifiers only resolve the URL. Exact + `(playlistId, contentType, xtreamId)` matches are authoritative, while + complete playlist/series/season/episode coordinates are a fail-closed legacy + fallback that migrates reusable rows to the canonical id. Pending and active + rows plus completed available/unknown rows are skipped; failed, canceled, + completed-missing, and row-less episodes remain eligible. - Ready movie and grouped-series cards open a focused local detail. Movies play the finalized local file; series list only locally available episode rows and every episode action targets its own downloaded file. Focused routes disable diff --git a/docs/architecture/download-manager.md b/docs/architecture/download-manager.md index 42825f6fc..9fb8cf4d4 100644 --- a/docs/architecture/download-manager.md +++ b/docs/architecture/download-manager.md @@ -56,6 +56,32 @@ variants, contextual buttons, and theme-aware styling. resume the service asks the main process for an authorized folder and calls the corresponding IPC command. The `onDownloadsUpdate` broadcast triggers a new global load. +- **Series season queueing** + `SeasonDownloadCoordinator` owns synchronous, per-identity pending + reservations and submits an individual episode or selected-season snapshot + through `DownloadsService.startDownload()`. Season batches are sequential + and best-effort: one candidate failing does not stop later candidates. After + accepted submissions, one authoritative list refresh closes the pending-to- + queued handoff, and the coordinator returns `added`, `skipped`, and `failed` + counts. Xtream and Stalker adapters own provider URL, request header, and + metadata preparation; the coordinator owns only provider-neutral + orchestration. Both providers use normalized `episode.id` as the canonical + episode `xtreamId`; Stalker `originalCmd` and `originalId` participate only + in URL resolution. + The exact `(playlistId, contentType, xtreamId)` identity is authoritative. + Complete `(playlistId, seriesXtreamId, seasonNumber, episodeNumber)` + coordinates are a legacy episode-compatibility fallback; ambiguous or + conflicting matches fail closed, while restarting an eligible legacy row + migrates it to the canonical id. Renderer-pending, queued, downloading, and + paused episodes are skipped, as are completed rows whose file is available + or whose availability is still unknown. Failed, canceled, completed-missing, + and row-less episodes are eligible; a completed-missing row is restarted as + a fresh download. `DOWNLOADS_START` remains the only start IPC and may return + the stable `reason: 'already-in-progress'` for an active match, which the + coordinator counts as skipped. There is no batch IPC, schema migration, + parallel transfer, or queue reordering: destination authorization, persisted + header handling, and the backend's one-active-transfer FIFO semantics remain + unchanged. - **Pure manager model** (`download-manager.viewmodel.ts` and `download-library.viewmodel.ts`) derives the current route scope, search/category filtering, queue partitions,