696e8eb1b3
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
172 lines
5.8 KiB
Python
172 lines
5.8 KiB
Python
#!/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())
|