Files
hms-mediaengine/run.py
T
HMS MediaEngine Agent 7e191ff6c9 GENERATOREN + MEDIEN-VERTEILUNG + AUTOSTART-SETTINGS (33/33 E2E)
1) MADRIX-artige Generator-Layer (15 prozedurale Quellen):
- engine.GENERATORS: ball (animiert), gradient, pinwheel (animiert),
  zone-plate (animiert), checkers, snow (Rauschen animiert), circular,
  spokes, smpte, gamut, red/green/blue/black/white - alle per
  gst-launch verifiziert, kombinierbar mit FX1/FX2 (Generator+FX
  gerendert, 28 Frames im Test)
- add_generator + Pattern-Wechsel zur Laufzeit (ball->snow) mit
  Rebuild, Katalog-Endpoint /api/generators, UI-Dropdown im Layer-Tab
- Generatoren in Projekten speicher-/ladbar (gradient nach Load aktiv)
- Ungueltige Generator-IDs werden mit 400 abgewiesen

2) Medien-Verteilung ueber mehrere Server:
- hms_app/distribution.py: Server-Check (/api/health),
  Remote-Medien-Liste, Streaming-Import mit Upload-Sicherung
- config.servers: Remote-Server registrieren (Name+URL, HTTP-only,
  Duplikat-Schutz), /api/servers (online/offline + Medien),
  servers/import: Datei von Remote ins lokale Archiv ziehen
- UI: Remote-Server in Einstellungen verwalten, Remote-Medien im
  Medien-Tab mit 'Ins Archiv ziehen'-Button
- run.py --data-root: mehrere Instanzen auf einer Maschine mit
  getrennten config/media/projects-Ordnern
- E2E mit ZWEI echten Servern: A registriert B, sieht B-Medien,
  zieht clip byte-identisch (SHA verifiziert) und spielt ihn als
  Layer; Offline-Erkennung nach B-Stop; 404/400-Faelle

3) Autostart in den Einstellungen:
- Einstellungen: Autostart-Projekt-Dropdown (aus Projektliste),
  abwaehlbar; Projekte-Tab weiterhin mit Autostart-Button
- FIX Settings-Save-Merge: POST /api/settings merged jetzt auf die
  AKTUELLEN Settings - Autostart und registrierte Server gehen beim
  Speichern anderer Einstellungen nicht mehr verloren (E2E: Autostart
  ueberlebt Settings-Save, FPS uebernommen)

E2E: 33/33 PASS (Verteilung/Generatoren) + Regressionen:
Projekte 25/25, App 14/14, FX 16/16 - alle gruen
2026-09-11 13:11:54 +02:00

152 lines
4.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""HMS MediaEngine Einstiegspunkt.
Startet Engine (GStreamer), Art-Net-Empfang, Web-Server (UI/API/Upload).
Alle Einstellungen (inkl. Art-Net-Mapping) sind in der Web-UI konfigurierbar
und werden in config.json gespeichert.
Usage:
python3 run.py # Standardstart (UI im Browser)
python3 run.py video1.mp4 # Dateien importieren + als Layer
python3 run.py --setup # Nativer Dialog (Port/Fullscreen)
python3 run.py --bootstrap # GStreamer pruefen/installieren
python3 run.py --generate-setup # Setup-Skripte erzeugen
"""
from __future__ import annotations
import argparse
import signal
import sys
import time
from pathlib import Path
ROOT = Path(__file__).resolve().parent
sys.path.insert(0, str(ROOT))
import gi # noqa: E402
gi.require_version("Gst", "1.0")
from gi.repository import Gst # noqa: E402
Gst.init(None)
from hms_app import config as cfgmod # noqa: E402
from hms_app.artnet import ArtNetInput # noqa: E402
from hms_app.engine import MediaEngine # noqa: E402
from hms_app.media import MediaLibrary # noqa: E402
from hms_app.server import EngineServer # noqa: E402
def main() -> int:
ap = argparse.ArgumentParser(description="HMS MediaEngine")
ap.add_argument("files", nargs="*", help="Medien-Dateien importieren")
ap.add_argument("--setup", action="store_true",
help="Nativer Einstellungsdialog (tkinter)")
ap.add_argument("--bootstrap", action="store_true",
help="GStreamer pruefen/nachinstallieren")
ap.add_argument("--generate-setup", action="store_true",
help="setup_windows.ps1 / setup_linux.sh erzeugen")
ap.add_argument("--port", type=int, default=None,
help="Web-Port ueberschreibt config.json")
ap.add_argument("--data-root", type=str, default=None,
help="Eigener Datenordner fuer mehrere Instanzen "
"(config/media/projekte dort statt neben run.py)")
args = ap.parse_args()
if args.generate_setup:
from hms_app import setup as setupmod
setupmod.generate_setup_scripts(ROOT)
return 0
if args.bootstrap:
from hms_app import setup as setupmod
ok = setupmod.check_and_install_dependencies()
return 0 if ok else 1
DATA_ROOT = (Path(args.data_root).expanduser().resolve()
if args.data_root else ROOT)
if DATA_ROOT != ROOT:
DATA_ROOT.mkdir(parents=True, exist_ok=True)
settings = cfgmod.load(DATA_ROOT)
if args.port is not None:
settings["web"]["port"] = args.port
if args.setup:
from hms_app import setup as setupmod
cfg = setupmod.run_setup(settings)
settings["web"]["port"] = cfg["port"]
settings["output"]["fullscreen"] = cfg["fullscreen"]
cfgmod.save(DATA_ROOT, settings)
library = MediaLibrary(settings, DATA_ROOT)
library.ensure_dirs()
engine = MediaEngine(settings, library)
artnet = ArtNetInput(engine, settings)
server = EngineServer(engine, artnet, library, settings, DATA_ROOT)
# CLI-Dateien importieren und als Layer aktivieren
for f in args.files:
p = Path(f).expanduser()
name = library.import_copy(p) if p.exists() else None
if name:
engine.add_layer(name)
print(f"[Start] Importiert + Layer: {name}")
else:
print(f"[Start] Import fehlgeschlagen: {f}")
engine.rebuild()
# Autostart-Projekt laden (falls konfiguriert und vorhanden)
auto = settings["engine"].get("autostart_project")
if auto:
from hms_app.projects import ProjectStore
doc = ProjectStore(DATA_ROOT).load(str(auto))
if doc is not None:
if engine.load_state(doc.get("state") or {}):
print(f"[Start] Autostart-Projekt geladen: {auto}")
else:
print(f"[Start] Autostart-Projekt fehlgeschlagen: {auto}")
else:
print(f"[Start] Autostart-Projekt nicht gefunden: {auto}")
artnet.start()
if not server.start():
return 1
running = True
def stop_handler(_sig, _frame):
nonlocal running
running = False
signal.signal(signal.SIGINT, stop_handler)
signal.signal(signal.SIGTERM, stop_handler)
print()
print("=" * 62)
print(" HMS MediaEngine laeuft")
print(f" UI : http://localhost:{settings['web']['port']}/")
print(f" Art-Net : UDP {settings['artnet']['listen_port']} "
f"(Universes {settings['artnet']['universes']})")
print(" Einstellungen (Art-Net, Preview, Layer, Upload): Web-UI")
print(" Beenden : Strg+C")
print("=" * 62)
try:
while running:
time.sleep(1)
except KeyboardInterrupt:
pass
finally:
print("\nBeende...")
server.stop()
artnet.stop()
engine.stop()
print("Beendet.")
return 0
if __name__ == "__main__":
sys.exit(main())