6.8 KiB
Validation du lot #3
Protocole
bash tests/run.sh compile le plugin, installe le paquet dans un Jellyfin jetable
et exécute la suite complète. Pour ne lancer que le parcours des sous-titres :
bash tests/run.sh --test-name-pattern='external subtitles'.
node --test tests/history.test.mjs vérifie séparément le navigateur à la frontière
HTTP simulée ; ce test ne constitue pas une validation du serveur.
Serveur : Jellyfin 12.0.0, image et digest figés dans tests/run.sh.
Lecteur témoin et compagnon : Chromium 149.0.7827.55, Playwright 1.61.1,
Jellyfin Web livré avec cette image ; compagnon de 375 × 812 pixels.
Hébergement : HTTP local, assets sans préfixe puis suite sous /jellyfin.
Les autres clients, HTTPS et reverse proxy ne sont pas qualifiés par ce lot.
Le média est généré par FFmpeg : mire originale, audio synthétique et timecode
PTS incrusté à 25 images/s. Deux fichiers SRT externes originaux sont conservés
dans tests/fixtures/, avec deux débuts identiques, des intervalles sans bloc
actif et du texte contenant des balises. Un second média n'a aucune piste.
Contrat et droits
La source est choisie par égalité avec PlayState.MediaSourceId parmi les sources
de Items/{id}/PlaybackInfo. Les valeurs Index des flux sont conservées ; ce ne
sont pas les positions dans la liste filtrée. Les libellés comportent cet indice,
la langue ou le titre, le codec et les marqueurs forcés/malentendants disponibles.
Sur le serveur testé, le convertisseur Jellyfin renvoie TrackEvents, avec
Text, StartPositionTicks et EndPositionTicks en ticks de 100 ns. Le premier
bloc du fichier de référence est vérifié à 50 000 000–60 000 000 ticks, texte
Block one. Le plugin utilise ce convertisseur existant, sans parseur propre.
L'endpoint natif Videos/{item}/{source}/Subtitles/{index}/Stream.json retourne
HTTP 200 sans authentification sur ce serveur. Son contrôleur ne vérifie pas
l'utilisateur avant conversion (source Jellyfin v12.0).
Cette limite observée justifie la route interne du plugin, conformément à l'ADR
d'hébergement et à la décision 2 de la spec.
Compagnon/Subtitles/{item}/{source}/{index} exige un utilisateur authentifié,
vérifie la visibilité du média via Jellyfin et l'appartenance de la source et
de l'indice de piste, puis appelle le convertisseur. Les tests vérifient 401
sans jeton, 403/404 pour Bob privé de l'accès à la bibliothèque et 404 pour une
source ou un indice inconnu. Les droits sont contrôlés côté serveur, pas seulement
par masquage dans l'interface. Le plugin ne corrige pas l'endpoint natif de Jellyfin.
Synchronisation
Le compagnon utilise PositionTicks tel que renvoyé par Jellyfin, sans ajouter
le temps écoulé depuis LastPlaybackCheckIn. La position reste explicitement
une estimation serveur ; l'âge de la dernière remontée du lecteur est distinct.
Les pauses conservent l'historique ; les déplacements le reconstruisent à la
nouvelle estimation. Une panne efface les données et invalide les réponses tardives.
Le test capture six images vidéo réellement décodées après douze secondes de
lecture à 1×. requestVideoFrameCallback().mediaTime identifie leur PTS ; les PNG
permettent de contrôler les chiffres incrustés. Il relève ensuite la position
affichée dans le compagnon, indépendamment de l'API des sessions. La capture et
la lecture du DOM sont séquentielles ; leur petite latence fait partie du relevé.
Les fichiers bruts se trouvent dans .test-state/issue-3/ après exécution.
Le délai de recalage est mesuré de la commande de déplacement du lecteur jusqu'à
l'affichage de la fenêtre attendue, avec le délai d'observation de Playwright inclus.
Mesure finale du 14 septembre 2026, paquet 0.2.0 :
| Timecode de l'image (s) | Position compagnon (s) | Écart absolu (ms) |
|---|---|---|
| 62,000 | 60,773 | 1 227 |
| 62,680 | 61,773 | 907 |
| 63,360 | 62,773 | 587 |
| 64,040 | 63,773 | 267 |
| 64,720 | 63,773 | 947 |
| 65,400 | 64,773 | 627 |
Lecture stable : échec du seuil strict de 500 ms (5 relevés sur 6 le dépassent). Déplacement : réussite sur cet essai, recalage en 1 286 ms, inférieur à deux secondes. La précision en lecture stable de ce couple serveur/lecteur n'est donc pas validée. Aucun lissage supplémentaire ni précision supérieure aux informations reçues n'est revendiqué.
Les mesures brutes et une image témoin à 62 secondes sont conservées. Les timecodes visibles des première et dernière captures ont été relus : 01:02.000 et 01:05.400.
Résultats fonctionnels
Exécution finale : bash tests/run.sh, code de sortie 0 ; compilation Release
avec 0 avertissement et 0 erreur, typechecking réussi, contrôle d'installation
sans préfixe réussi, puis 6 tests réussis, 0 échec sous /jellyfin (52,5 s).
L'archive et sa somme SHA-256 correspondent au DLL compilé. Le conteneur jetable
a été supprimé par le runner.
| Comportement | Frontière vérifiée |
|---|---|
| Ouverture à 35 s : cinq blocs immédiatement après chargement | Lecteur Web + plugin installé |
| Pause, déplacements avant/arrière, début exact à 20 s et juste avant | Lecteur Web + plugin installé |
| Blocs simultanés, intervalle sans bloc actif, moins de cinq blocs | Lecteur Web + plugin installé et navigateur/HTTP simulé |
| Choix indépendant, présélection disponible, indices de source | Lecteur Web + plugin installé |
| Autre média sans piste : ancien historique effacé | Lecteur Web + plugin installé |
| Refus anonyme, compte sans accès, source/indice incorrect | HTTP réel |
| Réponse retardée, changement de source, sous-titres du lecteur désactivés | Navigateur/HTTP simulé |
| Balises rendues comme texte, piste invalide, pistes graphiques signalées | Navigateur/HTTP simulé ; rendu sûr aussi testé sur SRT réel |
| Perte réseau, contrôles désactivés et ancien historique effacé | Navigateur/HTTP simulé et suivi réel |
Les vérifications simulées permettent de contrôler des réponses tardives et des états précis ; elles ne qualifient pas une seconde source réelle ni une piste graphique réelle. Ces extensions et la mesure multi-clients restent aux lots suivants.
Revue
Standards : aucune violation documentée. La duplication de navigation du lecteur dans les tests a été corrigée avec un helper partagé ; les sélecteurs de statut sont désormais explicites après l'ajout du statut des pistes.
Spec : aucun écart substantiel relevé dans le code. Les seuils de synchronisation doivent être qualifiés séparément, comme demandé par le ticket ; réussir les assertions fonctionnelles ne suffit pas à déclarer ces seuils atteints.