Alumira — Spécification
Statut : v1 — implémentée. Ce document décrit ce que fait Alumira, et pourquoi. Le code fait foi : quand ils divergent, c'est le code qui a raison et ce document est corrigé.
0. Vision & principes
Alumira est un serveur multimédia auto-hébergé pour les films et séries d'un foyer. Il lit les fichiers là où ils sont, les reconnaît, et les présente comme le ferait un service de streaming à soi : les visuels d'abord, un profil pour chacun, des suggestions, toutes les versions et toutes les langues d'un titre. Il se regarde sur un ordinateur, un téléphone, ou une télé avec sa télécommande.
Deux rôles. Le spectateur est passif : il choisit ce qu'il regarde et le regarde, rien de plus. Rien à régler, trier ni corriger : titres, visuels, versions, langues et sous-titres lui arrivent prêts. L'administrateur est éditorialiste. Il a la main sur tout, pour peaufiner le serveur dans le moindre détail : le fichier qui sert chaque version, le texte d'un titre, ses visuels et son âge limite, les collections et la page d'accueil, les sous-titres, les qualités, le matériel, qui voit quoi.
Chaque fichier est diffusé de l'une de deux façons, choisie pour son dossier, puis fichier par fichier :
- Pré-encodé : encodé une seule fois, à l'avance, en une échelle de qualités. La lecture ne fait ensuite que servir des octets prêts : qualité prévisible, beaucoup de spectateurs sur un matériel modeste.
- Lecture directe : le fichier est servi tel qu'il est, rien n'est stocké deux fois. Un appareil qui ne sait pas le lire, ou dont la connexion est trop lente pour lui, reçoit des qualités converties à la volée par la puce vidéo.
Alumira tourne sur ce qu'un foyer a déjà : un NAS (Docker), un ordinateur (une application pour macOS et Windows), n'importe quel serveur. D'autres ordinateurs du réseau peuvent l'aider à encoder. Il se rend accessible de l'extérieur en un clic (Alumira Connect, §10.3).
Les modèles de langage sont des aides facultatives, choisies par l'administrateur : ils traduisent des sous-titres, lisent en texte des sous-titres en images, écrivent des sous-titres en écoutant, nomment des chapitres, reconnaissent la langue d'une piste.
Principes directeurs
- Media first. UI très graphique, simple, to the point ; UX simplissime. Une identité propre : on n'imite pas les plateformes de streaming.
- Des spectateurs passifs, un éditorialiste aux commandes. Les spectateurs ne décident que de ce qu'ils regardent : aucun réglage ne se met entre eux et l'image. Chaque choix revient à l'administrateur, et chacun peut se faire à la main, dans le détail ; ce qu'Alumira fait seul est un premier jet que l'administrateur peut reprendre.
- Des mots simples pour les spectateurs. Aucun jargon technique côté spectateur (codecs, protocoles, formats de sous-titres, systèmes de classification) : les termes grand public quand il en faut un (HDR10, 4K), sans infobulle pour les expliquer. L'administration garde les termes techniques là où ils sont le sujet.
- Préparé à l'avance plutôt qu'à la lecture. Tout ce qui peut être fait avant qu'on appuie sur lecture l'est : encodages, sous-titres, vignettes de navigation, chapitres, images. Le transcodage à la lecture n'existe que pour les fichiers en lecture directe, seulement sur la puce vidéo, dans un nombre fixe de places.
- Stems éclatés, recomposés au service. Chaque piste (vidéo par rendition, audio par langue et format, sous-titre) est un stem indépendant ; le client reçoit un manifest multi-piste / multi-bitrate généré à la volée.
- Évaluation paresseuse plutôt que liens. On stocke des identités (empreintes, règles) et on résout au moment du besoin plutôt que de maintenir des liens qui se désynchronisent.
- Les sources sont sacrées. Une store n'est jamais une source ; une source n'est jamais modifiée, sauf en mode
inboxexplicite. - La bibliothèque est locale. Les métadonnées importées (TMDB) appartiennent à la bibliothèque une fois importées : modifiées sur place, jamais surchargées par une couche au-dessus. Seul un rafraîchissement explicite réimporte un titre.
- L'IA aide, n'est jamais requise. Tout fonctionne sans modèle. Les modèles sont appelés par des tâches, à la demande, avec ce qu'ils consomment affiché.
- Code impératif et simple. Pas d'OOP ; favoriser la vitesse de build et d'itération.
1. Périmètre
Aujourd'hui
- Un serveur pour Windows, macOS et Linux, avec worker d'encodage intégré ; des images Docker pour NAS (amd64, arm64) ; une application de bureau pour macOS et Windows.
- Une seule app web, installable, pour les spectateurs et les administrateurs (l'administration en est une section), utilisable à la télécommande.
- Films, séries (découpages, versions, découpages propres), versions d'un titre, bonus, collections.
- Comptes avec profils (dont des profils Enfant), limites d'âge et publics, suggestions, favoris, reprise de lecture, téléchargements.
- Chaque fichier pré-encodé ou en lecture directe, transcodé à la volée au besoin.
- Sources de type dossier et disque.
- Workers d'encodage additionnels sur le réseau local.
- Accès à distance : Alumira Connect, un domaine à soi (ACME intégré) ou un reverse proxy.
- Fonctions facultatives par modèles de langage.
Plus tard
- Clients natifs : Android, iOS, Android TV. Clients TV web : LG webOS, Samsung Tizen (réutilisent le client web).
- Workers d'encodage via Internet.
- Override de ladder par média exposé dans l'UI (le modèle le prévoit, cf. §6.12).
- Détection d'intro / générique (chapitres + empreinte audio inter-épisodes).
- Composition de pistes issues de plusieurs sources (ex. VF d'un DVD sur vidéo Blu-ray, correction du PAL speedup).
- Dolby Vision ; playlists I-frame HLS pour les lecteurs d'Apple.
- Vidéos téléchargées du web (yt-dlp) identifiées par l'identifiant dans leur nom.
- Créer une fiche de titre à la main d'un seul tenant.
- Listes de lecture.
2. Glossaire
| Terme | Définition |
|---|---|
| Source | Emplacement (dossier/disque) scanné pour trouver des fichiers originaux. Lecture seule (watch) ou consommée (inbox). |
| Diffusion | Comment un fichier se lit : pré-encodé (ses stems) ou lecture directe (le fichier lui-même, transcodé à la volée au besoin). Réglée par fichier. |
| Store | Emplacement où Alumira écrit les stems normalisés. Nombre illimité. Une store n'est jamais une source. |
| Empreinte | Identité d'un fichier source, indépendante de son chemin. |
| Catalogue des sources | Index empreinte → chemins actuels (0..n). |
| Stem | Une piste normalisée unique, stockée comme un fichier fMP4 CMAF (ex. « vidéo 1080p H264 SDR », « audio fr-FR AAC 2.0 », « sous-titre en WebVTT »). |
| Rendition | Un niveau de qualité vidéo (résolution × codec × plage dynamique × profondeur). Une rendition = un stem vidéo. |
| Rung | Un échelon de la ladder (définition d'une rendition à produire). |
| Ladder | Ensemble ordonné de rungs + définitions audio/sous-titres à produire pour un média. |
| Profil d'encodage | Configuration nommée et versionnée de la ladder, éditable par l'admin (Administration → Profils). |
| Pass | Groupe de rungs encodés ensemble dans une même chaîne ; les passes sont ordonnées. |
| Work | Œuvre : film, série, saison, épisode. |
| Edition | Une version d'une œuvre (« 1977 Cut », « Blu-ray 2011 », « Deluxe »…), servie par un fichier. La première est la version principale. |
| Extra | Bonus (making-of, interview…) rattaché à une œuvre ou à une édition. |
| Groupe | Une collection : manuelle ou dynamique. |
| Découpage | Comment une série est découpée en saisons : l'ordre de diffusion de TMDB ou l'un de ses groupes d'épisodes. |
| Découpage propre | Une version d'une série découpée en saisons et épisodes à elle (un coffret DVD), pas ceux de TMDB. |
| Compte | Une personne connectée : mot de passe, passkeys, appareils, limites. Il contient quelques profils. |
| Profil | Un spectateur d'un compte : historique, favoris, langues, âge ; un profil Enfant ne voit que les titres pour enfants. |
| Public | À qui s'adresse un titre : Enfant, Tous ou Adulte. |
| Worker | Processus d'encodage (intégré au serveur ou distant). |
| Signature d'encodeur | Identité d'une configuration d'encodage garantissant une sortie homogène (cf. §7.3). |
3. Architecture générale
┌───────────────────────────────────────────────┐
│ Serveur │
Sources ──scan──▶ Catalogue ─▶ Queues ─▶ Scheduler ──────┐ │
(watch/inbox) │ empreintes │ │ │
│ ├─ identification ▼ │
│ ├─ normalisation Worker intégré
│ ├─ images │ │
│ ├─ QA │ │
│ └─ IA (modèles) ▼ │
│ SQLite ◀── métadonnées Stores (stems fMP4)
│ │ │
│ HTTP : app web + API + manifests HLS ◀─┘ │
│ + fichiers en lecture directe, │
│ transcodés à la volée (puce vidéo) │
└──────────────▲──────────────────────▲─────────┘
│ │
Client web / TV Workers LAN
(source streamée
en HTTP)
- Un binaire serveur unique par plateforme ; un binaire worker (le serveur embarque la même logique worker).
- Le serveur sert l'app web, l'API, les manifests HLS, les stems (byte-range), les fichiers en lecture directe et les téléchargements.
- Un seul écrivain en base : les écritures attendent leur tour (jamais « database is locked »), les lecteurs ne font que lire. Une tâche, un chunk ou une image n'est jamais laissé « en cours » sans rien pour le faire : baux, et transitions écrites dans la même transaction que ce qui les suit ; des invariants sont vérifiés chaque minute et les réparations journalisées.
4. Sources, diffusion & catalogue
4.1 Modes de source
| Mode | Comportement |
|---|---|
watch |
Lecture seule stricte. Alumira ne modifie, ne déplace, ne supprime jamais rien (seedbox, disque d'archive). |
inbox |
Dossier de dépôt consommé : une fois l'encodage d'un média complet, son fichier source est supprimé, avec les sous-titres à côté et les dossiers qu'il laisse vides. Il n'y a pas de corbeille. |
Dans un dossier inbox, il reste d'un fichier ce que garde son profil d'encodage :
- ses seules versions encodées ;
- ou, en plus, ses pistes telles quelles, rien de réencodé (la copie de l'original, §6.4 : le défaut pour les dossiers de dépôt).
L'admin est averti qu'un profil modifié plus tard ne pourra partir que du meilleur stem existant, ou de la copie de l'original.
À l'ajout d'un dossier, l'admin choisit entre trois façons, dites en mots simples : Pré-encodé, Pré-encodé, puis supprimé (inbox), Lecture directe, transcodée à la volée (un dossier watch dont les fichiers sont en lecture directe).
4.2 Diffusion
Chaque fichier a sa propre diffusion : pré-encodé ou lecture directe. Un nouveau fichier prend le choix de son dossier à son arrivée, puis garde le sien. Un fichier d'un dossier inbox est toujours pré-encodé.
L'admin la change dans Administration → Diffusion, fichier par fichier ou par lots (filtres : diffusion, non transcodable, dossier, chemin). Changer le choix d'un dossier vaut pour les fichiers à venir ; il propose ensuite de l'appliquer aux fichiers déjà là.
Un fichier qui change continue de se lire comme avant jusqu'à ce que la nouvelle façon soit prête :
- vers la lecture directe : ses encodages en attente sont annulés, il est préparé (ci-dessous), puis ses renditions sont retirées ;
- vers le pré-encodage : sa première passe est mise en file (refusé avec un profil qui ne fait aucune qualité) ; il se lit en direct jusqu'à ce qu'elle soit faite.
La source d'un fichier en lecture directe n'est jamais supprimée. Quand elle a disparu, le fichier est illisible : la Maintenance le liste en source disparue, bloquante.
4.3 Fichiers en lecture directe
Rien n'est encodé ni stocké à l'avance, à part de petits extras. Chaque fichier est préparé une fois, à son arrivée :
- les images clés de sa vidéo sont lues (ses paquets seulement), et des segments d'au moins 4 s sont coupés dessus (une grille de 6 s quand ses images clés sont introuvables) ;
- ses sous-titres, vignettes de navigation et chapitres sont faits comme pour un fichier encodé. Les vignettes viennent de ses seules images clés, décodées par la puce vidéo quand elle le peut, celles d'un fichier HDR converties en SDR ;
- ses pistes audio en AAC se lisent telles quelles ; tout autre codec (AC-3, DTS, TrueHD…) est converti une fois en AAC, par le processeur (il n'y a pas de matériel audio ; c'est petit). Une version stéréo légère de chaque piste (AAC 64 kb/s) est faite aussi, pour les petites qualités (§6.6).
Il se lit dès qu'il est identifié et préparé, en HLS comme les autres. Sa vidéo et ses pistes AAC sont des renditions virtuelles dont ffmpeg copie les segments depuis le fichier à la demande, sans encodage.
Chaque segment est lu avec les temps du fichier et coupé exactement à ses images clés. Toutes les pistes sont retardées des mêmes quelques millisecondes dont ses premières images se décodent en avance (jamais sous 0, en phase) : les fragments se suivent exactement, et un saut se lit aussitôt.
4.4 Transcodage à la volée
Un appareil qui ne sait pas décoder un fichier en lecture directe, ou dont la connexion est trop lente pour lui, reçoit des qualités transcodées à la volée.
Son échelle. Chaque profil d'encodage a une échelle à la volée, réglée sur la page Profils comme ses encodages : une résolution, un codec (H.264 ou HEVC) et un débit par barreau. Par défaut, en H.264 : 1080p 8 Mb/s, 720p 4,5 Mb/s, 480p 1,8 Mb/s, 360p 900 kb/s, 240p 450 kb/s. La changer ne réencode rien.
À côté du fichier. Ses qualités sont des renditions virtuelles à côté du fichier lui-même, coupées sur les mêmes segments : un lecteur passe de l'une à l'autre sans coupure. Chaque segment est fait en morceaux de 3 s au plus, envoyés à mesure : un démarrage ou un saut se lit en quelques secondes. Jamais au-dessus du fichier : deux barreaux au-dessus de sa taille n'en font qu'un, à sa taille ; quand le fichier lui-même se lit, seuls les barreaux sous les trois quarts de son débit sont proposés.
Sur la puce vidéo seulement. VideoToolbox, NVENC, Quick Sync, AMF, VA-API : sur NAS aussi (Synology, QNAP, Unraid avec un iGPU Intel ou une carte NVIDIA). Décodé par la puce quand elle le peut, sans images B ; un serveur sans encodeur matériel n'en propose aucune. Ses formats sont testés un par un au démarrage (codec et profondeur, images relues depuis la puce).
Sur une puce Intel l'image n'en sort pas. Elle y est réduite comme pour les encodages (§6.3) : par moitiés au plus, avec le meilleur filtre de la puce. Ses couleurs y sont fixées aussi (BT.709 en plage limitée, ou le HDR gardé), si bien que les sources SD et celles sans étiquette se convertissent aussi.
HDR. Un fichier HDR reste HDR pour un appareil qui l'affiche : HEVC 10 bits, en PQ ou HLG comme le fichier, quel que soit le codec de l'échelle, si bien que la luminosité ne saute jamais quand le lecteur change de qualité. Sans encodeur matériel pour cela, le fichier seul est proposé. Tout autre appareil le reçoit ramené en SDR.
Quand ce n'est pas possible. Un fichier que la puce ne sait pas décoder, et que le processeur ne peut pas suivre (seulement des fichiers légers : 8 bits SD, 720p des codecs légers), ne propose que lui-même. L'appareil reçoit la raison, et la Maintenance le liste (Transcodage indisponible, avec les appareils qui n'ont pas pu le lire), pour qu'il soit pré-encodé.
Partagé équitablement. Les segments sont faits à la demande, le suivant pendant ce temps, et gardés en mémoire (512 Mo) pour les retours en arrière et les autres spectateurs. Un segment en cours continue tant que quelqu'un l'attend. Un lecteur a 3 morceaux coupés ou faits à la fois (sa vidéo, son son, le suivant), le serveur 16 copies ; une demande au-delà attend son tour.
Un maximum de spectateurs sont transcodés à la fois : 2 par défaut, de 1 à 32, sur la page Profils (un encodeur matériel est autant occupé à 100 kb/s qu'à 25 Mb/s). Au-delà personne n'est servi moins bien : un appareil qui sait lire le fichier le lit tel quel, un autre est invité à réessayer dans quelques minutes ou sur un autre appareil. Une place se libère 45 s après son dernier segment.
4.5 Détection
- Les dossiers locaux sont suivis par les événements du système de fichiers : rien n'est regardé tant que rien n'y change, leurs disques dorment en paix. Un événement perdu, ou un dossier impossible à suivre, bascule sur le point suivant.
- Les dossiers réseau (NFS, SMB, FUSE… : les changements faits par d'autres machines ne déclenchent aucun événement) sont regardés toutes les heures par défaut (de 15 min à un jour, ou seulement à la demande, dans Sources), et chaque fois qu'Alumira y lit déjà (toutes les 10 min au plus).
- Les pages d'administration ne touchent jamais un disque : l'état d'une source et l'espace libre d'une store viennent du dernier coup d'œil.
- Stabilité : un fichier n'est pris en compte que lorsque sa taille et sa date de modification sont stables pendant une durée configurable (une copie en cours).
4.6 Empreintes
- Empreinte rapide :
taille + hash(début) + hash(fin) + hash(k blocs échantillonnés). Sert d'identité. - Hash complet : calculé paresseusement en tâche de fond, uniquement si nécessaire (vérification, conflit).
4.7 Catalogue
- Le catalogue maintient
empreinte → {chemins actuels}. Un fichier déplacé = même empreinte, nouveau chemin : aucune réimportation. - Un média ne référence que l'empreinte de sa source. Le chemin est résolu dans le catalogue au moment du besoin (réencodage, etc.).
- Même empreinte vue plusieurs fois → importée une seule fois.
- Disparition d'une source : un média pré-encodé reste disponible (il ne dépend plus de sa source une fois normalisé).
- Les incohérences (doublons probables à empreinte différente, sources disparues, etc.) remontent dans la Maintenance (§13.2).
5. Pipeline & identification
détection ─▶ probe ─┬─▶ identification ─────────────┐
├─▶ normalisation (passes 1..n) ├─▶ visible
│ ou préparation (directe) │
└─▶ images (dérivés) ┘
QA (VMAF échantillonné) ─▶ maintenance
tâches IA (à la demande)
- Queues indépendantes : identification, normalisation, images, QA, et une file à part pour les tâches qui appellent un modèle (§13.3). L'identification et la normalisation tournent en parallèle ; les stems sont rattachés au fichier source (empreinte), l'édition est résolue par l'identification.
- Visibilité : un média n'est visible des spectateurs que lorsqu'il est complètement catalogué : identifié, et sa pass 1 faite (pré-encodé) ou préparé (lecture directe). Un média non identifiable reste invisible et est listé à identifier.
- Priorités : la pass 1 de tous les médias passe avant les passes suivantes, ce qui maximise le nombre de médias visibles pendant l'import initial. Les tâches de fond tournent avec une priorité processeur et disque plus basse : elles cèdent la place à ce que quelqu'un attend.
- Chaque job est rejouable et idempotent ; les erreurs sont visibles dans l'administration.
5.1 Fournisseurs de métadonnées
- TMDB est la source principale : métadonnées localisées, visuels, cast, collections, classifications par pays, groupes d'épisodes. Chaque installation fournit sa propre clé API, saisie à l'onboarding (§13.4).
- IMDb est joint via l'
imdb_iddonné par TMDB, et ses jeux de données publics donnent les notes (facultatif, à l'onboarding). - La note d'un titre va de 1 à 5 étoiles, ou aucune : la note IMDb convertie en étoiles quand le titre est importé ou rafraîchi, puis la note propre du titre, modifiable au catalogue. La mise à jour hebdomadaire d'IMDb n'y touche plus.
- Une fois importées, les données d'un titre sont les siennes (principe 8) : seul Rafraîchir depuis TMDB le réimporte.
5.2 Identification
Chaque fichier est lu d'après son nom et ses dossiers : un film, un épisode, un bonus, quelle version (montage). Il est ensuite cherché sur TMDB.
- Deux séries d'un même nom (un remake : La Bande à Picsou 1987 et 2017) : l'année que donne le nom du fichier tranche ; s'il n'en donne pas, TMDB est interrogé.
- Un fichier dont la série est connue reste à la série même quand aucun épisode n'est trouvé : il n'est jamais à identifier de nouveau. Ses épisodes s'affinent sur la page de la série (§9.3), et la Maintenance liste la série dans Incohérences d'épisodes.
- Une série choisie à la main pour des fichiers les fait siens, y compris ceux déjà identifiés comme une autre : ils sont réidentifiés par elle, plus par leur nom.
- L'identification n'est retentée que quand elle peut aboutir ; les fichiers attendent entre-temps.
Pourquoi un fichier n'est pas identifié est gardé sous forme de code, et dit en toutes lettres dans chaque langue : « Aucune série TMDB ne s'appelle “Les Schtroumpfs” (1981) », « Son nom dit S02E05, mais TMDB n'a pas de saison 2 pour cette série »…
La page Identification a quatre onglets : À identifier, Identifiés, Corrigés à la main, Tout.
- À identifier, un par un : d'abord une ligne par série inconnue (comment se lit son nom, ses fichiers, pourquoi), avec un seul bouton, Identifier, qui cherche parmi les séries de TMDB ; ses épisodes se trouvent ensuite tout seuls. Puis un fichier par ligne, une recherche TMDB préremplie d'après son nom ; un choix est aussitôt proposé aux fichiers qui se lisent pareil.
- À identifier, en liste : la table de lot, avec ses outils (épisodes, film, bonus).
- Un fichier peut aussi être saisi à la main : titre, année, version, résumé.
- Un poster survolé montre la carte du titre (son poster, ce qu'il est, son résumé), pour distinguer des titres de même nom.
6. Normalisation
Tout ce qui suit concerne les fichiers pré-encodés ; les fichiers en lecture directe en partagent les sous-titres, chapitres, images et vignettes de navigation.
6.1 Préparation de la source
Avant l'encodage, dans la chaîne de filtres :
- désentrelacement / IVTC (DVD, sources TV) ;
- framerate constant (sources VFR) ;
- détection et crop des bandes noires ;
- normalisation des offsets de démarrage des pistes (tous les stems partagent la même timeline, t=0).
6.2 Règles de cohérence des rungs
- H264 ⇒ 8 bit SDR uniquement.
- HDR ⇒ 10 bit ⇒ HEVC ou AV1.
- Jamais d'upscale : les rungs au-dessus de la résolution source sont ignorés.
- HDR supporté : HDR10 (et HLG passthrough). Dolby Vision hors périmètre.
- Les stems HDR portent les métadonnées HDR10 de la source (mastering display, niveau de lumière : boîtes
mdcvetclli), écrites à l'assemblage des morceaux : FFmpeg ne les donne qu'à une sortie d'un graphe de filtres. - Variantes SDR d'un master HDR : tone-mapping réel (libplacebo), jamais une simple conversion.
6.3 Chaîne de downscale
Point fondamental : la qualité des basses résolutions doit être impeccable.
- Un seul décodage par pass, puis un
filter_complexen cascade : chaque étage divise la résolution par 2 au plus (ex. 2160 → 1080 → 540 → 270), en partant de l'étage précédent et non de la source. - On encode aux étages qui nous intéressent ; les étages intermédiaires n'existent que pour la qualité du downscale.
- Scaler de référence : libplacebo (Vulkan ; MoltenVK sur macOS) ou zscale en logiciel. Les scalers matériels (
scale_qsv,scale_cuda,scale_vt…) ne sont pas utilisés par défaut (ex. QSV low-power fait du nearest en fort downscale). Le matériel sert à l'encodage. - Le tone-mapping HDR→SDR est placé dans la chaîne à l'étage approprié.
- La chaîne alimente aussi les vignettes de navigation (§6.10).
6.4 Passes ordonnées
Les rungs d'une ladder sont répartis en passes, encodées dans l'ordre. Chaque pass = un décodage + une chaîne. Le manifest intègre les rungs dès qu'ils sont prêts.
L'installation propose deux points de départ, décrits par la façon dont la bibliothèque est regardée plutôt que par des codecs : Beaucoup de spectateurs, pleine qualité (la ladder par défaut ci-dessous) et À la maison, parfois en déplacement (peu de qualités, bien espacées : la 4K pour le salon, des versions très légères pour un voyage). L'un comme l'autre se modifie ensuite.
Ladder par défaut :
| Pass | Contenu |
|---|---|
| 1 | Vidéo H264 8 bit SDR : 1080p, 480p, 240p. Audio : toutes les langues, AAC 2.0 128 kb/s (downmix, loudnorm) + AAC 5.1 320 kb/s si source ≥ 5.1 + AAC 2.0 léger 64 kb/s. Sous-titres. Vignettes de navigation. Chapitres. |
| 2 | Vidéo HEVC 10 bit (HDR conservé si source HDR) : 2160p, 1080p ; qualités de secours HEVC 8 bit SDR : 480p, 240p (bien plus légères que le H264 à image égale sur une connexion faible). Audio : E-AC-3 5.1 448 kb/s si source ≥ 5.1, Opus 2.0 96 kb/s / 5.1 192 kb/s / 7.1 256 kb/s + Opus 2.0 léger 48 kb/s. |
| dernière | Copie de l'original : les pistes de la source remuxées telles quelles vers la store (sans réencodage). Ne dépend de rien, la plus longue à finir. Par profil d'encodage : jamais, pour les dossiers inbox seulement (le défaut), ou pour tous les fichiers. |
Une qualité qu'une passe ultérieure du même profil refait remplace la précédente : elle n'est plus proposée, et supprimée dès que plus personne n'a le média ouvert.
Codecs supportés : vidéo H264, HEVC, AV1 ; audio AAC-LC, Opus, E-AC-3 ; layouts 2.0, 5.1, 7.1.
6.5 Débit & images clés
- CRF plafonné (
crf+maxrate+bufsize) plutôt que bitrate fixe : un dessin animé ne consomme pas le débit d'un film à fort grain. - GOP fixe et fermé, keyframes alignées sur toutes les renditions, scene-cut désactivé (keyframes forcées). Une image clé et un fragment toutes les 3 s, encodages distribués compris.
6.6 Audio
- AAC-LC obligatoire (seul codec universel : Apple, TV, web). Opus en bonus (web, Android). E-AC-3 pour le multicanal TV / ampli.
- Downmix stéréo toujours produit, avec normalisation de volume EBU R128 (loudnorm).
- Piste nuit (dynamique compressée, stéréo) : option de profil, désactivée par défaut. Elle ajoute environ un stem stéréo par langue.
- Langues identifiées en BCP-47 (ex.
fr-FR/fr-CApour VFF / VFQ).
Audio léger. Chaque langue a une version stéréo légère (palier light), dans un groupe audio à part (aac-light, opus-light). Le manifest l'attribue aux qualités vidéo de 480p et moins, pour que le son ne pèse pas plus lourd que l'image sur une connexion faible ; elle n'est utilisée que si chaque langue en a une.
Elle porte le nom de la piste stéréo qu'elle remplace : le lecteur garde le choix du spectateur d'un groupe à l'autre, et son menu ne liste que des langues et des formats (le codec est laissé au lecteur).
Chaque piste source à part. L'audio n'est jamais chunké (problèmes de priming aux jonctions) : il est encodé d'un seul tenant, pendant les chunks vidéo. Chaque piste a son propre FFmpeg (à plusieurs, une piste mono pouvait s'arrêter en route sans erreur), est gardée dès qu'elle est faite (jamais réencodée), et échoue seule.
Une piste impossible à encoder est laissée de côté : la pass continue, le média se lit sans elle, la Maintenance le signale (encodages incomplets), et un nouvel essai ne fait que les pistes manquantes.
6.7 Sous-titres
| Source | Traitement |
|---|---|
| Texte (SRT, WebVTT, mov_text…) | → WebVTT. |
| ASS/SSA stylé | → WebVTT (fallback universel) + original conservé pour un rendu riche côté client (libass wasm, JASSUB). |
| Image (PGS, VobSub) | Overlay image : sous-titres conservés comme images, rendus par le client ; ou lus en texte par un modèle de langage, quand l'admin en choisit un pour cela (§6.9). |
Les flags sont conservés et exposés : forcé, SDH (malentendants), langue BCP-47. Les spectateurs les voient en mots simples : une piste stylée n'est pas distinguée dans le lecteur (c'est la seule listée), et se lit « stylés » dans les téléchargements.
Une piste par langue et par usage (complets, forcés) : la meilleure.
- Les SDH sont des sous-titres complets, pris seulement sans sous-titres simples.
- Parmi les pistes de la source : ASS, puis texte simple, puis images (les images avant le texte simple quand elles sont lues en texte : elles gardent leur place à l'écran).
- Un fichier qui n'est pas dans la source (§6.8) remplace la piste de sa langue et de son usage, le plus récent l'emportant.
- Une piste retirée par l'admin, ou remplacée par un fichier, ne revient jamais : un réencodage la saute, un scan laisse son fichier de côté, la recherche automatique ne la retélécharge pas (un fichier choisi à la main est pris, même retiré avant).
- Les médias encodés avant gardent leurs pistes jusqu'à leur prochain encodage, ou En garder un par langue dans la Maintenance.
Pistes en langue inconnue (sans étiquette dans leur fichier, und, mul) : les lecteurs les affichent ainsi, et elles ne comptent pour aucune langue. La Maintenance les liste, chacune avec la langue à lui donner (la langue originale du titre en premier) et ses premières répliques.
- Une piste de la source garde la langue qu'on lui donne par son flux : tous les encodages à venir l'utilisent. Un fichier de sous-titres la garde sur sa ligne.
- Les rendus déjà faits la prennent aussitôt et sont renommés d'après elle ; pour les sous-titres, la règle d'une piste par langue s'applique alors.
- La langue de toute piste se change depuis la fiche technique du média, une langue fausse aussi.
- Avec un modèle pour les sous-titres, la Reconnaissance IA les nomme par lots (§6.9).
Forcé ou non. Une piste de sous-titres se dit forcée ou non depuis la fiche technique du média, par un interrupteur. C'est gardé par son flux, par-dessus le flag et le titre de son fichier ; celui d'un fichier de sous-titres est gardé par-dessus son nom aux scans.
6.8 Sous-titres venus d'ailleurs
Fichiers qui ne sont pas dans la source : trouvés à côté d'elle (<nom>.<langue>[.forced|.sdh].srt, ou dans un dossier Subs), ou téléchargés depuis OpenSubtitles.
- Chacun devient un stem à part, hors des passes : ajouté dès que le titre se lit, sans rien réencoder, et jamais touché par un réencodage ou un upgrade.
- Un fichier texte dans un ancien jeu de caractères est lu dans la page de code habituelle de sa langue.
- Un fichier WebVTT est pris tel quel (ffmpeg perdrait la place des répliques).
- Dans la fiche d'un média, tout fichier ajouté hors des passes (à côté de la source, téléchargé, traduit, lu, écouté) est dans une section Ajouts après les passes.
OpenSubtitles (facultatif ; chaque installation apporte sa propre clé API, un compte augmente le quota de téléchargements par jour). Des sous-titres sont voulus dans les langues de sous-titres par défaut, sauf si le titre les a déjà, ou du son dans cette langue.
- Les fichiers faits pour l'empreinte exacte de la source sont pris automatiquement dès que le titre se lit. Les autres se choisissent à la main, la recherche couvrant toutes les langues de sous-titres de la plateforme (celles par défaut et celles de chaque compte).
- Un titre sans aucun sous-titre texte est aussi cherché dans la langue parlée (son originale, sinon celle de son son), bien qu'on l'entende : c'est de là que partent les traductions. Un tel fichier n'est pris que si rien de trouvé pour les langues manquantes ne peut servir à traduire.
- Les téléchargements sont comptés par jour : le reste attend le lendemain.
- Les fichiers téléchargés restent dans le dossier de données ; la base garde leur fiche (identifiant OpenSubtitles, version, correspondance par empreinte ou par titre), de quoi les retélécharger.
6.9 Sous-titres par modèles de langage
Chacune de ces fonctions est à la demande et facultative, avec un modèle choisi pour son usage dans les Paramètres (§13.3). Chaque résultat est un fichier de sous-titres comme un téléchargé ; ses stems sont marqués comme faits par une machine pour l'admin, jamais pour les spectateurs.
Traduction. Depuis la fiche d'un média, l'admin choisit les sous-titres texte à traduire (tous sauf une traduction ; les meilleurs proposés d'abord : complets, dans la langue originale, puis l'anglais) et les langues dans lesquelles les traduire (celles sans sous-titres ; celles par défaut qui manquent cochées). En lot depuis la Maintenance : les meilleurs sous-titres, dans chaque langue par défaut qui manque à chaque média.
- Des notes courtes d'abord (noms, termes, façon dont les personnages s'adressent les uns aux autres), puis les répliques par lots de 120, avec la scène autour. Les horaires ne passent jamais par le modèle.
- Chaque lot fait est gardé : une traduction arrêtée reprend où elle en était.
- Le résultat est un fichier
translation, ses stems nommésext.tr-….
Sous-titres en images lus en texte. Avec un modèle choisi pour cela, chaque import lit ses sous-titres en images ; ceux du catalogue se lisent à la demande depuis la Maintenance.
- Les images partent vers le modèle par 24, sur un fond neutre. Chacune devient une réplique WebVTT à son horaire, à sa place à l'écran (haut, gauche, droite ; en bas et centrée là où le lecteur met les sous-titres), italiques gardées.
- Chaque lot lu est gardé. Le résultat (
ocr, stems nommésext.ocr-<flux>) remplace les images : il se traduit, et n'est jamais relu.
Sous-titres depuis la parole. Chaque piste audio peut avoir des sous-titres faits en l'écoutant.
- La première piste d'une langue (celle que le média parle d'abord : dans la langue originale du titre, sinon celle par défaut) fait les sous-titres de cette langue. Ils sont proposés comme Créer dans les sous-titres manquants du média quand il n'en a pas dans cette langue, à la place d'une traduction ; les Sous-titres manquants de la Maintenance font ceux de la langue qu'il parle.
- Toute autre piste d'une langue (un commentaire, ou quoi que ce soit : aucun usage n'est deviné) fait des sous-titres à elle, nommés d'après son titre, un usage à part : ils ne remplacent jamais ceux de la langue, et le lecteur ne les choisit jamais de lui-même.
Un modèle de parole choisi pour l'usage Écoute entend la piste (chez le fournisseur des paramètres IA : Grok Voice Transcribe de xAI pour l'instant ; facturé à l'heure de son).
- Seulement son canal central pour un mixage 5.1 ou plus (là où sont les dialogues), depuis la source, sinon son plus grand rendu.
- Envoyée en Opus (environ 15 Mo par heure), en morceaux d'environ cinq minutes coupés dans des silences, quatre à la fois, les mots de chacun remis à leur place : un film entier prend au service plus de temps que n'en attendent les relais placés devant lui.
- Sa langue est donnée, les hésitations retirées, les noms des personnages donnés pour les entendre juste.
Ses mots, chacun daté et son locuteur distingué, sont découpés en répliques comme le ferait un sous-titreur. Ce qu'un modèle invente sur la musique, des crédits de sous-titres ou une longue phrase en boucle est écarté.
- Coupés aux pauses, aux fins de phrase, aux changements de locuteur et de plan (mesurés comme pour les chapitres).
- En 2 lignes de 42 caractères (13 à 16 signes en japonais, chinois, coréen), assez longues pour être lues (17 caractères par seconde ; 4, 9, 12 pour ceux-là), 5/6 s au moins, 7 s au plus.
- Restant 0,3 s après la voix, commençant avec un plan qui démarre juste avant elle, finissant 2 images avant un changement de plan plutôt qu'au-dessus.
- Un échange rapide (deux locuteurs, des lignes courtes, la première trop courte pour être lue avant que le second parle) est une seule réplique, une ligne à tiret chacun.
Puis le modèle des sous-titres, s'il y en a un, reçoit les répliques par 80, quatre lots à la fois, avec la scène autour, le titre, le résumé et les noms des personnages. Il écrit ce qui est dit : mots et noms mal entendus corrigés, hésitations et faux départs retirés, condensé seulement au-delà de ce que la réplique peut tenir dans son temps (qui lui est donné), les tirets des locuteurs gardés, un bruit pris pour de la parole vidé. Les horaires ne passent jamais par lui.
Le résultat est un fichier transcription (stems nommés ext.stt-<flux>), dont les traductions peuvent partir. Il prend deux tâches dans la file IA, l'écoute et l'écriture ; ce qui a été entendu et chaque lot réécrit sont gardés jusqu'à ce que le fichier soit fait.
Reconnaissance de la langue. Depuis Langue inconnue dans la Maintenance, la Reconnaissance IA envoie au modèle des sous-titres, pour chaque piste en langue inconnue qui se lit en texte :
- ses chiffres : répliques par heure, part de la durée couverte, plus long silence ;
- une quarantaine de répliques prises tout du long.
Le modèle répond la langue de chaque piste et si elle est forcée (une seule réplique suffit à dire une langue ; une ou quelques répliques sont des forcés). Les réponses s'appliquent comme celles de l'admin ; une piste laissée inconnue est journalisée.
6.10 Vignettes de navigation & chapitres
- Les vignettes de navigation sont générées depuis un étage basse résolution de la chaîne (pass 1), en planches (web, Android). Les playlists I-frame HLS pour Apple viendront plus tard.
- Les chapitres sont extraits de la source autant que possible, et montrés dans le lecteur.
Chapitres sans nom : ceux numérotés par leur outil d'authoring : « Chapter 01 », « Scene 2 », « 第3章 », un horaire, rien. Ils sont reconnus dans toutes les langues sans modèle : leurs titres, sans numéros, horaires ni ponctuation, sont identiques. Trois au moins, ou la moitié des chapitres, et le média est listé dans la Maintenance (et dans Manquants de sa fiche).
À la demande, un modèle de langage (un usage à part) les nomme d'après les dialogues des meilleurs sous-titres texte, en un appel :
- 1 à 5 mots chacun, comme sur le menu d'un disque, qui dessinent l'histoire sans la dévoiler (ni rebondissement ni fin) ;
- dans chaque langue de la plateforme (par défaut et des comptes) ; les chapitres qui ont leur propre nom (Recap, Credits) le gardent, traduit.
Les noms sont gardés à côté des chapitres avec leurs débuts (un réencodage qui extrait les mêmes chapitres les garde), aussi dans le source.json de la store. Le lecteur montre ceux de sa langue d'interface. Ils se retirent depuis la fiche du média (Ajouts).
Chapitres créés quand il n'y en a pas, pour un média de 15 minutes ou plus dont la source n'en a aucun, ou un seul.
À chaque encodage de la passe qui fait ses extras, ses changements de scène sont mesurés sur sa plus petite rendition et gardés : changements de plan et leur force, passages noirs, silences d'une rendition audio (une à deux minutes de décodage).
À la demande, les plus forts deviennent des points de coupure : un fondu, du noir, un changement de plan net, d'autant plus que personne ne parle et que rien ne sonne ; à 30 s au moins les uns des autres, le début d'abord. Le modèle reçoit les dialogues horodatés, les passages sans dialogue et les points de coupure. Il choisit environ un chapitre toutes les 7 minutes, chacun commençant à un point de coupure (une scène muette peut en ouvrir un : l'image dit où elle est), et les nomme comme ci-dessus.
Chaque début revient ensuite là où sa scène commence : avant l'échange qui le suit (une question, pas sa réponse), sur le meilleur repère 2 minutes avant au plus (la sortie d'un fondu d'abord, puis la fin d'un silence, puis un changement de plan net). Les repères où quelqu'un parle par-dessus le plan, ou entre deux répliques rapprochées, ne comptent presque pas. Les chapitres créés sont gardés avec leurs noms, et un réencodage les garde.
6.11 Contrôle qualité
- Le VMAF échantillonné est mesuré sur chaque rendition (3 échantillons, 5 pour un média de plus de 30 minutes). Les anomalies remontent dans la Maintenance.
- Un second VMAF est mesuré sur un écran 4K : source et rendition mises en 3840×2160, modèle VMAF 4K. Contrairement au premier, il compte ce qu'une image plus petite a perdu : un étalon commun pour classer les encodages de toutes hauteurs et de tous codecs, quel que soit l'écran de l'appareil.
La lecture ne propose à chaque appareil que les renditions qui valent leur débit. Celle qu'une autre rendition lisible bat (ou égale à 1 point près) pour moins de débit est écartée, sur cet étalon ; pas encore mesuré, sur le premier VMAF à hauteur égale, sinon le codec le plus efficace s'il est aussi moins cher. Les renditions HDR (le VMAF ne s'y applique pas) ne se départagent que par le codec.
Plus de débit apporte alors toujours une meilleure image : un H264 à deux fois le débit d'un HEVC aussi beau n'est jamais choisi.
6.12 Profils d'encodage : versionnage & résolution
Les profils sont versionnés, et un stem enregistre la version de profil qui l'a produit. Modifier un profil, ou en donner un autre à un média, rend ses stems obsolètes : une information dans la Maintenance, pas une erreur (ils se lisent toujours).
Rien n'est réencodé tout seul : l'admin les met à niveau, un média ou un lot.
- Les stems que le profil actuel produit déjà sont gardés (rangés dans leur passe) ; les autres sont retirés d'emblée.
- Les passes tournent ensuite en mode complément, n'encodant que ce qui manque ; chaque passe repart de la source avec sa propre chaîne de réduction.
- Impossible une fois la source disparue.
La ladder effective d'un média est résolue paresseusement : défaut global → type (film / épisode / extra) → override média. L'override par média est prévu dans le modèle ; son UI viendra plus tard.
L'admin voit une estimation de taille lorsqu'il édite un profil. Le pré-encodage est refusé avec un profil qui ne fait aucune qualité ; Profils peut redonner à un tel profil les qualités équilibrées.
7. Workers & distribution
7.1 Workers
- Le serveur embarque un worker. Des workers additionnels peuvent être ajoutés sur le réseau local.
- Appairage par token, trafic en TLS.
- FFmpeg appelé en sous-process : celui installé sur la machine, jamais lié (cherché via
-ffmpeg/ALUMIRA_FFMPEG, un dossierffmpeg/à côté de l'exécutable, puis lePATH).
Les capacités sont auto-détectées au démarrage, par un encodage de test d'une seconde avec chaque encodeur connu du build (être compilé ne veut pas dire que le matériel est là) : codecs, matériel, 10 bit, HDR, et les formats que la puce vidéo décode.
Le résultat reste en cache jusqu'au changement de build FFmpeg (une semaine au plus) ; l'administration peut relancer les tests. Un test qui échoue lentement sur une machine chargée, ou qui contredit la détection précédente, est refait ; ce qui reste incertain est retesté 10 minutes plus tard.
7.2 Distribution par tranche de temps
- La vidéo de toute pass est encodée en chunks temporels (un réglage, 2 min par défaut ; un seul chunk pour un média court), alignés sur les keyframes, avec ou sans workers distants : un seul chemin pour tous les encodages, initiaux ou non, et pour un rendu retenté.
- Chaque chunk exécute la chaîne complète de la pass (tous les rungs) sur sa tranche. Ce serveur en prend sa part ; les workers distants partageant la signature du job en prennent d'autres (quand c'est permis). Les workers envoient un chunk pendant qu'ils encodent le suivant.
- L'audio, les sous-titres et la copie de l'original sont traités hors chunks (§6.6), chaque étape gardée dès qu'elle est finie.
Rien de fait n'est perdu.
- Un chunk terminé est gardé : un encodage interrompu (arrêt, plantage, redémarrage) reprend à partir des chunks qui manquent. Changer la longueur des chunks recommence le découpage.
- Un encodage échoué ou annulé garde ce qu'il a fait (chunks terminés, rendus assemblés, pistes audio) : le relancer depuis la Maintenance reprend le même job et ne fait que le reste.
Assemblage. Le serveur concatène les chunks en stems, chaque rendu une fois, en libérant ses chunks au fur et à mesure. Un chunk est vérifié avant d'être joint (ses fichiers couvrent sa tranche) : un chunk trop court est réencodé, seul.
Un seul encodage à la fois par média (une pass, un rendu retenté) : chacun remplace des stems que l'autre peut utiliser. Les encodages des autres médias continuent.
Un job en cours indique son étape sous la forme clé ou clé:détail (video:3/12 : chunk 3 sur 12), affichée de la même façon sur toutes les pages.
7.3 Signature d'encodeur
Pour éviter les inégalités de qualité au sein d'un même média :
- Signature =
implémentation encodeur (libx265, hevc_nvenc…)pour chaque rendu ; le preset/paramètres voyage avec le job. Le build FFmpeg n'en fait pas partie. - Tous les chunks d'un même job sont encodés avec le même encodeur. À l'assemblage, les chunks doivent avoir les mêmes réglages de codec (
avcC/hvcC…) : sinon le job échoue au lieu de produire un flux cassé. - Choix de l'encodeur d'un job : celui que le plus de machines en ligne savent faire tourner (serveur et workers), le mode du profil départageant. Seul, le serveur garde son choix ; avec un PC en ligne, un encodeur partagé (libx264…) l'emporte.
- Chunk échoué : réassigné à un worker ayant le même encodeur ; à défaut, le job est relancé en entier.
- Mise à jour d'un worker (changement de signature) : le worker draine ses jobs en cours avant de se mettre à jour.
- Choix par profil :
quality(logiciel) ouspeed(matériel).
7.4 Accès à la source
- Le serveur streame la source aux workers en HTTP (byte-range). Pas de système de fichiers partagé, pas de correspondance de chemins entre systèmes.
8. Stockage & service
8.1 Stores
- Nombre illimité. Une store n'est jamais une source.
- Politique de placement par défaut : remplissage ordonné. Les stores forment une liste ordonnée ; on écrit dans la première store non pleine, puis on passe à la suivante.
- Une store est pleine quand les fichiers d'Alumira atteignent sa taille maximale configurable (vide : tout le disque), ou quand son disque n'a plus que sa marge de sécurité de 5 Go.
- Un stem est toujours écrit entièrement sur une seule store. Si la store se remplit pendant l'écriture, le stem est réécrit sur la store suivante.
- Les stems d'un même média peuvent être répartis sur plusieurs stores (résolution paresseuse).
- D'autres politiques (répartition, tiering SSD/HDD) pourront venir plus tard.
Une store se décrit elle-même : aucun fichier de correspondance, aucune base nécessaire pour la comprendre.
- Les stems d'une source sont dans
media/<son empreinte>/, chaque fichier nommé d'après ce qu'il est : passe, nature, piste, langue, réglages, marqueurs, profil (p1.video.1080p.hevc.10b.hdr10.crf22.8000k.e1.libx265.@default@3.mp4,p1.audio.t1.fr-FR.aac.2ch.128k.light.@default@3.mp4,ext.os-123456.fr.vtt…). Codec, dimensions et index se relisent dans les fichiers eux-mêmes. - Un
source.jsonà côté garde ce que seule la source savait : son nom, sa taille, sa durée et ses chapitres. - Le marqueur
.alumira-storenomme la store, l'installation qui l'utilise et sa dernière activité (toutes les heures).
Reprise. Une store ajoutée avec du contenu (remplie par une autre installation, ou avant la perte d'une base) est reprise par une tâche de fond. Chaque dossier de source devient un média avec ses stems, après vérification que chaque fichier est complet ; il se lit dès qu'il a image et son, identifié d'après le nom de la source. Quand le dernier utilisateur de la store semble encore actif (moins de deux jours), l'admin est prévenu ; rien n'est refusé.
Ses sources la rejoignent ensuite. Un fichier scanné dont l'empreinte a un dossier y prend sa place : il est analysé, et seul ce qui manque est encodé (rien quand ses stems suivent un autre profil : mis à niveau à la demande, §6.12). Les rendus sont mesurés à nouveau une fois la source là. Les sources sans stems s'encodent normalement ; les stems sans source restent lisibles (Maintenance : sources disparues sans perte).
La même source dans deux stores (un disque copié) : une copie est lue, l'autre est listée dans la Maintenance, à supprimer ou garder en secours.
8.2 Format des stems
- Un fichier fMP4 CMAF par stem, avec index (
sidx), servi en byte-range. Pas de fichier par segment : pas de milliers de petits fichiers, et des stems faciles à déplacer entre stores.
8.3 Packaging
- HLS uniquement (CMAF fMP4) : AVPlayer l'exige ; ExoPlayer, TV, Shaka, hls.js le lisent. Pas de DASH.
- Manifests générés à la volée par session : rungs disponibles, capacités du client, restrictions utilisateur, plafond de bande passante (§11).
- URLs de segments signées (§10.4).
8.4 Téléchargement
- Le spectateur choisit une version, une qualité et les pistes (ex. 720p SDR + son français + sous-titres anglais).
- Le fichier est un remux à la volée des stems choisis, sans réencodage, streamé pendant sa construction. Deux sortes, en mots simples : Complet (MKV), par défaut, et Lisible partout (MP4) (sous-titres stylés écartés : leur copie simple est proposée). Chaque choix dit les formats qu'il prend (H.264 · AAC).
- Ce qui se télécharge : les qualités et le son encodés, la source elle-même quand c'est un MP4, les sous-titres. Un titre ne propose de téléchargement que si l'un de ses fichiers le permet (un fichier à l'image encodée) ; sinon, pas de lien.
- Autorisé par compte (les profils Enfant ne téléchargent jamais), soumis au contrôle de bande passante (§11). Un fichier préparé est gardé 3 heures.
8.5 Images
Formats. Une queue dédiée génère les dérivés (tailles multiples) en JPEG, plus AVIF quand la machine encode l'AV1, sinon WebP ; le navigateur reçoit le meilleur format qu'il accepte, servi avec un cache agressif.
Le flou d'abord. Chaque image garde un ThumbHash (environ 25 octets) envoyé avec les cartes et les pages : son flou s'affiche aussitôt, l'image par-dessus en fondu (50 ms) une fois entière, jamais dessinée en cours d'arrivée.
Jamais périmée. L'adresse d'une image porte sa version (quand ses tailles ont été faites) : gardée un an par le navigateur ; une image refaite, ou une nouvelle sous un numéro réutilisé, a une autre adresse. Là où la version n'est pas connue, le navigateur redemande à chaque fois (une réponse sans l'image tant qu'elle n'a pas changé).
Visuels. Posters, images de fond, logos transparents, portraits du cast, localisés ; couleurs dominantes extraites pour thématiser l'UI. Les images TMDB sont récupérées dans leur résolution originale, leurs tailles en sont tirées (images de fond jusqu'à 3840 px). Les images de fond sont choisies les plus nettes d'abord (par paliers : 4K, 2560, 1920), puis les mieux notées.
Derrière un titre (sa page, le grand visuel de l'accueil, le lecteur) : l'image de fond TMDB, ou un diaporama d'images prises dans le film (dans les épisodes pour une série : sa première saison d'elle-même).
- 6 s chacune, la suivante apparaissant en fondu en une demi-seconde, dans leur ordre, communes à ses versions. L'image de fond TMDB peut l'ouvrir (8 s, puis les images du film).
- Tous les diaporamas suivent une même horloge : image, fondu et glissement sont ceux de la tranche de 6 s en cours. Il continue sans saut de l'accueil à la page du titre et au lecteur, ou après une page quittée puis retrouvée. Chaque tranche montre le flou, puis une petite taille, puis la pleine, chacune une fois décodée.
Images choisies à la main. Au catalogue, l'admin repère un moment sur les vignettes de navigation, puis l'image exacte (une image ou une seconde à la fois), vue en grand, et la prend. Autant que voulu, réordonnées par glisser-déposer.
- Elle est prise dans le meilleur disponible : le fichier source tant qu'il est là (comme le montrent les renditions : recadré, désentrelacé, à sa taille d'affichage), sinon la plus grande rendition (dans le fragment qui contient l'instant, trouvé grâce à son index : rapide même sur un gros fichier sur un disque lent). Une image HDR est convertie comme les renditions SDR.
- Elle est décodée par la puce vidéo quand il y en a une (sur une puce Intel, seule l'image demandée en sort), par le processeur quand la puce échoue. En faisant défiler, seule l'image demandée en dernier est prise.
- Les bandes noires sont retirées, y compris pour un fichier en lecture directe : trouvées sur les vignettes de navigation, comme les lignes et colonnes depuis les bords sombres sur neuf sur dix d'entre elles (une scène sombre ne l'est que sur quelques-unes).
- Chaque image se souvient de sa provenance. Quand mieux arrive (un encodage 4K fait depuis, la source revenue), la Maintenance le signale et les reprend.
Images choisies automatiquement (sans modèle) : 6 images parmi les vignettes de navigation du film, entre 5 % et la moitié du film (rien de dévoilé).
- Notées sur l'exposition, la netteté, la couleur et le contraste rapportés à ceux du film ; le texte (sous-titres, génériques) et un bas gauche chargé ou clair (là où s'écrit le titre) comptés contre elles ; à quelques minutes d'écart et de plans différents.
- Les meilleures sont prises en pleine taille, les plus nettes gardées, dans l'ordre du film.
- Depuis la fiche du film (remplace ses images, une fois ses vignettes faites), ou par lots depuis la Maintenance, qui liste les films sans images du film, et les épisodes sans image.
9. Modèle de données
Esquisse conceptuelle (pas un schéma SQL).
SourceRoot (id, path, mode: watch|inbox, direct) -- direct : diffusion par défaut de ses fichiers
SourceFile (quick_fingerprint, full_hash?, size, probe, moviehash?) -- catalogue, gardé quand le fichier disparaît
SourcePath (fingerprint, source_root, path, seen_at) -- 0..n chemins
Store (id, path, capacity, policy)
TrackLang (fingerprint, stream, language) -- donnée par l'admin (ou un modèle)
TrackForced (fingerprint, stream, forced)
Work (id, kind: movie|series|season|episode, parent?, numéros,
audience: kids|all|adults, age?, rating? (étoiles), boost?)
Edition (id, work, name, media, position, champs d'override clairsemés)
SeriesVersion (show, position, name, own_cut) -- versions d'une série
Layout (show, kind: aired|group, saisons et épisodes) -- importé une fois
Extra (id, work|edition, kind, title)
MediaItem (id, edition|extra, source_fingerprint, delivery: encoded|direct)
Stem (id, media_item, kind: video|audio|sub|trickplay, spec, store,
path, profile@version, pass, state, origin: source|external, external_sub?)
ExternalSub (id, source_fingerprint, origin: file|opensubtitles|translation|ocr|transcription,
path, language, forced, sdh, opensubtitles_id?, release?, matched: hash|title)
Localized (entity, language, title, overview, …) -- métadonnées multilingues
Artwork (entity, kind, language?, file)
Person (id, name, …)
Credit (person, work|edition, role, character?, audio_language?, image_override?)
Group (id, kind: manual|dynamic, rules?, sort, parent?, pinned, landing_order)
GroupItem (group, work|edition|group, position) -- groupes manuels
Profile (id, version, rungs[], passes[], audio[], subs[], live[], keep_original) -- encodage
LadderOverride (scope: type|media, profile) -- prévu, UI plus tard
User (id, role, parent?, kids, birth_date, audio_langs[], sub_langs[],
sub_mode, ui_lang, code?) -- un compte, ou un profil d'un compte (parent)
Limits (account, downloads, max_kbps, max_streams, max_profiles, suspended, …)
Session (token_hash, account, profile?, junior) -- « Qui regarde ? », mode Jeunesse
Login (account, at, ip, agent, method) -- gardé un an
Invitation (token, role, birth_date, expires)
Device (id, user, name, token) -- appairage TV
Progress (profile, edition|episode-in-edition, position, updated) -- par édition
Watched (profile, work) -- par œuvre
Favorite (profile, work)
Impression (profile, work, shown) -- suggestions montrées sans être ouvertes
9.1 Éditions
Chaque édition est servie par un fichier de l'œuvre, choisi par l'admin (Edition.media_id). Les éditions sont ordonnées, et la première est la version principale : lue par défaut, renommable, jamais supprimée.
Un fichier peut servir plusieurs éditions, ou aucune : c'est alors une copie de rechange, gardée et encodée mais proposée à personne (« Fichiers non servis » au catalogue, « Doublons probables » dans la Maintenance).
- Un nouveau fichier rejoint l'édition que son nom indique. Un montage encore inconnu nomme la version principale s'il est le premier fichier de l'œuvre, sinon devient une nouvelle édition.
- Il sert son édition si elle n'a pas encore de fichier ; sinon c'est une copie de rechange.
- Une édition qui perd son fichier prend la meilleure copie de rechange. De même pour une édition servie par un fichier qui ne sera jamais lisible (une source abîmée, ou disparue sans rien d'encodé), dès qu'une de ses copies de rechange se lit.
Override clairsemé : une édition ne stocke que ce qui diffère de la version principale ; lecture = edition.champ ?? canonique.champ (titre, résumé, visuels, cast, âge…).
Les éditions n'ont pas la même timeline : les pistes audio appartiennent à une édition, jamais partagées. Les spectateurs ne voient que les éditions qui ont un fichier ; une seule n'affiche pas de sélecteur.
9.2 Cast
- Le thumbnail d'un membre du cast peut être spécialisé par œuvre / édition (
Credit.image_override). - Le doublage dépend de la langue audio, pas de l'édition : un crédit de doublage porte une
audio_language. L'UI affiche les voix de la langue audio du spectateur.
9.3 Séries
Découpages. Une série est découpée en saisons par un découpage : l'ordre de diffusion de TMDB, ou l'un de ses groupes d'épisodes (par année, comme TheTVDB, par arc…). Les découpages sont importés une fois avec la série, et de nouveau seulement quand l'admin la rafraîchit depuis TMDB.
- Un fichier est sur un épisode TMDB quel que soit le découpage : en changer ne déplace aucun fichier et ne perd rien de ce qui a été vu.
- L'admin choisit le découpage montré (automatiquement : l'ordre de diffusion, sauf si une saison compte plus de 100 épisodes) et ceux proposés. Chaque spectateur choisit parmi ceux proposés, sur la page de la série. Libellés, reprise, épisode suivant et épisodes manquants le suivent.
- Les épisodes spéciaux restent spéciaux (saison 0). Les épisodes spéciaux qu'un découpage laisse de côté gardent leur place de diffusion ; les autres épisodes qu'il laisse de côté vont dans Autres épisodes.
Lecture des noms de fichiers. La numérotation que suivent les noms des fichiers d'une série est trouvée d'après eux : l'ordre de diffusion, ou le découpage sous lequel le plus grand nombre tombe sur un épisode, leurs titres (dans n'importe quelle langue) et leurs durées concordant. C'est ainsi que se lisent les saisons TheTVDB de Sonarr. L'admin peut la fixer ; la changer réidentifie les fichiers, ceux placés à la main à part. Un découpage ne sert à lire les noms que s'il contient tous les épisodes (un arc jamais).
Les versions d'une série (VF, Remastered…) sont définies sur la série, dans l'ordre où les spectateurs les reçoivent ; la première qui a des fichiers est montrée d'abord. La version principale n'a pas besoin de nom. Un épisode a une édition par version dans laquelle il a un fichier.
Le spectateur choisit une version sur la page de la série et ne voit que les saisons et épisodes de cette version : rien ne se rabat sur une autre version. Une seule version n'affiche pas de sélecteur ; une seule saison, pas d'onglet ; au-delà de trois saisons, un menu.
Découpages propres. Une version peut avoir son propre découpage : la même série découpée autrement, comme dans un coffret DVD. Ses fichiers quittent les épisodes de TMDB pour des épisodes à elle, un par fichier, dans des saisons à elle (jamais la version principale). Les spectateurs voient ses saisons comme « Saison n », ses épisodes comme S1 · E1.
Fichiers et épisodes.
- Un fichier qui couvre plusieurs épisodes (ex. E01-E02) correspond à une plage : le spectateur voit un seul bloc, lu depuis son premier épisode ; les autres épisodes de la plage ne se lisent pas seuls.
- Deux fichiers sur le même épisode de la même version : un de trop, signalé (en mettre un dans une autre version).
- Des fichiers sur aucun épisode, ou dont le titre ou la durée correspondent à un autre épisode, font lister la série dans la Maintenance (Incohérences d'épisodes). Les titres ne sont comparés que là où la plupart des titres de la série concordent.
Épisodes & versions (au catalogue, pour chaque série) édite tout cela, dans le design de lot de l'Identification (§13.1) :
- La table des versions : ce que chacune contient (saisons, saisons incomplètes, épisodes, manquants, doublons, fichiers) ; ordre, noms, découpage propre, nouvelles versions. Une ligne déplie ses saisons, chacune s'édite là ; les saisons d'un découpage propre s'y ajoutent et s'y retirent.
- Les fichiers de la version travaillée (choisie dans un menu), épisode par épisode, les épisodes sans fichier servant de places où en déposer un ; à côté, les fichiers sur aucun épisode.
- Les fichiers se glissent sur un épisode (celui qui y était retourne parmi ceux à placer), plusieurs comblant les trous à partir de là ; se glissent en retour pour les retirer ; dans un découpage propre comme dernier épisode, et s'y réordonnent.
- Les fichiers sélectionnés passent à un outil dans une fenêtre : d'après les noms, un motif (
{s}saison,{e}épisode,*n'importe quoi, une espace n'importe quel séparateur ; sans{s}, la saison vient des dossiers), saison, décalage, renumérotation, version. Où irait chaque fichier est prévisualisé par le serveur, puis appliqué ; les changements récents s'annulent. - La ligne d'un épisode modifie son titre sur place et ouvre son éditeur (épisodes de TMDB et d'un découpage propre de la même façon) ; la fiche technique d'un fichier s'ouvre dans une colonne.
9.4 Groupes
- Manuels (liste ordonnée) ou dynamiques (règles, ex.
studio = Disney AND année < 1967), imbricables. - Pré-remplissage possible depuis les collections des fournisseurs (TMDB…) ; les collections dont la bibliothèque n'a pas tous les films sont listées dans la Maintenance.
- Épinglables sur l'accueil (global, par l'admin), dans l'ordre que l'admin fixe.
- Un groupe vide après filtrage (âge, public, visibilité) n'est pas affiché.
9.5 Progression
- Progression par édition (timelines différentes) ; statut vu par œuvre ; l'une et l'autre par profil.
- Reprendre montre où chaque titre s'est arrêté, avec une image prise dans le film. « Épisode suivant » reste dans la version et le découpage choisis de la série, sans repli : la version s'arrête où s'arrêtent ses fichiers.
- Les écritures de progression sont bufferisées en mémoire et flushées périodiquement (charge d'écriture SQLite).
10. Utilisateurs, accès & sécurité
10.1 Comptes & profils
Deux rôles : admin et user, dans une seule app dont l'administration est une section réservée. Un admin ne peut ni retirer son propre rôle, ni suspendre ou supprimer son propre compte.
Un compte contient des profils : 5 par défaut, lui compris (l'admin le change par compte). Une fois connecté, le spectateur en choisit un : Qui regarde ?
- Un profil a son propre nom, sa couleur, sa photo, son historique, ses favoris et ses langues, et un âge toujours inférieur ou égal à celui du compte.
- Les limites du compte, sa suspension et sa connexion (mot de passe, passkeys, appareils, sessions) sont partagées, et seul le compte les change.
- Seul le compte crée, modifie et supprime ses profils, depuis Qui regarde ? (derrière son code quand il en a un). Un profil supprimé est déconnecté là où il était choisi.
- Un code de 4 chiffres peut protéger chaque profil, tapé sur un pavé qu'une télécommande atteint. Les mauvais codes sont limités (5 en 15 minutes, 20 par jour).
Les profils Enfant ne voient que les titres pour enfants et pour tous, dans leur âge. Ils ne téléchargent jamais, ne se modifient pas eux-mêmes, et en sortir demande le code du compte (le compte en a donc un).
Le mode Jeunesse : sur un appareil (sa session), un profil adulte montre ce que montrerait un profil Enfant : les titres pour enfants, et ceux pour tous classés tous publics. Il vaut partout (accueil, listes, recherche, Reprendre) ; l'historique et les goûts du profil restent les siens. Il s'active depuis le menu du profil ; le nom de l'app se lit alors Jeunesse en lettres pastel claires. Il n'a pas de verrou : c'est un profil Enfant qui protège un enfant.
Les utilisateurs n'ont aucun contrôle éditorial. Ils lisent, choisissent leurs pistes audio et sous-titres, ajoutent des favoris, téléchargent (quand c'est permis), et règlent leurs langues : audio, sous-titres (jamais, forcés seulement, toujours), métadonnées et interface. Elles pilotent le choix automatique des pistes.
10.2 Authentification
- Invitations (admin seulement) : un lien valable 7 jours par défaut (60 au plus), à usage unique. L'admin fixe le rôle et la date de naissance ; l'invité crée un mot de passe ou une passkey (WebAuthn ; il lui faut un nom de domaine, pas une adresse IP).
- Appairage TV par code : la TV affiche un code (
XXX-XXX, 10 minutes) ; le spectateur le saisit sur<serveur>/link. - Les sessions durent 90 jours, prolongées à l'usage ; seul leur hash est stocké. Changer son mot de passe déconnecte ses autres navigateurs (pas les TV).
- Limitation de débit sur la connexion, les invitations, les passkeys, les changements de mot de passe et l'appairage (par adresse et par compte).
- La suspension refuse les nouvelles sessions et met fin aux sessions en cours.
- Connexions et visites (une session revenue après une demi-heure) sont gardées un an : quand, d'où (IP), avec quoi, comment.
10.3 Exposition Internet
Alumira Connect, en un clic : un nom sous alumira.app (martin.alumira.app) qui mène à la maison, en HTTPS.
- Son certificat vient de Let's Encrypt par DNS-01, pour le nom et
*.nom. - La box est priée d'ouvrir le port (UPnP). Les adresses (IPv4 telle que le service la voit, l'IPv6 propre de la machine) sont renvoyées toutes les 10 minutes, et l'ensemble est vérifié depuis Internet.
- Un seul port partout : 443 quand la machine le laisse à Alumira, sinon 7576, le même sur la machine, la box et dans l'adresse (l'IPv6 ne traduit aucun port).
- Rien de la vidéo ne passe par Connect : il ne fait que nommer. Une adresse locale écrite en nom (
192-168-1-31.martin.alumira.app) mène à la maison en HTTPS sans Internet. - Le service est dans
internal/connect, le côté d'Alumira dansinternal/network. Il tourne dans le serveur d'alumira.app (cmd/alumira-app: le serveur DNS de la zone, qui sert aussi les noms propres de la zone — le site, sa messagerie — depuis un fichier de zone, et l'API de Connect).
Sinon : TLS intégré avec ACME automatique (Let's Encrypt) pour un domaine à soi, ou un reverse proxy devant.
- En HTTPS, le cookie de session est un
__Host-: sous un domaine partagé, un voisin ne peut pas le masquer.
10.4 URLs signées
- Manifests, segments et téléchargements passent par des URLs signées (HMAC, valables 24 heures) : les lecteurs natifs ne peuvent pas toujours envoyer d'en-têtes d'authentification.
10.5 Limites d'âge & public
Limite d'âge. Aucun système de classification n'est exposé : tout est traduit en limite d'âge, affichée (-15) dans un rond blanc, sur le grand visuel de l'accueil et la page du titre seulement.
- La table classification → âge est intégrée à Alumira (par pays), ce n'est pas un réglage. Une classification qui n'y figure pas est signalée dans le journal quand un titre la porte, puis ajoutée à la table.
- À l'identification (ou à un rafraîchissement depuis TMDB), la classification du pays de référence (sinon celle des États-Unis, sinon la plus stricte connue) donne la limite d'âge du titre. Les classifications elles-mêmes ne sont pas stockées ; l'admin modifie ensuite l'âge.
- La limite d'âge peut différer par édition (ex. version non censurée).
- L'âge d'un spectateur vient de sa date de naissance ; un titre au-dessus est invisible. Les titres non classés sont invisibles pour les mineurs.
Public : à qui s'adresse un titre, fixé par l'admin.
- Enfant : profils Enfant (et mode Jeunesse) seulement. Les listes d'un adulte ne montrent jamais ces titres ; demandés par leur nom (la recherche, après les autres ; la page du titre ; leur lecture), ils sont trouvés, et le poster le dit.
- Adulte : jamais dans un profil Enfant.
- Tous.
Il est fixé une fois par une règle quand un titre est identifié (genres famille, animation ou enfants et un âge bas ; le genre enfants de TMDB : Enfant), puis seulement modifié. Les saisons et épisodes d'une série suivent la série.
Le filtrage est appliqué côté serveur, partout : listes, recherche, pages cast, groupes, accès URL direct, émission d'URLs signées, images, téléchargements. Une page d'admin dédiée modifie la limite d'âge de chaque titre, classé ou non, avec des actions par lot.
11. Bande passante & sessions
Contrainte : un manifest HLS VOD est chargé une fois par le lecteur ; on ne peut pas retirer un rung en cours de lecture.
- Budget d'upload global : mesuré à l'onboarding (§13.4), puis confirmé ou ajusté par l'admin ; modifiable ensuite dans les Paramètres. Le réseau local n'est jamais plafonné.
- Admission à l'ouverture d'une session de lecture : le serveur plafonne le rung maximal du manifest émis selon le budget.
- Throttling par session (token bucket) : en cas de saturation, les lectures se partagent le budget équitablement, selon ce que chacune utilise vraiment, et le lecteur descend de lui-même.
- La lecture avant les téléchargements : les téléchargements n'utilisent que ce que les lectures laissent ; au-delà ils sont mis en attente (priés de revenir).
- Limites par compte, fixées par l'admin : lectures simultanées, débit, résolution maximale, codecs, HDR, stéréo, téléchargements.
12. Client web & UX
12.1 Contraintes
- Le client web est aussi le futur client webOS / Tizen : bundle léger, JS/CSS prudents (les navigateurs TV ont du retard sur Chromium).
- Navigation D-pad dès le premier jour : toute l'UI est utilisable avec flèches + OK, focus toujours visible. Un petit module maison, impératif, déplace le focus dans l'espace.
- Framework : SolidJS + TypeScript + Vite. Pas de virtual DOM (réactivité fine) : meilleures perfs sur les CPU TV faibles, bundle de quelques Ko. Cible de build prudente (ES2017).
- Listes longues (20k films) : grilles toujours virtualisées.
12.2 Accueil
Un grand visuel, puis des rangées, un titre n'apparaissant que dans une seule, dans cet ordre :
- Le grand visuel : tiré des meilleures suggestions qui ont une image de fond, pas celles des dernières visites.
- Reprendre : où chaque titre s'est arrêté, avec une image prise dans le film.
- Pour vous.
- Nouveautés : les plus récentes d'abord, jamais le fonds de la bibliothèque. Un titre est nouveau s'il est arrivé une semaine après que la moitié de la bibliothèque était là (une bibliothèque importée d'un coup n'a rien de nouveau tant que rien n'arrive).
- Les mieux notés : les meilleurs pas déjà montrés au-dessus.
- Les collections épinglées, dans l'ordre de l'admin.
- À découvrir.
Suggestions. Chaque titre qu'un profil peut voir est noté en points :
- sa note (deux points par étoile ; aucune : la moyenne de la bibliothèque) ;
- les genres que le profil regarde ces temps-ci, épisode par épisode, les plus récents comptant le plus ;
- sa langue originale, pour une langue que le profil regarde plus que la bibliothèque n'en contient ;
- sa collection, quand elle vaut la moyenne ; sa fraîcheur ; le boost de l'admin ;
- moins le nombre de fois qu'il a été montré sans être ouvert. Un titre en cours, ou vu dans l'année, est écarté.
Les rangées sont tirées de ces notes, pondérées, et restent les mêmes pendant une visite. La page Suggestions de l'admin dit pourquoi chaque titre est où il est.
La carte d'une série dit combien de saisons il y a à voir (« 2022 • 3 saisons »).
12.3 Page du titre
- Visuel plein cadre ou diaporama (§8.5), logo, résumé, étoiles, limite d'âge.
- Un sélecteur de version, quand il y en a plus d'une (au niveau de la série pour les séries) ; les saisons en onglets, ou un menu au-delà de trois ; un menu de découpage quand l'admin en propose plus d'un.
- Lecture ou Reprendre, Depuis le début, favori, téléchargement (seulement quand c'est possible : §8.4).
- Bonus, cast (portraits) et voix de la langue audio du spectateur, puis les détails : titre original, genres, réalisation, studios, collections.
12.4 Lecteur
- Lecteur : hls.js sur un
<video>nu, avec une UI de lecteur maison. Centré HLS (pas de DASH), léger, répandu sur les TV ; gère ManagedMediaSource (Safari iOS). - Pistes : audio et sous-titres choisis automatiquement d'après les préférences du profil, modifiables en cours de lecture sans perdre ce qui est en mémoire. Les sous-titres forcés s'appliquent automatiquement.
- Sous-titres stylés (libass en WebAssembly, JASSUB) et sous-titres en images dessinés en overlay par notre UI.
- Chapitres (nommés dans la langue de l'interface) et vignettes de navigation.
Choix de la qualité (web/src/abr.ts, à la place de celui de hls.js) : chaque fragment arrivé aussi vite que le lien le porte est mesuré (sa vitesse, son attente du premier octet).
- Un lien régulier saute aussitôt à ce qu'il porte la plupart du temps. Un lien par à-coups monte à mesure que son tampon se remplit, jusqu'à sa vitesse habituelle.
- Il descend sous 20 s de tampon, aussitôt sous 10 s.
- Il charge 40 s d'avance à la maison, cinq minutes hors de la maison.
Un panneau de statistiques (technique par nature) montre le codec, la qualité et le débit de chaque flux, le segment en cours de chargement et la vitesse. Les admins peuvent lire n'importe quel fichier par-dessus une page pour voir ce que c'est, sans garder sa progression.
12.5 Localisation
- L'UI est écrite en anglais ; les autres langues sont des dictionnaires indexés par la chaîne anglaise, chargés à la demande : français, allemand, espagnol, italien, portugais, néerlandais, russe, japonais, coréen, chinois. Les messages du serveur affichés dans l'UI sont traduits de la même façon. Un nouveau compte suit la langue du navigateur quand Alumira l'a.
- Les métadonnées sont localisées dans la langue du spectateur, avec repli sur la langue originale.
- La recherche couvre le titre original, les titres traduits et alternatifs, et les personnes (SQLite FTS5). Les noms sont triés comme ils se lisent : accents, casse et ligatures ignorés.
12.6 Application installable & application de bureau
App web installable : un manifeste et ses icônes (dont une avec la marge d'Android). Chrome et Edge proposent « Installer », Safari « Ajouter au Dock » ou « Sur l'écran d'accueil » : Alumira s'ouvre dans une fenêtre à lui, sans les barres du navigateur (sur un iPhone ses cookies sont les siens : une connexion de plus).
Application de bureau (cmd/alumira-desktop, macOS et Windows, Wails v3 sur le moteur web du système : WebKit, WebView2) : l'app web dans une fenêtre à elle, et une icône dans la barre des menus ou la zone de notification tant qu'elle tourne (un clic : la fenêtre ; un clic droit : son menu).
À son premier lancement elle demande :
- héberger le serveur ici : démarré dans le même programme, sur le port 7575 (ou
ALUMIRA_LISTEN), ses données là où la ligne de commande les garde. Fermer la fenêtre la cache ; le serveur continue pour les autres appareils. Son menu donne l'adresse sur le réseau, le démarrage avec l'ordinateur, le journal, et quitter (qui l'arrête comme une interruption) ; - ou utiliser un serveur du réseau : ceux entendus sont listés, ou une adresse se saisit (vérifiée via
/api/status).
FFmpeg absent, un port pris, un serveur qui ne répond pas : dit dans la fenêtre, avec quoi faire et un bouton pour réessayer. Une seule instance à la fois ; lancée avec l'ordinateur, l'icône seulement. Sur macOS, le Dock la montre tant que sa fenêtre est ouverte.
Trouvé sur le réseau : un serveur qui écoute au-delà de sa machine le fait savoir (mDNS / DNS-SD, _alumira._tcp, sous son nom, sinon celui de sa machine) ; l'application de bureau liste ceux qu'elle entend. Sous Docker, seulement avec le réseau de l'hôte.
Paquets : une image disque pour macOS (une app pour puces Apple et Intel, macOS 13 et plus) et un installeur pour Windows (NSIS : Program Files, menu Démarrer, une règle de pare-feu pour le réseau privé ; la désinstallation laisse les données), construits par scripts/build-desktop.sh et inclus dans scripts/release.sh.
Les deux embarquent le FFmpeg d'Alumira (jellyfin-ffmpeg avec VMAF, construit depuis github.com/ejulien/jellyfin-ffmpeg), dans un dossier ffmpeg à côté du programme, là où Alumira regarde en premier : rien d'autre à installer. Ils ne sont pas signés : macOS demande d'autoriser l'app une fois, SmartScreen de l'exécuter quand même.
13. Administration
13.1 Pages
Le menu, dans son ordre :
- Normalisation : ce qui s'encode maintenant et sur quelle machine, ce qui vient ensuite, les tâches terminées récemment, workers, stores (temps réel ; pas d'estimation de durée, trop dépendante des workers).
- Identification : les fichiers à identifier, un par un ou par lot (§5.2).
- Maintenance : ce qui demande une intervention (§13.2), avec les doublons probables et les épisodes et films qui manquent à la bibliothèque.
- Tâches : toutes les tâches de fond (analyse, identification, passes d'encodage, contrôles qualité, tâches IA), par état et par type, avec annulation et relance ; chaque tâche IA affiche ses tokens.
- Catalogue : chaque titre, avec sa source dessous (son fichier, ou le dossier que partagent ses fichiers).
- Un titre se réidentifie depuis une loupe, sans quitter la liste.
- Note, limite d'âge, public et boost se modifient sur place, ou pour beaucoup de titres à la fois (demandé d'abord à partir de dix).
- Chaque fiche de titre se modifie à la main : informations, textes dans chaque langue, visuels, cast et équipe, voix, versions et fichiers, saisons et épisodes, images du film ; Rafraîchir depuis TMDB. Pour une série, Épisodes & versions (§9.3).
- Collections : manuelles ou dynamiques, épinglées, ordonnées sur l'accueil.
- Limites d'âge : la limite d'âge de n'importe quel titre, classé ou non, un par un ou par lot.
- Suggestions : les suggestions d'un profil, les meilleures d'abord, la note de chaque titre expliquée.
- Utilisateurs : les comptes avec leurs profils, et les invitations.
- Filtres (recherche, rôle, état, dernière visite) gardés dans l'adresse ; actions sur plusieurs (suspendre, réactiver, déconnecter partout, supprimer), jamais sur soi.
- Par compte : rôle, date de naissance, limites (§11), profils permis.
- Une bulle d'information dit ses dernières visites, ses jours actifs, son temps de visionnage (en tout et par titre), ses appareils et ses lieux (IP).
- Sources : ajout, mode, état du scan.
- Diffusion : chaque fichier pré-encodé ou en lecture directe (§4.2).
- Stockage : ajout, capacité, placement.
- Profils : passes d'encodage, l'échelle et les places à la volée, versionnage, estimation de taille.
- Workers : appairage, capacités, signatures, état, chunks simultanés, pause.
- Statut : la machine (processeur, puce vidéo, mémoire) et les chiffres du moment.
- Ce qui se lit et tourne en ce moment ; chaque lecture : qui, où elle en est dans le titre, son tampon, les qualités qu'elle a reçues, son débit sur les deux dernières minutes.
- Des graphiques de 10 minutes à 30 jours, zoomés ensemble : débit montant empilé par spectateur, lectures, tâches, heures les plus chargées.
- La vie du serveur depuis son démarrage : records, ce qu'il a servi et encodé, ce qui a été vu.
- Journal : chaque événement du serveur (ce qu'affiche le terminal).
- Gardé sur disque en lignes JSON, dans des fichiers qui tournent (une taille réglée dans les Paramètres, 100 Mo par défaut, les plus anciens écartés).
- Chaque événement a son niveau (débogage, info, avertissement, erreur), son système (serveur, sources, catalogue, encodage, lecture, images, sous-titres, IA, stockage, comptes, tâches) et ses lignes.
- La page les filtre par niveau, système, texte et période (gardés dans l'URL), suit les nouveaux tant que son haut est visible, et relie les médias nommés à leur fiche. Ce qui est affiché se télécharge en texte ; le journal entier en zip.
- Les événements de débogage (dont chaque commande FFmpeg) seulement s'ils sont activés dans les Paramètres, ou avec
ALUMIRA_DEBUG_FF=1.
- Sauvegardes : snapshots planifiés de la base (le bien le plus précieux : matching, éditions, groupes, modifications), chacun téléchargeable ; une sauvegarde à la demande.
- Paramètres : catalogue, langues, OpenSubtitles, fonctions IA, accès réseau, partage du débit montant, journal.
L'édition par lot a partout le même aspect : la table de l'Identification, son en-tête de sélection et ses fenêtres d'outils. La vignette de chaque fichier dans l'administration le lit.
13.2 Maintenance
Dans la première partie du menu : ce qui demande une intervention et ce qui manque à la bibliothèque, le plus urgent d'abord, un onglet par sorte présente, avec des actions par lot (Tout sélectionner allant au-delà des éléments listés). La page suit les tâches qui la changent.
- Normalisations échouées.
- Encodages incomplets (pistes audio laissées de côté).
- Transcodage indisponible (§4.4).
- Sources disparues qui bloquent un encodage, ou un fichier en lecture directe.
- Sources trop abîmées pour être encodées (à remplacer).
- Qualité insuffisante (une rendition sous 85 VMAF).
- Doublons probables.
- Fichiers non identifiés.
- Incohérences d'épisodes (§9.3).
- Pistes en langue inconnue (une langue à donner à chacune, ou la Reconnaissance IA).
- Genres inconnus.
- Stems d'un autre profil (§6.12).
- Épisodes manquants : épisodes diffusés qu'aucun fichier n'apporte (certains peuvent être mis de côté).
- Épisodes sans image.
- Collections incomplètes.
- Qualité non mesurée.
- Sous-titres manquants dans une langue par défaut (recherche OpenSubtitles dans le quota du jour, faits en écoutant, traduits).
- Plusieurs sous-titres pour une langue.
- Sous-titres en images (à lire en texte).
- Chapitres sans nom ; sans chapitres.
- Images plus nettes disponibles ; films sans images du film.
- Copies en trop d'un média dans une autre store (§8.1).
- Sources disparues sans perte.
13.3 Fonctions IA
Dans les Paramètres : un fournisseur (OpenAI, Anthropic, Grok de xAI, ou toute API compatible avec celle d'OpenAI, y compris une locale : Ollama, LM Studio, vLLM…) et un modèle par usage, choisi dans la liste du fournisseur, ou aucun :
- Sous-titrage : traduire des sous-titres, écrire ceux entendus, reconnaître la langue des pistes ;
- Écoute : sous-titres depuis la parole ;
- Lecture des sous-titres en images ;
- Chapitrage : nommer les chapitres, et en créer là où il n'y en a pas.
Les modèles ne sont appelés que par des tâches, à la demande, dans une file à part qui n'attend jamais un encodage ni n'en retient un. Combien tournent à la fois est un réglage (2 par défaut, 16 au plus). Le fournisseur facture chaque appel au titulaire du compte.
- Une clé est vérifiée dès qu'elle est saisie, en listant les modèles du fournisseur.
- Chaque tâche affiche les tokens qu'elle consomme à mesure : lus (dont ceux que le service avait en cache) et écrits ; le total reste dans l'historique des tâches.
- Les échecs passagers (limites de débit, service surchargé) sont retentés. Un appel de plus de 20 s, ou retenté, est signalé dans le journal : quand sa réponse a commencé, quand son texte a commencé, ses tokens de réflexion, pourquoi ses essais ont échoué.
13.4 Onboarding
Au premier lancement, l'admin passe par un onboarding qui pose les questions clés pour obtenir une installation fonctionnelle. Principes :
- Chaque étape a des valeurs par défaut sensées ; tout est modifiable plus tard dans l'administration.
- L'onboarding est reprenable : son état est persisté, une interruption ne perd rien.
- Aucun scan ni encodage ne démarre avant la confirmation finale.
Étapes :
- Compte admin : nom d'utilisateur, mot de passe ou passkey, date de naissance, langue de l'UI (qui fixe aussi les langues audio, sous-titres et métadonnées par défaut, et le pays des classifications d'âge).
- Accès réseau : Alumira Connect en un clic ; ou, à la main, URL publique, mode TLS (ACME intégré / reverse proxy / réseau local seulement), port.
- Stores : au moins une. Vérification d'écriture, espace libre, débit disque mesuré. Ordre de la liste (§8.1).
- Sources : zéro ou plusieurs dossiers, chacun pré-encodé, pré-encodé puis supprimé (
inbox), ou en lecture directe. Pré-scan rapide (nombre et volume de fichiers). - Encodage : détection des capacités du worker intégré (encodages de test), choix
quality/speed. - Ladder : un point de départ (§6.4), modifiable, garder ou non une copie de l'original, et l'estimation de stockage (volume des sources × ladder) face à la capacité des stores.
- Métadonnées : clé API TMDB, avec un lien vers la page de création de clé TMDB (ouvert dans un nouvel onglet) ; langues des métadonnées ; pays dont les classifications fixent les limites d'âge ; notes IMDb.
- Bande passante : mesure du débit montant (sur accord, via un service de test de débit externe ; saisie manuelle en repli), puis le budget (§11).
- Langues par défaut des nouveaux utilisateurs (audio, sous-titres et quand les afficher).
- Sauvegardes : emplacement et fréquence des snapshots de la base.
- Invitations : premiers utilisateurs (facultatif).
- Récapitulatif → le scan et les queues démarrent.
14. Stack technique
| Élément | Choix |
|---|---|
| Serveur & worker | Go, style impératif (structs + fonctions), sans cgo → binaire statique, cross-compilation triviale. |
| Base de données | SQLite (WAL), driver pur Go (modernc.org/sqlite), FTS5 pour la recherche ; un seul écrivain, des lecteurs en parallèle. Couche d'accès fine pour permettre Postgres plus tard. |
| Média | FFmpeg / ffprobe en sous-process (isolation des crashs ; un programme à part, jamais lié), le build installé sur la machine. |
| Scaling / tone-mapping | libplacebo (Vulkan / MoltenVK), zscale ; ceux de la puce vidéo pour le transcodage à la volée. |
| Packaging | HLS CMAF fMP4, manifests dynamiques. |
| Client | TypeScript, SolidJS, Vite, hls.js. |
| Application de bureau | Wails v3 sur le moteur web du système. |
| Accès à distance | Alumira Connect : son propre serveur DNS (miekg/dns), ACME via x/crypto/acme. |
15. Ordres de grandeur
Alumira est fait pour la bibliothèque d'un foyer, et dimensionné pour qu'une grande tienne sur un matériel modeste : 20 000 films, 1 000 comptes.
| Sujet | Ordre de grandeur | Conséquence |
|---|---|---|
| Stockage | 20k × 10–20 Go ≈ 200–400 To (hors séries et copies de l'original) | Plusieurs stores et le placement sont indispensables ; la lecture directe ne stocke rien deux fois. |
| Encodage | ~40 000 h de contenu | Un import initial de plusieurs semaines/mois → passes ordonnées et priorités cruciales. |
| Streaming | 50–100 flux simultanés × ~15 Mbps (4K) ≈ 1–1,5 Gbps | Limité par le disque et le réseau, pas le CPU → contrôle de bande passante (§11) ; transcodage à la volée limité à quelques places. |
| Base | ~100k entrées média (épisodes compris) | SQLite suffit avec un seul écrivain, des écritures de progression bufferisées, et des pages d'admin lues en quelques requêtes. |