chore: Configure Renovate See merge request nessar/subtitle-compagnion!1
Compagnon de sous-titres
Plugin Jellyfin hébergeant un compagnon sur second écran. Sélectionnez votre lecture puis une piste principale texte externe ou embarquée connue de Jellyfin : l'historique donne accès à tous les blocs commencés à la position estimée. La piste du lecteur est présélectionnée lorsqu'elle est exploitable. Le choix du compagnon reste indépendant, même si les sous-titres du lecteur sont désactivés. Choisissez aussi une piste d'aide : tous ses blocs partageant une durée positive avec chaque bloc principal sont affichés au-dessus, sans action de révélation. Les frontières adjacentes ne comptent pas ; une absence d'aide est signalée. La langue de la dernière piste d'aide choisie manuellement est conservée dans ce navigateur, sans être synchronisée avec le compte Jellyfin, et réessayée pour chaque nouveau média ou source ; sans choix enregistré, le français est essayé. À langue égale, une piste texte complète ordinaire est préférée, puis une piste pour malentendants, puis une piste de sous-titres forcés. Si la langue préférée est absente, l'historique principal reste consultable, l'absence est annoncée et le choix manuel reste possible ; choisir l'option vide désactive l'aide pour le média courant sans effacer la langue préférée. Si la piste choisie est inexploitable, l'historique principal reste consultable. Aucune traduction automatique des sous-titres n'est effectuée.
Sur une piste principale en anglais, toucher un mot ouvre une fiche avec des sens français du Wiktionnaire. La recherche envoie uniquement ce mot au Wiktionnaire ; pour un pluriel comme « appliances », elle consulte aussi le singulier. Elle ne transmet ni la phrase ni le jeton Jellyfin. Les sens sont présentés hors contexte et la fiche contient un lien vers l'entrée complète. Pour une expression, touchez son premier mot, choisissez « Choisir une expression » dans la fiche, puis touchez son dernier mot dans le même sous-titre. Le Wiktionnaire est consulté pour ce groupe de mots ; s'il n'a pas d'entrée, MyMemory propose une traduction signalée comme telle. Seule l'expression sélectionnée est envoyée à ces services, sans la réplique entière ni le jeton Jellyfin. Ces recherches nécessitent une connexion au Wiktionnaire et, en cas de repli, à MyMemory.
La coquille garde l'état du suivi visible en permanence, même page défilée : une pastille compacte (icône, libellé, couleur) pour le suivi synchronisé, une bannière bordée plus visible lorsque le suivi est incertain, et un libellé à icône de pause pour l'affichage suspendu. Un tap sur la zone de lecture suspend l'affichage et demande la pause de la session suivie à Jellyfin si le lecteur accepte le contrôle à distance. L'historique affiché est figé pendant que le suivi continue en arrière-plan. Le bouton « Reprendre » réaffiche immédiatement l'historique à la position suivie et demande la reprise du lecteur seulement si le compagnon l'avait mis en pause. Faire défiler l'historique suspend seulement l'affichage ; une fois suspendu, seul le bouton de reprise revient au direct. Un refus ou une absence de contrôle à distance est signalé. Le choix de la session, les détails de lecture et les deux pistes sont repliés derrière le déclencheur « Réglages », refermable en un geste, sans masquer le statut. Un bouton « Plein écran » demande le plein écran du navigateur, comme un lecteur vidéo, autour d'une surcouche à fond noir pur pour OLED. Si le navigateur refuse la demande, la surcouche reste active et l'indique dans sa barre minimale. La surcouche ne montre que la lecture ; un tap y suspend l'affichage et révèle une barre minimale (sortie, reprise, statut), masquée à la reprise, et l'indicateur d'incertitude s'y affiche de lui-même. Quitter le plein écran ne reprend pas l'affichage, qui reste repris manuellement ; Échap sort au clavier. Un écran maintenu éveillé est demandé au navigateur quand il le permet, sans échec visible sinon, et le choix plein écran n'est pas conservé entre les visites. Avant le visionnage, un écran de connexion dédié présente Quick Connect : titre, étapes d'approbation, code mis en avant dans son propre encadré, et un état explicite et annoncé — préparation, attente d'approbation, refus ou déconnexion. Un refus (code expiré, Quick Connect désactivé, serveur injoignable) s'affiche en alerte bordée marquée d'une croix, avec l'action nommée correspondante (« Réessayer », « Générer un nouveau code ») qui reçoit le foyer ; un jeton refusé pendant le suivi ramène à l'écran avec le motif en alerte et un nouveau code préparé puis attendu. La barre de statut reste réservée au suivi pendant le visionnage. Les pistes embarquées utilisent l'extraction et le cache de Jellyfin, sans copie externe à préparer ni second plugin. SRT, WebVTT et ASS sont couverts par les tests ; les effets et la mise en scène ASS ne sont pas reproduits. Entre deux remontées du serveur, le compagnon prolonge localement la position estimée, sans la dépasser : l'affichage se fige et devient « Suivi incertain » dès qu'une observation manque, et un état actualisé de la session choisie est récupéré avant toute reprise présentée comme synchronisée après une coupure réseau ou une suspension du navigateur. Un changement de média ou de source pendant une coupure reconstruit les deux pistes et l'historique pour le nouveau contexte ; une session disparue est signalée sans basculer sur une autre lecture.
Installation depuis le dépôt Jellyfin
Dans le tableau de bord Jellyfin, ouvrir Plugins → Dépôts → +, puis ajouter
https://gitlab.nessar.fr/nessar/subtitle-compagnion/-/raw/main/manifest.json.
Le plugin Compagnon de sous-titres apparaît alors dans le catalogue :
l'installer, redémarrer Jellyfin, puis ouvrir /Compagnon/ dans le second
navigateur.
Installation manuelle (sans compilation)
Version publiée : 1.0.8. Version compatible : Jellyfin Server 12.1.0, Linux x64. Les autres versions ne sont pas déclarées compatibles.
- Télécharger le paquet 1.0.8 et sa somme SHA-256.
- Arrêter Jellyfin. Extraire le dossier
Compagnonde l'archive dans le dossierpluginsde ses données. Avec l'image Docker officielle et son volume/config, le résultat est/config/plugins/Compagnon/Compagnon.dll. Sur une autre installation, retrouver le chemin des données du serveur dans son tableau de bord. En mise à jour, remplacer l'ancien dossierCompagnon. - Redémarrer Jellyfin et vérifier que le plugin est actif dans le tableau de bord.
- Ouvrir
https://votre-serveur/Compagnon/dans le second navigateur. Si Jellyfin utilise le préfixe/jellyfin, ouvrirhttps://votre-serveur/jellyfin/Compagnon/. - Le compagnon affiche un code Quick Connect : ouvrez un client Jellyfin déjà connecté (Réglages → Quick Connect), saisissez-y ce code et approuvez-le. Le compagnon se connecte alors avec le compte approbateur, sans mot de passe. Une lecture unique est suivie automatiquement ; plusieurs lectures demandent un choix. Une session choisie reste sélectionnée après son arrêt, jusqu'à un nouveau choix explicite.
Le plugin ne modifie pas Jellyfin Web et ne nécessite aucun service supplémentaire. L'accès distant utilise l'hébergement HTTPS habituel de Jellyfin. Quick Connect doit être activé sur le serveur (Tableau de bord → Général) ; sinon le compagnon affiche comment l'activer. La connexion propre au compagnon est conservée dans ce navigateur, y compris après sa fermeture ; un jeton refusé par Jellyfin ramène au code Quick Connect, y compris pendant le suivi. Une panne réseau ou une erreur serveur temporaire, elle, conserve la connexion et le suivi reprend au retour du serveur, sans nouvelle approbation. Le bouton « Se déconnecter » arrête le suivi, efface cette connexion, réinitialise l'affichage de la lecture, des pistes et de l'historique, et demande aussi sa révocation au serveur, sans déconnecter les autres clients Jellyfin ; si le serveur ne confirme pas la révocation, le compagnon le signale.
Limites et permissions
La position affichée est l'estimation de Jellyfin, prolongée localement d'au plus quelques secondes entre deux remontées. Elle ne garantit pas le temps exact visible sur le lecteur : la cadence de remontée du lecteur, la latence réseau et un décalage d'horloge entre le navigateur et le serveur se reportent sur elle. Une observation de session absente depuis plus de 2,5 s, ou une remontée du lecteur vieille de plus de 30 s hors pause, fige l'affichage en « Suivi incertain » au lieu de continuer à avancer. La page interroge les sessions toutes les secondes après réception de la réponse ; une panne de connexion efface les détails et l'historique, puis le suivi reprend avec un état actualisé. Aucun flux vidéo n'est demandé par le compagnon. Seules les actions explicites de suspension et de reprise peuvent envoyer une commande de lecture à la session suivie.
L'historique est reconstruit à l'ouverture en cours de film et après les
déplacements. Une pause le conserve. Un changement de session, média, source
ou piste invalide les réponses précédentes. Les pistes graphiques sont signalées
sans OCR ; le chargement, l'absence de piste et les erreurs sont affichés séparément.
Le premier chargement peut attendre une extraction : la requête dispose de deux
minutes, sans ralentir le suivi des sessions. En cas d'échec, l'historique est
effacé ; désélectionnez puis resélectionnez la piste pour réessayer.
Les balises de mise en forme <i>, <b> et <u> sont affichées dans les deux
pistes ; les autres balises restent du texte inoffensif. Les effets de mise en
scène ne sont pas reproduits. Les mesures du lot #3
distinguent les comportements vérifiés des objectifs de synchronisation atteints.
Les ressources de l'interface sont publiques. La connexion du compagnon utilise
QuickConnect/Enabled, QuickConnect/Initiate, QuickConnect/Connect et
Users/AuthenticateWithQuickConnect ; le suivi utilise Sessions,
Items/{id}/PlaybackInfo, Sessions/{id}/Playing/Pause,
Sessions/{id}/Playing/Unpause et Sessions/Logout. Aucune requête d'authentification
par mot de passe n'est émise par le compagnon, et la session de Jellyfin Web n'est
ni lue, ni modifiée, ni réutilisée.
La route authentifiée Compagnon/Subtitles/{item}/{source}/{index} vérifie l'accès
au média, sa source et sa piste avant d'utiliser le convertisseur JSON de Jellyfin.
Ce contrôle est nécessaire car l'endpoint natif Stream.json de Jellyfin 12
ne vérifie pas ces droits ; le plugin ne modifie pas cet endpoint serveur.
La page restreint également l'affichage au compte principal de chaque session.
Les refus HTTP vérifiés figurent dans les rapports des lots
#2 et #3.
Développement et vérification
Prérequis : Docker, Node.js 24 avec npm, Python 3, curl et les bibliothèques système nécessaires à Chromium/Playwright. Aucun de ces outils n'est requis pour installer le paquet fourni.
bash scripts/package.sh # construit l'interface (npm ci, Vite), compile C# avec avertissements bloquants et archive
npm ci
npm run typecheck # sources TypeScript et Vue vérifiées par vue-tsc
npm run test:unit # logique pure et contrat visuel de l'interface, testés avec node:test
bash tests/run.sh # serveur jetable, installation du paquet et tests réels
L'interface vit dans web/ (Vue 3, Vite, TypeScript) ; src/Compagnon/Web/
est un dossier généré, ignoré par Git et embarqué dans le DLL sous des noms
fixes. Pour itérer sans empaqueter, JELLYFIN_URL=http://127.0.0.1:8096 npm run dev
sert l'interface sur http://127.0.0.1:5173/ et relaie l'API à ce serveur
Jellyfin. La logique pure (estimation de position, historique, chevauchement de
l'aide, lecture des pistes) est dans web/src/lib/ et testée sans navigateur.
La feuille de style est verrouillée par un test de contrat
(web/src/style.test.ts) : jetons, contrastes AA, focus et mouvement réduit.
tests/run.sh utilise un port local libre et supprime son propre conteneur en
sortie. Il crée deux comptes ordinaires, un compte administrateur de préparation
et une vidéo originale de deux minutes avec timecode, et active Quick Connect sur
ce serveur jetable. Les identifiants aléatoires restent dans .test-state/ (ignoré
par Git). Il vérifie l'accès sans préfixe, puis exécute la suite complète sous
/jellyfin. Il ne touche aucun serveur existant.
Le rapport du lot #4 décrit la vérification bilingue. Le rapport du lot #5 décrit les pistes embarquées et les délais de chargement à froid et à chaud. Le rapport du lot #6 consolide la reprise du suivi et les mesures de synchronisation contre le timecode incrusté : il distingue le lecteur Web validé, ses mesures et les clients non testés. Le rapport du lot #7 consolide versions, protocole, résultats et limites de la connexion par Quick Connect, de sa conservation entre les visites et de la déconnexion ; le détail ticket par ticket (#8 à #12) figure dans le rapport du lot #8. Le rapport de la réécriture de l'interface décrit la migration Vue 3 + Vite et la parité prouvée par la suite existante.
Pour relancer un seul scénario pendant le développement sur cette instance :
JELLYFIN_URL=http://127.0.0.1:PORT/jellyfin \
PLAYWRIGHT_BROWSERS_PATH=/tmp/compagnon-browsers \
node --test --test-name-pattern='real playback' tests/installed.test.mjs