mirror of
https://github.com/R0m1k3/xtremflow.git
synced 2026-10-12 09:46:21 +02:00
fix/repli-gpu-cpu
8
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
db13c85c1d |
Lecture : repli CPU quand le GPU sature, plus de spinner infini
Serveur chargé ou VRAM limite : NVENC refuse d'ouvrir l'encodeur (OpenEncodeSessionEx failed: out of memory), FFmpeg meurt en une seconde et la playlist part en 502. Chaque relance retombait sur le même GPU saturé : le flux restait illisible. - Serveur : un échec imputable au GPU (NVENC, CUDA, mémoire) relance la session une fois en libx264, sous le même identifiant — les segments suivent sans que le lecteur s'en aperçoive. Direct, VOD et enregistrements - Le GPU est écarté 2 min après une panne : les sessions suivantes partent directement sur le CPU au lieu d'échouer une à une - Source injoignable ou délai dépassé : pas de repli, il doublerait la charge d'un serveur déjà occupé - Process.start qui échoue (serveur à court de ressources) : 502/503 au lieu d'une exception remontée en 500 - Client : une erreur réseau fatale avant la première playlist appelait hls.startLoad(), qui ne relance pas ce chargement → spinner infini, aucune erreur, relances de page jamais déclenchées. L'erreur remonte désormais à la page - mpegts.js : au-delà de 3 erreurs en 60 s, bascule sur HLS au lieu de recréer sans fin (un FFmpeg neuf toutes les 1,2 s) - player_lite.html : 3 relances avant l'erreur, comme les autres pages Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
9843999809 |
Logos des chaînes : repli par nom quand l'hébergeur de picons tombe
L'hébergeur des picons du fournisseur (51.158.145.100) répond toujours 503 sur toutes ses URL. Couper le flot de requêtes ne suffisait pas : la grille restait sans logo. Pire, le pixel transparent servi en 200 était pris pour une image valide par Flutter, qui n'affichait plus son icône de repli — d'où des tuiles entièrement vides. - Nouvelle route `/api/logo?src=…&name=…` : URL du panneau d'abord (via le proxy, mêmes protections anti-SSRF et même mémoire des hôtes morts), puis recherche par nom dans le dépôt public tv-logo/tv-logos - Appariement par nom : préfixe pays et suffixes de qualité ignorés, `+` → `plus`, variante plus longue (`bein-sports-1-french`), puis marque sans numéro pour les canaux événementiels. Aucune approximation sur un mot seul - Liste de logos en cache 24 h (API GitHub : 60 requêtes/h non authentifiées), 15 min de recul après échec, images en mémoire - Image indisponible : 410 mis en cache au lieu du pixel. 410 et non 404 : la Cascade du serveur rattrape les 404 et renvoyait celui du handler statique, sans cache-control - Client (grille bureau, mobile, enregistrements) : logo toujours demandé au backend, nom compris, même sans stream_icon. L'onglet Enregistrements chargeait l'URL http brute, bloquée en HTTPS Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
b6ca9dab96 |
Logos, guide TV et fluidité : couper le flot de requêtes mortes
Logos absents : l'hébergeur des picons du fournisseur répond 503 sur tout, y compris sa racine (serveur en maintenance). Le proxy relayait ce 503 au navigateur, qui redemandait chaque logo à chaque affichage de la grille — des centaines de requêtes sortantes mortes par seconde. - Un statut >= 400 sur une image rend désormais un pixel transparent avec `Cache-Control: max-age=600`, au lieu de propager l'erreur - Après trois échecs consécutifs, l'hôte est marqué hors service cinq minutes : plus aucun appel sortant, réponse servie localement - Le corps d'erreur amont est drainé, sinon la connexion restait ouverte jusqu'au timeout Guide TV très lent (4 à 8 s par chaîne dans les logs) : `player_api` demande un appel par chaîne, et le panneau met ~4 s à répondre — une grille de trente chaînes dépassait la minute. - Le dump `xmltv.php` du panneau passe en premier : une requête couvre toutes les chaînes, réutilisée trois heures - L'interrogation chaîne par chaîne reste le repli, puis la source XMLTV externe - Une source XMLTV en échec n'est plus retéléchargée à chaque consultation : quinze minutes de recul - Index borné à 48 h en avant : un dump national couvre sept jours pour un millier de chaînes, soit des centaines de Mo pour un guide qui n'affiche que le programme courant Lecture saccadée : le relais FFmpeg -> navigateur de `/turbo.ts` ne transmettait pas la pause du client à la source. Un lecteur plus lent que le flux laissait FFmpeg produire à pleine vitesse et le serveur empilait les paquets en mémoire, d'où la dérive derrière le direct. La contre-pression est maintenant propagée (`pipeWithBackpressure`). Le serveur tourne sur un seul isolate : le flot de requêtes mortes et les attentes EPG de plusieurs secondes partageaient la boucle d'événements avec les paquets vidéo. Tests ajoutés : cache d'échecs par hôte, contre-pression du relais, priorité des sources EPG, recul après échec XMLTV, horizon d'index. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
b95d72fe80 |
Enregistrements : lecteur avancé, navigation et reprise
La lecture d'un enregistrement ne permettait ni de reculer, ni d'avancer, ni de reprendre où on s'était arrêté. Cause côté serveur : la playlist était servie en `EXT-X-PLAYLIST-TYPE:VOD`. hls.js la considère alors comme définitive et ne la relit jamais — il ne voyait donc que les quelques secondes déjà transcodées à l'ouverture, d'où une durée absurde et une barre de progression inutilisable. Elle passe en `EVENT` : la zone navigable grandit au rythme de l'encodage. FFmpeg transcodant séquentiellement, sauter à 45 min imposait malgré tout d'attendre qu'il y arrive. La playlist accepte donc `?start=<secondes>` et démarre une session dédiée (`-ss` avant `-i`). Les segments y sont préfixés par l'offset (`t2700/segment_000.ts`) : la query string ne survit pas aux URLs relatives de la playlist, deux sessions du même enregistrement se seraient mélangées. Les sessions d'offsets abandonnés sont fermées dès qu'aucun lecteur ne les consomme (killIdleSiblings). Côté lecteur, tout ce qui sort du moteur est désormais en temps absolu (`offset` réinjecté) : une recherche dans la zone déjà transcodée est un simple `currentTime`, au-delà le lecteur répond `seek_out_of_range` et l'app relance le flux à cet instant. Ajouts visibles : - reprise proposée à l'ouverture d'un enregistrement entamé, et mention « Reprendre à … » avec sa progression dans la liste ; - durée réelle mesurée par ffprobe (`duration_seconds`), mise en cache tant que le fichier ne bouge pas — `end_time - start_time` n'est que la durée programmée ; - sauts ±10 s et ±1 min, sélecteur de vitesse (0,5× à 2×), portion déjà transcodée visible sur la barre ; - raccourcis K/espace, J/L, Maj+←/→, 0-9, F, M, virgule/point. L'identifiant de vue de plateforme porte maintenant un numéro de génération : sans lui, réutiliser le même identifiant laissait l'ancienne iframe en place (`registerViewFactory` conserve la première fabrique enregistrée) — ce qui affectait déjà le retour sur une chaîne précédemment zappée. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T1A6WRnoktWcT7ZG4b2rbC |
||
|
|
f798b43a1e |
fix(enregistrements): un enregistrement mené à terme n'est plus marqué « échoué »
Chaque enregistrement arrivé au bout de sa fenêtre était marqué « Échoué / Interruption inattendue du serveur » alors que le fichier était bien sur le disque : le tick lisait la base AVANT d'arrêter les captures terminées, si bien que l'instantané annonçait encore « recording » pour un enregistrement déjà retiré de la table des processus actifs — la détection d'orphelin le requalifiait aussitôt en échec. La base est désormais lue après les arrêts, et la requalification revérifie le statut courant. Deux autres façons de perdre un enregistrement sont corrigées au passage : - FFmpeg livre encore des morceaux de stderr après la résolution de `exitCode` ; écrire sur l'`IOSink` déjà fermé levait une `StateError` depuis un callback de stream, donc une erreur asynchrone non rattrapée qui tue l'isolate — et avec lui le serveur et toutes les captures en cours. Le log passe par un écrivain tolérant et n'est fermé qu'une fois stdout et stderr drainés. - Une coupure amont terminait la capture définitivement. FFmpeg reçoit maintenant les options de reconnexion (comme le proxy live), et le planificateur relance la capture sur la fin de fenêtre quand le process sort trop tôt (backoff 3→30 s, quota remis à zéro après une capture saine). Les parties issues des relances sont recollées dans le fichier principal via le demuxer `concat`, la lecture reste donc un seul fichier. Également : - reprise des captures interrompues par un redémarrage du conteneur tant que la fenêtre est ouverte, au lieu d'un échec sec ; fichier partiel conservé (statut « terminé ») quand la fenêtre est passée - `-t` calculé sur le temps restant jusqu'à la fin programmée : un démarrage tardif ne rogne plus la fin du programme - `-hide_banner -nostats` : le log d'enregistrement redevient lisible (et ne pèse plus des mégaoctets, il est relu en entier par l'API) - `RECORDINGS_DIR` et `FFMPEG_PATH` surchargeables, et le motif d'erreur est effacé au (re)démarrage d'une capture Vérifié avec un faux ffmpeg sur un planificateur réel : avant correctif, un enregistrement mené jusqu'à la fin de fenêtre ressort « failed / Interruption inattendue du serveur » ; après, « completed » — de même que la reprise après interruption, la relance après coupure et la fusion des parties. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E5xWYTbtZwJuYB3E51K243 |
||
|
|
5b9eb74a84 |
feat(epg): source XMLTV de repli quand le panneau ne rend rien d'actuel
Le guide du fournisseur est figé sur beaucoup de revendeurs : sur le compte de test, get_simple_data_table répond correctement mais le dernier programme français date de deux jours, et le xmltv.php du panneau ne couvre aucune chaîne française. Aucun correctif côté appel Xtream ne pouvait donc remplir la grille. Nouveau service XmltvEpgService : téléchargement de dumps XMLTV publics, décompression gzip détectée sur le nombre magique, parsing en flux via XmlEventReader — un dump national fait 46 Mo décompressés, en charger l'arbre DOM coûterait plusieurs centaines de mégaoctets dans le conteneur. Mesuré à environ 1 s pour 973 chaînes. Index rafraîchi toutes les 6 h, programmes terminés depuis plus de 6 h écartés à l'indexation. L'appel sortant reste un dernier recours. L'ordre est inchangé — get_simple_data_table, puis get_short_epg — et le dump n'est consulté que si le panneau ne rend aucun programme couvrant l'instant présent : compter les entrées ne suffisait pas, un guide périmé en renvoie des centaines. La source se configure par EPG_XMLTV_URLS ; vider la variable supprime tout appel sortant. La réponse porte un en-tête X-Epg-Source pour savoir qui a répondu. Les identifiants sont rapprochés après normalisation : le dump écrit France.2.fr là où le panneau annonce France2.fr. Repli sur le nom affiché de la chaîne, et table stream_id vers identifiant EPG mise en cache 6 h. Le client passe désormais par /api/epg au lieu d'appeler player_api.php en direct. Il dupliquait le choix de l'action, la conversion des fuseaux et le décodage base64 ; le backend le fait une fois, met en cache pour tous les utilisateurs, et sait basculer sur le repli — bascule impossible depuis le navigateur. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
537312908a |
fix(epg,ui): utiliser get_simple_data_table pour l'EPG et recaler la sidebar sur le thème
EPG — les trois chemins (backend /api/epg, XtreamService.getShortEpg et getShortEPG) n'utilisaient que get_epg et get_short_epg. Beaucoup de panneaux Xtream ne connaissent pas get_epg : ils répondent 200 avec le bloc d'authentification, sans epg_listings, ce qui produisait un guide vide indiscernable d'une chaîne sans programme. get_short_epg y renvoie epg_listings vide en permanence. - backend : get_simple_data_table en premier, get_short_epg en repli - client : bascule automatique et mémorisée vers le tableau complet dès qu'un panneau est muet sur l'action légère, pour ne pas doubler les requêtes sur chaque tuile de grille - dates dérivées des *_timestamp epoch et émises en ISO-8601 UTC : les champs texte sont dans le fuseau du panneau, sans indicateur de zone, et étaient relus comme de l'heure locale — le guide était décalé et les enregistrements planifiés depuis le guide l'étaient aussi - cache backend : la clé incluait seulement le channelId, deux playlists partageant un stream_id se servaient mutuellement leur guide UI — les deux halos d'ambiance du dashboard étaient restés de l'ancien thème : primary (#FFB68C) à 40 % posé sur le coin haut-gauche, donc pile derrière la sidebar, et info (#A3B8C4, bleu-gris) à 35 % dans une palette entièrement chaude. Ramenés à des braises ember à 8 % / 6 %. - fond via AppColors.backgroundGradient - onglet actif rempli en primaryContainer (surface) et non primary (teinte claire réservée au texte/icônes) - sidebar en niveau flottant : en niveau 1 son fond #181310 se confondait avec le haut du dégradé #1A1310 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
60d3f42901 |
feat: security hardening, streaming overhaul, design polish, tests
Security: - Replace unsalted SHA-256 password hashing with bcrypt (lazy rehash on login) - Add authenticated /api/xtream-api gateway: Xtream credentials are injected server-side and never sent to the frontend; /api/playlists no longer returns passwords - Redact credentials from all logs (login body, proxy/FFmpeg/scheduler URLs) - Add auth to recordings, EPG, season-passes and streaming routes (HttpOnly session cookie for hls.js; loopback bypass for local FFmpeg) - Lock player postMessage to same-origin in both directions - Vendor and pin hls.js 1.6.7 / mpegts.js 1.7.3 (drop CDN @latest) - Fix rate limiter (client IP was never resolved), add login rate limit, restrict CORS, add CSP Report-Only, block private-IP SSRF targets, fix path traversal in recording log retrieval, chmod 777 -> 770 - Remove dead HiveService (seeded admin/admin into IndexedDB with SHA-256) - Fix authMiddleware not populating 'user' context (getPlaylist ignored the logged-in user; admin purge always returned 403) Streaming: - New FfmpegSessionManager: process registry, idle reaper (4 min live / 15 min VOD), orphan cleanup at startup, clean SIGTERM shutdown, fast-fail with stderr instead of 30 s timeout - Quality selection (source/high/medium/low) for live and VOD; source mode streams with -c:v copy (zero transcoding); selector wired into the player - Concurrent recordings (MAX_CONCURRENT_RECORDINGS, default 2); conflicts retry on the next tick instead of silently failing - Lower live latency (HLS window 20 -> 10 segments, liveSync 10 -> 3) - Fix recording log lookup (.mp4 vs .mkv mismatch) Design: - Replace hardcoded colors with AppColors tokens (12 files) - web/theme.css syncs HTML players with the Flutter palette - DPAD/keyboard navigation (arrow-key focus, player shortcuts) - Tooltips on player icon buttons, Semantics on content cards - Remove 7 dead widgets broken since the Stitch merge Quality: - bin/test/: 21 unit tests (bcrypt, redaction, traversal, SSRF, recording conflicts) plus a quality-selector widget test - GitHub Actions CI (analyze + test + build web) - Archive stale status docs into docs/archive/ Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |