Alumira Documentation

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

  1. Media first. UI très graphique, simple, to the point. UX simplissime. Une identité propre : on n'imite pas les plateformes de streaming.
  2. Pré-normalisation, jamais de transcodage à la lecture. Le serveur ne fait que servir des octets pendant la lecture. Qualité et comportement prévisibles.
  3. 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.
  4. É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.
  5. Les sources sont sacrées. Une store n'est jamais une source ; une source n'est jamais modifiée sauf mode inbox explicite.
  6. Code impératif et simple. Pas d'OOP, favoriser la vitesse de build et d'itération.

1. Périmètre

V1

Plus tard


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)

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

4.3 Empreintes

4.4 Catalogue


5. Pipeline & queues

détection ─▶ probe ─┬─▶ identification ─────────────┐
                    ├─▶ normalisation (passes 1..n) ├─▶ visible
                    └─▶ images (dérivés)            ┘
                         QA (VMAF échantillonné) ─▶ maintenance

5.1 Identification & fournisseurs de métadonnées


6. Normalisation

6.1 Préparation de la source

Avant l'encodage, dans la chaîne de filtres :

6.2 Règles de cohérence des rungs

6.3 Chaîne de downscale

Point fondamental : la qualité des basses résolutions doit être impeccable.

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

6.6 Audio

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).

6.8 Trickplay & chapitres

6.9 Contrôle qualité

6.10 Profils : versionnage & résolution


7. Workers & distribution

7.1 Workers

7.2 Distribution par tranche de temps

7.3 Signature d'encodeur

Pour éviter les inégalités de qualité au sein d'un même média :

7.4 Accès à la source


8. Stockage & service

8.1 Stores

8.2 Format des stems

8.3 Packaging

8.4 Téléchargement

8.5 Images


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

9.2 Cast

9.3 Séries

9.4 Groupes

9.5 Progression


10. Utilisateurs, accès & sécurité

10.1 Rôles

10.2 Authentification

10.3 Exposition Internet

10.4 Restriction par âge


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.


12. Client web & UX

12.1 Contraintes

12.2 Landing

  1. Reprendre
  2. Nouveautés
  3. Groupes épinglés (ordre défini par l'admin)
  4. Liste des films

12.3 Page média

12.4 Lecteur

12.5 Localisation


12.6 Application installable et application de bureau

13. Administration

13.1 Onboarding

Au premier lancement, l'admin passe par un onboarding qui pose les questions clés pour obtenir une install fonctionnelle. Principes :

Étapes :

  1. 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).
  2. Accès réseau : Alumira Connect en un clic ; ou, à la main, URL publique, mode TLS (ACME intégré / reverse proxy / LAN seulement), port.
  3. Stores : au moins une store. Vérification d'écriture, espace libre, débit disque mesuré. Ordre de la liste (§8.1).
  4. Sources : zéro ou plusieurs sources, avec leur mode (watch / inbox). Pour une source inbox, question explicite : conserver une copie Original dans les stores ? (oui par défaut, cf. §6.4). Pré-scan rapide (nombre et volume de fichiers).
  5. Encodage : détection des capacités du worker intégré (encodages de test), choix quality / speed.
  6. Ladder : ladder par défaut présentée, modifiable. Affiche l'estimation de stockage (volume des sources × ladder) face à la capacité des stores.
  7. 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.
  8. 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).
  9. Langues par défaut des nouveaux utilisateurs (audio, sous-titres).
  10. Sauvegardes : emplacement et fréquence des snapshots de la base.
  11. Invitations : premiers utilisateurs (optionnel).
  12. 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

  1. 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 contient THIRD_PARTY_LICENSES.md (généré par scripts/notices). Reste à régler avant toute offre payante : la licence commerciale TMDB et les datasets IMDb, non commerciaux (§5.1).