Files
hms-mediaengine/run.py
T
HMS MediaEngine Agent 70502458d8 PROJEKTE: Speichern/Laden/Autostart (25/25 E2E inkl. Server-Neustart)
- hms_app/projects.py: ProjectStore mit atomarem Speichern
  (.hms.json in projects/), Format-Kennung, Namens-Sanitize
  (Pfad-Traversal-Schutz, Leerzeichen erlaubt), Liste/Loeschen
- engine.py: export_state() (Layer mit Alpha/Pos/Groesse/Z/FX +
  Intensitaeten + Master) und load_state() (ersetzt alle Layer,
  fehlende Medien werden uebersprungen und gemeldet, Werte geclamped,
  ungueltige FX-IDs verworfen)
- server.py: /api/projects (GET Liste + Autostart),
  POST projects/save|load|delete|autostart; Loeschen des
  Autostart-Projekts raeumt config.json automatisch auf
- config.py: engine.autostart_project (validiert, optional)
- run.py: laedt Autostart-Projekt beim Programmstart
- ui.py: neuer Reiter 'Projekte': Name eingeben + Speichern,
  Tabelle mit Laden/Autostart/Loeschen, Autostart-Badge

E2E-BEWEIS (25/25 PASS, mit ECHTEM Server-Neustart):
1 Server startet · 2 Upload · 3 Layer · 4 FX/Alpha/Pos gesetzt ·
5 Projekt 'Show A' gespeichert · 6 Traversal-Name sanitizet zu 'evil' ·
7 Liste · 8 Layerzahl · 9 Zustand geleert (master=0.1) ·
10 Projekt geladen · 11 Layer wiederhergestellt ·
12 Alpha 0.7 wiederhergestellt · 13 FX1 blur 0.8 wiederhergestellt ·
14 Position x=50 y=20 wiederhergestellt · 15 Master 0.9 ·
16 Engine rendert · 17 Autostart gesetzt ·
18 Server NEU GESTARTET · 19 Autostart: Layer+FX automatisch geladen ·
20 Autostart: Engine rendert (60 Frames) · 21 config.json Autostart ·
22 fehlendes Projekt 404 · 23 geloescht ·
24 Autostart automatisch geleert · 25 config.json geleert

Regressionen: App-E2E 14/14 (app=0) + FX-E2E 16/16 (fx=0) unveraendert gruen
2026-09-11 12:55:50 +02:00

144 lines
4.5 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")
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
settings = cfgmod.load(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(ROOT, settings)
library = MediaLibrary(settings, ROOT)
library.ensure_dirs()
engine = MediaEngine(settings, library)
artnet = ArtNetInput(engine, settings)
server = EngineServer(engine, artnet, library, settings, 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(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())