Lockfiles : - pubspec.lock et bin/pubspec.lock versionnés (le .gitignore les excluait via *.lock ; le Dockerfile résolvait des versions fraîches à chaque build, comme son commentaire l'assumait) ; le Dockerfile les COPY désormais, .dockerignore ajusté CI (ci.yml) : - flutter-version 3.38.4 et Dart 3.13.2 épinglés (channel: stable seul fait dériver le SDK et peut casser la CI sans changement du dépôt) - cache pub activé, concurrency: cancel-in-progress Publication (docker-publish.yml) : - Chaîné sur la CI via workflow_run : l'image :latest n'est publiée qu'après analyze/test/build verts sur main (l'ancien push: [main] tournait en parallèle et pouvait publier une image cassée) ; build du head_sha validé par la CI docker-compose : - Défaut RECORDINGS_PATH: ./data/recordings au lieu du chemin unRAID spécifique à une machine (/mnt/user/Data/Sport) - Variable TZ (défaut Europe/Paris) Docs : - README réécrit : il décrivait une architecture disparue (Hive/ IndexedDB, hachage SHA-256, dhttpd port 8080, --web-renderer html) au lieu de l'état réel (SQLite serveur, bcrypt, binaire natif port 8089, enregistrements, transcodage, CI) - DEPLOYMENT_CHECKLIST.md archivé (6 fichiers cités inexistants, versions de dépendances fausses) - docs/archive/README.md : avertissement que ces documents sont historiques (plusieurs se déclarent « COMPLETE » à tort) Non traité ici : l'épinglage de la release FFmpeg du Dockerfile (l'accès aux releases BtbN est bloqué depuis cet environnement, impossible de vérifier un tag valide) et la suppression du code mort (reportée après le merge des PRs #7/#8/#9 pour éviter les conflits). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015oEu9QayWsw7hCKhenxgVa
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-apiet 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-datarecrée la base et affiche un nouveau mot de passe (⚠ efface utilisateurs et historique d'enregistrements) - EPG vide : vérifier
EPG_XMLTV_URLSet que la chaîne a unepg_channel_id; l'en-têteX-Epg-Sourcedes 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.