From d7114c1580b60cac4826b0056c0da1fe054bb5d6 Mon Sep 17 00:00:00 2001 From: 4gray Date: Tue, 21 Apr 2026 08:55:58 +0200 Subject: [PATCH] docs: add playlist backup/restore architecture documentation and update related links Entire-Checkpoint: c6e522b4276c --- docs/architecture/playlist-backup-restore.md | 171 +++++++++++++++++++ docs/architecture/stalker-portal.md | 29 ++++ 2 files changed, 200 insertions(+) create mode 100644 docs/architecture/playlist-backup-restore.md diff --git a/docs/architecture/playlist-backup-restore.md b/docs/architecture/playlist-backup-restore.md new file mode 100644 index 000000000..3a4589bf8 --- /dev/null +++ b/docs/architecture/playlist-backup-restore.md @@ -0,0 +1,171 @@ +# Playlist Backup/Restore Architecture + +This document describes the versioned playlist backup/restore flow used by the +settings screen. + +## Entry Points + +- UI: `/Users/4gray/Code/iptvnator/apps/web/src/app/settings/settings.component.ts` +- Backup service: `/Users/4gray/Code/iptvnator/libs/services/src/lib/playlist-backup.service.ts` +- Manifest types: `/Users/4gray/Code/iptvnator/libs/shared/interfaces/src/lib/playlist-backup.interface.ts` +- Xtream pending restore storage: + `/Users/4gray/Code/iptvnator/libs/services/src/lib/xtream-pending-restore.service.ts` + +## Manifest Contract + +Backups are versioned JSON manifests, not raw `Playlist[]` dumps and not SQLite +database snapshots. + +Top-level shape: + +- `kind: "iptvnator-playlist-backup"` +- `version: 1` +- `exportedAt` +- `includeSecrets` +- `settings?.epgUrls` +- `playlists[]` + +The manifest is portable across machines because it stores playlist definitions +and portable user state, while excluding cache-only database content. + +## Export Scope + +### M3U + +M3U backups are self-contained. + +- Always export canonical `rawM3u` from `PlaylistsService.getRawPlaylistById()` +- Preserve source metadata when available: + - original source kind: `url`, `file`, or `text` + - original URL + - `userAgent`, `referrer`, `origin` + - `filePathHint` for provenance only +- Export playlist-scoped user state: + - favorites by channel URL + - recently viewed M3U items + - hidden group titles + +The embedded raw text is the canonical restore artifact. The internal parsed +playlist object graph is not the backup format. + +### Xtream + +Xtream backups export only connection metadata plus portable user state. + +- Connection metadata: + - `serverUrl` + - `username` + - `password` +- User state: + - hidden categories by `{ categoryType, xtreamId }` + - favorites by `{ contentType, xtreamId, addedAt?, position? }` + - recently viewed by `{ contentType, xtreamId, viewedAt }` + - playback positions as `PlaybackPositionData[]` + +Explicitly excluded: + +- cached categories/content rows +- import-status flags and other app-state cache markers +- downloads + +### Stalker + +Stalker backups export connection metadata plus playlist-scoped favorites/recent +state. + +- Exported connection fields: + - `portalUrl` + - `macAddress` + - `isFullStalkerPortal` + - `username` + - `password` + - `userAgent` + - `referrer` + - `origin` + - serial/device/signature fields when present +- Exported user state: + - favorites snapshots + - recently viewed snapshots + +Explicitly excluded: + +- `stalkerToken` +- `stalkerAccountInfo` +- playback positions in v1 + +### App Settings + +Only EPG source URLs are backed up at the app-settings level. + +- Exported: `settings.epgUrls` +- Excluded: cached EPG database content + +## Import Flow + +The settings component hands file contents to `PlaylistBackupService`. + +The service: + +1. Validates the manifest kind/version before any writes. +2. Rejects legacy raw `Playlist[]` JSON blobs. +3. Builds stable source fingerprints for merge-vs-create decisions. +4. Upserts playlists into app playlist storage. +5. Restores provider-specific user state. + +Fingerprint rules: + +- M3U URL playlists: normalized URL +- M3U without URL: hash of canonical `rawM3u` +- Xtream: normalized `serverUrl + username` +- Stalker: normalized `portalUrl + macAddress` + +If a fingerprint matches an existing playlist: + +- keep the existing playlist ID +- update mutable metadata from the backup +- replace playlist-scoped state with the backup payload + +If no fingerprint matches: + +- create a new playlist +- reuse `exportedId` only when it is unused +- otherwise generate a new UUID + +## Xtream Restore Contract + +Xtream restore is type-aware end to end. The app no longer stores plain +`xtream_id[]` arrays for refresh/import restore because IDs can collide across +`live`, `movie`, and `series`. + +Runtime contract: + +- shared shape: `XtreamPendingRestoreState` +- persisted in local storage by playlist ID +- consumed by: + - Xtream refresh actions + - settings backup import + - Xtream content initialization + +Electron restore behavior: + +1. Category import reads pending hidden-category state while saving categories. +2. After content import, favorites/recent state is restored by typed + `{ contentType, xtreamId }` matching. +3. Playback positions are cleared and re-applied from backup state. + +For existing Xtream playlists with a fully populated offline cache, backup +import applies the restore immediately. Otherwise the typed restore payload is +left pending until the next Xtream initialization/import. + +## Current UX + +The settings page now exports/imports “playlist backups” instead of the old +raw JSON application dump. + +- Export filename: + `iptvnator-playlist-backup-YYYY-MM-DD.json` +- Import summary reports: + - imported + - merged + - skipped + - failed diff --git a/docs/architecture/stalker-portal.md b/docs/architecture/stalker-portal.md index 429a9b6b3..a8051ec6d 100644 --- a/docs/architecture/stalker-portal.md +++ b/docs/architecture/stalker-portal.md @@ -5,6 +5,7 @@ This document describes the Stalker portal implementation in IPTVnator and where ## Related Docs - [Stalker Portal EPG Architecture](./stalker-epg.md) +- [Playlist Backup/Restore Architecture](./playlist-backup-restore.md) - [Portal Detail Navigation](./portal-detail-navigation.md) - [Embedded Inline Playback](./embedded-inline-playback.md) - [Remote Control Architecture](./remote-control.md) @@ -147,6 +148,34 @@ Navigation rule to preserve: - They should not redirect into a canonical content/category/item route because Stalker detail rendering is currently store-state/inline driven, not route driven. - See [Portal Detail Navigation](./portal-detail-navigation.md). +## Backup and Restore + +Versioned playlist backups include Stalker connection metadata plus playlist- +scoped favorites/recent snapshots. + +Exported fields: + +- `portalUrl` +- `macAddress` +- `isFullStalkerPortal` +- optional `username` / `password` +- optional request headers (`userAgent`, `referrer`, `origin`) +- full-portal serial/device/signature fields when present +- favorites and recently viewed collections + +Excluded fields: + +- `stalkerToken` +- `stalkerAccountInfo` +- playback positions in backup v1 + +Import rule: + +- backups restore the saved portal definition and replace the stored + favorites/recent state for the matched playlist +- a fresh handshake must happen after import for full-portal sessions; imported + backups never trust a serialized token + ## Remote Control Integration Stalker live remote control is implemented in: