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 une collection personnelle de films et de séries. Il lit les fichiers là où ils sont, les identifie, et encode chacun une seule fois, à l'avance, en une échelle de qualités ; la lecture sert ensuite ces fichiers prêts en streaming adaptatif à une interface web construite autour des visuels, sur ordinateur, téléphone ou télé avec sa télécommande.
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.
- Pré-normalisation, jamais de transcodage à la lecture. Le serveur ne fait que servir des octets pendant la lecture. Qualité et comportement prévisibles.
- Stems éclatés, recomposés au service. Chaque piste (vidéo par rendition, audio par langue/layout, 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 mode
inboxexplicite. - Code impératif et simple. Pas d'OOP, favoriser la vitesse de build et d'itération.
1. Périmètre
V1
- Serveur headless (Windows, macOS, Linux) avec worker d'encodage intégré.
- Une seule app web, avec rôles (admin / utilisateur) ; l'admin est une section de l'app.
- Films, séries, éditions (variantes), bonus, groupes.
- Multi-utilisateur, restriction par âge, favoris, reprise de lecture.
- Sources de type dossier/disque uniquement.
- Workers d'encodage additionnels sur le LAN.
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 dès la V1, cf. §6.10).
- 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 Bluray, correction du PAL speedup).
- OCR des sous-titres image.
- Dolby Vision.
- Recommandations, playlists et tout contrôle éditorial côté utilisateur.
2. Glossaire
| Terme | Définition |
|---|---|
| Source | Emplacement (dossier/disque) scanné pour trouver des fichiers originaux. Lecture seule (watch), consommée (inbox), ou lue telle quelle (direct). |
| 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 | Configuration nommée et versionnée de la ladder, éditable par l'admin. |
| 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 | Variante d'une œuvre (« 1977 Cut », « Bluray 2011 », « Deluxe »…). Une édition est canonique. |
| Extra | Bonus (making-of, interview…) rattaché à un Work ou à une Edition. |
| Group | Méta-entrée regroupant des médias (manuelle ou dynamique). |
| 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 │ │
│ ▼ │
│ SQLite ◀── métadonnées Stores (stems fMP4)
│ │ │
│ HTTP : app web + API + manifests HLS ◀─┘ │
└──────────────▲──────────────────────▲─────────┘
│ │
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) et les téléchargements.
4. Sources & catalogue d'empreintes
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 du média complet, le fichier source est supprimé (avec les sous-titres à côté et les dossiers qu'il laisse vides) ; pas de corbeille. Le fichier lui-même est perdu ; il en reste ce que garde son profil : ses pistes telles quelles, rien de réencodé (Original, §6.4, par défaut pour les dossiers de dépôt), à côté de ses versions encodées, ou ses seules versions encodées, comme le dit la fenêtre d'ajout de source. |
direct |
Lecture directe, en lecture seule comme watch : rien n'est encodé ni stocké à l'avance (sauf de petits extras). Chaque fichier est préparé une fois arrivé : les images clés de sa vidéo lues (ses paquets seulement), des segments d'au moins 4 s faits sur elles ; ses sous-titres, vignettes de navigation (depuis ses images clés seulement, décodées par la puce vidéo quand elle le peut, celles d'un fichier HDR converties en SDR ; leur avancement suivi) et chapitres faits comme pour un fichier encodé ; ses pistes audio en AAC lues telles quelles, tout autre codec (AC-3, DTS, TrueHD…) converti une fois en AAC (logiciel : pas de matériel audio ; petit). Il se lit dès qu'il est identifié et préparé. Il se lit en HLS comme les autres : sa vidéo et ses pistes AAC sont des rendus virtuels dont ffmpeg copie les segments depuis le fichier à la demande (sans encodage) : lus avec les temps du fichier, coupés exactement aux images clés du segment (les fragments vidéo d'avant écartés, le son coupé exactement), chaque fragment remis à son temps, toutes les pistes 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, un saut se lit aussitôt. Un appareil qui ne la décode pas, ou dont la connexion est trop lente pour elle, reçoit les qualités transcodées à la volée (ci-dessous). Un fichier en lecture directe dont la source a disparu est illisible (Maintenance : sources disparues, bloquantes). |
En mode inbox, l'admin est averti qu'un profil ajouté plus tard ne pourra partir que du meilleur stem existant (ou du rung Original, cf. §6.4).
Transcodage à la volée : pour les fichiers en lecture directe, chaque profil 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 1080p 8 Mb/s, 720p 4,5 Mb/s, 480p 1,8 Mb/s, 360p 900 kb/s, 240p 450 kb/s en H.264) ; la changer ne réencode rien. Ses qualités sont des rendus virtuels à côté du fichier lui-même, coupés sur les mêmes segments (un lecteur passe de l'un à l'autre sans coupure) : le même menu de qualité et la même adaptation à la connexion que les fichiers encodés. 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. Vidéo par l'encodeur matériel uniquement (VideoToolbox, NVENC, Quick Sync, AMF, VA-API : sur serveurs et NAS aussi, Synology, QNAP, Unraid avec un iGPU Intel ou une carte NVIDIA ; cartes ARM via V4L2 plus tard), décodée par le matériel quand il le peut, sans images B ; un serveur sans encodeur matériel n'en propose aucune. Sur une puce Intel l'image n'en sort pas, et y est réduite comme pour les encodages (§6.3) : par moitiés au plus, avec le meilleur filtre de la puce (scale_vaapi en mode hq, quand son pilote l'a) ; les vignettes de navigation des fichiers en lecture directe aussi. 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 : toutes les qualités dans la même plage que le fichier, pas de saut de luminosité quand le lecteur change de qualité ; sans encodeur matériel pour cela, le fichier seul), et est ramené en SDR pour tout autre. 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, et s'arrête quand plus personne ne 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 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.2 Détection
- Dossiers locaux 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). Dossiers réseau (NFS, SMB, FUSE… : les changements faits par d'autres machines ne déclenchent aucun événement) 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'un 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 (copie en cours).
4.3 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.4 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 : le média reste disponible (il ne dépend plus de la source une fois normalisé).
- Les incohérences (doublons probables à empreinte différente, sources disparues, etc.) remontent dans la section maintenance de l'admin (§13).
5. Pipeline & queues
détection ─▶ probe ─┬─▶ identification ─────────────┐
├─▶ normalisation (passes 1..n) ├─▶ visible
└─▶ images (dérivés) ┘
QA (VMAF échantillonné) ─▶ maintenance
- Queues indépendantes : identification, normalisation, images, QA. 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 pour les utilisateurs que lorsqu'il est complètement catalogué : identification terminée ET pass 1 terminée. Un média non identifiable reste invisible et remonte dans la maintenance pour matching manuel.
- Priorités de la queue de normalisation : pass 1 de tous les médias avant les passes suivantes (maximiser le nombre de médias visibles pendant l'import initial).
- Chaque job est rejouable et idempotent ; les erreurs sont visibles dans l'admin.
5.1 Identification & fournisseurs de métadonnées
- Fournisseurs : TMDB et IMDb.
- TMDB : source principale (métadonnées localisées, artwork, cast, collections, classifications par pays, ordres alternatifs d'épisodes). Nécessite une clé API et une attribution ; usage commercial soumis à licence.
- IMDb : pas d'API publique gratuite (API officielle payante via AWS Data Exchange ; scraping contraire aux CGU). Utilisé via l'
imdb_idfourni par TMDB comme identifiant pivot, et via les jeux de données non commerciaux IMDb (notes). - Clé TMDB fournie par chaque install, saisie à l'onboarding (§13.1).
6. Normalisation
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 le trickplay (§6.8).
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.
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. Trickplay. 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 | Original : remux passthrough des pistes source vers la store (sans réencodage). Ne dépend de rien, le plus long à finir. Activé par défaut pour les sources inbox, désactivé pour les sources watch. |
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
- 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). GOP 2 s, segments 6 s.
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).
- Audio léger : une version stéréo légère de chaque langue (palier
light), dans un groupe audio à part (aac-light,opus-light) que le manifeste 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 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 l'affaire du lecteur). Utilisée seulement si chaque langue en a une. - Piste nuit (dynamique compressée, stéréo) : option de profil, désactivée par défaut. Coût : ~1 stem stéréo par langue.
- L'audio n'est jamais chunké (problèmes de priming aux jonctions) : encodé en une passe, indépendamment des chunks vidéo.
- Chaque piste source à part : son propre FFmpeg (à plusieurs, une piste mono pouvait s'arrêter en route sans erreur), gardée dès qu'elle est faite (jamais réencodée), son propre échec. 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.
- Langues identifiées en BCP-47 (ex.
fr-FR/fr-CApour VFF / VFQ).
6.7 Sous-titres
| Source | Traitement |
|---|---|
| Texte (SRT, WebVTT, mov_text…) | → WebVTT. |
| ASS/SSA stylé | → WebVTT (fallback universel) + original conservé pour rendu riche côté client (libass wasm, type 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 (ci-dessous). |
- Flags conservés et exposés : forcé, SDH, langue BCP-47.
- 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) : une piste de la source la garde par son flux, appliquée à chaque lecture de l'analyse de la source, si bien que 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. - 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 (ci-dessous) 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.
- Sous-titres en images lus en texte (facultatif, payant) : 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 est un fichier de sous-titres comme un téléchargé (
ocr, stems nommésext.ocr-<flux>) qui remplace les images : il se traduit, et n'est jamais relu. Un fichier WebVTT qui n'est pas dans la source est pris tel quel (ffmpeg perdrait la place des répliques). - Fichiers de sous-titres qui ne sont pas dans la source (§9) : 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. - 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 ; pris seulement 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.
- Traduction par un modèle de langage (à la demande) : depuis la fiche d'un média, 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 de sous-titres comme un téléchargé (
translation), ses stems marqués « traduction automatique » pour l'admin (le lecteur ne le dit pas ; leur nom commence parext.tr-). Les tâches qui appellent un modèle (traductions, lectures) ont leur propre file, sans jamais attendre un encodage ni en retenir un ; combien tournent à la fois est un réglage (2 par défaut, 16 au plus) : chaque appel est facturé. Dans la fiche d'un média, tout fichier ajouté hors des passes (à côté de la source, téléchargé, traduit, lu) est dans une section Ajouts après les passes. - Sous-titres depuis la parole (à la demande ; payant) : 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, proposés comme Créer dans les sous-titres manquants du média quand il n'en a pas (à 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 (chez le fournisseur des paramètres IA : Grok Voice Transcribe de xAI pour l'instant, celui d'OpenAI à venir ; facturé à l'heure de son) entend la piste : 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 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é (ce qu'un modèle invente sur la musique, des crédits de sous-titres ou une longue phrase en boucle, écartés), sont découpés en répliques comme le ferait un sous-titreur : 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 (celui qui les traduit), 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 : 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 de sous-titres comme une traduction (
transcription, stems nommésext.stt-<flux>), dont les traductions peuvent partir. 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. Un appel à un modèle qui dure plus de 20 s, ou qui est relancé, est signalé dans le journal : quand sa réponse a commencé, quand son texte a commencé, les tokens de sa réflexion, pourquoi ses essais ont échoué.
6.8 Trickplay & chapitres
- Trickplay généré depuis un étage basse résolution de la chaîne (pass 1) : planches de vignettes (web, Android) ; playlist I-frame HLS pour Apple (plus tard).
- Chapitres extraits de la source autant que possible, exposés dans le lecteur.
- Chapitres sans nom (numérotés par leur outil d'authoring : « Chapter 01 », « Scene 2 », « 第3章 », un horaire, rien) 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 dans les réglages) 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.jsondu stockage ; 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 (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 (ffmpeg : changements de plan et leur force, passages noirs, silences d'une rendition audio ; une à deux minutes de décodage) et gardés. À 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, 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, à côté de ceux de la source (aucun), et un réencodage les garde.
6.9 Contrôle qualité
- Mesure VMAF échantillonnée sur chaque rendition. Les anomalies remontent dans la maintenance.
- Un second VMAF 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é : à hauteur égale sur le premier VMAF, sinon le codec le plus efficace s'il est aussi moins cher) ; les 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.10 Profils : versionnage & résolution
- Les profils sont versionnés. 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 en 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), ce qui garde les stems que le profil actuel produit déjà (rangés dans leur passe), retire les autres d'emblée, puis lance les passes en mode complément, qui n'encodent 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 dès la V1 ; son UI viendra plus tard. - L'admin voit une estimation de taille lorsqu'il édite un profil.
7. Workers & distribution
7.1 Workers
- Le serveur embarque un worker. Des workers additionnels peuvent être ajoutés sur le LAN.
- Appairage par token, trafic en TLS.
- FFmpeg appelé en sous-process : celui installé sur la machine, jamais fourni (cherché via
-ffmpeg/ALUMIRA_FFMPEG, un dossierffmpeg/à côté de l'exécutable, puis lePATH). - Capacités 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. 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).
- 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.
- Les chunks sont concaténés en stems par le serveur, 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.
- L'audio, les sous-titres et l'Original sont traités hors chunks (§6.6), chaque étape gardée dès qu'elle est finie.
- Un job en cours indique son étape sous la forme
cléouclé: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 FS partagé requis, pas de mapping de chemins entre OS.
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).
- Les stems d'une source sont dans
- 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 ; lisible 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é, seul ce qui manque est encodé, et rien quand ses stems suivent un autre profil (upgrade à la demande, §6.10). 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 en 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, 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.3).
8.4 Téléchargement
- L'utilisateur choisit une combinaison précise (ex. 720p SDR + Opus fr-FR 2.0 + sous-titres en).
- Le fichier est un remux à la volée (MP4 ou MKV) des stems choisis, sans réencodage, streamé pendant sa construction.
- Ouvert à tout utilisateur authentifié, soumis au contrôle de bande passante (§11).
8.5 Images
- Queue dédiée : génération de dérivés (tailles multiples) en JPEG, plus AVIF quand la machine encode l’AV1, sinon WebP, servis avec cache agressif (le meilleur format que le navigateur accepte). 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. Les images faites avant reçoivent leur hash par une tâche de fond, toutes d'abord (quelques millisecondes chacune), puis leur WebP (un FFmpeg par image). 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é). Le diaporama garde son horloge : chaque créneau montre le flou, puis une petite taille, puis la pleine, chacune une fois décodée.
- Artwork localisé : posters, backdrops, logos transparents, portraits cast. Extraction des couleurs dominantes 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 film (sa page, le grand visuel de l'accueil, le lecteur) : l'image de fond TMDB par défaut, ou un diaporama d'images prises dans le film (6 s chacune, la suivante apparaissant en fondu en une demi-seconde, dans leur ordre, communes à ses versions ; tous les diaporamas suivent une même horloge : image, fondu et glissement sont ceux de la tranche de 6 s en cours, si bien qu'il continue sans saut de l'accueil à la page du titre et au lecteur, ou après une page quittée puis retrouvée). 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 : 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, les images depuis l'image clé précédente y restent, seule celle demandée en sort), par le processeur quand la puce échoue ; en faisant défiler, seule l'image demandée en dernier est prise (la précédente arrêtée). Ses bandes noires sont retirées, y compris pour un fichier en lecture directe (jamais analysé) : trouvées sur les vignettes de navigation, 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) ; les images prises avec sont reprises une fois. 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 ; les images prises avant que ce soit noté sont reprises une fois. Autant que voulu, réordonnées par glisser-déposer. Ou 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 prises en pleine taille, les plus nettes gardées, dans l'ordre du film. Depuis la fiche du film (remplace ses images ; seulement une fois ses vignettes faites), ou par lots depuis la Maintenance, qui liste les films sans images du film.
9. Modèle de données
Esquisse conceptuelle (pas un schéma SQL).
SourceRoot (id, chemin, mode: watch|inbox)
SourceFile (empreinte_rapide, hash_complet?, taille, probe, moviehash?) -- catalogue, gardé quand le fichier disparaît
SourcePath (empreinte, source_root, chemin, vu_le) -- 0..n chemins
Store (id, chemin, capacité, politique)
Work (id, kind: movie|series|season|episode, parent?, numéros/ordres)
Edition (id, work, nom, canonique: bool, âge_min?, champs override clairsemés)
Extra (id, work|edition, kind, titre)
MediaItem (id, edition|extra, empreinte_source) -- ce qui est normalisé
Stem (id, media_item, kind: video|audio|sub|trickplay, spec, store,
chemin, profil@version, pass, état, origine: source|externe, sous_titre_externe?)
ExternalSub (id, empreinte_source, origine: fichier|opensubtitles, chemin, langue,
forcé, sdh, id_opensubtitles?, version?, correspondance: empreinte|titre)
Localized (entité, langue, titre, résumé, …) -- métadonnées multilingues
Artwork (entité, kind, langue?, fichier)
Person (id, nom, …)
Credit (person, work|edition, rôle, personnage?, langue_audio?, image_override?)
Group (id, kind: manual|dynamic, règles?, tri, parent?, épinglé, ordre_landing)
GroupItem (group, work|edition|group, position) -- groupes manuels
Profile (id, version, rungs[], passes[], audio[], subs[], options)
LadderOverride (scope: type|média, profil) -- prévu, UI plus tard
User (id, rôle, date_naissance, langues_audio[], langues_sub[], langue_ui)
Invitation (token, rôle, expire)
Device (id, user, nom, token) -- appairage TV
Progress (user, edition|episode-dans-édition, position, maj) -- par édition
Watched (user, work) -- par œuvre
Favorite (user, work)
9.1 Éditions
- Une édition canonique par œuvre ; les autres éditions héritent d'elle.
- 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 » en 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), et la sert 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 (source abîmée ou disparue, rien d'encodé), dès qu'une de ses copies de rechange se lit (un fichier abîmé remplacé par un bon). - Override clairsemé : une édition ne stocke que ce qui diffère ; lecture =
edition.champ ?? canonique.champ(titre, résumé, artwork, cast, âge…). - Les éditions n'ont pas la même timeline : les pistes audio appartiennent à une édition, jamais partagées.
- Séries : cf. §9.3.
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
langue_audio. L'UI affiche le cast correspondant à la langue audio de l'utilisateur.
9.3 Séries
- Les versions d'une série (VF, Remastered…) sont définies sur la série, ordonnées ; la première est la version principale (les éditions canoniques des épisodes). 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.
- Correspondance des épisodes : les fichiers d'une série sont associés à ses épisodes dans une seule vue, chaque fichier avec sa correspondance actuelle (version, saison, épisode ou plage d'épisodes). Des outils s'appliquent à une sélection ou à tous les fichiers : automatique (
S01E02,1x02…), un motif ({s}saison,{e}épisode,*n'importe quoi, une espace n'importe quel séparateur ; sans{s}, la saison vient des dossiers), version, saison, décalage, renumérotation. Les changements s'appliquent tout de suite et peuvent être annulés. Seuls les épisodes connus de TMDB sont acceptés. - 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é dans la correspondance (en mettre un dans une autre version).
- Les ordres alternatifs de TMDB (DVD, absolu) sont pris en charge par la numérotation mais pas proposés dans l'UI.
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…).
- Épinglables sur la landing (global, par l'admin).
- Un groupe vide après filtrage (âge, visibilité) n'est pas affiché.
9.5 Progression
- Progression par édition (timelines différentes) ; statut vu par œuvre.
- « Épisode suivant » reste dans la version choisie 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 Rôles
adminetuser. Une seule app ; l'admin est une section réservée.- Les utilisateurs n'ont aucun contrôle éditorial. Ils peuvent : lire, choisir pistes audio / sous-titres dans le lecteur, mettre en favori, télécharger.
- Préférences de langue par utilisateur (audio, sous-titres, métadonnées/UI), utilisées pour la sélection automatique des pistes.
10.2 Authentification
- Invitation par lien ; l'invité crée un mot de passe ou une passkey (WebAuthn).
- Appairage TV par code (« aller sur
<serveur>/link, saisir ABCD »). - Rate limiting sur l'auth.
10.3 Exposition Internet
- Alumira Connect, en un clic : un nom en
alumira.net(martin.alumira.net), son certificat Let's Encrypt par DNS-01 (pour le nom et*.nom), la box priée d'ouvrir le port (UPnP), les adresses (l'IPv4 vue par le service, l'IPv6 propre de la machine) renvoyées toutes les 10 minutes, le tout vérifié depuis Internet. Un seul port partout (443 quand la machine le laisse à Alumira, sinon 7576) : 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.net) mène à la maison en HTTPS sans Internet. Le service (cmd/alumira-connect: le serveur DNS de la zone et son API) est dansinternal/connect, le côté d'Alumira dansinternal/network. - TLS intégré avec ACME (Let's Encrypt) automatique pour un domaine à soi, optionnel ; fonctionnement derrière un reverse proxy supporté.
- En HTTPS le cookie de session est un
__Host-: sous un domaine partagé, un voisin ne peut pas le masquer. - URLs signées (HMAC, expiration) pour manifests, segments et téléchargements : les players natifs ne peuvent pas toujours envoyer de headers d'auth.
10.4 Restriction par âge
- Pas de normes de classification exposées : tout est traduit en limite d'âge. Affichage : (-15) dans un cercle blanc.
- Table classification → limite d'âge intégrée à Alumira (par pays) : ce n'est pas un réglage. Une classification absente est signalée dans les logs quand un titre la porte, et ajoutée à la table.
- Les classifications ne sont pas stockées : à l'identification, celle du pays de référence (sinon celle des États-Unis, sinon la plus stricte des classifications connues) donne la limite d'âge du titre, et seule cette limite est conservée.
- La limite d'âge peut différer par édition (ex. version unrated).
- L'âge de l'utilisateur est calculé depuis sa date de naissance ; si
âge < âge_min, le média est invisible. - Média non classifié ⇒ invisible pour les mineurs.
- Filtrage appliqué côté serveur, partout : listes, recherche, pages cast, groupes, accès direct par URL, émission d'URLs signées, téléchargements.
- UI admin dédiée à l'édition des limites d'âge de tous les médias, avec actions batch.
11. Bande passante & sessions
Contrainte : un manifest HLS VOD est chargé une fois par le player ; on ne peut pas retirer un rung en cours de lecture.
- Budget d'upload global : mesuré à l'onboarding (§13.1), puis confirmé ou ajusté par l'admin ; modifiable ensuite.
- Admission à l'ouverture d'une session de lecture : le serveur plafonne le rung maximum dans le manifest émis selon le budget restant.
- Bridage de débit par session (token bucket) : en cas de saturation, les sessions sont bridées et l'ABR du player redescend de lui-même.
- Priorité streaming > téléchargement : les téléchargements n'utilisent que le reliquat ; au-delà, ils sont mis en file ou refusés.
12. Client web & UX
12.1 Contraintes
- Le client web est aussi le futur client webOS / Tizen : bundle léger, JS/CSS conservateurs (navigateurs TV en retard sur Chromium).
- Navigation D-pad dès le jour 1 : toute l'UI est utilisable avec flèches + OK, focus toujours visible.
- Framework : SolidJS + TypeScript + Vite. Pas de DOM virtuel (réactivité fine) : meilleures performances sur les CPU faibles des TV, bundle de quelques ko. Cible de build conservatrice (ES2017).
- Player : hls.js sur un
<video>nu, avec UI de lecteur maison. Spécialisé HLS (on n'a pas de DASH), léger, très répandu sur TV ; supporte ManagedMediaSource (iOS Safari). Trickplay et sous-titres image sont rendus par notre UI. - Navigation spatiale D-pad : module maison, petit et impératif.
- Listes longues (20k films) : virtualisation systématique des grilles.
12.2 Landing
- Reprendre
- Nouveautés
- Groupes épinglés (ordre défini par l'admin)
- Liste des films
12.3 Page média
- Artwork plein cadre, logo, résumé, cast (portraits), limite d'âge.
- Sélecteur d'édition (au niveau série pour les séries).
- Extras.
- Favori.
- Téléchargement (choix de la combinaison rendition / audio / sous-titres).
12.4 Lecteur
- Sélection automatique audio / sous-titres selon les préférences, modifiable en lecture.
- Sous-titres forcés appliqués automatiquement.
- Trickplay, chapitres.
- Sous-titres ASS riches (libass wasm) et sous-titres image en overlay.
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 la connaît.
- Métadonnées localisées selon la langue de l'utilisateur, fallback langue originale.
- Recherche sur titre original, titres traduits et titres alternatifs (SQLite FTS5).
12.6 Application installable et application de bureau
- Application 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 iPhone, ses cookies sont les siens : on se reconnecte une fois).
- Application de bureau (
cmd/alumira-desktop, macOS et Windows, Wails v3 sur le moteur web du système : WebKit, WebView2) : l'application 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). Au premier lancement, elle demande : héberger le serveur ici (démarré dans le même programme, sur le port 7575 ouALUMIRA_LISTEN, ses données là où les garde la ligne de commande ; 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 listés, ou une adresse tapée ; vérifiée par/api/status). FFmpeg absent, port pris, 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 seule. 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 dit (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 application pour les puces Apple et Intel, macOS 13 et suivants), un installeur pour Windows (NSIS : Program Files, menu Démarrer, une règle du pare-feu pour le réseau privé, une désinstallation qui laisse les données), construits par
scripts/build-desktop.shet inclus dansscripts/release.sh. Les deux embarquent le FFmpeg d'Alumira (jellyfin-ffmpeg avec VMAF, construit depuis github.com/ejulien/jellyfin-ffmpeg), dans un dossierffmpegà côté du programme, là où Alumira le cherche en premier : rien d'autre à installer. Non signés (pas de budget) : macOS demande de l'autoriser une fois, SmartScreen de l'exécuter quand même.
13. Administration
- Sources : ajout, mode
watch/inbox, état du scan. - Stores : ajout, capacité, placement.
- Profils : édition des ladders et passes, versionnage, estimation de taille.
- Workers : appairage, capacités, signatures, état, drain.
- Jobs : toutes les tâches de fond (analyse, identification, passes d'encodage, contrôles qualité), par état et par type, avec annulation et relance.
- Dashboard de normalisation (surveillance temps réel ; pas de durée estimée, trop variable avec le nombre de workers) :
- Catalogue : médias visibles / en attente d'identification / en attente de pass 1 / passes suivantes / entièrement normalisés.
- Workers : état, signature, job et chunk en cours, vitesse (× temps réel), charge.
- Queues : profondeur par queue et par pass, débit global (heures de contenu traitées par heure).
- Stores : remplissage, store active dans l'ordre de remplissage.
- Erreurs récentes, avec relance directe.
- Détail d'un média : passes, rungs, chunks, stems produits.
- Identification : correction des identifications à la main, fichier par fichier ou par lot (série et numéros d'épisodes, versions d'un film, bonus).
- Catalogue : par titre, ses versions et bonus, le cast et les voix de doublage, la limite d'âge, les visuels ; pour une série, la correspondance des épisodes et ses versions (§9.3).
- Collections : création (manuelles / dynamiques), épinglage, ordre sur la landing.
- Limites d'âge : UI dédiée pour éditer la limite d'âge de n'importe quel titre, classé ou non, un par un ou par lot.
- Utilisateurs : invitations, rôles, date de naissance, langues.
- Bande passante : le budget dans les Réglages (saisi ou mesuré) ; dans la page Statut, ce qui est envoyé et à qui.
- Maintenance, dans la première partie du menu : ce qui demande une intervention et ce qui manque à la bibliothèque, du plus urgent au moins urgent, avec actions batch : normalisations échouées, encodages incomplets (pistes audio laissées de côté), sources disparues qui bloquent un encodage, sources trop abîmées pour être encodées (à remplacer), qualité insuffisante (un rendu sous 85 en VMAF), doublons probables, fichiers non identifiés, pistes en langue inconnue (une langue à donner à chacune), stems d'un autre profil (§6.10), copies en trop d'un média dans une autre store (§8.1), épisodes diffusés qu'aucun fichier n'apporte (on peut en écarter), qualité non mesurée, sous-titres manquants dans une langue de sous-titres par défaut (recherche OpenSubtitles, dans le quota du jour ; traduction par un modèle de langage), sources disparues sans perte.
- Journal : chaque événement du serveur (ce qu'affiche le terminal), gardé sur disque en lignes JSON dans des fichiers qui tournent (une taille fixée dans les Réglages, 100 Mo par défaut, les plus anciens supprimés). Chaque événement a sa gravité (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 gravité, système, texte et période (gardés dans l'URL), suit les nouveaux tant que son haut est visible, reprend les couleurs du terminal, lie les médias cités à leur fiche et met à part tailles, durées et chemins. Ce qui est affiché se télécharge en texte ; le journal entier en zip de ses fichiers. Les événements de débogage (dont chaque commande FFmpeg) seulement s'ils sont activés dans les Réglages, ou avec
ALUMIRA_DEBUG_FF=1. - Sauvegardes : snapshots planifiés de la base (bien le plus précieux : matching, éditions, groupes, overrides).
- Fonctionnalités IA, dans les Réglages : un fournisseur (OpenAI, Anthropic, Grok de xAI, ou toute API compatible avec celle d'OpenAI, locale comprise : Ollama, LM Studio, vLLM…) et un modèle par fonctionnalité (traduction de sous-titres, lecture des sous-titres en images, nommage des chapitres), choisi dans la liste du fournisseur, ou aucun. Ils ne sont appelés que par des jobs (leur propre file, autant à la fois que réglé), à la demande ; le propriétaire du compte paie le fournisseur. Chaque job affiche les tokens consommés au fur et à mesure : lus (dont ceux que le service avait en cache) et écrits ; le total reste dans l'historique des jobs. Une clé est vérifiée dès sa saisie, en listant les modèles du fournisseur. Les échecs passagers (limites de débit, service surchargé) sont retentés.
13.1 Onboarding
Au premier lancement, l'admin passe par un onboarding qui pose les questions clés pour obtenir une install fonctionnelle. Principes :
- Chaque étape a des valeurs par défaut raisonnables ; tout reste modifiable ensuite dans l'admin.
- L'onboarding est reprenable : son état est persisté, une interruption ne fait rien perdre.
- Aucun scan ni encodage ne démarre avant la validation finale.
Étapes :
- Compte admin : identifiant, 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 / LAN seulement), port.
- Stores : au moins une store. Vérification d'écriture, espace libre, débit disque mesuré. Ordre de la liste (§8.1).
- Sources : zéro ou plusieurs sources, avec leur mode (
watch/inbox). Pour une sourceinbox, question explicite : conserver une copie Original dans les stores ? (oui par défaut, cf. §6.4). 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 : ladder par défaut présentée, modifiable. Affiche l'estimation de stockage (volume des sources × ladder) face à la capacité des stores.
- Métadonnées : saisie de la 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 à récupérer ; pays par défaut pour la correspondance des classifications d'âge.
- Bande passante : mesure du débit montant (opt-in, via un service de speed test externe ; saisie manuelle en repli), puis confirmation ou ajustement du budget (§11).
- Langues par défaut des nouveaux utilisateurs (audio, sous-titres).
- Sauvegardes : emplacement et fréquence des snapshots de la base.
- Invitations : premiers utilisateurs (optionnel).
- Récapitulatif → démarrage du scan et des queues.
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. Couche d'accès fine pour permettre Postgres plus tard. |
| Média | FFmpeg / ffprobe en sous-process (isolation des crashs, pas de contamination GPL), le build installé sur la machine. |
| Scaling / tone-mapping | libplacebo (Vulkan / MoltenVK), zscale. |
| Packaging | HLS CMAF fMP4, manifests dynamiques. |
| Client | TypeScript, SolidJS, Vite, hls.js. |
15. Échelle cible & ordres de grandeur
Cible : 20 000 films, 1 000 utilisateurs.
| Sujet | Ordre de grandeur | Conséquence |
|---|---|---|
| Stockage | 20k × 10–20 Go ≈ 200–400 To (hors séries, hors rung Original) | Multi-store et placement indispensables. |
| Encodage | ~40 000 h de contenu | Import initial de plusieurs semaines/mois → passes ordonnées et priorités critiques. |
| Streaming | 50–100 lectures simultanées × ~15 Mbps (4K) ≈ 1–1,5 Gbps | Goulot disque/réseau, pas CPU → contrôle de bande passante (§11). |
| Base | ~100k entrées média (épisodes compris) | SQLite suffit avec écritures de progression bufferisées. |
16. Questions ouvertes
Licence du projet— tranché : AGPL-3.0-or-later (LICENSE), contributions sous accord de contribution (CLA.md) pour pouvoir proposer aussi une licence commerciale ; « Alumira » est une marque. FFmpeg reste un programme séparé, ni lié ni distribué, sa licence (GPL avec x264/x265) ne s'étend donc pas à Alumira ; chaque distribution contientTHIRD_PARTY_LICENSES.md(généré parscripts/notices). Reste à régler avant toute offre payante : la licence commerciale TMDB et les datasets IMDb, non commerciaux (§5.1).