"""Bootstrap, Setup-Skripte für Windows/Linux, Einstellungs-Dialog.""" from __future__ import annotations import shutil import subprocess import sys from pathlib import Path LINUX_PKGS = [ "gstreamer1.0-tools", "gstreamer1.0-plugins-base", "gstreamer1.0-plugins-good", "gstreamer1.0-plugins-bad", "gstreamer1.0-plugins-ugly", "gstreamer1.0-libav", "python3-gst-1.0", "gir1.2-gst-1.0", "python3-tk", ] def check_and_install_dependencies() -> bool: """Prüft GStreamer; installiert bei Bedarf (Linux) bzw. lädt MSI (Windows).""" print("[Bootstrap] Prüfe GStreamer...") try: import gi # noqa: F401 gi.require_version("Gst", "1.0") from gi.repository import Gst as _Gst _Gst.init(None) print(f"[Bootstrap] GStreamer {_Gst.version_string()} OK") return True except (ImportError, ValueError): pass if sys.platform == "linux": print("[Bootstrap] Installiere GStreamer (sudo apt-get)...") try: subprocess.run(["sudo", "apt-get", "update", "-qq"], check=True, timeout=180) subprocess.run( ["sudo", "apt-get", "install", "-y", "-qq"] + LINUX_PKGS, check=True, timeout=600) print("[Bootstrap] GStreamer installiert. Programm neu starten.") except (subprocess.CalledProcessError, subprocess.TimeoutExpired, FileNotFoundError): print("[Bootstrap] Manuell installieren:") print(" sudo apt-get install " + " ".join(LINUX_PKGS)) return False if sys.platform == "darwin": pkgs = ["python@3.12", "python-tk", "gstreamer", "gst-plugins-base", "gst-plugins-good", "gst-plugins-bad", "gst-plugins-ugly", "gst-libav", "pygobject"] brew = shutil.which("brew") if brew: print("[Bootstrap] Installiere GStreamer + PyGObject " "via Homebrew (dauert evtl. einige Minuten)...") try: subprocess.run([brew, "install"] + pkgs, check=False, timeout=1800) print("[Bootstrap] Fertig. Programm mit brew-python3 " "neu starten.") except subprocess.TimeoutExpired: print("[Bootstrap] Timeout. Manuell: brew install " + " ".join(pkgs)) else: print("[Bootstrap] Homebrew nicht gefunden.") print(" Erst Homebrew installieren:") print(' /bin/bash -c "$(curl -fsSL ' 'https://raw.githubusercontent.com/Homebrew/' 'install/HEAD/install.sh)"') print(" Danach: ./setup_macos.sh") return False if sys.platform == "win32": url = ("https://gstreamer.freedesktop.org/data/pkg/windows/" "1.28.4/msvc/" "gstreamer-1.0-msvc-x86_64-1.28.4.msi") msi = Path.home() / "Downloads" / "gstreamer-1.0-msvc-x86_64.msi" try: import urllib.request if not msi.exists(): print(f"[Bootstrap] Lade {url}") urllib.request.urlretrieve(url, msi) subprocess.run(["msiexec", "/i", str(msi), "/quiet", "ADDLOCAL=ALL"], check=False) print("[Bootstrap] GStreamer-MSI gestartet. " "Nach Installation neu starten.") except Exception as e: # noqa: BLE001 print(f"[Bootstrap] Download-Fehler: {e}") print(f"[Bootstrap] Manuell installieren: {url}") return False return False MACOS_SETUP = r'''#!/bin/bash # HMS MediaEngine - macOS Setup (Apple Silicon & Intel, einmalig ausfuehren) set -e echo "=== HMS MediaEngine macOS Setup ===" echo "Architektur: $(uname -m)" if ! command -v brew >/dev/null; then echo "Homebrew fehlt. Zunaechst installieren:" echo ' /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"' echo "Dann dieses Skript erneut ausfuehren." exit 1 fi echo "[1/2] Installiere GStreamer, PyGObject und tkinter (brew)..." brew install python@3.12 python-tk gstreamer gst-plugins-base \ gst-plugins-good gst-plugins-bad gst-plugins-ugly gst-libav pygobject echo "[2/2] Umgebung pruefen..." if [ "$(uname -m)" = "arm64" ] && [ -f /opt/homebrew/bin/brew ]; then echo "Apple Silicon: Homebrew-Umgebung einbinden mit:" echo ' eval "$(/opt/homebrew/bin/brew shellenv)"' fi echo "Pruefung:" python3 -c "import gi; gi.require_version('Gst','1.0'); from gi.repository import Gst; Gst.init(None); print('GStreamer OK:', Gst.version_string())" || \ { echo "PyGObject fehlt noch -> brew python3 verwenden (PATH!)"; exit 1; } echo "" echo "Hinweis: Die .pkg-Pakete von gstreamer.freedesktop.org/data/pkg/macos/" echo "enthalten KEINE PyGObject-Bindings - Homebrew ist der empfohlene Weg." echo "" echo "=== Fertig ===" echo "Start: python3 run.py" echo "Dialog: python3 run.py --setup" ''' WINDOWS_SETUP = r'''# HMS MediaEngine - Windows Setup (einmalig ausfuehren) $ErrorActionPreference = "Stop" Write-Host "=== HMS MediaEngine Windows Setup ===" -ForegroundColor Cyan $isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) if (-not $isAdmin) { Write-Host "Als Administrator ausfuehren (Rechtsklick -> Als Administrator ausfuehren)" -ForegroundColor Red exit 1 } Write-Host "[1/2] GStreamer 1.28.4 MSVC..." -ForegroundColor Yellow $gstRoot = [Environment]::GetEnvironmentVariable("GSTREAMER_1_0_ROOT_MSVC_X86_64", "Machine") if (-not $gstRoot) { $url = "https://gstreamer.freedesktop.org/data/pkg/windows/1.28.4/msvc/gstreamer-1.0-msvc-x86_64-1.28.4.msi" $msi = "$env:TEMP\gstreamer.msi" Invoke-WebRequest -Uri $url -OutFile $msi Start-Process msiexec.exe -ArgumentList "/i `"$msi`" /quiet ADDLOCAL=ALL" -Wait Write-Host " GStreamer installiert" } else { Write-Host " bereits vorhanden" } Write-Host "[2/2] Python 3.13 (falls fehlt) mit tkinter..." -ForegroundColor Yellow $py = Get-Command python -ErrorAction SilentlyContinue if (-not $py) { $pyUrl = "https://www.python.org/ftp/python/3.13.2/python-3.13.2-amd64.exe" $exe = "$env:TEMP\python-installer.exe" Invoke-WebRequest -Uri $pyUrl -OutFile $exe Start-Process $exe -ArgumentList "/quiet InstallAllUsers=1 PrependPath=1 Include_tcltk=1" -Wait Write-Host " Python installiert" } else { Write-Host " bereits vorhanden" } $env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";$env:Path" Write-Host "" Write-Host "=== Fertig ===" -ForegroundColor Green Write-Host "Start: python run.py" Write-Host "Dialog: python run.py --setup" Write-Host "Media-Verwaltung, Layer und Einstellungen: Web-UI im Browser" ''' LINUX_SETUP = r'''#!/bin/bash # HMS MediaEngine - Linux Setup (einmalig ausfuehren) set -e echo "=== HMS MediaEngine Linux Setup ===" if command -v apt-get >/dev/null; then sudo apt-get update -qq sudo apt-get install -y -qq gstreamer1.0-tools gstreamer1.0-plugins-base \ gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly \ gstreamer1.0-libav python3-gst-1.0 gir1.2-gst-1.0 python3-tk xvfb elif command -v dnf >/dev/null; then sudo dnf install -y gstreamer1 gstreamer1-plugins-base gstreamer1-plugins-good \ gstreamer1-plugins-bad-free gstreamer1-plugins-ugly gstreamer1-libav \ python3-gobject python3-tkinter fi echo "=== Fertig ===" echo "Start: python3 run.py" echo "Media-Verwaltung, Layer und Einstellungen: Web-UI im Browser" ''' def generate_setup_scripts(root: Path) -> None: p1 = root / "setup_windows.ps1" p1.write_text(WINDOWS_SETUP, encoding="utf-8") p2 = root / "setup_linux.sh" p2.write_text(LINUX_SETUP, encoding="utf-8") p2.chmod(0o755) p3 = root / "setup_macos.sh" p3.write_text(MACOS_SETUP, encoding="utf-8") p3.chmod(0o755) print(f"Erzeugt: {p1}") print(f"Erzeugt: {p2}") print(f"Erzeugt: {p3}") def run_setup(settings: dict) -> dict: """Nativer Dialog für Port/Fullscreen; Medien-Verwaltung bleibt im Web.""" result = {"port": int(settings["web"]["port"]), "fullscreen": bool(settings["output"]["fullscreen"])} try: import tkinter as tk from tkinter import ttk except ImportError: print("tkinter nicht verfügbar (Linux: apt install python3-tk).") return result root = tk.Tk() root.title("HMS MediaEngine – Einstellungen") root.geometry("440x240") root.configure(bg="#16181d") style = ttk.Style(root) style.theme_use("clam") style.configure("TLabel", background="#16181d", foreground="#c8ccd4") main = ttk.Frame(root, padding=18) main.pack(fill="both", expand=True) ttk.Label(main, text="HMS MediaEngine", font=("System", 15, "bold")).pack(pady=(0, 2)) ttk.Label(main, text="Grundeinstellungen", foreground="#8a8f9a").pack(pady=(0, 12)) row = ttk.Frame(main) row.pack(fill="x", pady=4) ttk.Label(row, text="Web-UI Port:").pack(side="left") port_var = tk.StringVar(value=str(result["port"])) ttk.Entry(row, textvariable=port_var, width=7).pack( side="left", padx=8) fs_var = tk.BooleanVar(value=result["fullscreen"]) ttk.Checkbutton(row, text="Fullscreen-Output", variable=fs_var).pack(side="left", padx=10) def go(): try: result["port"] = int(port_var.get() or 8080) except ValueError: pass result["fullscreen"] = bool(fs_var.get()) root.destroy() ttk.Button(main, text="Starten", command=go).pack( fill="x", pady=(14, 0), ipady=6) root.mainloop() return result