Pendant le visionnage d'un film ou d'un épisode en langue originale, l'utilisateur manque parfois une réplique et souhaite retrouver rapidement son sens sans remplacer les sous-titres du lecteur principal. L'aide doit être consultable dans un navigateur sur un second écran, avec quelques répliques de contexte, sans nécessiter un service supplémentaire à administrer.
Le compagnon doit exploiter les pistes de sous-titres déjà présentes dans Jellyfin. Une piste d'aide peut avoir un découpage différent de la piste principale : ce projet ne suppose ni correspondance phrase à phrase ni traduction littérale.
Solution
Distribuer un plugin Jellyfin qui héberge une interface Web compagnon. L'utilisateur s'y connecte avec le même compte Jellyfin que sur le lecteur principal, choisit la session si plusieurs lectures existent, puis choisit une piste principale et une piste d'aide.
Le compagnon affiche au plus les cinq derniers blocs de la piste principale ayant commencé à la position suivie. Sous chacun figurent les blocs de la piste d'aide qui chevauchent son intervalle. L'aide est toujours visible. Un même bloc d'aide peut être répété sous plusieurs entrées ; une entrée peut rester sans aide malgré une piste disponible.
La première version est en consultation seule. Elle vise tous les clients Jellyfin, avec une validation mesurée à vitesse 1× ; cette cible ne constitue pas une promesse de compatibilité universelle.
User Stories
En tant qu'administrateur Jellyfin, je veux installer un seul plugin, afin de rendre le compagnon accessible sans déployer de service supplémentaire.
En tant qu'utilisateur, je veux ouvrir le compagnon dans un navigateur de téléphone, tablette ou ordinateur, afin de choisir librement mon second écran.
En tant qu'utilisateur non administrateur, je veux me connecter avec mon compte Jellyfin, afin d'utiliser le compagnon sans clé API administrateur.
En tant qu'utilisateur, je veux consulter uniquement les sessions et médias autorisés dans le périmètre de mon compte, afin de préserver la confidentialité des autres utilisateurs.
En tant qu'utilisateur, je veux suivre automatiquement mon unique lecture disponible, afin de commencer sans sélection inutile.
En tant qu'utilisateur ayant plusieurs lectures, je veux choisir la session à partir de l'appareil et du titre, afin d'accompagner le bon écran.
En tant qu'utilisateur, je veux que le compagnon conserve la session choisie, afin qu'une autre lecture ne détourne pas spontanément mon suivi.
En tant qu'utilisateur, je veux identifier le média suivi et savoir lorsqu'aucune lecture n'est disponible, afin de comprendre l'état du compagnon.
En tant qu'utilisateur, je veux choisir la piste principale indépendamment du lecteur principal, afin de consulter des sous-titres même s'ils sont désactivés sur la TV.
En tant qu'utilisateur, je veux retrouver la piste principale du lecteur présélectionnée lorsqu'elle est connue et exploitable, afin de limiter les réglages.
En tant qu'utilisateur, je veux distinguer les pistes disponibles, notamment les variantes complètes, forcées ou pour malentendants, afin de choisir une aide adaptée.
En tant qu'utilisateur, je veux choisir une piste d'aide déjà présente dans Jellyfin, afin de bénéficier de sous-titres existants dans la langue souhaitée.
En tant qu'utilisateur, je veux voir les cinq derniers blocs de la piste principale avec l'aide toujours visible, afin de retrouver une réplique sans action de révélation.
En tant qu'utilisateur ouvrant le compagnon au milieu d'un film, je veux disposer immédiatement de l'historique à cette position une fois les pistes chargées, afin de ne pas attendre cinq nouvelles répliques.
En tant qu'utilisateur, je veux que les blocs anglais et français soient associés par leurs intervalles, afin de tolérer des découpages différents.
En tant qu'utilisateur, je veux conserver tous les blocs d'aide chevauchants, même lorsqu'un bloc est répété sous plusieurs entrées, afin de ne pas inventer une correspondance linguistique.
En tant qu'utilisateur, je veux que l'absence d'aide pour un bloc soit explicite, afin de ne pas confondre absence de correspondance et panne du compagnon.
En tant qu'utilisateur sans piste d'aide disponible, je veux continuer à consulter la piste principale, afin de conserver une partie de l'utilité du compagnon.
En tant qu'utilisateur, je veux conserver l'historique pendant une pause et le retrouver à la bonne position après un déplacement sur le lecteur principal, afin de revoir un passage.
En tant qu'utilisateur, je veux que le changement de média ou de source de lecture remplace les sous-titres précédents, afin de ne pas lire l'aide d'une autre version ou d'un autre épisode.
En tant qu'utilisateur, je veux exploiter les sous-titres texte externes et embarqués pris en charge par Jellyfin, afin d'utiliser mes médias sans préparer manuellement une seconde copie des pistes.
En tant qu'utilisateur, je veux distinguer un chargement ou une extraction en cours d'une piste absente ou inexploitable, afin de savoir pourquoi le texte n'est pas encore affiché.
En tant qu'utilisateur dont le média ne contient que des pistes graphiques, je veux comprendre cette limite, afin de ne pas attendre une reconnaissance de texte inexistante.
En tant qu'utilisateur, je veux être informé d'une perte de suivi ou de connexion, afin de ne pas prendre un historique périmé pour le passage actuel.
En tant qu'utilisateur revenant sur le navigateur après verrouillage ou coupure réseau, je veux un recalage sur la session suivie avant la reprise de l'affichage en direct, afin de retrouver le bon passage.
En tant qu'utilisateur, je veux consulter des sous-titres sans qu'ils puissent exécuter du contenu actif dans mon navigateur, afin de préserver mon compte.
En tant qu'utilisateur, je veux un affichage lisible sur petit écran et des contrôles utilisables au clavier, afin de consulter le compagnon sans difficulté d'accès.
En tant qu'utilisateur, je veux connaître les couples serveur/lecteur réellement validés et leurs limites de synchronisation, afin de savoir si mon installation satisfait les objectifs annoncés.
Implementation Decisions
Installation et architecture. Le plugin héberge ses propres ressources Web sur le serveur Jellyfin, sans modification des fichiers du lecteur Web existant et sans service compagnon indépendant. Il doit être utilisable par un compte ordinaire ; une page réservée à la configuration administrateur ne suffit pas. Respecter le préfixe de base et l'hébergement HTTPS/reverse proxy existants. Fournir un paquet installable avec ses versions compatibles et ses instructions, sans exiger de compilation chez l'utilisateur.
Réutilisation de Jellyfin. Commencer par les API existantes pour les sessions, les sources du média et les sous-titres. Utiliser la conversion de sous-titres du serveur vers des événements texte horodatés, sans développer un nouveau parseur multi-format ni un service d'extraction. Le format JSON repéré pendant la recherche reste un contrat à vérifier sur le serveur de test ; une sortie texte standard existante reste une alternative. N'ajouter un accès interne spécifique au plugin qu'en réponse à une limite observée. Aucun schéma de base de données supplémentaire n'est nécessaire à ce périmètre.
Identité du suivi. Identifier ensemble la session, le média, la source réellement lue et les indices de pistes. Une même œuvre peut posséder plusieurs sources incompatibles temporellement. À chaque changement d'identité, abandonner les données et réponses tardives du contexte précédent. Si la session disparaît, signaler l'arrêt ou la perte de suivi sans sélectionner une autre session arbitrairement.
Pistes et rendu. Proposer uniquement les pistes texte exploitables pour l'affichage, en conservant des libellés permettant de distinguer leurs variantes. Signaler explicitement les pistes graphiques non prises en charge. Les choix du compagnon ne changent ni les pistes ni la lecture sur l'écran principal. Le rendu vise le texte lisible ; les effets graphiques et la mise en scène ASS ne sont pas reproduits. Les sous-titres sont des données non fiables : ne jamais les exécuter comme HTML ou script.
Historique et aide associée. Un bloc est l'unité horodatée existante de la piste, pas une phrase reconstruite. Ordonner les blocs par leur début et conserver les cinq derniers dont le début a été atteint ; en afficher moins lorsque le média n'en contient pas encore cinq. Conserver un ordre déterministe en cas d'horodatages identiques. Les blocs en cours font partie de cette fenêtre et les blocs principaux futurs en sont exclus. Reconstruire la même fenêtre lors d'une ouverture en cours de média ou d'un déplacement. Deux blocs sont associés lorsqu'ils partagent une durée positive ; une simple frontière commune ne suffit pas. Accepter zéro, un ou plusieurs blocs d'aide par entrée et les répétitions entre entrées. Ne pas remplacer une aide manquante par un voisin supposé équivalent.
Synchronisation. Suivre les remontées Jellyfin en distinguant la position estimée par le serveur d'une nouvelle observation du lecteur. Ne pas compter deux fois le temps déjà extrapolé. Un lissage local éventuel reste une estimation, se recale sur les nouvelles informations et ne doit pas continuer indéfiniment après une perte de suivi. À la reprise du navigateur ou du réseau, récupérer un état actualisé avant de présenter l'affichage comme synchronisé. Les objectifs validés sont un écart inférieur à 500 ms en lecture stable à vitesse 1× et un recalage en moins de deux secondes après un déplacement sur le lecteur principal.
Authentification et permissions. Réutiliser une authentification utilisateur native Jellyfin et respecter ses permissions. Ne pas intégrer de clé API administrateur ni conserver le mot de passe dans le compagnon. Protéger explicitement toute route de données ajoutée par le plugin et contrôler l'accès à la session et au média demandés ; masquer un élément dans l'interface n'est pas une autorisation. Le périmètre initial est le même compte sur les deux écrans, sans extension des droits d'accès aux lectures d'autres comptes. Ne pas inscrire de secrets dans les URL ou journaux applicatifs. La connexion utilisateur n'implique pas automatiquement une session de lecture à suivre.
Compatibilité et maintenance. Relever et figer la version du serveur de test et les bibliothèques compatibles avant de produire le paquet. La compatibilité entre versions du plugin et du serveur est explicite, jamais déduite de l'existence d'un endpoint. Tous les clients restent une cible ; distinguer ceux qui satisfont les mesures, ceux qui échouent et ceux qui n'ont pas été testés. Si une information n'est pas remontée par le lecteur, ne pas attribuer au plugin une précision qu'il ne peut pas établir.
Testing Decisions
Le niveau de test a été validé par l'utilisateur : privilégier le parcours externe complet du compagnon sur un Jellyfin de test avec le plugin installé. Les vérifications portent sur les comportements visibles et les accès autorisés, sans figer les classes ou méthodes internes. Le dépôt ne contient actuellement ni code produit ni tests similaires à réutiliser.
Utiliser un compte non administrateur, un second compte distinct pour l'isolation et un court média de test autorisé avec timecode incrusté. Prévoir deux pistes texte au découpage volontairement différent, dont une aide couvrant plusieurs blocs principaux.
Automatiser lorsque possible le parcours avec un lecteur Web témoin et un navigateur compagnon : installation/accessibilité, connexion, sélection parmi plusieurs sessions, récupération des bonnes pistes, affichage des cinq blocs et de l'aide associée.
Vérifier les frontières temporelles, les blocs simultanés, les intervalles sans aide, les répétitions et l'ouverture au milieu du média. Vérifier que pause, déplacements dans les deux sens, changement de média et changement de source ne laissent pas de texte périmé.
Couvrir les pistes texte externes puis embarquées, notamment SRT, WebVTT et ASS pris en charge par le serveur testé. Mesurer séparément chargement/extraction à froid et à chaud. Couvrir aide absente, piste inexploitable, média sans piste texte et contenu de sous-titres potentiellement actif.
Vérifier réellement le refus d'accès avec un autre compte ou un identifiant de ressource non autorisé, ainsi que la perte ou l'expiration d'authentification. Ne pas se limiter à vérifier qu'une session est cachée dans l'interface.
Vérifier coupure et reprise réseau, arrêt de session et retour du navigateur après suspension. L'affichage doit indiquer son état et se recaler, sans avancer indéfiniment sur une ancienne estimation.
Mesurer la précision contre le lecteur principal réel. Comparer le timecode visible dans la vidéo et l'affichage du compagnon ; deux valeurs issues de la même API ne sont pas deux observations indépendantes. Relever l'écart en lecture stable à 1× et le délai de recalage à partir du déplacement visible sur le lecteur principal. Conserver les mesures et les conditions d'essai ; les seuils sont strictement inférieurs à 500 ms et deux secondes respectivement.
Reprendre ce protocole sur les autres clients réellement disponibles et documenter pour chacun le lecteur, sa version, le navigateur compagnon, la version serveur et le résultat. Un passage réussi sur le lecteur Web ne valide pas automatiquement une TV ou une application mobile.
Chaque lot d'implémentation devra livrer sa propre vérification de bout en bout. La mesure multi-clients complète qualifie ensuite le périmètre assemblé. Aucun essai sur un serveur réel n'a encore démontré la faisabilité des objectifs.
Out of Scope
Traduction automatique, acquisition de sous-titres auprès de fournisseurs externes et services associés.
OCR et conversion en texte des pistes graphiques PGS/VobSub.
Commandes de pause, reprise, retour arrière ou navigation par réplique depuis le compagnon.
Révélation ou masquage de l'aide, alignement linguistique et reconstruction grammaticale des phrases.
Révision de vocabulaire, export, favoris ou historique personnel persistant.
Lectures appartenant à d'autres comptes, nouvelle gestion des droits ou clé API globale.
Garantie des vitesses autres que 1×, de tous les clients ou de versions serveur non testées.
Service externe supplémentaire, modification des lecteurs Jellyfin ou admission au catalogue officiel des plugins dans cette première étape.
Further Notes
Cette spec synthétise les Q1 à Q11 et le niveau de test explicitement validés pendant l'entretien. L'installation sous forme de plugin est une décision retenue ; la suffisance des seules API existantes et la précision disponible restent des hypothèses à éprouver sur un serveur réel.
La publication avec le label ready-for-agent décrit un travail spécifié ; elle ne déclenche pas son implémentation. Le découpage en tickets doit encore être proposé et approuvé. L'utilisateur décidera ensuite de ce qui sera effectivement implémenté.
La recherche préalable et le glossaire du dépôt conservent le contexte détaillé et les sources primaires. Pour l'hébergement et la distribution, les références de départ sont le template officiel Jellyfin et la documentation des plugins. Les contrats observés dans le code doivent être revalidés sur la version du serveur choisie.
## Problem Statement
Pendant le visionnage d'un film ou d'un épisode en langue originale, l'utilisateur manque parfois une réplique et souhaite retrouver rapidement son sens sans remplacer les sous-titres du lecteur principal. L'aide doit être consultable dans un navigateur sur un second écran, avec quelques répliques de contexte, sans nécessiter un service supplémentaire à administrer.
Le compagnon doit exploiter les pistes de sous-titres déjà présentes dans Jellyfin. Une piste d'aide peut avoir un découpage différent de la piste principale : ce projet ne suppose ni correspondance phrase à phrase ni traduction littérale.
## Solution
Distribuer un plugin Jellyfin qui héberge une interface Web compagnon. L'utilisateur s'y connecte avec le même compte Jellyfin que sur le lecteur principal, choisit la session si plusieurs lectures existent, puis choisit une piste principale et une piste d'aide.
Le compagnon affiche au plus les cinq derniers blocs de la piste principale ayant commencé à la position suivie. Sous chacun figurent les blocs de la piste d'aide qui chevauchent son intervalle. L'aide est toujours visible. Un même bloc d'aide peut être répété sous plusieurs entrées ; une entrée peut rester sans aide malgré une piste disponible.
La première version est en consultation seule. Elle vise tous les clients Jellyfin, avec une validation mesurée à vitesse 1× ; cette cible ne constitue pas une promesse de compatibilité universelle.
## User Stories
1. En tant qu'administrateur Jellyfin, je veux installer un seul plugin, afin de rendre le compagnon accessible sans déployer de service supplémentaire.
2. En tant qu'utilisateur, je veux ouvrir le compagnon dans un navigateur de téléphone, tablette ou ordinateur, afin de choisir librement mon second écran.
3. En tant qu'utilisateur non administrateur, je veux me connecter avec mon compte Jellyfin, afin d'utiliser le compagnon sans clé API administrateur.
4. En tant qu'utilisateur, je veux consulter uniquement les sessions et médias autorisés dans le périmètre de mon compte, afin de préserver la confidentialité des autres utilisateurs.
5. En tant qu'utilisateur, je veux suivre automatiquement mon unique lecture disponible, afin de commencer sans sélection inutile.
6. En tant qu'utilisateur ayant plusieurs lectures, je veux choisir la session à partir de l'appareil et du titre, afin d'accompagner le bon écran.
7. En tant qu'utilisateur, je veux que le compagnon conserve la session choisie, afin qu'une autre lecture ne détourne pas spontanément mon suivi.
8. En tant qu'utilisateur, je veux identifier le média suivi et savoir lorsqu'aucune lecture n'est disponible, afin de comprendre l'état du compagnon.
9. En tant qu'utilisateur, je veux choisir la piste principale indépendamment du lecteur principal, afin de consulter des sous-titres même s'ils sont désactivés sur la TV.
10. En tant qu'utilisateur, je veux retrouver la piste principale du lecteur présélectionnée lorsqu'elle est connue et exploitable, afin de limiter les réglages.
11. En tant qu'utilisateur, je veux distinguer les pistes disponibles, notamment les variantes complètes, forcées ou pour malentendants, afin de choisir une aide adaptée.
12. En tant qu'utilisateur, je veux choisir une piste d'aide déjà présente dans Jellyfin, afin de bénéficier de sous-titres existants dans la langue souhaitée.
13. En tant qu'utilisateur, je veux voir les cinq derniers blocs de la piste principale avec l'aide toujours visible, afin de retrouver une réplique sans action de révélation.
14. En tant qu'utilisateur ouvrant le compagnon au milieu d'un film, je veux disposer immédiatement de l'historique à cette position une fois les pistes chargées, afin de ne pas attendre cinq nouvelles répliques.
15. En tant qu'utilisateur, je veux que les blocs anglais et français soient associés par leurs intervalles, afin de tolérer des découpages différents.
16. En tant qu'utilisateur, je veux conserver tous les blocs d'aide chevauchants, même lorsqu'un bloc est répété sous plusieurs entrées, afin de ne pas inventer une correspondance linguistique.
17. En tant qu'utilisateur, je veux que l'absence d'aide pour un bloc soit explicite, afin de ne pas confondre absence de correspondance et panne du compagnon.
18. En tant qu'utilisateur sans piste d'aide disponible, je veux continuer à consulter la piste principale, afin de conserver une partie de l'utilité du compagnon.
19. En tant qu'utilisateur, je veux conserver l'historique pendant une pause et le retrouver à la bonne position après un déplacement sur le lecteur principal, afin de revoir un passage.
20. En tant qu'utilisateur, je veux que le changement de média ou de source de lecture remplace les sous-titres précédents, afin de ne pas lire l'aide d'une autre version ou d'un autre épisode.
21. En tant qu'utilisateur, je veux exploiter les sous-titres texte externes et embarqués pris en charge par Jellyfin, afin d'utiliser mes médias sans préparer manuellement une seconde copie des pistes.
22. En tant qu'utilisateur, je veux distinguer un chargement ou une extraction en cours d'une piste absente ou inexploitable, afin de savoir pourquoi le texte n'est pas encore affiché.
23. En tant qu'utilisateur dont le média ne contient que des pistes graphiques, je veux comprendre cette limite, afin de ne pas attendre une reconnaissance de texte inexistante.
24. En tant qu'utilisateur, je veux être informé d'une perte de suivi ou de connexion, afin de ne pas prendre un historique périmé pour le passage actuel.
25. En tant qu'utilisateur revenant sur le navigateur après verrouillage ou coupure réseau, je veux un recalage sur la session suivie avant la reprise de l'affichage en direct, afin de retrouver le bon passage.
26. En tant qu'utilisateur, je veux consulter des sous-titres sans qu'ils puissent exécuter du contenu actif dans mon navigateur, afin de préserver mon compte.
27. En tant qu'utilisateur, je veux un affichage lisible sur petit écran et des contrôles utilisables au clavier, afin de consulter le compagnon sans difficulté d'accès.
28. En tant qu'utilisateur, je veux connaître les couples serveur/lecteur réellement validés et leurs limites de synchronisation, afin de savoir si mon installation satisfait les objectifs annoncés.
## Implementation Decisions
1. **Installation et architecture.** Le plugin héberge ses propres ressources Web sur le serveur Jellyfin, sans modification des fichiers du lecteur Web existant et sans service compagnon indépendant. Il doit être utilisable par un compte ordinaire ; une page réservée à la configuration administrateur ne suffit pas. Respecter le préfixe de base et l'hébergement HTTPS/reverse proxy existants. Fournir un paquet installable avec ses versions compatibles et ses instructions, sans exiger de compilation chez l'utilisateur.
2. **Réutilisation de Jellyfin.** Commencer par les API existantes pour les sessions, les sources du média et les sous-titres. Utiliser la conversion de sous-titres du serveur vers des événements texte horodatés, sans développer un nouveau parseur multi-format ni un service d'extraction. Le format JSON repéré pendant la recherche reste un contrat à vérifier sur le serveur de test ; une sortie texte standard existante reste une alternative. N'ajouter un accès interne spécifique au plugin qu'en réponse à une limite observée. Aucun schéma de base de données supplémentaire n'est nécessaire à ce périmètre.
3. **Identité du suivi.** Identifier ensemble la session, le média, la source réellement lue et les indices de pistes. Une même œuvre peut posséder plusieurs sources incompatibles temporellement. À chaque changement d'identité, abandonner les données et réponses tardives du contexte précédent. Si la session disparaît, signaler l'arrêt ou la perte de suivi sans sélectionner une autre session arbitrairement.
4. **Pistes et rendu.** Proposer uniquement les pistes texte exploitables pour l'affichage, en conservant des libellés permettant de distinguer leurs variantes. Signaler explicitement les pistes graphiques non prises en charge. Les choix du compagnon ne changent ni les pistes ni la lecture sur l'écran principal. Le rendu vise le texte lisible ; les effets graphiques et la mise en scène ASS ne sont pas reproduits. Les sous-titres sont des données non fiables : ne jamais les exécuter comme HTML ou script.
5. **Historique et aide associée.** Un bloc est l'unité horodatée existante de la piste, pas une phrase reconstruite. Ordonner les blocs par leur début et conserver les cinq derniers dont le début a été atteint ; en afficher moins lorsque le média n'en contient pas encore cinq. Conserver un ordre déterministe en cas d'horodatages identiques. Les blocs en cours font partie de cette fenêtre et les blocs principaux futurs en sont exclus. Reconstruire la même fenêtre lors d'une ouverture en cours de média ou d'un déplacement. Deux blocs sont associés lorsqu'ils partagent une durée positive ; une simple frontière commune ne suffit pas. Accepter zéro, un ou plusieurs blocs d'aide par entrée et les répétitions entre entrées. Ne pas remplacer une aide manquante par un voisin supposé équivalent.
6. **Synchronisation.** Suivre les remontées Jellyfin en distinguant la position estimée par le serveur d'une nouvelle observation du lecteur. Ne pas compter deux fois le temps déjà extrapolé. Un lissage local éventuel reste une estimation, se recale sur les nouvelles informations et ne doit pas continuer indéfiniment après une perte de suivi. À la reprise du navigateur ou du réseau, récupérer un état actualisé avant de présenter l'affichage comme synchronisé. Les objectifs validés sont un écart inférieur à 500 ms en lecture stable à vitesse 1× et un recalage en moins de deux secondes après un déplacement sur le lecteur principal.
7. **Authentification et permissions.** Réutiliser une authentification utilisateur native Jellyfin et respecter ses permissions. Ne pas intégrer de clé API administrateur ni conserver le mot de passe dans le compagnon. Protéger explicitement toute route de données ajoutée par le plugin et contrôler l'accès à la session et au média demandés ; masquer un élément dans l'interface n'est pas une autorisation. Le périmètre initial est le même compte sur les deux écrans, sans extension des droits d'accès aux lectures d'autres comptes. Ne pas inscrire de secrets dans les URL ou journaux applicatifs. La connexion utilisateur n'implique pas automatiquement une session de lecture à suivre.
8. **Compatibilité et maintenance.** Relever et figer la version du serveur de test et les bibliothèques compatibles avant de produire le paquet. La compatibilité entre versions du plugin et du serveur est explicite, jamais déduite de l'existence d'un endpoint. Tous les clients restent une cible ; distinguer ceux qui satisfont les mesures, ceux qui échouent et ceux qui n'ont pas été testés. Si une information n'est pas remontée par le lecteur, ne pas attribuer au plugin une précision qu'il ne peut pas établir.
## Testing Decisions
Le niveau de test a été validé par l'utilisateur : privilégier le parcours externe complet du compagnon sur un Jellyfin de test avec le plugin installé. Les vérifications portent sur les comportements visibles et les accès autorisés, sans figer les classes ou méthodes internes. Le dépôt ne contient actuellement ni code produit ni tests similaires à réutiliser.
- Utiliser un compte non administrateur, un second compte distinct pour l'isolation et un court média de test autorisé avec timecode incrusté. Prévoir deux pistes texte au découpage volontairement différent, dont une aide couvrant plusieurs blocs principaux.
- Automatiser lorsque possible le parcours avec un lecteur Web témoin et un navigateur compagnon : installation/accessibilité, connexion, sélection parmi plusieurs sessions, récupération des bonnes pistes, affichage des cinq blocs et de l'aide associée.
- Vérifier les frontières temporelles, les blocs simultanés, les intervalles sans aide, les répétitions et l'ouverture au milieu du média. Vérifier que pause, déplacements dans les deux sens, changement de média et changement de source ne laissent pas de texte périmé.
- Couvrir les pistes texte externes puis embarquées, notamment SRT, WebVTT et ASS pris en charge par le serveur testé. Mesurer séparément chargement/extraction à froid et à chaud. Couvrir aide absente, piste inexploitable, média sans piste texte et contenu de sous-titres potentiellement actif.
- Vérifier réellement le refus d'accès avec un autre compte ou un identifiant de ressource non autorisé, ainsi que la perte ou l'expiration d'authentification. Ne pas se limiter à vérifier qu'une session est cachée dans l'interface.
- Vérifier coupure et reprise réseau, arrêt de session et retour du navigateur après suspension. L'affichage doit indiquer son état et se recaler, sans avancer indéfiniment sur une ancienne estimation.
- **Mesurer la précision contre le lecteur principal réel.** Comparer le timecode visible dans la vidéo et l'affichage du compagnon ; deux valeurs issues de la même API ne sont pas deux observations indépendantes. Relever l'écart en lecture stable à 1× et le délai de recalage à partir du déplacement visible sur le lecteur principal. Conserver les mesures et les conditions d'essai ; les seuils sont strictement inférieurs à 500 ms et deux secondes respectivement.
- Reprendre ce protocole sur les autres clients réellement disponibles et documenter pour chacun le lecteur, sa version, le navigateur compagnon, la version serveur et le résultat. Un passage réussi sur le lecteur Web ne valide pas automatiquement une TV ou une application mobile.
Chaque lot d'implémentation devra livrer sa propre vérification de bout en bout. La mesure multi-clients complète qualifie ensuite le périmètre assemblé. Aucun essai sur un serveur réel n'a encore démontré la faisabilité des objectifs.
## Out of Scope
- Traduction automatique, acquisition de sous-titres auprès de fournisseurs externes et services associés.
- OCR et conversion en texte des pistes graphiques PGS/VobSub.
- Commandes de pause, reprise, retour arrière ou navigation par réplique depuis le compagnon.
- Révélation ou masquage de l'aide, alignement linguistique et reconstruction grammaticale des phrases.
- Révision de vocabulaire, export, favoris ou historique personnel persistant.
- Lectures appartenant à d'autres comptes, nouvelle gestion des droits ou clé API globale.
- Garantie des vitesses autres que 1×, de tous les clients ou de versions serveur non testées.
- Service externe supplémentaire, modification des lecteurs Jellyfin ou admission au catalogue officiel des plugins dans cette première étape.
## Further Notes
Cette spec synthétise les Q1 à Q11 et le niveau de test explicitement validés pendant l'entretien. L'installation sous forme de plugin est une décision retenue ; la suffisance des seules API existantes et la précision disponible restent des hypothèses à éprouver sur un serveur réel.
La publication avec le label ready-for-agent décrit un travail spécifié ; elle ne déclenche pas son implémentation. Le découpage en tickets doit encore être proposé et approuvé. L'utilisateur décidera ensuite de ce qui sera effectivement implémenté.
La recherche préalable et le glossaire du dépôt conservent le contexte détaillé et les sources primaires. Pour l'hébergement et la distribution, les références de départ sont le [template officiel Jellyfin](https://github.com/jellyfin/jellyfin-plugin-template) et la [documentation des plugins](https://jellyfin.org/docs/general/server/plugins/). Les contrats observés dans le code doivent être revalidés sur la version du serveur choisie.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Problem Statement
Pendant le visionnage d'un film ou d'un épisode en langue originale, l'utilisateur manque parfois une réplique et souhaite retrouver rapidement son sens sans remplacer les sous-titres du lecteur principal. L'aide doit être consultable dans un navigateur sur un second écran, avec quelques répliques de contexte, sans nécessiter un service supplémentaire à administrer.
Le compagnon doit exploiter les pistes de sous-titres déjà présentes dans Jellyfin. Une piste d'aide peut avoir un découpage différent de la piste principale : ce projet ne suppose ni correspondance phrase à phrase ni traduction littérale.
Solution
Distribuer un plugin Jellyfin qui héberge une interface Web compagnon. L'utilisateur s'y connecte avec le même compte Jellyfin que sur le lecteur principal, choisit la session si plusieurs lectures existent, puis choisit une piste principale et une piste d'aide.
Le compagnon affiche au plus les cinq derniers blocs de la piste principale ayant commencé à la position suivie. Sous chacun figurent les blocs de la piste d'aide qui chevauchent son intervalle. L'aide est toujours visible. Un même bloc d'aide peut être répété sous plusieurs entrées ; une entrée peut rester sans aide malgré une piste disponible.
La première version est en consultation seule. Elle vise tous les clients Jellyfin, avec une validation mesurée à vitesse 1× ; cette cible ne constitue pas une promesse de compatibilité universelle.
User Stories
Implementation Decisions
Installation et architecture. Le plugin héberge ses propres ressources Web sur le serveur Jellyfin, sans modification des fichiers du lecteur Web existant et sans service compagnon indépendant. Il doit être utilisable par un compte ordinaire ; une page réservée à la configuration administrateur ne suffit pas. Respecter le préfixe de base et l'hébergement HTTPS/reverse proxy existants. Fournir un paquet installable avec ses versions compatibles et ses instructions, sans exiger de compilation chez l'utilisateur.
Réutilisation de Jellyfin. Commencer par les API existantes pour les sessions, les sources du média et les sous-titres. Utiliser la conversion de sous-titres du serveur vers des événements texte horodatés, sans développer un nouveau parseur multi-format ni un service d'extraction. Le format JSON repéré pendant la recherche reste un contrat à vérifier sur le serveur de test ; une sortie texte standard existante reste une alternative. N'ajouter un accès interne spécifique au plugin qu'en réponse à une limite observée. Aucun schéma de base de données supplémentaire n'est nécessaire à ce périmètre.
Identité du suivi. Identifier ensemble la session, le média, la source réellement lue et les indices de pistes. Une même œuvre peut posséder plusieurs sources incompatibles temporellement. À chaque changement d'identité, abandonner les données et réponses tardives du contexte précédent. Si la session disparaît, signaler l'arrêt ou la perte de suivi sans sélectionner une autre session arbitrairement.
Pistes et rendu. Proposer uniquement les pistes texte exploitables pour l'affichage, en conservant des libellés permettant de distinguer leurs variantes. Signaler explicitement les pistes graphiques non prises en charge. Les choix du compagnon ne changent ni les pistes ni la lecture sur l'écran principal. Le rendu vise le texte lisible ; les effets graphiques et la mise en scène ASS ne sont pas reproduits. Les sous-titres sont des données non fiables : ne jamais les exécuter comme HTML ou script.
Historique et aide associée. Un bloc est l'unité horodatée existante de la piste, pas une phrase reconstruite. Ordonner les blocs par leur début et conserver les cinq derniers dont le début a été atteint ; en afficher moins lorsque le média n'en contient pas encore cinq. Conserver un ordre déterministe en cas d'horodatages identiques. Les blocs en cours font partie de cette fenêtre et les blocs principaux futurs en sont exclus. Reconstruire la même fenêtre lors d'une ouverture en cours de média ou d'un déplacement. Deux blocs sont associés lorsqu'ils partagent une durée positive ; une simple frontière commune ne suffit pas. Accepter zéro, un ou plusieurs blocs d'aide par entrée et les répétitions entre entrées. Ne pas remplacer une aide manquante par un voisin supposé équivalent.
Synchronisation. Suivre les remontées Jellyfin en distinguant la position estimée par le serveur d'une nouvelle observation du lecteur. Ne pas compter deux fois le temps déjà extrapolé. Un lissage local éventuel reste une estimation, se recale sur les nouvelles informations et ne doit pas continuer indéfiniment après une perte de suivi. À la reprise du navigateur ou du réseau, récupérer un état actualisé avant de présenter l'affichage comme synchronisé. Les objectifs validés sont un écart inférieur à 500 ms en lecture stable à vitesse 1× et un recalage en moins de deux secondes après un déplacement sur le lecteur principal.
Authentification et permissions. Réutiliser une authentification utilisateur native Jellyfin et respecter ses permissions. Ne pas intégrer de clé API administrateur ni conserver le mot de passe dans le compagnon. Protéger explicitement toute route de données ajoutée par le plugin et contrôler l'accès à la session et au média demandés ; masquer un élément dans l'interface n'est pas une autorisation. Le périmètre initial est le même compte sur les deux écrans, sans extension des droits d'accès aux lectures d'autres comptes. Ne pas inscrire de secrets dans les URL ou journaux applicatifs. La connexion utilisateur n'implique pas automatiquement une session de lecture à suivre.
Compatibilité et maintenance. Relever et figer la version du serveur de test et les bibliothèques compatibles avant de produire le paquet. La compatibilité entre versions du plugin et du serveur est explicite, jamais déduite de l'existence d'un endpoint. Tous les clients restent une cible ; distinguer ceux qui satisfont les mesures, ceux qui échouent et ceux qui n'ont pas été testés. Si une information n'est pas remontée par le lecteur, ne pas attribuer au plugin une précision qu'il ne peut pas établir.
Testing Decisions
Le niveau de test a été validé par l'utilisateur : privilégier le parcours externe complet du compagnon sur un Jellyfin de test avec le plugin installé. Les vérifications portent sur les comportements visibles et les accès autorisés, sans figer les classes ou méthodes internes. Le dépôt ne contient actuellement ni code produit ni tests similaires à réutiliser.
Chaque lot d'implémentation devra livrer sa propre vérification de bout en bout. La mesure multi-clients complète qualifie ensuite le périmètre assemblé. Aucun essai sur un serveur réel n'a encore démontré la faisabilité des objectifs.
Out of Scope
Further Notes
Cette spec synthétise les Q1 à Q11 et le niveau de test explicitement validés pendant l'entretien. L'installation sous forme de plugin est une décision retenue ; la suffisance des seules API existantes et la précision disponible restent des hypothèses à éprouver sur un serveur réel.
La publication avec le label ready-for-agent décrit un travail spécifié ; elle ne déclenche pas son implémentation. Le découpage en tickets doit encore être proposé et approuvé. L'utilisateur décidera ensuite de ce qui sera effectivement implémenté.
La recherche préalable et le glossaire du dépôt conservent le contexte détaillé et les sources primaires. Pour l'hébergement et la distribution, les références de départ sont le template officiel Jellyfin et la documentation des plugins. Les contrats observés dans le code doivent être revalidés sur la version du serveur choisie.
mentioned in issue #2
mentioned in issue #3
mentioned in issue #4
mentioned in issue #5
mentioned in issue #6
Preserved GitLab #1 history: source attribution, exact timestamps, discussion grouping and review positions. Historical diff snapshots: 0; visible source notes: 5.
gitlab-49-issue-1.md (SHA-256
59ea2fc27750497a6ff683ebddb693f1ebf990b531262b99696235cee2c7e045)gitlab-49-issue-1.json (SHA-256
55df4ba79e3cf82ba9df358df2ba2d5065277c3ee228a467903e438ed300d9fc)Old CI job execution and reports are omitted. Original source files and rollback backups remain protected.