diff --git a/HMS MediaEngine.app/Contents/MacOS/HMS-Launcher b/HMS MediaEngine.app/Contents/MacOS/HMS-Launcher index f393f05..43aea18 100755 --- a/HMS MediaEngine.app/Contents/MacOS/HMS-Launcher +++ b/HMS MediaEngine.app/Contents/MacOS/HMS-Launcher @@ -10,6 +10,10 @@ if [ -z "$PY" ]; then exit 1 fi cd "$DIR" +# Portable GStreamer-Umgebung laden (falls HMS-Portable-Install genutzt) +if [ -f "$DIR/runtime/env.sh" ]; then + source "$DIR/runtime/env.sh" +fi if "$PY" -c "import gi; gi.require_version('Gst','1.0')" >/dev/null 2>&1; then nohup "$PY" "$DIR/run.py" >>/tmp/hms-mediaengine.log 2>&1 & PORT=8080 diff --git a/HMS-Portable-Install.command b/HMS-Portable-Install.command new file mode 100755 index 0000000..52072ae --- /dev/null +++ b/HMS-Portable-Install.command @@ -0,0 +1,165 @@ +#!/bin/bash +# HMS MediaEngine – PORTABLE Installation (macOS) +# +# Loesung fuer: "16 GB freier Speicher noetig" +# Grund: macOS Installer.app verlangt als Systemreserve 10-16 GB +# freien Speicher – NICHT weil GStreamer so gross ist (nur 146 MB!). +# +# Dieser Installer UMGEHT Installer.app komplett: +# 1. Laedt GStreamer .pkg herunter (146 MB) +# 2. Entpackt es mit 'pkgutil' OHNE Installation (keine 16GB-Pruefung) +# 3. Kopiert das Framework in den Projektordner (ca. 500 MB) +# 4. Loescht Download + Temp-Dateien +# 5. Ergebnis: Portable App, ~600 MB im Ordner, NICHTS Systemweit +# +# Benoetigt: ca. 800 MB FREIEN Speicher WAEHREND der Installation +# Danach bleiben: ca. 500-600 MB im Projektordner + +cd "$(dirname "$0")" + +echo "==================================================" +echo " HMS MediaEngine – PORTABLE Installation" + echo " (umgeht macOS Installer.app komplett)" +echo "==================================================" +echo "" +echo ">> Pruefe System..." + +# --- Python pruefen (macOS bringt python3 mit) --- +PY="$(command -v python3)" +if [ -z "$PY" ]; then + osascript -e 'display alert "Python fehlt" message "macOS sollte python3 mitbringen. Falls nicht: Xcode Command Line Tools installieren (xcode-select --install) und erneut versuchen." as critical' >/dev/null 2>&1 + exit 1 +fi +echo " Python: $PY" + +# --- Freien Speicher pruefen (nur ~800 MB noetig) --- +FREE_MB=$(df -m . | awk 'NR==2 {print $4}') +echo " Freier Speicher: ${FREE_MB} MB" +if [ "$FREE_MB" -lt 800 ]; then + osascript -e 'display alert "Zu wenig Speicher" message "Es werden nur ca. 800 MB fuer die Installation gebraucht (danach 500-600 MB). Aktuell frei: '"$FREE_MB"' MB." as critical' >/dev/null 2>&1 + exit 1 +fi + +# --- .pkg herunterladen --- +PKG_URL="https://gstreamer.freedesktop.org/data/pkg/macos/1.28.4/gstreamer-1.0-1.28.4-universal.pkg" +PKG_FILE="/tmp/gstreamer-1.28.4.pkg" + +if [ ! -f "runtime/GStreamer.framework/Versions/1.0/lib/gstreamer-1.0" ]; then + echo "" + echo ">> Lade GStreamer herunter (146 MB)..." + curl -L -o "$PKG_FILE" "$PKG_URL" + if [ $? -ne 0 ] || [ ! -s "$PKG_FILE" ]; then + echo " FEHLER: Download fehlgeschlagen" + osascript -e 'display alert "Download fehlgeschlagen" message "GStreamer konnte nicht geladen werden. Internetverbindung pruefen und erneut versuchen." as critical' >/dev/null 2>&1 + rm -f "$PKG_FILE" + exit 1 + fi + echo " Download abgeschlossen: $(du -h "$PKG_FILE" | cut -f1)" + + # --- pkgutil: entpacken OHNE Installer.app --- + echo "" + echo ">> Entpacke GStreamer (ohne Installation, ohne 16GB-Pruefung)..." + EXTRACT_DIR="/tmp/hms_gst_extract" + rm -rf "$EXTRACT_DIR" + pkgutil --expand-full "$PKG_FILE" "$EXTRACT_DIR" + if [ $? -ne 0 ]; then + echo " FEHLER: pkgutil konnte nicht entpacken" + osascript -e 'display alert "Entpacken fehlgeschlagen" message "pkgutil meldete einen Fehler. Bitte Meldung an den Support schicken." as critical' >/dev/null 2>&1 + rm -rf "$EXTRACT_DIR" "$PKG_FILE" + exit 1 + fi + echo " Entpackt nach: $EXTRACT_DIR" + + # --- Framework ins Projektverzeichnis kopieren --- + echo "" + echo ">> Kopiere GStreamer in den Projektordner (runtime/)..." + mkdir -p "runtime" + FRAMEWORK_SRC="" + # pkgutil legt es irgendwo unter payload ab - suchen + for f in $(find "$EXTRACT_DIR" -name "GStreamer.framework" -maxdepth 5 -type d 2>/dev/null); do + FRAMEWORK_SRC="$f" + break + done + if [ -z "$FRAMEWORK_SRC" ]; then + # Fallback: ganze Payload kopieren (enthaelt das Framework) + echo " Suche Framework in Payload..." + PAYLOAD_DIR=$(find "$EXTRACT_DIR" -name "Payload" -maxdepth 3 -type d 2>/dev/null | head -1) + if [ -n "$PAYLOAD_DIR" ]; then + FRAMEWORK_SRC=$(find "$PAYLOAD_DIR" -name "GStreamer.framework" -maxdepth 3 -type d 2>/dev/null | head -1) + fi + fi + if [ -z "$FRAMEWORK_SRC" ]; then + echo " FEHLER: GStreamer.framework nicht im .pkg gefunden" + osascript -e 'display alert "Framework nicht gefunden" message "GStreamer.framework wurde im entpackten Paket nicht gefunden. Bitte Meldung an den Support." as critical' >/dev/null 2>&1 + rm -rf "$EXTRACT_DIR" "$PKG_FILE" + exit 1 + fi + echo " Quelle: $FRAMEWORK_SRC" + cp -R "$FRAMEWORK_SRC" "runtime/" + if [ $? -ne 0 ]; then + echo " FEHLER: Kopieren fehlgeschlagen" + osascript -e 'display alert "Kopieren fehlgeschlagen" message "GStreamer konnte nicht in den Projektordner kopiert werden." as critical' >/dev/null 2>&1 + rm -rf "$EXTRACT_DIR" "$PKG_FILE" + exit 1 + fi + + # --- Aufraeumen (gibt ~750 MB zurueck) --- + echo "" + echo ">> Raeume auf (loesche Download + Temp)..." + rm -rf "$EXTRACT_DIR" "$PKG_FILE" + echo " Aufgeraeumt." +else + echo " GStreamer bereits vorhanden (runtime/)" +fi + +# --- Verifizieren --- +echo "" +echo ">> Verifiziere portable Installation..." +FW_PATH="$(pwd)/runtime/GStreamer.framework/Versions/1.0" +if [ ! -d "$FW_PATH/lib/gstreamer-1.0" ]; then + echo " FEHLER: GStreamer-Bibliotheken nicht gefunden" + osascript -e 'display alert "Installation unvollstaendig" message "GStreamer-Bibliotheken fehlen. Bitte erneut installieren." as critical' >/dev/null 2>&1 + exit 1 +fi +PLUGIN_COUNT=$(ls "$FW_PATH/lib/gstreamer-1.0/" 2>/dev/null | wc -l | tr -d ' ') +echo " GStreamer-Plugins gefunden: $PLUGIN_COUNT" +echo " Framework-Pfad: $FW_PATH" + +# --- Environment-Datei fuer run.py schreiben --- +cat > "runtime/env.sh" << 'ENVEOF' +# HMS MediaEngine portable environment (wird von run.py geladen) +FW_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/GStreamer.framework/Versions/1.0" +export DYLD_LIBRARY_PATH="$FW_DIR/lib:$DYLD_LIBRARY_PATH" +export DYLD_FRAMEWORK_PATH="$(dirname "$FW_DIR")/../..:$DYLD_FRAMEWORK_PATH" +export GST_PLUGIN_PATH="$FW_DIR/lib/gstreamer-1.0" +export GST_PLUGIN_SYSTEM_PATH_1_0="$FW_DIR/lib/gstreamer-1.0" +export GI_TYPELIB_PATH="$FW_DIR/lib/girepository-1.0:$GI_TYPELIB_PATH" +export PKG_CONFIG_PATH="$FW_DIR/lib/pkgconfig:$PKG_CONFIG_PATH" +# PyGObject-Bindings im Framework (falls vorhanden): +for _pydir in "$FW_DIR/lib/python3.12/site-packages" \ + "$FW_DIR/lib/python3.13/site-packages" \ + "$FW_DIR/lib/python3.11/site-packages"; do + if [ -d "$_pydir/gi" ]; then + export PYTHONPATH="$_pydir:$PYTHONPATH" + break + fi +done +ENVEOF +chmod +x "runtime/env.sh" +echo " Environment-Datei: runtime/env.sh" + +# --- Größen-Bericht --- +echo "" +RUNTIME_MB=$(du -sm runtime/ | cut -f1) +echo ">> Fertig! Portable GStreamer-Installation belegt: ${RUNTIME_MB} MB" +echo " (Nichts wurde systemweit installiert)" + +echo "" +echo ">> Oeffne grafischen Installer zum Verifizieren und Starten..." +"$PY" installer_gui.py + +echo "" +echo "==================================================" +echo " FERTIG! Ab jetzt: HMS MediaEngine.app doppelklicken" +echo " (nutzt automatisch die portable GStreamer-Installation)" +echo "==================================================" diff --git a/launcher_core.py b/launcher_core.py index 57be452..ce914c6 100644 --- a/launcher_core.py +++ b/launcher_core.py @@ -19,6 +19,23 @@ from pathlib import Path ROOT = Path(__file__).resolve().parent IS_WIN = sys.platform == "win32" +# Portable GStreamer laden (macOS: HMS-Portable-Install.command) +_fw = ROOT / "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 + _os.environ["GI_TYPELIB_PATH"] = (str(_fw / "lib" / "girepository-1.0") + + ":" + _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 + def web_port() -> int: try: diff --git a/make_mac_app.py b/make_mac_app.py index 08e10e7..f6d1888 100644 --- a/make_mac_app.py +++ b/make_mac_app.py @@ -45,6 +45,10 @@ if [ -z "$PY" ]; then exit 1 fi cd "$DIR" +# Portable GStreamer-Umgebung laden (falls HMS-Portable-Install genutzt) +if [ -f "$DIR/runtime/env.sh" ]; then + source "$DIR/runtime/env.sh" +fi if "$PY" -c "import gi; gi.require_version('Gst','1.0')" >/dev/null 2>&1; then nohup "$PY" "$DIR/run.py" >>/tmp/hms-mediaengine.log 2>&1 & PORT=8080 diff --git a/run.py b/run.py index 7931bb3..d6c07cf 100644 --- a/run.py +++ b/run.py @@ -24,6 +24,26 @@ 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