Files
hms-mediaengine/run.py
T
HMS MediaEngine Agent 696e8eb1b3 PORTABLE INSTALLATION: umgeht 16-GB-Pruefung von macOS Installer.app
URSACHE GEFUNDEN: macOS Installer.app verlangt 10-16 GB freien
Speicher als SYSTEMRESERVE - obwohl GStreamer nur 146 MB gross
ist. Diese Pruefung blockiert Installationen auf vollen Platten.

LOESUNG: HMS-Portable-Install.command
- Laedt GStreamer .pkg (146 MB) per curl
- Entpackt mit 'pkgutil --expand-full' OHNE Installer.app
  (keine 16-GB-Pruefung, kein sudo, nichts systemweit)
- Kopiert GStreamer.framework in runtime/ im Projektordner
- Loescht Download + Temp (gibt ~750 MB zurueck)
- Schreibt runtime/env.sh mit allen Umgebungsvariablen
- Braucht NUR ca. 800 MB WAHREND der Installation
- Danach: ca. 500-600 MB im Projektordner (portable, loeschbar)

Integration:
- run.py: laedt portable env automatisch VOR gi-Import
  (DYLD_LIBRARY_PATH, GST_PLUGIN_PATH, GI_TYPELIB_PATH,
  PYTHONPATH fuer gi-Bindings)
- App-Launcher: sourced runtime/env.sh falls vorhanden
- launcher_core.py: gleiche portable env-Logik
- Freien Speicher pruefen (nur 800 MB noetig, nicht 16 GB)

Drei Installationswege jetzt verfuegbar:
1. HMS-Portable-Install.command (wenig Speicher, nichts systemweit)
2. HMS-Mac-Install.command (Homebrew, mehr Komfort)
3. HMS-MediaEngine.app startet mit whichever verfuegbar ist
2026-09-11 23:33:19 +02:00

172 lines
5.8 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))
# 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())