7e191ff6c9
1) MADRIX-artige Generator-Layer (15 prozedurale Quellen): - engine.GENERATORS: ball (animiert), gradient, pinwheel (animiert), zone-plate (animiert), checkers, snow (Rauschen animiert), circular, spokes, smpte, gamut, red/green/blue/black/white - alle per gst-launch verifiziert, kombinierbar mit FX1/FX2 (Generator+FX gerendert, 28 Frames im Test) - add_generator + Pattern-Wechsel zur Laufzeit (ball->snow) mit Rebuild, Katalog-Endpoint /api/generators, UI-Dropdown im Layer-Tab - Generatoren in Projekten speicher-/ladbar (gradient nach Load aktiv) - Ungueltige Generator-IDs werden mit 400 abgewiesen 2) Medien-Verteilung ueber mehrere Server: - hms_app/distribution.py: Server-Check (/api/health), Remote-Medien-Liste, Streaming-Import mit Upload-Sicherung - config.servers: Remote-Server registrieren (Name+URL, HTTP-only, Duplikat-Schutz), /api/servers (online/offline + Medien), servers/import: Datei von Remote ins lokale Archiv ziehen - UI: Remote-Server in Einstellungen verwalten, Remote-Medien im Medien-Tab mit 'Ins Archiv ziehen'-Button - run.py --data-root: mehrere Instanzen auf einer Maschine mit getrennten config/media/projects-Ordnern - E2E mit ZWEI echten Servern: A registriert B, sieht B-Medien, zieht clip byte-identisch (SHA verifiziert) und spielt ihn als Layer; Offline-Erkennung nach B-Stop; 404/400-Faelle 3) Autostart in den Einstellungen: - Einstellungen: Autostart-Projekt-Dropdown (aus Projektliste), abwaehlbar; Projekte-Tab weiterhin mit Autostart-Button - FIX Settings-Save-Merge: POST /api/settings merged jetzt auf die AKTUELLEN Settings - Autostart und registrierte Server gehen beim Speichern anderer Einstellungen nicht mehr verloren (E2E: Autostart ueberlebt Settings-Save, FPS uebernommen) E2E: 33/33 PASS (Verteilung/Generatoren) + Regressionen: Projekte 25/25, App 14/14, FX 16/16 - alle gruen
50 lines
1.6 KiB
Python
50 lines
1.6 KiB
Python
"""Medien-Verteilung: Remote-Server registrieren, Medien abrufen."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import urllib.error
|
|
import urllib.parse
|
|
import urllib.request
|
|
|
|
TIMEOUT_SHORT = 5
|
|
TIMEOUT_IMPORT = 180
|
|
|
|
|
|
def check(url: str) -> bool:
|
|
"""Prueft ob ein Remote-Server ein HMS MediaEngine ist."""
|
|
try:
|
|
with urllib.request.urlopen(
|
|
url.rstrip("/") + "/api/health",
|
|
timeout=TIMEOUT_SHORT) as r:
|
|
return json.loads(r.read().decode()).get("status") == "ok"
|
|
except Exception: # noqa: BLE001
|
|
return False
|
|
|
|
|
|
def list_media(url: str) -> dict:
|
|
"""Medienbibliothek eines Remote-Servers abrufen."""
|
|
with urllib.request.urlopen(
|
|
url.rstrip("/") + "/api/media",
|
|
timeout=TIMEOUT_SHORT) as r:
|
|
return json.loads(r.read().decode())
|
|
|
|
|
|
def import_media(url: str, media_name: str, library) -> tuple[bool, object]:
|
|
"""Zieht eine Datei per HTTP von einem Remote-Server in die lokale
|
|
Bibliothek (Streaming, gleiche Upload-Pfad-Sicherung wie Web-Upload)."""
|
|
q = urllib.parse.quote(media_name)
|
|
try:
|
|
with urllib.request.urlopen(
|
|
url.rstrip("/") + "/media/" + q,
|
|
timeout=TIMEOUT_IMPORT) as r:
|
|
length = int(r.headers.get("Content-Length", "0"))
|
|
saved, err = library.save_stream(media_name, r, length)
|
|
if err:
|
|
return False, err
|
|
return True, saved
|
|
except urllib.error.HTTPError as e:
|
|
return False, f"Remote-Fehler HTTP {e.code}"
|
|
except (urllib.error.URLError, OSError, ValueError) as e:
|
|
return False, f"Verbindung fehlgeschlagen: {e}"
|