mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-14 05:06:16 -08:00
fix(downloads): preserve offline library ownership
This commit is contained in:
1 parent
81e098d52c
commit
d35c255157
25 files changed
+846
-227
No files matched your search
@@ -115,14 +115,17 @@ variants, contextual buttons, and theme-aware styling.
|
||||
targets that row's local file. The provider's other seasons and episodes are
|
||||
deliberately absent from this view.
|
||||
- `View in portal` resolves an exact category/item route for Xtream. For
|
||||
Stalker it preserves the raw mode and item shape when a matching
|
||||
recently-viewed snapshot is available; otherwise it builds a regular VOD or
|
||||
series target from the persisted identity and title without first proving
|
||||
that the provider still serves it. The handoff opens the normal provider
|
||||
detail host in explicit `provider-only` presentation: provider content and
|
||||
playback remain available when that host resolves them, while local,
|
||||
Offline, and download actions are hidden. Regular provider navigation does
|
||||
not inherit this one-shot presentation state.
|
||||
Stalker it accepts a matching recently-viewed snapshot only when its raw
|
||||
movie/regular-series/VOD-series markers agree with the download type, so
|
||||
overlapping movie and series ids cannot select the wrong item. An exact
|
||||
numeric category stored in the download snapshot wins over the recent
|
||||
collection's virtual `vod`/`series` category. Without a matching recent
|
||||
shape, only a movie with that exact numeric category can form a
|
||||
metadata-only target; episodes and legacy movies without one leave the
|
||||
handoff unavailable. The normal provider detail opens in one-shot
|
||||
`provider-only` presentation: provider content and playback remain available
|
||||
when that host resolves them, while local, Offline, and download actions are
|
||||
hidden.
|
||||
- A completed row that is no longer locally available is never rendered as a
|
||||
ready offline detail. Direct or stale detail URLs return to the manager; if
|
||||
navigation fails, the detail shell shows the missing-file error with Back and
|
||||
@@ -132,6 +135,10 @@ variants, contextual buttons, and theme-aware styling.
|
||||
including any TMDB fields already present. The snapshot keeps provider
|
||||
identity/category separately from presentation metadata and lets the offline
|
||||
view render even when the source portal is unavailable.
|
||||
- A grouped series selects its newest valid parent snapshot, then fills only
|
||||
missing parent metadata from older valid member snapshots. Newer values,
|
||||
per-episode metadata, language, and freshness identity remain authoritative.
|
||||
Each episode row still uses its own stored episode metadata.
|
||||
- Legacy rows and sparse, stale, or wrong-language snapshots are backfilled
|
||||
when focused details open: row metadata supplies a safe local fallback,
|
||||
provider data is merged when it can be resolved, and opt-in TMDB enrichment
|
||||
@@ -162,11 +169,15 @@ variants, contextual buttons, and theme-aware styling.
|
||||
`/workspace/stalker/:id/downloads/:downloadId`. The workspace shell treats
|
||||
all three as focused content: route search is disabled and the context panel
|
||||
is `none`, so provider categories are not shown beside a local-only item.
|
||||
- The manager persists its selected All/Movies/Series/In progress filter in
|
||||
the current route query with `replaceUrl`. Opening a focused item then keeps
|
||||
that exact scoped URL as its validated return target, so Back restores the
|
||||
manager's scope, search query, filter, and browser-history position.
|
||||
- Downloads navigation is data-driven: `libs/portal/shared/util/src/lib/navigation/portal-rail-links.ts` emits a `downloads` section link (`path: [...root, 'downloads']`) for both portals, so they reuse the same download page.
|
||||
|
||||
## Queuing, persistence, and UX notes
|
||||
|
||||
- Every download row writes to the shared `downloads` table with statuses (`queued`, `downloading`, `paused`, `completed`, `failed`, `canceled`) plus metadata such as `bytesDownloaded`, `totalBytes`, `errorMessage`, `requestHeaders`, `resumeValidator`, the offline-detail metadata snapshot, and Xtream identifiers. Existing SQLite tables are rebuilt on startup when their status CHECK still lacks `paused`; additive columns are applied through the idempotent column migrations.
|
||||
- Every download row writes to the shared `downloads` table with statuses (`queued`, `downloading`, `paused`, `completed`, `failed`, `canceled`) plus metadata such as `bytesDownloaded`, `totalBytes`, `errorMessage`, `requestHeaders`, `resumeValidator`, the offline-detail metadata snapshot, and Xtream identifiers. Downloads are locally owned records rather than playlist children: deleting a source retains its rows and local files, keeps them visible in the global library, and disables provider handoff until that source exists again. Startup rebuilds older tables that still carry the playlist foreign key, while additive columns use the idempotent column migrations.
|
||||
- On startup, `download-recovery.ts` converts stale `downloading` rows with a non-empty `.part` file to `paused`, converts stale `queued` rows to `paused` while keeping any retained `.part` (a resumed download waiting behind an active one persists as `queued` with its partial), and marks stale `downloading` rows without recoverable partial bytes as `failed`.
|
||||
- Queue cancellation removes a queued task or records an active cancellation request and aborts the request when available. Pausing follows the same abort path but persists `paused` and keeps the `.part`. Retries reuse the same database entry: a failed row with a retained `filePath` resumes its `.part` through HTTP Range, otherwise the retry starts from zero. Resume appends to the existing `.part` through HTTP Range with `If-Range` validation.
|
||||
- A `.part` that cannot be deleted (locked, permission denied) never loses its database path: cancel persists `canceled` while retaining `filePath` for later cleanup, and `DOWNLOADS_REMOVE` keeps the row and answers `success: false` (surfaced as a snackbar) so retrying the remove re-attempts the deletion once the lock is released.
|
||||
|
||||
@@ -161,13 +161,18 @@ Behavior to preserve:
|
||||
Download handoff behavior:
|
||||
|
||||
- `View in portal` preserves Stalker's inline/store-state architecture. A
|
||||
matching recently-viewed snapshot is carried as `openStalkerItem` into the
|
||||
normal category host, preserving regular series, embedded VOD `series[]`, or
|
||||
lazy Ministra VOD `is_series=1` shape when that raw snapshot exists.
|
||||
- When no matching snapshot exists, a movie download falls back to a regular
|
||||
VOD item and an episode download to a regular-series item derived from the
|
||||
persisted provider identity and title. This fallback is not existence-checked
|
||||
before navigation and cannot reconstruct embedded or `is_series=1` mode.
|
||||
type-compatible recently-viewed snapshot is carried as `openStalkerItem`
|
||||
into the normal category host, preserving regular series, embedded VOD
|
||||
`series[]`, or lazy Ministra VOD `is_series=1` shape. Candidate filtering
|
||||
rejects live items and the opposite movie/series namespace even when ids
|
||||
overlap. When the download snapshot carries an exact numeric provider
|
||||
category, that category wins over the recent record's virtual `vod` or
|
||||
`series` marker while the raw mode fields stay intact.
|
||||
- When no compatible recent snapshot exists, only a movie download with an
|
||||
exact numeric provider category can form a metadata-only VOD target. A
|
||||
legacy movie without that category and every episode without a recoverable
|
||||
raw series mode leave `View in portal` unavailable instead of fabricating an
|
||||
unverified provider target.
|
||||
- The provider-only marker is scoped to the resulting selected item. Its normal
|
||||
provider host supplies the seasons, episodes, and playback it can resolve,
|
||||
while the shared VOD or series UI suppresses local Offline and download
|
||||
|
||||
@@ -245,11 +245,13 @@ counts only content and category deletion candidates; favorite,
|
||||
recently-viewed, and hidden-category user data is timed but is not added to
|
||||
that count. The matching write count uses the same deletion-candidate
|
||||
definition. Playlist-delete collection counts every collected favorite,
|
||||
recently-viewed, playback-position, download, content, and category ID. Its
|
||||
write count adds the final playlist row. Both write spans include every
|
||||
existing cooperative checkpoint, 100-row transaction, progress callback, and,
|
||||
for playlist deletion, the final playlist-row autocommit; they are not exact
|
||||
SQLite commit-time measurements.
|
||||
recently-viewed, playback-position, content, and category ID. Download rows are
|
||||
intentionally excluded: they own local offline files independently of the
|
||||
source playlist and survive source deletion, with provider handoff disabled
|
||||
while that source is absent. The write count adds the final playlist row. Both
|
||||
write spans include every existing cooperative checkpoint, 100-row
|
||||
transaction, progress callback, and, for playlist deletion, the final
|
||||
playlist-row autocommit; they are not exact SQLite commit-time measurements.
|
||||
|
||||
Successful end markers carry only row/item counts. Error or cancellation still
|
||||
closes the active phase without metadata and preserves the original error.
|
||||
|
||||
@@ -369,15 +369,17 @@ The VOD-series contract is cross-surface:
|
||||
Manager detail uses only locally available episode rows; it does not reuse
|
||||
the provider season resource as an offline availability list.
|
||||
- `View in portal` first looks for a matching recently-viewed Stalker snapshot.
|
||||
When found, it hands the raw shape to the normal category/inline detail host,
|
||||
so regular series, embedded `series[]`, and lazy `is_series=1` mode are
|
||||
preserved with identity-scoped provider-only presentation.
|
||||
- Without that matching snapshot, the episode download supplies only persisted
|
||||
identity/title metadata and falls back to a regular-series item. The fallback
|
||||
is not existence-checked before navigation and cannot reconstruct embedded or
|
||||
`is_series=1` mode. In either path, the provider host renders whatever
|
||||
seasons, episodes, and playback it can resolve while Offline/local/download
|
||||
controls stay hidden. A normal Stalker item open remains unchanged.
|
||||
Candidates must match both identity and the requested movie/series mode, so
|
||||
overlapping provider ids cannot bind an episode to a movie or vice versa.
|
||||
When found, the handoff preserves the raw regular-series, embedded
|
||||
`series[]`, or lazy `is_series=1` shape; an exact numeric category from the
|
||||
download snapshot replaces a virtual `vod`/`series` collection category.
|
||||
- Without that compatible snapshot, only a movie with an exact persisted
|
||||
numeric category can form a metadata-only VOD target. Episodes and legacy
|
||||
movies without that proof leave the provider handoff unavailable rather than
|
||||
inventing a regular-series or generic VOD target. When a target does resolve,
|
||||
the provider host renders its content in identity-scoped provider-only
|
||||
presentation while Offline/local/download controls stay hidden.
|
||||
|
||||
Core decision logic and normalization are centralized in:
|
||||
|
||||
|
||||
Reference in new issue
Block a user