Generiert statische HTML-Bibliotheken aus Videodateien – archivierungsfähig, ohne Server, ohne Datenbank.
Project description
Mediathek Manager
Generiert statische HTML-Bibliotheken aus Videodateien — archivierungsfähig, ohne Server, ohne Datenbank.
Das Tool scannt Verzeichnisse mit Videodateien (M4V, MP4, MKV, MOV), extrahiert eingebettete Metadaten (Titel, Tags, Genre, Kapitel, Beschreibungen) und generiert eine navigierbare HTML-Bibliothek direkt neben den Videos. Eine einzige Bibliothek pro Aufruf — rekursiv über alle Unterverzeichnisse mit hierarchie-gespiegelter Struktur, Breadcrumb-Navigation, Tag- und Genre-Filtern sowie Detailseiten inkl. klickbaren Kapiteln.
Scope — was der Mediathek Manager nicht tut
Der Mediathek Manager erzeugt keine neuen Vorschaubilder aus dem Videoinhalt. Poster-Generierung (Frame-Extraktion, Resize, Tone-Mapping, Komposition) ist eine eigenständige Kompetenz und gehört in spezialisierte Tools (z. B. den kurmann-vorschaubild-manager, aufgerufen aus dem kurmann-mediaset-creator). Der Mediathek Manager konsumiert nur existierende Bildquellen nach folgender Prioritätenkette:
- Poster-Sidecar
{stem}-poster.jpg(z. B.Urlaub-poster.jpgnebenUrlaub.m4v) wird direkt als Thumbnail für Grid-Cards verwendet. - Eingebettetes Cover-Atom im Video-Header (iTunes-
covrbzw.disposition.attached_picin ffprobe-Sprech). Liegt kein Sidecar neben dem Video, aber trägt die Datei bereits ein Cover-Atom, wird es verlustfrei (ffmpeg -c copy) nachmediathek/embedded-poster/<layout>.jpgextrahiert und als Thumbnail genutzt. Wichtig: Das Tool liest damit nur, was im Datei-Header bereits existiert — kein Bildinhalt wird neu berechnet, kein Frame extrahiert. - Platzhalter-Card mit zentriertem Titel. Sie ist weiterhin voll klickbar und führt zur Detailseite.
Zusätzlich wird Fanart ({stem}-fanart.jpg, typisch landscape 16:9) — wenn vorhanden — auf der Detailseite als <video poster="…"> eingesetzt, damit sich der Player harmonisch füllt, bevor man auf Play klickt. Fehlt das Fanart, dient das Poster (Sidecar oder extrahiertes Cover) als Fallback.
Dadurch bleibt der Mediathek Manager fokussiert auf Scannen, Metadaten und HTML — der einzige ffmpeg-Einsatzzweck ist ein narrow-scoped Atom-Copy, kein Bildverarbeitungs-Pipeline.
Medienset-Verzeichnisse (Infuse-Konvention)
Bibliotheken nach Infuse-Konvention organisieren jeden Einzelfilm in einem eigenen Ordner, dessen Name dem Video entspricht und in dem auch die Sidecars liegen:
Familienfilme/
└── 2026/
└── 2026-03-22-Geburtstag-Dennis-2026/ ← Medienset-Ordner
├── 2026-03-22 Geburtstag Dennis 2026.mov
├── 2026-03-22 Geburtstag Dennis 2026-poster.jpg
├── 2026-03-22 Geburtstag Dennis 2026-fanart.jpg
└── 2026-03-22 Geburtstag Dennis 2026.nfo
Der Mediathek Manager erkennt solche Ordner automatisch und rendert sie nicht als Zwischen-Ordner. Das Video erscheint stattdessen direkt in der Eltern-Ebene (im Beispiel: unter 2026) als Video-Card. Klick führt direkt zur Detailseite — ein Klick weniger als zuvor.
Erkennungsregel: Das Verzeichnis enthält genau ein Video, und dessen Dateiname (ohne Erweiterung) entspricht nach Normalisierung dem Ordnernamen (Leerzeichen und Bindestriche werden gleichgesetzt, Groß-/Kleinschreibung ignoriert). Enthält der Ordner zwei oder mehr Videos, bleibt er ein normales Unterverzeichnis.
Metadaten-Quellen (Reihenfolge)
Pro Feld wird die jeweils zuverlässigste verfügbare Quelle verwendet. NFO ersetzt nicht alles — fehlt ein NFO-Feld, wird die nächste Quelle geprüft:
| Feld | Reihenfolge |
|---|---|
| Titel | NFO <title> → ffprobe tags.title → Dateiname-Stem ohne ISO-Präfix |
| Datum | NFO <published> → Dateiname-Präfix YYYY-MM-DD<sep>… → ffprobe tags.date / content_create_date |
| Genre | NFO erstes <genre> → ffprobe tags.genre |
| Quelle/Autor | NFO erstes <author> → ffprobe tags.artist ∨ tags.album_artist |
| Beschreibung | ffprobe tags.description |
| Langbeschreibung | NFO <plot> → ffprobe tags.synopsis ∨ tags.long_description |
NFO-Dateien folgen der Infuse-Konvention (<media type="Other"> mit <title>, <published>, <genres>, <authors>, optional <plot>). NFO-Werte haben Vorrang vor YAML-Sidecars.
Was nicht als Datumsquelle verwendet wird: Das Feld creation_time (QuickTime/MP4 mvhd-Header-Timestamp) ist bei den meisten Workflows der Encoder- oder Kopierzeitpunkt und nicht das echte Aufnahmedatum — vor allem bei Apple-Memories-Renderings wie *.mov. Verlässlich ist hingegen das eingebettete date-Atom (iTunes-/M4V-Atom ©day, exiftool: "Content Create Date"), das User-Tools beim Kodieren explizit setzen.
Eine optionale YAML-Sidecar-Datei ({stem}.yaml neben dem Video) wird weiterhin unterstützt — sie liegt in der Reihenfolge zwischen ffprobe und NFO und eignet sich als schneller Ad-hoc-Override für Bibliotheken ohne vollständige NFO-Pflege.
Voraussetzungen
- Python >= 3.11
- ffprobe (für Metadaten-Extraktion aus eingebetteten Tags und Kapiteln)
- ffmpeg (optional — nur nötig, wenn eingebettete Cover-Atome als Poster-Fallback verlustfrei kopiert werden sollen; fehlt ffmpeg, bleibt es bei der Platzhalter-Card für Videos ohne
-poster.jpg-Sidecar) - rclone (optional, für den Zugriff auf Remote-Speicher wie Synology via SFTP)
Hinweis zu rclone + ffprobe: Da ffprobe in einer Pipe nicht seeken kann, muss bei MP4-/M4V-Dateien mit moov-Atom am Ende die gesamte Datei gestreamt werden. Bei großen Videos auf langsamen Verbindungen läuft das in den Timeout. Für verlässliche Workflows empfiehlt sich stattdessen ein lokal gemountetes Volume (SMB oder
rclone mount) und die lokale Pfadangabe.
Installation
# Via PyPI
pip install kurmann-mediathek-manager
# Lokale Entwicklung
uv sync
Verwendung
# Bibliothek lokal generieren
mediathek-manager generate /pfad/zum/videoverzeichnis
# Mit eigenem Titel (sonst wird der Ordnername verwendet)
mediathek-manager generate /pfad/zum/Familienfilme --title "Familienfilme"
# Über rclone-Remote (z. B. Synology via SFTP)
mediathek-manager generate lyssach-nas:/Familienfilme --title "Familienfilme"
# Ohne Rekursion: nur Videos im Root-Verzeichnis
mediathek-manager generate /pfad/zum/videoverzeichnis --no-recursive
# Mit ausführlicher Ausgabe
mediathek-manager generate /pfad/zum/videoverzeichnis --verbose
# Nur scannen (zeigt gefundene Videos und Metadaten)
mediathek-manager scan /pfad/zum/videoverzeichnis
# Dateinamen auf SMB/URL/APFS-Kompatibilität prüfen (Read-only-Diagnose)
mediathek-manager check-names /pfad/zum/videoverzeichnis
# Konfiguration
mediathek-manager config list
mediathek-manager config set tools.ffprobe_path /usr/local/bin/ffprobe
Erzeugte Struktur
videoverzeichnis/ ← CLI-Input (Root der Bibliothek)
├── index.html ← Einstiegspunkt (einzige HTML im Root)
├── Film1.m4v
├── Film1-poster.jpg ← optionales Sidecar (nicht vom Tool erzeugt)
├── Ferien/
│ ├── Urlaub.m4v
│ └── Urlaub-poster.jpg
└── mediathek/ ← alle weiteren Artefakte
├── style.css
├── browse/ ← Unterverzeichnis-Indexe (Hierarchie gespiegelt)
│ └── Ferien/
│ └── index.html
├── detail/ ← Detailseiten (Hierarchie gespiegelt)
│ ├── Film1.html
│ └── Ferien/
│ └── Urlaub.html
├── embedded-poster/ ← extrahierte Cover-Atome (nur bei Bedarf)
│ └── Ferien/
│ └── Urlaub.jpg
├── tag/ ← Tag-Filterseiten (über gesamte Bibliothek)
│ └── weltraum.html
└── genre/ ← Genre-Filterseiten
└── dokumentation.html
Poster werden direkt vom {stem}-poster.jpg-Sidecar neben dem Video referenziert. Fehlt das Sidecar, prüft der Generator, ob das Video einen attached_pic-Stream im Header trägt, und kopiert ihn verlustfrei nach mediathek/embedded-poster/<layout>.jpg. Fehlt beides, zeigt die Card einen Platzhalter mit zentriertem Titel.
Safari-optimiert: Die Detailseiten nutzen native <video>-Tags, die in Safari via AVFoundation rendern — dieselbe Pipeline wie QuickTime. HDR10 / Dolby Vision / Rec.2020 PQ wird korrekt tone-mapped. Safari ist daher der empfohlene Browser.
Suche: Die Bibliothek nutzt keine JavaScript-Suche — die Browser-interne Textsuche (⌘+F bzw. Strg+F) funktioniert bestens auf der langen Videoliste.
Card-Layout
Grid-Cards zeigen per Default das Poster, darunter Jahr links und Laufzeit rechts — keinen zusätzlichen Titel-Text, weil das Poster den Titel typischerweise bereits trägt (YouTube-Thumbnails, gestaltete Mediaset-Poster). Bei Videos ohne Poster-Sidecar und ohne eingebettetes Cover-Atom erscheint der Titel zentriert in der Platzhalter-Box.
Wer den Titel-Text zusätzlich unter jeder Card rendern will (z. B. bei Bibliotheken mit rein fotografischen Postern, die keinen Titel enthalten), nutzt:
mediathek-manager generate /pfad --show-card-titles
Die Tag- und Genre-Listen auf der Startseite sind als Stichwort-Index nach Buchsatz-Vorbild gesetzt — enge Zeilen, rechtsbündige Zählungen im Tabular-Numerics-Stil, ruhiger Rhythmus.
Dateinamen: NFC vs. NFD und file://-Kompatibilität
Wer die generierte Bibliothek im SMB-gemounteten Finder per Doppelklick auf index.html öffnet (statt über rclone serve http oder einen lokalen Webserver), bewegt sich im file://-Schema und stößt schnell auf drei Fallen, die im Browser keine Fehlermeldung erzeugen:
NFC vs. NFD — die macOS-Unicode-Falle
macOS speichert Dateinamen mit Umlauten historisch in NFD (Normalization Form Decomposed): Das ä in Bäbibett liegt nicht als ein einzelnes Codepoint U+00E4 vor, sondern als a (U+0061) + Combining Diaeresis (U+0308). Dieselbe Datei, erzeugt unter Linux/Windows oder über git, liegt in NFC (Normalization Form Composed) als ein einzelnes U+00E4 vor.
Der Finder und Safari zeigen beide Formen identisch an. Aber:
- Beim URL-Encoding eines NFD-Namens für einen
file://-Link erzeugt der Browser zwei Escape-Sequenzen (%CC%88für das Combining-Zeichen nach dema), statt einem einzigen%C3%A4. Manche Player und Downloader (z. B. einige Browser-Plugins, ältere Video-Tools) lösen diese Kaskade nicht auf und finden die Datei nicht. - SMB-Shares auf Windows-Servern speichern intern NFC und können NFD-kodierte Pfade vom macOS-Client als „nicht existent" melden, obwohl das Finder-Listing die Datei zeigt.
- Sortierungen und Vergleiche über Dateisystem-Grenzen hinweg schlagen fehl, wenn eine Seite NFC und die andere NFD liefert.
Zielformat: NFC für alles. Das ist der Default überall außer auf macOS-HFS+/APFS, wenn der Name ursprünglich im Finder getippt wurde. Konvertierung nachträglich z. B. mit convmv:
brew install convmv
convmv -f utf-8 -t utf-8 --nfc --notest /pfad/zur/bibliothek -r
URL-reservierte Zeichen
file://-Links in Browsern interpretieren folgende Zeichen besonders, egal wie der Name intern kodiert ist:
| Zeichen | Folge |
|---|---|
# |
Terminiert die URL als Fragment-Anker — alles danach wird abgeschnitten |
? |
Startet den Query-String — alles danach wird abgeschnitten |
& |
Trennt Query-Parameter |
+ |
Wird zum Leerzeichen interpretiert |
% |
Leitet eine Escape-Sequenz ein |
Dateinamen wie Urlaub#2024.m4v oder Doku & Film.m4v führen deshalb zu stumm kaputten Links. Der Browser lädt nichts, zeigt aber auch keinen Fehler.
Windows/SMB-verbotene Zeichen
< > : " | ? * sind unter Windows/SMB in Dateinamen komplett verboten. Wer die Bibliothek später auf eine Windows-Freigabe oder OneDrive legt, verliert diese Dateien still (Invalid name beim Kopieren).
Der check-names-Befehl
Zur Read-only-Diagnose der eigenen Bibliothek liefert der Mediathek Manager einen dedizierten Subcommand:
mediathek-manager check-names /pfad/zum/videoverzeichnis
Der Befehl scannt alle Videos und Unterverzeichnisse und meldet sieben Klassen von Auffälligkeiten:
| Tag | Bedeutung |
|---|---|
NFD |
Dekomponierte Umlaute (macOS-Default), NFC erwartet |
URL |
Zeichen, die file://-Links brechen: # ? & + % |
WIN |
Unter Windows/SMB verbotene Zeichen: < > : " | ? * |
LEN |
Pfadlänge > 240 Zeichen (Windows-MAX_PATH-Zone) |
TRIM |
Führende/abschließende Leerzeichen oder trailing Punkte |
RSVD |
Windows-Device-Name (CON, PRN, AUX, NUL, COM1…LPT9) |
CASE |
Groß-/Kleinschreibungs-Kollision mit anderem Pfad |
Ausgabeformat:
[URL] Bäbibett & Co/film.m4v
[TRIM] ferien /urlaub.m4v
[NFD URL] 2024/Bäbibett & Co/film.m4v
stdout enthält nur die maschinenlesbare [TAG] <pfad>-Liste (pipebar), stderr enthält Zusammenfassung und Details. Exit-Code 1 bei Fundstellen, sonst 0 — damit ist check-names scriptbar:
if ! mediathek-manager check-names /pfad/zur/bibliothek > /tmp/issues.txt; then
echo "Bibliothek hat $(wc -l < /tmp/issues.txt) auffällige Pfade"
fi
Der Mediathek Manager benennt niemals um. Er ist ein reines Diagnose-Werkzeug — die Korrektur bleibt beim Nutzer (typisch convmv --nfc für NFD→NFC, manuell für den Rest).
Konfiguration
Persistente Konfiguration unter ~/.config/mediathek-manager/config.toml:
[tools]
ffprobe_path = "ffprobe"
ffmpeg_path = "ffmpeg"
rclone_path = "rclone"
Upgrade von 0.1.x auf 0.2.0
Version 0.2.0 ändert die Ausgabestruktur grundlegend. Alte .mediathek/-Ordner sind nicht kompatibel und sollten vor der erneuten Generierung gelöscht werden:
rm -rf /pfad/zum/videoverzeichnis/.mediathek
mediathek-manager generate /pfad/zum/videoverzeichnis --title "Deine Bibliothek"
Änderungsverlauf
Siehe CHANGELOG.md.
Lizenz
MIT — siehe LICENSE.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file kurmann_mediathek_manager-0.3.0.tar.gz.
File metadata
- Download URL: kurmann_mediathek_manager-0.3.0.tar.gz
- Upload date:
- Size: 37.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7d70ff202e7900916738bc006b2cbe6373b4824feed1edb8b8764634726eb6f4
|
|
| MD5 |
b979a4caad2d3e4638c626d6d52a4373
|
|
| BLAKE2b-256 |
4d0842a5c9b25dc34d60e44bc2dc9d013e50473b20dc069a87ab8a5dd21712d1
|
Provenance
The following attestation bundles were made for kurmann_mediathek_manager-0.3.0.tar.gz:
Publisher:
publish.yml on kurmann/mediathek-manager
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
kurmann_mediathek_manager-0.3.0.tar.gz -
Subject digest:
7d70ff202e7900916738bc006b2cbe6373b4824feed1edb8b8764634726eb6f4 - Sigstore transparency entry: 1277699428
- Sigstore integration time:
-
Permalink:
kurmann/mediathek-manager@70ddbbf7e8be378e6df71760038be7332a62b1e2 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/kurmann
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@70ddbbf7e8be378e6df71760038be7332a62b1e2 -
Trigger Event:
release
-
Statement type:
File details
Details for the file kurmann_mediathek_manager-0.3.0-py3-none-any.whl.
File metadata
- Download URL: kurmann_mediathek_manager-0.3.0-py3-none-any.whl
- Upload date:
- Size: 42.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e9a9fc0eed64f2afd9175262b6a7e96606205667c6b772aeff2b11449ed2e26b
|
|
| MD5 |
fc105b619719b57b9afc36d369d56226
|
|
| BLAKE2b-256 |
413ea592ef4433d8165a5c3eee90976a32090b3cd2f5f0e2c4bb8ebba7206f36
|
Provenance
The following attestation bundles were made for kurmann_mediathek_manager-0.3.0-py3-none-any.whl:
Publisher:
publish.yml on kurmann/mediathek-manager
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
kurmann_mediathek_manager-0.3.0-py3-none-any.whl -
Subject digest:
e9a9fc0eed64f2afd9175262b6a7e96606205667c6b772aeff2b11449ed2e26b - Sigstore transparency entry: 1277699475
- Sigstore integration time:
-
Permalink:
kurmann/mediathek-manager@70ddbbf7e8be378e6df71760038be7332a62b1e2 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/kurmann
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@70ddbbf7e8be378e6df71760038be7332a62b1e2 -
Trigger Event:
release
-
Statement type: