Alumira Documentation

Alumira — Specification

Status: v1 — implemented. This document describes what Alumira does and why. The code is the reference: where they disagree, the code wins and this document is corrected.


0. Vision & principles

Alumira is a self-hosted media server for a personal collection of films and series. It reads the files where they are, identifies them, and encodes each one once, ahead of time, into a ladder of qualities; playback then serves these ready-made files as adaptive streams to a web interface built around the artwork, on a computer, a phone or a TV with its remote.

Guiding principles

  1. Media first. A very visual, simple, to-the-point UI. Dead simple UX. Its own identity: we do not imitate the streaming platforms.
  2. Pre-normalization, never transcoding at playback. During playback the server only serves bytes. Predictable quality and behaviour.
  3. Split stems, recombined when served. Every track (video per rendition, audio per language/layout, subtitle) is an independent stem; the client receives a multi-track / multi-bitrate manifest generated on the fly.
  4. Lazy evaluation rather than links. We store identities (fingerprints, rules) and resolve them when needed, rather than maintaining links that drift out of sync.
  5. Sources are sacred. A store is never a source; a source is never modified, except in explicit inbox mode.
  6. Simple, imperative code. No OOP; favour build and iteration speed.

1. Scope

V1

Later


2. Glossary

Term Definition
Source Location (folder/drive) scanned for original files. Read-only (watch), consumed (inbox), or played as its files are (direct).
Store Location where Alumira writes normalized stems. Unlimited number. A store is never a source.
Fingerprint Identity of a source file, independent of its path.
Source catalogue Index fingerprint → current paths (0..n).
Stem A single normalized track, stored as one CMAF fMP4 file (e.g. "1080p H264 SDR video", "fr-FR AAC 2.0 audio", "English WebVTT subtitle").
Rendition A video quality level (resolution × codec × dynamic range × bit depth). One rendition = one video stem.
Rung One step of the ladder (definition of a rendition to produce).
Ladder Ordered set of rungs + audio/subtitle definitions to produce for a media item.
Profile Named, versioned ladder configuration, editable by the admin.
Pass Group of rungs encoded together in one chain; passes are ordered.
Work A title: movie, series, season, episode.
Edition Variant of a work ("1977 Cut", "Blu-ray 2011", "Deluxe"…). One edition is canonical.
Extra Bonus material (making-of, interview…) attached to a Work or an Edition.
Group Meta entry grouping media (manual or dynamic).
Worker Encoding process (built into the server or remote).
Encoder signature Identity of an encoding configuration that guarantees consistent output (see §7.3).

3. Overall architecture

                 ┌───────────────────────────────────────────────┐
                 │                    Server                     │
 Sources ──scan──▶  Fingerprint ─▶ Queues ─▶ Scheduler ────┐     │
 (watch/inbox)   │  catalogue     │                        │     │
                 │                ├─ identification        ▼     │
                 │                ├─ normalization   Built-in worker
                 │                ├─ images                │     │
                 │                └─ QA                    │     │
                 │                                         ▼     │
                 │  SQLite ◀── metadata              Stores (fMP4 stems)
                 │                                         │     │
                 │  HTTP: web app + API + HLS manifests ◀──┘     │
                 └──────────────▲──────────────────────▲─────────┘
                                │                      │
                        Web / TV client          LAN workers
                                                 (source streamed
                                                  over HTTP)

4. Sources & fingerprint catalogue

4.1 Source modes

Mode Behaviour
watch Strictly read-only. Alumira never modifies, moves or deletes anything (seedbox, archive drive).
inbox Consumed drop folder: once the media is fully encoded, the source file is deleted (with the subtitles beside it and the folders it leaves empty); no trash. The file itself is lost; what is left is what its profile keeps: its tracks as they are, nothing re-encoded (Original, §6.4, the default for inbox folders), beside its encoded versions, or its encoded versions only, as the add-source dialog says.
direct Played as they are, read-only like watch: nothing encoded nor stored ahead (but small extras). Each file is prepared once it is in: its video keyframes read (packets only), segments of 4 s at least made on them; its subtitles, seek thumbnails (from its keyframes only, decoded by the video chip when it can, an HDR file's turned into SDR; their progress followed) and chapters made as for an encoded file; its audio tracks in AAC played as they are, any other codec (AC-3, DTS, TrueHD…) converted into AAC once (software: there is no audio hardware; small). It is playable as soon as it is identified and prepared. It plays through HLS like the others: its video and AAC tracks are virtual renditions whose segments ffmpeg copies from the file when asked (no encoding): read with the file's own times, cut exactly at the segment's keyframes (the video's fragments before it left out, the sound cut exactly), each fragment given its time back, every track delayed by the same few milliseconds its first frames decode ahead (times never below 0, in step); fragments follow on exactly, a seek plays at once. A device that cannot decode it, or whose connection is too slow for it, gets the qualities transcoded on the fly (below). A file played as it is whose source is gone is unplayable (Maintenance: missing sources, blocking).

