Files
hms-mediaengine/_entwicklung/apps/launcher/hms_launcher/paths.py
T
HMS MediaEngine Agent 362e089be0 AUFGERAUMT: Root auf 10 sichtbare Elemente reduziert
Der Nutzer hat recht: Der Ordner war voller Entwicklungs-Muell.
Jetzt ist sauber getrennt:

ROOT (was der Nutzer sieht und braucht):
- run.py                     = das Programm
- hms_app/                   = der Anwendungscode
- HMS MediaEngine.app        = macOS Doppelklick-Starter
- HMS-Start.vbs              = Windows Doppelklick-Starter
- HMS-Install.vbs             = Windows Erst-Installation
- HMS-Mac-Install.command     = macOS Homebrew-Installation
- HMS-Portable-Install.command = macOS Portable-Installation (16GB-Fix)
- installer_gui.py           = grafischer Installer
- launcher.pyw + launcher_core.py = interne Start-Logik
- LIESMICH.txt               = 10-Zeilen-Kurzanleitung
- .gitignore

_entwicklung/ (alles andere, NICHT benoetigt):
- packages/ apps/ native/ plugins/ tools/ schemas/ tests/ docs/
  build/ fixture_profiles/
- PLAN.md STATUS.md ERRORS.md TEST_REPORT.md CHANGELOG.md README.md
- pyproject.toml uv.lock setup_*.sh/ps1 make_mac_app.py

Diese Trennung gilt ab sofort fuer alle Commits. Der Nutzer kann
_entwicklung/ loeschen wenn er Platz braucht - die App laeuft ohne.

Verifiziert: App startet nach Aufraeumen unveraendert (Health 200).
2026-09-11 23:44:06 +02:00

112 lines
3.2 KiB
Python

"""Portable Pfadregeln (PLAN.md §9, §9.1).
- Alle Pfade relativ zum Anwendungsroot; keine Laufwerksbuchstaben.
- Keine Abhängigkeit vom Working Directory.
- Temporäre Dateien in userdata/cache, nicht im OS-Profil.
- Schreibbarkeit wird beim Start geprüft (Read-only-Modus folgt Phase 1).
"""
from __future__ import annotations
import os
from dataclasses import dataclass
from pathlib import Path
@dataclass(frozen=True)
class AppPaths:
"""Alle portablen Pfade je Anwendungsroot (§9-Struktur)."""
root: Path
@property
def app(self) -> Path:
return self.root / "app"
@property
def runtime(self) -> Path:
return self.root / "runtime"
@property
def gstreamer_bin(self) -> Path:
return self.runtime / "gstreamer" / "bin"
@property
def gstreamer_plugins(self) -> Path:
return self.runtime / "gstreamer" / "lib" / "gstreamer-1.0"
@property
def web(self) -> Path:
return self.root / "web"
@property
def projects(self) -> Path:
return self.root / "projects"
@property
def media(self) -> Path:
return self.root / "media"
@property
def userdata(self) -> Path:
return self.root / "userdata"
@property
def database(self) -> Path:
return self.userdata / "database"
@property
def cache(self) -> Path:
return self.userdata / "cache"
@property
def identity(self) -> Path:
return self.userdata / "identity" / "node_id"
@property
def config(self) -> Path:
return self.root / "config"
@property
def logs(self) -> Path:
return self.root / "logs"
def ensure_writable(self) -> bool:
"""Prüft Schreibbarkeit des Roots (§9.1)."""
probe = self.root / ".write_probe"
try:
probe.write_text("ok", encoding="ascii")
probe.unlink()
return True
except OSError:
return False
def portable_environment(self) -> dict[str, str]:
"""Umgebungsvariablen für gebündelte GStreamer-Runtime (§9.2, ADR-0002).
System-Plugins werden unterdrückt (leerer GST_PLUGIN_SYSTEM_PATH_1_0),
damit ausschließlich die gebündelte, manifestierte Untermenge lädt.
"""
env = dict(os.environ)
gs_bin = self.gstreamer_bin
if gs_bin.is_dir():
path_var = "PATH"
existing = env.get(path_var, "")
env[path_var] = f"{gs_bin}{os.pathsep}{existing}" if existing else str(gs_bin)
env["GST_PLUGIN_PATH_1_0"] = str(self.gstreamer_plugins)
env["GST_PLUGIN_SYSTEM_PATH_1_0"] = ""
return env
def resolve_app_root(start_from: Path | None = None) -> Path:
"""Bestimmt den Anwendungsroot anhand der PORTABLE_MODE-Markierung (§9).
Sucht vom gegebenen Pfad (Default: dieses Paket) aufwärts nach der
Datei PORTABLE_MODE; im Entwickungsbaum ist das Repo-Root gemeint.
"""
current = Path(start_from or __file__).resolve()
for candidate in [current, *current.parents]:
if (candidate / "PORTABLE_MODE").is_file() or (candidate / "pyproject.toml").is_file():
return candidate
raise RuntimeError("app root not found (PORTABLE_MODE or pyproject.toml missing)")