Multi-IPTV : chaque fonction utilise la playlist sélectionnée

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>
This commit is contained in:
MichaelandClaude Opus 5.5 committed 2026-10-18 19:53:45 +02:00
1 parent 29b4e11d13
commit 4f978755fd
15 files changed
+501 -70

No files matched your search

+15 -3
View File
@@ -454,8 +454,16 @@ impl RecordingScheduler {
.unwrap_or("contains")
.to_string();
// Playlist du PROPRIÉTAIRE du pass, résolue au moment du scan.
let playlists = self.db.get_playlists(&user_id);
// IPTV du pass ; à défaut (pass ancien), la plus récente du
// propriétaire, résolue au moment du scan.
let pass_playlist_id = field("playlist_id");
let playlists: Vec<crate::models::Playlist> = self
.db
.get_playlist_by_id(&pass_playlist_id)
.filter(|p| p.user_id == user_id)
.into_iter()
.chain(self.db.get_playlists(&user_id))
.collect();
let Some(playlist) = playlists.first() else {
println!(
"[SeasonPass] Aucune playlist pour le propriétaire du pass \"{show_title}\", scan ignoré"
@@ -544,9 +552,12 @@ impl RecordingScheduler {
break;
}
self.db
let stream_url = crate::models::stream_url_for_playlist(&stream_url, &playlist.id);
let rec = self
.db
.create_recording(&user_id, &channel_id, &stream_url, &title, start, end)
.map_err(|e| e.to_string())?;
self.db.set_recording_playlist(&rec.id, &playlist.id);
created += 1;
println!(
"[SeasonPass] ✓ Planifié automatiquement: \"{title}\" le {}",
@@ -1272,6 +1283,7 @@ mod tests {
file_path: None,
error_reason: None,
source_rule_id: None,
playlist_id: None,
created_at: start,
updated_at: start,
}