Skip to main content

CLI-Tool zur Erstellung von Mediensets (statisches HTML mit OG-Tags, Vorschaubildern und ZIP-Download) aus Videodateien.

Project description

mediaset-creator

CLI-Tool und Python-Bibliothek zur Erstellung von Mediensets aus Videodateien. Ein Medienset ist ein ULID-basiertes Verzeichnis mit statischem HTML, Vorschaubildern, ZIP-Download und der Videodatei – bereit zum Hochladen auf einen Webserver.


Voraussetzungen


Installation

uv pip install kurmann-mediaset-creator

Im Entwicklungsmodus (editierbar):

uv sync

Verwendung (CLI)

Einzelnes Video

mediaset-creator create /pfad/zu/video.m4v \
  --title "Familienfilme 2024" \
  --video-title "Leah Treppen-Surfen" \
  --video-description "Leah demonstriert ihre hohe Kunst des Treppen-Surfens." \
  --video-category "Familie Kurmann-Glück" \
  --video-date "2024-07-01" \
  --video-quality "Dolby Vision"

Das Medienset wird im Verzeichnis der Quelldatei unter einem ULID-Unterverzeichnis erstellt. Ein anderes Ausgabeverzeichnis kann mit --output-dir angegeben werden:

mediaset-creator create /pfad/zu/video.m4v \
  --output-dir /tmp/mediasets \
  --video-title "Mein Film"

Mehrere Videos (JSON)

Für Mediensets mit mehreren Videos wird eine JSON-Datei mit Metadaten übergeben:

mediaset-creator create --from-json metadaten.json

Format der JSON-Datei:

{
  "title": "Familienfilme 2024",
  "videos": [
    {
      "path": "/pfad/zu/video1.m4v",
      "title": "Leah Treppen-Surfen",
      "description": "Beschreibung des Videos.",
      "category": "Familie Kurmann-Glück",
      "recording_date": "2024-07-01",
      "quality_label": "Dolby Vision",
      "frame_number": 500,
      "crop_position": "center-left"
    },
    {
      "path": "/pfad/zu/video2.m4v",
      "title": "Herbst-Spaziergang",
      "description": "Ein sonniger Herbsttag.",
      "recording_date": "2024-11-01",
      "quality_label": "4K HDR",
      "timestamp_seconds": 45.0
    }
  ]
}

Optionen

Option Beschreibung
--title TEXT Übergeordneter Mediaset-Titel (h1). Bei Einzelvideo auch als Video-Titel verwendet, wenn --video-title fehlt. Fallback: Dateiname.
--video-title TEXT Titel des Videos (nur bei Multi-Video nötig; bei Einzelvideo wird --title übernommen)
--video-description TEXT Beschreibung des Videos
--video-category TEXT Kategorie (z.B. «Familie Kurmann-Glück»)
--video-date TEXT Aufnahmedatum im ISO-Format (YYYY-MM-DD). Wird als Dateiname-Prefix und in der Anzeige als deutsches Long-Datum verwendet.
--video-quality TEXT Qualitätsangabe (z.B. «Dolby Vision», «4K HDR»)
--output-dir PATH Basisverzeichnis für die Ausgabe (Default: Verzeichnis der Quelldatei)
--from-json PATH JSON-Datei mit Metadaten für Multi-Video-Mediaset
--ulid TEXT Bestehende ULID verwenden (überschreibt ein vorhandenes Medienset)
--poster-frame N Video-Framenummer für Vorschaubild (überspringt KI-Auswahl)
--poster-at SEKUNDEN Zeitpunkt in Sekunden für Vorschaubild-Frame (z.B. 90 oder 1.5)
--poster-crop POS Bildausschnitt für Poster (left/center-left/center/center-right/right)
--force Erzwingt Neuerstellung aller Dateien, auch wenn Video bereits vorhanden ist
--no-og-tags OpenGraph-Tags deaktivieren
--verbose, -v Zusätzliche Ablaufinformationen auf stderr ausgeben

