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) Der Mediathek Manager arbeitet ausschließlich mit lokal erreichbaren Pfaden — das schliesst SMB-/NFS-/FUSE-Mounts ein. Remote-Speicher wie Synology-NAS werden typischerweise als SMB-Share gemountet (z. B./Volumes/Familienfilme). rclone-Remotes werden seit v0.4.0 nicht mehr unterstützt.
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"
# 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
# HTML an einem separaten Ort generieren (Quellverzeichnis bleibt unberührt)
mediathek-manager generate /pfad/zum/videoverzeichnis --output ~/Mediathek-HTML
# Mediathek über lokalen Webserver im Browser öffnen
mediathek-manager serve ~/Mediathek-HTML
# 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.
Metadaten-Cache
Ab v0.4.0 speichert der Mediathek Manager die extrahierten Metadaten (ffprobe-Ergebnisse, Sidecar-Daten, Filename-Heuristiken) in mediathek/.metadata-cache.json. Beim nächsten generate-Lauf wird ffprobe nur für geänderte oder neue Videos aufgerufen — unveränderte Videos werden direkt aus dem Cache geladen. Das beschleunigt wiederholte Läufe auf grossen Bibliotheken erheblich, besonders über SMB-Mounts.
Der Cache-Key pro Video besteht aus mtime + size der Videodatei sowie den mtimes aller Sidecars (.yaml, .nfo, -poster.jpg, -fanart.jpg). Ändert sich eines dieser Attribute, wird das Video neu extrahiert. Der Cache ist ein Hilfsmittel — er kann jederzeit gelöscht werden, die HTML-Dateien sind das archivierungswürdige Artefakt.
Bei Abbruch (Ctrl+C, Verbindungsabbruch) bleibt die alte Cache-Datei intakt. Der nächste Lauf profitiert von allen bisherigen Einträgen des vorherigen Laufs und extrahiert nur die fehlenden Videos nach.
Separater Ausgabepfad
Die HTML-Bibliothek kann optional an einem separaten Ort generiert werden (z. B. auf einem schnellen lokalen Laufwerk statt auf dem NAS):
mediathek-manager generate /Volumes/Familienfilme/Final --output ~/Mediathek-HTML --title "Familienfilme"
Das Quellverzeichnis bleibt vollständig unberührt — es erfolgt keinerlei Schreibzugriff darauf. Im Output-Verzeichnis wird automatisch ein Symlink _videos → <Quellverzeichnis> erstellt — alle Video- und Sidecar-Referenzen in den HTML-Seiten gehen über _videos/….
Wichtig: Bei file:// blockiert Safari den Cross-Origin-Zugriff auf Sub-Ressourcen auch über Symlinks hinweg. Videos in Detailseiten spielen daher bei unterschiedlichen Pfadbäumen (z. B. /Volumes/NAS vs. /Users/…) nicht ab. Die Lösung: mediathek-manager serve startet einen lokalen Webserver, der den Symlink transparent auflöst:
mediathek-manager generate /Volumes/Internetfilme-Patrick --output ~/Movies/Internetfilme --title "Internetfilme"
mediathek-manager serve ~/Movies/Internetfilme
# → http://127.0.0.1:8080/ öffnet sich — alle Videos spielen ab
Auch ohne --output ist serve nützlich: es umgeht sämtliche file://-Probleme mit NFD-Unicode, URL-reservierten Zeichen und Browser-Sicherheitsrestriktionen.
Dateinamen: NFC vs. NFD und file://-Kompatibilität
Wer die generierte Bibliothek im SMB-gemounteten Finder per Doppelklick auf index.html öffnet, 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"
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"
Architektur und Hosting-Strategie
Für das Hosting der generierten Mediathek im Heimnetz und das übergreifende Architektur-Pattern für zukünftige Apps siehe das Fachdokument:
docs/static-hosting-architecture.md— beschreibt die Entscheidung für Synology Web Station als statischen HTTP-Backend, das Webserver-Mental-Model, Symlinks vs. URLs als Pfad-Indirection, HTTP-Range-Requests vs. SMB-Random-Access, mDNS-Hostnames, Apple TV via Infuse und AirPlay, Sicherheits-Setup und das übergreifende Pattern „NAS als Read-Only-Backend, Apps als HTTP-Clients".
Ä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.6.0.tar.gz.
File metadata
- Download URL: kurmann_mediathek_manager-0.6.0.tar.gz
- Upload date:
- Size: 42.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
06ef9bb89a5ba2a5c182e7bff4df28974a9546d7b110ae406eb88e79a3f3b786
|
|
| MD5 |
dae6f368fdc33c47c68c69c3d31c066c
|
|
| BLAKE2b-256 |
84933a5746b4dc7137e4b1f0cd3cb4f28c0d15e4a2b9726bd4b8bc13c6fded38
|
Provenance
The following attestation bundles were made for kurmann_mediathek_manager-0.6.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.6.0.tar.gz -
Subject digest:
06ef9bb89a5ba2a5c182e7bff4df28974a9546d7b110ae406eb88e79a3f3b786 - Sigstore transparency entry: 1427000567
- Sigstore integration time:
-
Permalink:
kurmann/mediathek-manager@9b19095dea709957134c2bfbccf996cca1e1ff3c -
Branch / Tag:
refs/tags/v0.6.0 - Owner: https://github.com/kurmann
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@9b19095dea709957134c2bfbccf996cca1e1ff3c -
Trigger Event:
release
-
Statement type:
File details
Details for the file kurmann_mediathek_manager-0.6.0-py3-none-any.whl.
File metadata
- Download URL: kurmann_mediathek_manager-0.6.0-py3-none-any.whl
- Upload date:
- Size: 48.2 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 |
292c3087a94eec93b6f633ce1f3556cb7e629099588e231f9760b38a2a7499de
|
|
| MD5 |
fd23a92408740d22eee2b82a89d770e2
|
|
| BLAKE2b-256 |
3ef97c47874d67685e312281b7c8b33a13c3cdedce40a0706090e7a8f621dd71
|
Provenance
The following attestation bundles were made for kurmann_mediathek_manager-0.6.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.6.0-py3-none-any.whl -
Subject digest:
292c3087a94eec93b6f633ce1f3556cb7e629099588e231f9760b38a2a7499de - Sigstore transparency entry: 1427000633
- Sigstore integration time:
-
Permalink:
kurmann/mediathek-manager@9b19095dea709957134c2bfbccf996cca1e1ff3c -
Branch / Tag:
refs/tags/v0.6.0 - Owner: https://github.com/kurmann
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@9b19095dea709957134c2bfbccf996cca1e1ff3c -
Trigger Event:
release
-
Statement type: