Le serveur ignorait l'IPTV choisie : direct, VOD, guide, enregistrements,
season passes et Auto NBA passaient tous par la playlist la plus récente
(ou celle du premier utilisateur pour la capture FFmpeg), et les
enregistrements s'affichaient sous toutes les playlists.
- get_playlist respecte ?playlist=<id> (prioritaire) puis l'en-tête
X-Playlist-Id, avec contrôle du propriétaire (admin : toutes).
- Sessions FFmpeg HLS/VOD séparées par IPTV (même stream_id possible) ;
le m3u8 propage ?playlist= aux segments, redirections et bascule
.ts → HLS du lecteur web aussi.
- tv_recordings, season_passes, auto_rules : colonne playlist_id
(migrations v4-v6) ; lignes existantes rattachées à la playlist la
plus récente de leur propriétaire ; URL de capture avec ?playlist= ;
listes filtrées par IPTV ; une règle NBA par IPTV.
- Proxy /api/xtream : cache d'allowlist par playlist, paramètre retiré
avant l'appel au panneau ; CORS autorise X-Playlist-Id.
- Client : AuthedHttp envoie X-Playlist-Id ; XtreamService ajoute
?playlist= aux flux, à l'EPG et aux appels xtream-api (cache distinct).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The security hardening in 60d3f42 moved /api/epg, /api/recordings and
/api/season-passes behind authMiddleware, but the Flutter client still
called them through bare package:http. Those requests carry no
Authorization header, and on web BrowserClient sets withCredentials to
false so the session cookie is not sent either — every call came back
401. Symptoms: empty TV guide, empty recordings list, empty season
passes.
Adds AuthedHttp, a thin wrapper that injects the same token ApiClient
and XtreamService already use (localStorage['auth_token']), and routes
the 13 affected calls through it.
subtitle_service is left on plain http: it fetches third-party subtitle
URLs, not our API, and must not leak the session token.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>