Ausgabe

stdout enthält den Pfad zum erstellten ULID-Verzeichnis:

/pfad/zum/output/01JNXYZ.../

Statusmeldungen werden auf stderr ausgegeben (nur mit --verbose).

Erzeugte Verzeichnisstruktur

<ULID>/
├── index.html              # HTML-Seite mit Vorschaubild und Video-Link
├── video.mp4               # Video (komprimiert oder Original-Kopie)
├── video.jpg               # Vorschaubild (16:9, 1920×1080)
└── video.zip               # ZIP für Infuse (Video, -fanart.jpg, -poster.jpg, .nfo)

Konfiguration

Einstellungen werden in ~/.config/mediaset-creator/config.toml gespeichert.

Befehle

# Wert speichern
mediaset-creator config set <schlüssel> "<wert>"

# Einzelnen Wert lesen
mediaset-creator config get <schlüssel>

# Alle gespeicherten Werte anzeigen
mediaset-creator config list

Erlaubte Schlüssel

Schlüssel Beschreibung Standard
og.base_url Stamm-URL für OG-Tags (z.B. https://example.com/shares/) (leer)
og.enabled OG-Tags aktivieren (true/false) true
og.site_name OG site_name Metadatum (leer)
og.locale OG locale Metadatum de_CH
thumbnails.portrait_suffix Suffix für Hochformat-Vorschaubilder (Infuse: -poster) -poster
thumbnails.sidecar Sidecar-Bilder als Landscape-Quelle verwenden (true/false) true
title.filename_fallback Dateiname (ohne Erweiterung) als Titel-Fallback verwenden (true/false) true
filename.date_prefix Datum (YYYY-MM-DD) als Dateiname-Prefix verwenden (true/false) true
video.auto_compress Automatische Komprimierung bei >4K-UHD oder >40 Mbit/s (true/false) true
video.crf CRF-Wert für libx265 (0–51, tiefer = bessere Qualität) 20
video.max_bitrate Maximale Bitrate in Mbit/s – darüber wird komprimiert/nachkomprimiert 40
video.preset libx265-Preset (ultrafast/fast/medium/slow/veryslow) slow
tools.ffmpeg Pfad zur ffmpeg-Binärdatei ffmpeg
tools.ffprobe Pfad zur ffprobe-Binärdatei ffprobe
tools.nice_level CPU-Priorität für ffmpeg via nice (0–19; leer = keine Drosselung) (leer)

Beispiel config.toml

[og]
base_url = "https://kurmannmedia.blob.core.windows.net/kurmann-glueck/"
enabled = "true"
site_name = "Patrick Kurmann Familienfilm-Freigabe"
locale = "de_CH"

[thumbnails]
portrait_suffix = "-poster"
sidecar = "true"

[title]
filename_fallback = "true"

[filename]
date_prefix = "true"

[video]
auto_compress = "true"
crf = "20"
max_bitrate = "40"
preset = "slow"

[tools]
ffmpeg = "ffmpeg"
ffprobe = "ffprobe"
nice_level = "10"

OG-Tags

Wenn og.base_url gesetzt ist und OG-Tags aktiviert sind, generiert das HTML OpenGraph-Metadaten für ansprechende Link-Vorschauen in Messengern und sozialen Netzwerken. Die vollständige URL wird aus base_url + ULID/ zusammengesetzt.

OG-Tags lassen sich deaktivieren via:

  • mediaset-creator config set og.enabled false (persistent)
  • mediaset-creator create --no-og-tags ... (pro Aufruf)

Automatische Videokompression

Wenn video.auto_compress aktiv ist (Standard), analysiert das Tool vor der Verarbeitung die technischen Eigenschaften des Videos. Bei Überschreiten eines der folgenden Schwellwerte wird automatisch eine komprimierte MP4-Version erstellt:

  • Auflösung > 4K UHD (Pixelzahl > 3840×2160, z.B. 5K, 6K)
  • Bitrate > 40 Mbit/s (konfigurierbar via video.max_bitrate)

Was wird wohin geschrieben:

Datei Inhalt
video.mp4 im ULID-Verzeichnis Komprimierte MP4 zum direkten Streaming
video.zip Original-Videodatei + Vorschaubilder (für Medienserver)

Wenn keine Kompression nötig ist, wird das Original wie bisher kopiert.

Warum Software-Encoding statt Hardware-Encoding?

Die komprimierte MP4-Datei dient dem Streaming via HTML über einen geheimen Link. Hier zählt bestmögliche Qualität pro Bit, nicht Encoding-Geschwindigkeit. libx265 (Software) erreicht bei gleicher Bitrate ca. 20–30% bessere Qualität als VideoToolbox (Hardware), insbesondere bei Szenen mit viel Bewegung (z.B. Schneefall, Regen, wehende Blätter). Der CRF-Modus (Constant Rate Factor) gewährleistet konstante visuelle Qualität über das gesamte Video – einfache Szenen werden kleiner, komplexe Szenen bekommen mehr Bitrate.

Das Original-Video bleibt unverändert im ZIP für den Download (z.B. für Infuse/Medienserver). Durch die Skalierung von z.B. 4K auf 2560×1440 ist die Encoding-Geschwindigkeit trotz Software-Encoding akzeptabel (~0.5–0.7x Echtzeit).

Komprimierungsparameter:

Parameter Wert Konfigurierbar
Codec HEVC (libx265), 10-Bit (yuv420p10le)
Qualität CRF 20 (visuell verlustfrei) video.crf
Preset slow (beste Qualität/Grösse) video.preset
Auflösung 2560×1440 (QHD), Lanczos-Skalierung
Audio AAC, 192 kbit/s
Streaming faststart (Moov-Atom am Dateianfang)
HDR Farbmetadaten (BT.2020, SMPTE 2084) werden durchgereicht

Nachkomprimierung bei hoher Bitrate:

Wenn die Bitrate nach der initialen Komprimierung noch über 40 Mbit/s liegt (z.B. bei Videos mit viel Bewegung wie Schneefall), wird automatisch erneut vom Original komprimiert – mit schrittweise erhöhtem CRF (20 → 22 → 24 → ...) bis die Bitrate unter dem Schwellwert liegt oder die CRF-Obergrenze von 28 erreicht ist.

Fallback: Falls libx265 nicht verfügbar ist (z.B. bei einer ffmpeg-Installation ohne --enable-libx265), wird automatisch auf VideoToolbox (macOS Hardware-Encoding) zurückgefallen. Prüfen ob libx265 verfügbar ist: ffmpeg -encoders 2>&1 | grep libx265

Komprimierung deaktivieren:

mediaset-creator config set video.auto_compress false

CPU-Drosselung (verhindert Lüfterlärm bei lang laufenden Encodings):

mediaset-creator config set tools.nice_level 10

Der nice-Wert (0–19) steuert die Prozesspriorität von ffmpeg. Höhere Werte = geringere Priorität. Leer lassen (Standard) bedeutet keine Drosselung.


Verwendung (Python API)

Die öffentliche API kann von anderen Python-Anwendungen genutzt werden:

from pathlib import Path
from mediaset_creator.api import (
    CreateMediasetRequest,
    MediaItem,
    RuntimeOptions,
    create_mediaset,
)

# Fachlicher Request
request = CreateMediasetRequest(
    items=[
        MediaItem(
            source_path=Path("/pfad/zu/video.m4v"),
            title="Leah Treppen-Surfen",
            description="Beschreibung des Videos.",
            category="Familie Kurmann-Glück",
            recording_date="1. Juli 2024",
            quality_label="Dolby Vision",
        ),
    ],
    mediaset_title="Familienfilme 2024",
    output_dir=Path("/tmp/mediasets"),
    enable_og_tags=True,
)

# Technische Laufzeitoptionen (getrennt vom fachlichen Request)
runtime = RuntimeOptions(
    base_url="https://example.com/shares/",
    ffmpeg_path="ffmpeg",
    ffprobe_path="ffprobe",
)

# Mediaset erstellen
result = create_mediaset(request, runtime)

if result.success:
    print(f"Mediaset erstellt: {result.output_dir}")
    print(f"HTML: {result.html_path}")
else:
    print(f"Fehler: {result.error_message}")

Fortschritts-Events

Für lang laufende Operationen kann ein Event-Callback übergeben werden. Events sind strukturierte Datenstrukturen (nicht Freitext) mit stabilen stage_id-Strings, die eine Host-Applikation maschinell auswerten kann.

from mediaset_creator.api import MediasetCreatorEvent, MediasetCreatorStage

def on_event(event: MediasetCreatorEvent) -> None:
    label = f" [{event.current}/{event.total}]" if event.current is not None else ""
    print(f"  {event.message}{label}")

result = create_mediaset(request, runtime, on_event=on_event)

Event-Reihenfolge pro Video:

Stage-ID Beschreibung Wann
mediaset_gestartet Mediaset-Erstellung beginnt Einmal zu Beginn
video_analysiert Videoanalyse (Auflösung, Bitrate, Codec) Pro Video
thumbnails_erstellt Breitbild- und Poster-Vorschaubilder erstellt Pro Video
nfo_erstellt Infuse/Firecore-Metadatei erstellt Pro Video
video_komprimiert Komprimierung abgeschlossen (mit CRF, Bitrate, Encoder) Pro Video (wenn nötig)
video_kopiert Original kopiert (keine Komprimierung nötig) Pro Video (alternativ)
video_nachkomprimiert Nachkomprimierung läuft (Bitrate noch zu hoch) 0–n mal pro Video
zip_erstellt ZIP-Archiv mit Original + Vorschaubilder erstellt Pro Video
html_generiert HTML-Seite mit OG-Tags generiert Einmal am Ende
mediaset_abgeschlossen Alle Dateien erstellt Einmal am Ende

Hinweis zur Reihenfolge: Thumbnails und NFO werden bewusst vor der Videokomprimierung erstellt. So kann eine Host-Applikation nach thumbnails_erstellt + nfo_erstellt bereits mit dem Infuse-Deployment beginnen (Original-Video + Vorschaubilder + NFO auf den Medienserver kopieren), während die langsame Software-Komprimierung noch läuft.

Komprimierungs-Zusammenfassung (video_komprimiert) wird in der CLI immer auf stderr angezeigt (auch ohne --verbose), z.B.:

Video komprimiert: video.mp4 | CRF=20 | 35 Mbit/s [1/1]

Öffentliche API-Exporte

from mediaset_creator.api import (
    create_mediaset,          # Hauptfunktion
    CreateMediasetRequest,    # Fachlicher Request
    MediaItem,                # Ein Medienelement (aktuell: Video)
    RuntimeOptions,           # Technische Laufzeitoptionen
    MediasetResult,           # Ergebnis mit Pfaden
    MediasetCreatorEvent,     # Strukturiertes Fortschritts-Event
    MediasetCreatorStage,     # Stage-IDs (StrEnum)
)

Änderungsverlauf

Beinhaltet die letzten drei Minorversionen.

1.4.0 – 2026-03-29

  • Software-Encoding: libx265 mit CRF-Modus statt VideoToolbox Hardware-Encoding für bessere Qualität pro Bit. Automatischer Fallback auf VideoToolbox wenn libx265 nicht verfügbar.
  • Vereinfachte Bildnamen: Vorschaubild heisst <stem>.jpg ohne Suffix, Portrait + NFO nur noch im ZIP
  • Reihenfolge optimiert: Thumbnails + NFO vor Videokomprimierung (ermöglicht frühes Infuse-Deployment durch Host-Applikation)
  • Neue Config-Keys: video.crf, video.preset, thumbnails.portrait_suffix
  • Neue Event-Stage: NFO_ERSTELLT
  • Standard-Bitrate-Schwellwert von 50 auf 40 Mbit/s gesenkt
  • Frame-Auswahl: --poster-frame, --poster-at, --poster-crop für manuelle Vorschaubild-Steuerung
  • Inkrementelle Verarbeitung: Vorhandene Videos werden übersprungen wenn Quelle nicht neuer (--force zum Übersteuern)

1.3.0 – 2026-03-28

  • Internet-freundliche Dateinamen: Umlaute transliteriert (ö→oe), Leerzeichen → Bindestriche, nur ASCII-Zeichen. Verhindert 404-Fehler durch Unicode-Normalisierungs-Unterschiede (macOS NFD vs. Server NFC).
  • ZIP-Inhalte behalten originale Dateinamen (Infuse-freundlich)
  • Neue Option --ulid zum Überschreiben bestehender Mediensets
  • Automatische Nachkomprimierung bei zu hoher Bitrate

1.2.0 – 2026-03-28

  • Verbessertes HTML-Layout: Mediathek-Header, Datenschutzhinweis, Videodauer
  • Infuse-Kompatibilität: NFO-Datei im Firecore-Format
  • Grösseres Play-Icon mit Drop-Shadow
  • Erstellungsdatum (Timestamp) wird auf der HTML-Seite angezeigt
  • Videodauer wird via ffprobe extrahiert und im HTML dargestellt

1.1.0 – 2026-03-27

  • Automatische Videokompression: Bei >4K-UHD oder >50 Mbit/s wird eine komprimierte HEVC-MP4 (2560×1440, VideoToolbox) erstellt – das Original kommt ins ZIP für den Medienserver
  • CPU-Drosselung via tools.nice_level (0–19) verhindert Lüfterlärm bei lang laufenden Encodings
  • Graceful Fallback: Bei Analysefehler oder fehlgeschlagenem Encoding wird das Original kopiert
  • Neue Config-Keys: video.auto_compress, tools.nice_level
  • Neue Event-Stages: VIDEO_ANALYSIERT, VIDEO_KOMPRIMIERT

Vollständige Historie: CHANGELOG.md


Lizenz

MIT

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

kurmann_mediaset_creator-1.4.0.tar.gz (23.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

kurmann_mediaset_creator-1.4.0-py3-none-any.whl (27.9 kB view details)

Uploaded Python 3

File details

Details for the file kurmann_mediaset_creator-1.4.0.tar.gz.

File metadata

  • Download URL: kurmann_mediaset_creator-1.4.0.tar.gz
  • Upload date:
  • Size: 23.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for kurmann_mediaset_creator-1.4.0.tar.gz
Algorithm Hash digest
SHA256 64bcf9be60a04b4a518ea701671dd07d132b04e09e1e6dce43a86cdc33209e29
MD5 991049e7fd4ccc65ad093c7735cd6ff9
BLAKE2b-256 52d4cd6b8b2ba2873ea624801cf66c3a85ff25b55067c428c029a43750729a1a

See more details on using hashes here.

Provenance

The following attestation bundles were made for kurmann_mediaset_creator-1.4.0.tar.gz:

Publisher: publish.yml on kurmann/mediaset-creator

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file kurmann_mediaset_creator-1.4.0-py3-none-any.whl.

File metadata

File hashes

Hashes for kurmann_mediaset_creator-1.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 d6ab574fb23617e5eb483b18c2fd4d5230ba01d725dd171e0c18e0c6bb5f5c29
MD5 33b0a163e3a00826b854c43c92e6e8d4
BLAKE2b-256 0b78edb4191a1df677ab89eae40fbae026ae53f793adb70559fcac00e30484b2

See more details on using hashes here.

Provenance

The following attestation bundles were made for kurmann_mediaset_creator-1.4.0-py3-none-any.whl:

Publisher: publish.yml on kurmann/mediaset-creator

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page