In inbox mode the admin is warned that a profile added later can only start from the best existing stem (or from the Original rung, see §6.4).

Transcoding on the fly: for files played as they are, each profile has a live ladder, set on the Profiles page like its encodes (a resolution, a codec, H.264 or HEVC, and a bitrate per rung; by default 1080p 8 Mb/s, 720p 4.5 Mb/s, 480p 1.8 Mb/s, 360p 900 kb/s, 240p 450 kb/s in H.264); changing it re-encodes nothing. Its qualities are virtual renditions beside the file itself, cut on the same segments (a player switches between them seamlessly): the same quality menu and adaptation to the connection as encoded files. Never above the file: two rungs above its size give one, at its size; when the file itself plays, only rungs under three quarters of its bitrate are offered. Video by the hardware encoder only (VideoToolbox, NVENC, Quick Sync, AMF, VA-API: on servers and NAS too, Synology, QNAP, Unraid with an Intel iGPU or an NVIDIA card; ARM boards through V4L2 later), decoded by the hardware when it can, without B-frames; a server without one offers none. On an Intel chip the picture never leaves it, and is scaled there as the encodes scale (§6.3): by halves at most, with the chip's best filter (scale_vaapi in hq mode, when its driver has it); seek thumbnails of files played as they are too. An HDR file stays HDR for a device showing it (HEVC 10 bits, the file's PQ or HLG, whatever the ladder's codec: every quality in the same range as the file, no change of brightness when the player switches; without a hardware encoder for it, the file alone), and is tone-mapped to SDR for any other. Segments are made when asked, the next one meanwhile, and kept in memory (512 MB) for seeks back and other viewers; one being made goes on as long as someone waits for it, and stops when none does. A player has 3 pieces cut or made at a time (its video, its sound, the next one), the server 16 copies; a request beyond waits its turn. A maximum of viewers transcoded at once (2 by default, 1 to 32, on the Profiles page; a hardware encoder is as busy at 100 kb/s as at 25 Mb/s): beyond it nobody is served worse, a device that can play the file plays it as it is, one that cannot is told to try again in a few minutes or on another device. A place is freed 45 s after its last segment.

4.2 Detection

4.3 Fingerprints

4.4 Catalogue


5. Pipeline & queues

detection ─▶ probe ─┬─▶ identification ─────────────┐
                    ├─▶ normalization (passes 1..n) ├─▶ visible
                    └─▶ images (derivatives)        ┘
                         QA (sampled VMAF) ─▶ maintenance

5.1 Identification & metadata providers


6. Normalization

6.1 Source preparation

Before encoding, in the filter chain:

6.2 Rung consistency rules

6.3 Downscale chain

A fundamental point: low resolutions must look flawless.

6.4 Ordered passes

The rungs of a ladder are split into passes, encoded in order. Each pass = one decode + one chain. The manifest includes rungs as soon as they are ready.

Default ladder:

Pass Content
1 8 bit SDR H264 video: 1080p, 480p, 240p. Audio: every language, AAC 2.0 128 kb/s (downmix, loudnorm) + AAC 5.1 320 kb/s if the source is ≥ 5.1 + light AAC 2.0 64 kb/s. Subtitles. Trickplay. Chapters.
2 10 bit HEVC video (HDR kept if the source is HDR): 2160p, 1080p; 8 bit SDR HEVC rescue qualities: 480p, 240p (far lighter than H264 for the same picture on a weak connection). Audio: E-AC-3 5.1 448 kb/s if the source is ≥ 5.1, Opus 2.0 96 kb/s / 5.1 192 kb/s / 7.1 256 kb/s + light Opus 2.0 48 kb/s.
last Original: passthrough remux of the source tracks into the store (no re-encoding). Depends on nothing, slowest to finish. Enabled by default for inbox sources, disabled for watch sources.

Supported codecs: video H264, HEVC, AV1; audio AAC-LC, Opus, E-AC-3; layouts 2.0, 5.1, 7.1.

6.5 Bitrate

6.6 Audio

6.7 Subtitles

Source Processing
Text (SRT, WebVTT, mov_text…) → WebVTT.
Styled ASS/SSA → WebVTT (universal fallback) + original kept for rich client-side rendering (libass wasm, e.g. JASSUB).
Image (PGS, VobSub) Image overlay: subtitles kept as images, rendered by the client; or read into text by a language model, when the admin chooses one for it (below).

6.8 Trickplay & chapters

6.9 Quality control

6.10 Profiles: versioning & resolution


7. Workers & distribution

7.1 Workers

7.2 Time-slice distribution

7.3 Encoder signature

To avoid uneven quality within one media item:

7.4 Source access


8. Storage & serving

8.1 Stores

8.2 Stem format

8.3 Packaging

8.4 Downloads

8.5 Images


9. Data model

Conceptual sketch (not an SQL schema).

