Files

172 lines
5.8 KiB
Python
Raw Permalink Normal View History

#!/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))
# Portable GStreamer-Installation (HMS-Portable-Install.command)
# laedt die Umgebungsvariablen, BEVOR gi importiert wird.
_RUNTIME = ROOT / "runtime"
_FW = _RUNTIME / "GStreamer.framework" / "Versions" / "1.0"
if _FW.is_dir():
import os as _os
_lib = str(_FW / "lib")
_os.environ["DYLD_LIBRARY_PATH"] = _lib + ":" + _os.environ.get("DYLD_LIBRARY_PATH", "")
_gst = str(_FW / "lib" / "gstreamer-1.0")
_os.environ["GST_PLUGIN_PATH"] = _gst
_os.environ["GST_PLUGIN_SYSTEM_PATH_1_0"] = _gst
_gi_types = str(_FW / "lib" / "girepository-1.0")
_os.environ["GI_TYPELIB_PATH"] = _gi_types + ":" + _os.environ.get("GI_TYPELIB_PATH", "")
for _v in ("python3.12", "python3.13", "python3.11"):
_sp = _FW / "lib" / _v / "site-packages"
if (_sp / "gi").is_dir():
sys.path.insert(0, str(_sp))
break
print(f"[Start] Portable GStreamer aktiv: {_FW}")
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())