# 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