SourceRoot     (id, path, mode: watch|inbox)
SourceFile     (quick_fingerprint, full_hash?, size, probe, moviehash?) -- catalogue, kept when the file is gone
SourcePath     (fingerprint, source_root, path, seen_at)               -- 0..n paths
Store          (id, path, capacity, policy)

Work           (id, kind: movie|series|season|episode, parent?, numbers/orders)
Edition        (id, work, name, canonical: bool, min_age?, sparse override fields)
Extra          (id, work|edition, kind, title)
MediaItem      (id, edition|extra, source_fingerprint)                 -- what gets normalized
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, path, language,
                forced, sdh, opensubtitles_id?, release?, matched: hash|title)

Localized      (entity, language, title, overview, …)                  -- multilingual metadata
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)                   -- manual groups

Profile        (id, version, rungs[], passes[], audio[], subs[], options)
LadderOverride (scope: type|media, profile)                            -- planned, UI later

User           (id, role, birth_date, audio_langs[], sub_langs[], ui_lang)
Invitation     (token, role, expires)
Device         (id, user, name, token)                                 -- TV pairing
Progress       (user, edition|episode-in-edition, position, updated)   -- per edition
Watched        (user, work)                                            -- per work
Favorite       (user, work)

9.1 Editions

9.2 Cast

9.3 Series

9.4 Groups

9.5 Progress


10. Users, access & security

10.1 Roles

10.2 Authentication

10.3 Internet exposure

10.4 Age restrictions


11. Bandwidth & sessions

Constraint: a VOD HLS manifest is loaded once by the player; a rung cannot be removed during playback.


12. Web client & UX

12.1 Constraints

12.2 Landing page

  1. Continue watching
  2. Recently added
  3. Pinned groups (order set by the admin)
  4. Movie list

12.3 Media page

12.4 Player

12.5 Localization


12.6 Installed app and desktop app

13. Administration

13.1 Onboarding

On first launch the admin goes through an onboarding that asks the key questions to get a working install. Principles:

Steps:

  1. Admin account: username, password or passkey, birth date, UI language (which also sets the default audio, subtitle and metadata languages, and the country of age ratings).
  2. Network access: Alumira Connect in one click; or, by hand, public URL, TLS mode (built-in ACME / reverse proxy / LAN only), port.
  3. Stores: at least one store. Write check, free space, measured disk throughput. List order (§8.1).
  4. Sources: zero or more sources, with their mode (watch / inbox). For an inbox source, an explicit question: keep an Original copy in the stores? (yes by default, see §6.4). Quick pre-scan (number and volume of files).
  5. Encoding: capability detection of the built-in worker (test encodes), quality / speed choice.
  6. Ladder: default ladder shown, editable. Shows the storage estimate (source volume × ladder) against store capacity.
  7. Metadata: TMDB API key, with a link to the TMDB key creation page (opened in a new tab); metadata languages to fetch; default country for age rating mapping.
  8. Bandwidth: upload throughput measurement (opt-in, through an external speed test service; manual entry as a fallback), then confirmation or adjustment of the budget (§11).
  9. Default languages of new users (audio, subtitles).
  10. Backups: location and frequency of database snapshots.
  11. Invitations: first users (optional).
  12. Summary → scanning and queues start.

14. Technical stack

Element Choice
Server & worker Go, imperative style (structs + functions), no cgo → static binary, trivial cross-compilation.
Database SQLite (WAL), pure Go driver (modernc.org/sqlite), FTS5 for search. Thin access layer to allow Postgres later.
Media FFmpeg / ffprobe as subprocesses (crash isolation, no GPL contamination), the build installed on the machine.
Scaling / tone-mapping libplacebo (Vulkan / MoltenVK), zscale.
Packaging HLS CMAF fMP4, dynamic manifests.
Client TypeScript, SolidJS, Vite, hls.js.

15. Target scale & orders of magnitude

Target: 20,000 movies, 1,000 users.

Topic Order of magnitude Consequence
Storage 20k × 10–20 GB ≈ 200–400 TB (excluding series and the Original rung) Multiple stores and placement are essential.
Encoding ~40,000 h of content An initial import of several weeks/months → ordered passes and priorities are critical.
Streaming 50–100 concurrent streams × ~15 Mbps (4K) ≈ 1–1.5 Gbps Disk/network bound, not CPU → bandwidth control (§11).
Database ~100k media entries (episodes included) SQLite is enough with buffered progress writes.

16. Open questions

  1. Project licence — decided: AGPL-3.0-or-later (LICENSE), contributions under a contributor agreement (CLA.md) so that a commercial licence can also be offered; "Alumira" is a trademark. FFmpeg stays a separate program, neither linked nor shipped, so its licence (GPL with x264/x265) does not extend to Alumira; each distribution carries THIRD_PARTY_LICENSES.md (generated by scripts/notices). Still to settle before any paid offer: TMDB's commercial licence and the IMDb datasets, which are non-commercial (§5.1).