Claude fa8eed38f2 Enregistrements : corriger le démarrage de la lecture
La lecture d'un enregistrement démarrait au milieu du programme et se
coupait aussitôt ; il fallait relancer une deuxième fois pour qu'elle
tienne.

Cause principale, introduite avec le passage de la playlist en `EVENT` :
tant que le transcodage n'est pas terminé, la playlist n'a pas
d'`EXT-X-ENDLIST`, et hls.js traite alors tout contenu comme du direct.
Sans `startPosition` explicite il démarre au « bord du direct » — donc
collé au front d'encodage, sans la moindre avance de segments. D'où un
départ en plein milieu, puis une coupure immédiate. Au second essai, le
transcodage était terminé (ENDLIST présent, contenu traité en VOD) et
tout se passait bien.

Hors direct : `startPosition: 0` (ou la position de reprise), rattrapage
de latence neutralisé (il accélérait la lecture puis forçait un saut en
avant vers un direct inexistant), et délai d'attente de playlist porté de
10 s à 45 s — une session FFmpeg qui démarre n'est plus prise pour un
manifeste mort.

Cause de fond : la lecture ré-encodait la vidéo. Or la capture se fait en
`-c copy`, donc le fichier contient le codec de la chaîne, presque
toujours du H.264, directement lisible en HLS. Le ré-encodage tenait à
peine le temps réel en 1080p, ce qui laissait le lecteur courir après
l'encodeur en permanence. La vidéo est désormais copiée telle quelle
quand elle est en H.264 (seul l'audio est converti en AAC) : la
segmentation va à la vitesse du disque, l'enregistrement devient
navigable en quelques secondes et les sauts ne redémarrent presque plus
jamais FFmpeg. Les codecs que le navigateur ne sait pas lire (HEVC,
MPEG-2…) restent ré-encodés.

En complément :
- le serveur attend trois segments d'avance avant de servir la playlist,
  et sert ce qu'il a plutôt qu'une erreur si le délai expire ;
- le lecteur de bureau réessaie seul jusqu'à 3 fois, comme le lecteur
  mobile le faisait déjà ;
- `XFPlayer.destroy()` retire ses écouteurs : sans ça, une relance
  laissait l'instance précédente réagir aux commandes du parent et
  republier des positions périmées ;
- les appels ffprobe passent par `MediaProbe`, qui mémorise durée et
  codec tant que le fichier ne change pas.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T1A6WRnoktWcT7ZG4b2rbC
2026-09-10 17:48:02 +00:00
2026-06-10 11:23:32 +02:00

XtremFlow — Application Web IPTV

Application IPTV auto-hébergée : frontend Flutter Web + serveur Dart natif, empaquetés dans une seule image Docker. Se connecte à un abonnement Xtream Codes et ajoute le magnétoscope (enregistrements planifiés, season passes), l'EPG avec repli XMLTV, et le transcodage FFmpeg à la demande.

Fonctionnalités

  • Live TV, Films, Séries : catalogues Xtream avec catégories, recherche, favoris, reprise de lecture
  • Guide TV (EPG) : panneau de l'abonné en priorité, repli automatique sur un dump XMLTV quand le panneau est figé
  • Enregistrements TV : planification depuis le guide, capture FFmpeg (-c copy), reprise après coupure amont ou redémarrage du serveur, fusion automatique des parties
  • Season Passes : enregistrement automatique de toutes les diffusions d'une émission (scan EPG toutes les 4 h)
  • Transcodage à la demande : source | high | medium | low (live et VOD), source = zéro transcodage ; NVENC optionnel
  • Multi-utilisateurs : comptes locaux (bcrypt), playlists par utilisateur, panneau d'administration

Stack

Couche Techno
Frontend Flutter Web (Riverpod, GoRouter), players HTML (hls.js / mpegts.js vendorisés)
Backend Dart compilé en natif (dart compile exe), shelf
Base SQLite côté serveur (/app/data/xtremflow.db)
Capture/Transcodage FFmpeg (build BtbN, NVENC inclus)
Conteneur Debian slim multi-stage, port 8089

Démarrage rapide (Docker)

docker-compose up -d --build
# Application sur http://localhost:8089

Au premier démarrage, un compte admin est créé avec un mot de passe aléatoire affiché une seule fois dans les logs (docker logs xtremflow), sauf si ADMIN_INITIAL_PASSWORD est défini. Changez-le après la première connexion.

Variables d'environnement (docker-compose.yml)

Variable Défaut Rôle
RECORDINGS_PATH ./data/recordings Dossier hôte des enregistrements
TZ Europe/Paris Fuseau du conteneur (les enregistrements sont stockés en UTC)
MAX_CONCURRENT_RECORDINGS 2 Enregistrements simultanés
EPG_XMLTV_URLS dump FR Sources XMLTV de repli (vide = aucun appel sortant)
NVIDIA_GPU false Transcodage NVENC
ADMIN_INITIAL_PASSWORD (généré) Mot de passe initial du compte admin
MIN_FREE_DISK_MB 500 Espace libre minimal pour démarrer une capture
RECORDINGS_QUOTA_GB 0 (off) Quota du dossier d'enregistrements (rotation des plus anciens terminés)
TRUSTED_PROXIES loopback + RFC1918 IPs de reverse proxy dont X-Forwarded-For est honoré

Développement local

# Frontend
flutter pub get
flutter analyze && flutter test
flutter run -d chrome        # nécessite le backend lancé pour les routes /api

# Backend
cd bin
dart pub get
dart analyze && dart test
dart run server.dart --port 8089 --path ../build/web

Le build Windows natif est documenté dans BUILD.md.

Structure du projet

bin/                    Serveur Dart
├── server.dart         Point d'entrée, routage, arrêt gracieux
├── api/                Handlers HTTP (auth, playlists, recordings, EPG, proxy…)
├── services/           Scheduler d'enregistrement, sessions FFmpeg, XMLTV
├── database/           SQLite (schéma, migrations)
├── middleware/         Auth (session), sécurité (rate limit, honeypot, logs redactés)
└── test/               Tests backend (dart test)

lib/                    Frontend Flutter
├── core/               Modèles, thème, router, clients API
├── features/           auth / admin / iptv (desktop)
└── mobile/             Variantes d'écrans mobiles

web/                    Players HTML + libs vendorisées (hls.js, mpegts.js)

Sécurité

  • Mots de passe bcrypt (migration lazy depuis les anciens hashes au login)
  • Les credentials Xtream ne quittent jamais le serveur : passerelle /api/xtream-api et proxy /api/xtream (authentifié par session) avec injection côté serveur
  • Redaction des credentials dans tous les logs ; anti-SSRF avec revalidation des redirections
  • Cookie de session HttpOnly ; rate limiting global + limite de tentatives de login par IP

CI / Publication

ci.yml (analyze + tests + build web/backend) tourne sur chaque PR et push main ; docker-publish.yml publie ghcr.io/r0m1k3/xtremflow:latest uniquement après une CI verte sur main.

Dépannage

  • Logs : docker logs xtremflow (les URLs y sont redactées)
  • Mot de passe admin perdu : supprimer le volume xtremflow-data recrée la base et affiche un nouveau mot de passe (⚠ efface utilisateurs et historique d'enregistrements)
  • EPG vide : vérifier EPG_XMLTV_URLS et que la chaîne a un epg_channel_id ; l'en-tête X-Epg-Source des réponses /api/epg/<id> indique la source utilisée
  • Enregistrement échoué : bouton « Logs » sur la ligne de l'enregistrement ; la raison (error_reason) est affichée sous le titre

Licence

Propriétaire — usage privé uniquement.

S
Description
No description provided
Readme
17 MiB
0 Stars 1 Watchers 0 Forks
Languages
Dart 79.8%
HTML 12.6%
JavaScript 4.1%
C++ 1.5%
CMake 0.8%
Other 1.1%