Files
nessar 70135ef991 feat: resume tracking with bounded local estimates (#6)
Extend the displayed position between Jellyfin observations without
counting a second twice, freeze it as explicitly uncertain when the
session observation or the player check-in goes stale, and fetch a
fresh state after a network cut, a suspended page or a restored one
before presenting the display as synchronized again.

Rebuild both tracks and the history when the media or source changed
during an interruption, keep a disappeared session selected without
switching playbacks, and discard in-flight observations computed
before a wake-up.

Validate the packaged companion end to end: network cuts, suspension,
session stop and media change on the installed server, plus a seek
measurement against the burnt-in timecode of the Web witness player
(323 ms worst stable gap, 788 ms seek recovery). Record versions,
conditions and limits in docs/validation/issue-6.md; only Jellyfin Web
is declared validated.
2026-09-18 13:37:38 +02:00

8.0 KiB
Raw Permalink Blame History

Validation du lot #6 — reprise du suivi et validation réelle

Protocole

Paquet 0.6.0, Jellyfin Server 12.0.0 (image et digest figés dans tests/run.sh), Jellyfin Web 12.0.0 livré avec cette image, Chromium 149.0.7827.55, Playwright 1.61.1. Compagnon de 375 × 812 pixels, compte ordinaire Alice, hébergement HTTP local sans préfixe puis sous /jellyfin. Les autres lecteurs, HTTPS et les reverse proxies ne sont pas qualifiés par ce lot.

bash tests/run.sh                                                     # suite complète
bash tests/run.sh --test-name-pattern='external subtitles'            # mesures
bash tests/run.sh --test-name-pattern='tracking recovery'             # reprise
node --test tests/recovery.test.mjs                                   # frontière navigateur/API

Le média Compagnon-test est généré par FFmpeg avec un timecode PTS incrusté à 25 images/s et deux pistes externes anglaises et françaises ; les copies Embedded-* portent les pistes embarquées SRT, WebVTT et ASS du lot #5.

Modèle de suivi

Le serveur avance PositionTicks par pas d'une seconde à partir de la dernière remontée du lecteur ; la remontée elle-même n'arrive qu'à la cadence du client (dix secondes pour Jellyfin Web). Le compagnon reconstruit la position rapportée lors de la remontée en soustrayant les secondes entières déjà comptées, puis ajoute une seule fois le temps écoulé : aucune seconde n'est comptée deux fois et le pas d'une seconde ne fait pas reculer l'affichage. La plus haute position reconstruite de la fenêtre récente est conservée pour absorber une seconde retardée par la charge du serveur. L'estimation locale :

  • ne reprend qu'après une observation fraîche de la session choisie (moins de 2,5 s) et une remontée récente du lecteur (moins de 30 s, sauf en pause) ;
  • se fige sinon, avec le statut explicite « Suivi incertain » ; une seconde manquante est remplacée par l'horloge locale pendant au plus 2,5 s ;
  • est invalidée à chaque retour de veille, de page ou de réseau, qui déclenche une observation immédiate et annule toute requête en vol ; l'affichage n'est présenté de nouveau comme synchronisé qu'après la réponse ;
  • est abandonnée avec l'historique et les pistes lors d'une coupure constatée (« Suivi indisponible ») ; au retour, le contexte est reconstruit.

Une session disparue est signalée sans basculer sur une autre lecture, un changement de média ou de source pendant une coupure reconstruit les deux pistes et l'historique, et un jeton refusé suit le parcours du lot #7 (retour à Quick Connect, données du compte précédent effacées, jeton non réutilisé).

Vérification de reprise

Le parcours installé tracking recovery resynchronizes both languages after a network cut, a suspension and a session stop exécute, avec le lecteur Web réel et les pistes externes anglaises et françaises :

Situation Vérification
Coupure réseau « Suivi indisponible », historique et pistes effacés, connexion conservée ; au retour, les deux langues sont de nouveau consultables
Navigateur suspendu Les observations sont retenues ; l'historique se fige, ne recule pas et n'avance plus ; au réveil, « Reprise après suspension », observation fraîche puis « Suivi actif »
Arrêt de session pendant une coupure Une autre lecture existe, mais la session suivie reste sélectionnée et signalée indisponible ; aucun basculement
Changement de média pendant une coupure Passage d'une piste embarquée WebVTT à une piste embarquée SRT : le média, la source, les pistes et l'historique sont reconstruits, sans texte de l'ancien contexte

tests/recovery.test.mjs rejoue ces états et la seconde retardée à la frontière navigateur/API, y compris le gel d'une estimation périmée et l'absence de saut en avant au réveil. Le refus d'authentification pendant le suivi reste couvert par les tests des lots #7 et #8 ; les droits ne sont ni étendus ni transférés entre comptes.

Précision contre le lecteur principal

La mesure est indépendante de l'API des sessions : le test capture des images réellement décodées par le lecteur Web (requestVideoFrameCallback().mediaTime), relève la position affichée par le compagnon au même instant et conserve les PNG permettant de relire le timecode incrusté. Les fichiers bruts sont dans issue-6-measurements.json et une image témoin à 62 s dans issue-6-frame.png.

Mesure finale du 18 septembre 2026, plugin 0.6.0 :

Timecode de l'image (s) Position compagnon (s) Écart absolu (ms)
62,000 61,737 263
62,680 62,437 243
63,360 63,037 323
64,040 63,737 303
64,720 64,437 283
65,400 65,137 263
66,080 65,837 243
66,760 66,437 323

Lecture stable : réussite du seuil strict de 500 ms, avec un écart maximal de 323 ms. Déplacement : réussite, recalage en 788 ms avant l'affichage de la fenêtre attendue, la position affichée étant alors à moins de deux secondes de la cible. L'écart résiduel vient surtout de la latence entre la position échantillonnée par le lecteur et sa remontée au serveur, de la cadence de remontée (dix secondes pour Jellyfin Web) et de la latence d'affichage ; un décalage d'horloge entre le navigateur compagnon et le serveur s'y ajoute s'il existe. Il n'est pas masqué.

Clients

Client Version Résultat
Jellyfin Web (lecteur témoin) 12.0.0, Chromium 149.0.7827.55 validé : seuils de 500 ms et de deux secondes respectés
Applications mobiles, TV et autres lecteurs Jellyfin — non testés : aucun résultat n'est revendiqué

Une réussite sur le lecteur Web ne vaut pas validation d'un autre client. Si un lecteur ne remonte pas sa position à une cadence suffisante, l'écart sera plus grand ; aucune précision supérieure aux informations reçues n'est promise.

Limites

Ce lot n'ajoute ni contrôle de lecture, ni OCR, ni traduction automatique, ni prise en charge garantie des vitesses autres que 1×. Les délais d'extraction à froid et à chaud restent ceux du lot #5 et ne sont pas des mesures de synchronisation. L'endpoint natif de sous-titres non authentifié signalé aux lots #3 et #5 reste inchangé. En lecture, une remontée vieille de plus de trente secondes fait passer l'affichage en suivi incertain ; en pause, l'absence de remontée ne le fait pas basculer. Aucune limite de ce lot n'exige de modifier un lecteur, d'ajouter un service ou de réduire les objectifs approuvés ; la précision est conditionnée à une horloge du navigateur compagnon alignée sur celle du serveur.

La suspension du navigateur est vérifiée à la frontière de ses événements de cycle de vie (visibilité, reprise, restauration de page) avec des observations retenues : Chromium sans interface ne gèle pas réellement une page. Le comportement évalué — affichage figé, observation fraîche exigée avant la reprise — est celui du navigateur réel, mais le gel du minuteur lui-même n'est pas reproduit.

Revue

Deux agents ont examiné séparément les axes standards et spec sur le diff complet. Leur constat principal — un changement de média ou de source pouvant réutiliser brièvement l'ancrage de position du passage précédent — a été corrigé et revérifié par un test dédié (changement de source pendant une coupure, avec recul de position). Ont aussi été corrigés : la version du plugin lue depuis le fichier projet au lieu d'être recopiée dans le rapport, les noms d'horloges et les unités de ticks clarifiés dans app.js, et la duplication des mocks de test regroupée dans tests/helpers.mjs et le fichier de reprise. Les points laissés en jugement sont documentés : borne de remontée à trente secondes pour rester tolérant avec les clients non mesurés, et suspension simulée par événements. Aucun constat bloquant restant.