recorder.py aktualisiert
This commit is contained in:
+74
-21
@@ -58,21 +58,47 @@ POLL_INTERVAL = float(os.environ.get("POLL_INTERVAL", "2"))
|
|||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Branding / theme configuration
|
# Branding / theme configuration
|
||||||
|
|
||||||
SITE_NAME = os.environ.get("SITE_NAME", "Event Audio")
|
|
||||||
SITE_SUBTITLE = os.environ.get("SITE_SUBTITLE", "Audiostream-Aufzeichnungen")
|
|
||||||
PAGE_TITLE = os.environ.get("PAGE_TITLE", "Event Audio – Aufnahmen")
|
|
||||||
PAGE_HEADING = os.environ.get("PAGE_HEADING", "EVENT AUDIO – Aufnahmen")
|
|
||||||
THEME_FILE = Path(os.environ.get("THEME_FILE", "/config/theme.json"))
|
THEME_FILE = Path(os.environ.get("THEME_FILE", "/config/theme.json"))
|
||||||
LOGO_FILE = Path(os.environ["LOGO_FILE"]) if os.environ.get("LOGO_FILE") else None
|
SETTINGS_FILE = Path(os.environ.get("SETTINGS_FILE", "/config/settings.json"))
|
||||||
FAVICON_FILE = Path(os.environ["FAVICON_FILE"]) if os.environ.get("FAVICON_FILE") else None
|
|
||||||
GIT_REPO_URL = os.environ.get("GIT_REPO_URL", "")
|
DEFAULT_SETTINGS = {
|
||||||
EDITOR_URL = os.environ.get("EDITOR_URL", "")
|
"site_name": os.environ.get("SITE_NAME", "Event Audio"),
|
||||||
|
"site_subtitle": os.environ.get("SITE_SUBTITLE", "Audiostream-Aufzeichnungen"),
|
||||||
|
"page_title": os.environ.get("PAGE_TITLE", "Event Audio – Aufnahmen"),
|
||||||
|
"page_heading": os.environ.get("PAGE_HEADING", "EVENT AUDIO – Aufnahmen"),
|
||||||
|
"edit_page_title": os.environ.get("EDIT_PAGE_TITLE", "Event Audio – Aufnahme bearbeiten"),
|
||||||
|
"edit_page_heading": os.environ.get("EDIT_PAGE_HEADING", "Aufnahme bearbeiten"),
|
||||||
|
"git_repo_url": os.environ.get("GIT_REPO_URL", ""),
|
||||||
|
"user_url": os.environ.get("USER_URL", ""),
|
||||||
|
"editor_url": os.environ.get("EDITOR_URL", ""),
|
||||||
|
"logo_file": os.environ.get("LOGO_FILE", ""),
|
||||||
|
"favicon_file": os.environ.get("FAVICON_FILE", ""),
|
||||||
|
}
|
||||||
|
|
||||||
DEFAULT_THEME = {'background': '#111', 'surface': '#181818', 'surface_alt': '#222', 'surface_dark': '#0b0b0b', 'text': '#eee', 'text_secondary': '#ccc', 'text_muted': '#aaa', 'text_faint': '#777', 'border': '#333', 'border_subtle': '#2b2b2b', 'control': '#333', 'control_hover': '#444', 'primary': '#1769aa', 'primary_hover': '#1f7fc2', 'accent': '#ffcc33', 'success': '#173d24', 'success_text': '#b8f5c9', 'warning': '#6b4e00', 'danger': '#8b0000', 'danger_soft': '#4a2020', 'danger_soft_hover': '#6a2b2b', 'error': '#4a1b1b', 'error_text': '#ffd0d0', 'recording': '#b00000', 'recording_border': '#3a2222', 'selection': 'rgba(80,150,255,.16)', 'shadow': '#000', 'scroll_thumb': '#777', 'scroll_thumb_hover': '#999', 'scroll_thumb_active': '#aaa', 'live_surface': '#1d1111', 'table_header': '#252525', 'table_text': '#eee', 'footer': '#666', 'white': '#fff','surface_modal':'#202020','overlay':'rgba(0,0,0,.78)'}
|
DEFAULT_THEME = {'background': '#111', 'surface': '#181818', 'surface_alt': '#222', 'surface_dark': '#0b0b0b', 'text': '#eee', 'text_secondary': '#ccc', 'text_muted': '#aaa', 'text_faint': '#777', 'border': '#333', 'border_subtle': '#2b2b2b', 'control': '#333', 'control_hover': '#444', 'primary': '#1769aa', 'primary_hover': '#1f7fc2', 'accent': '#ffcc33', 'success': '#173d24', 'success_text': '#b8f5c9', 'warning': '#6b4e00', 'danger': '#8b0000', 'danger_soft': '#4a2020', 'danger_soft_hover': '#6a2b2b', 'error': '#4a1b1b', 'error_text': '#ffd0d0', 'recording': '#b00000', 'recording_border': '#3a2222', 'selection': 'rgba(80,150,255,.16)', 'shadow': '#000', 'scroll_thumb': '#777', 'scroll_thumb_hover': '#999', 'scroll_thumb_active': '#aaa', 'live_surface': '#1d1111', 'table_header': '#252525', 'table_text': '#eee', 'footer': '#666', 'white': '#fff','surface_modal':'#202020','overlay':'rgba(0,0,0,.78)'}
|
||||||
|
|
||||||
|
def load_settings():
|
||||||
|
"""Load UI/branding settings, falling back to environment defaults."""
|
||||||
|
settings = dict(DEFAULT_SETTINGS)
|
||||||
|
if SETTINGS_FILE.is_file():
|
||||||
|
try:
|
||||||
|
data = json.loads(SETTINGS_FILE.read_text(encoding="utf-8"))
|
||||||
|
if isinstance(data, dict):
|
||||||
|
for key in DEFAULT_SETTINGS:
|
||||||
|
if key in data and isinstance(data[key], str):
|
||||||
|
settings[key] = data[key]
|
||||||
|
if isinstance(data.get("colors"), dict):
|
||||||
|
settings["colors"] = dict(data["colors"])
|
||||||
|
except Exception as exc:
|
||||||
|
print(f"[branding] Einstellungen konnten nicht geladen werden: {exc}", flush=True)
|
||||||
|
return settings
|
||||||
|
|
||||||
|
|
||||||
def load_theme():
|
def load_theme():
|
||||||
"""Load semantic theme colors from JSON, falling back to built-in defaults."""
|
"""Load semantic theme colors from settings, then theme.json, then defaults."""
|
||||||
theme = dict(DEFAULT_THEME)
|
theme = dict(DEFAULT_THEME)
|
||||||
|
|
||||||
|
# Keep the existing theme.json as a backwards-compatible fallback.
|
||||||
if THEME_FILE.is_file():
|
if THEME_FILE.is_file():
|
||||||
try:
|
try:
|
||||||
data = json.loads(THEME_FILE.read_text(encoding="utf-8"))
|
data = json.loads(THEME_FILE.read_text(encoding="utf-8"))
|
||||||
@@ -84,25 +110,50 @@ def load_theme():
|
|||||||
theme[key] = value.strip()
|
theme[key] = value.strip()
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
print(f"[branding] Theme konnte nicht geladen werden: {exc}", flush=True)
|
print(f"[branding] Theme konnte nicht geladen werden: {exc}", flush=True)
|
||||||
|
|
||||||
|
settings = load_settings()
|
||||||
|
colors = settings.get("colors") if isinstance(settings.get("colors"), dict) else {}
|
||||||
|
for key in theme:
|
||||||
|
value = colors.get(key)
|
||||||
|
if isinstance(value, str) and re.fullmatch(r"(?:#[0-9a-fA-F]{3,8}|rgba?\([^)]*\))", value.strip()):
|
||||||
|
theme[key] = value.strip()
|
||||||
return theme
|
return theme
|
||||||
|
|
||||||
|
|
||||||
def theme_css():
|
def theme_css():
|
||||||
theme = load_theme()
|
theme = load_theme()
|
||||||
return ":root {\n" + "\n".join(
|
return ":root {\n" + "\n".join(
|
||||||
f" --color-{key.replace('_', '-')}: {value};" for key, value in theme.items()
|
f" --color-{key.replace('_', '-')}: {value};" for key, value in theme.items()
|
||||||
) + "\n}"
|
) + "\n}"
|
||||||
|
|
||||||
|
|
||||||
def branding_context():
|
def branding_context():
|
||||||
|
settings = load_settings()
|
||||||
|
logo_file = Path(settings["logo_file"]) if settings.get("logo_file") else None
|
||||||
|
favicon_file = Path(settings["favicon_file"]) if settings.get("favicon_file") else None
|
||||||
return {
|
return {
|
||||||
"site_name": SITE_NAME,
|
"site_name": settings["site_name"],
|
||||||
"site_subtitle": SITE_SUBTITLE,
|
"site_subtitle": settings["site_subtitle"],
|
||||||
"page_title": PAGE_TITLE,
|
"page_title": settings["page_title"],
|
||||||
"page_heading": PAGE_HEADING,
|
"page_heading": settings["page_heading"],
|
||||||
|
"edit_page_title": settings["edit_page_title"],
|
||||||
|
"edit_page_heading": settings["edit_page_heading"],
|
||||||
"theme_css": theme_css(),
|
"theme_css": theme_css(),
|
||||||
"logo_url": "/branding/logo" if LOGO_FILE and LOGO_FILE.is_file() else None,
|
"logo_url": "/branding/logo" if logo_file and logo_file.is_file() else None,
|
||||||
"favicon_url": "/branding/favicon" if FAVICON_FILE and FAVICON_FILE.is_file() else None,
|
"favicon_url": "/branding/favicon" if favicon_file and favicon_file.is_file() else None,
|
||||||
|
"git_repo_url": settings["git_repo_url"],
|
||||||
|
"user_url": settings["user_url"],
|
||||||
|
"editor_url": settings["editor_url"],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def save_settings(data):
|
||||||
|
"""Atomically persist the editable page configuration."""
|
||||||
|
SETTINGS_FILE.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
temp = SETTINGS_FILE.with_name(SETTINGS_FILE.name + ".tmp")
|
||||||
|
temp.write_text(json.dumps(data, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
||||||
|
os.replace(temp, SETTINGS_FILE)
|
||||||
|
|
||||||
NORMALIZE = os.environ.get("NORMALIZE", "true").lower() not in ("0", "false", "no")
|
NORMALIZE = os.environ.get("NORMALIZE", "true").lower() not in ("0", "false", "no")
|
||||||
NORMALIZE_TARGET = os.environ.get("NORMALIZE_TARGET", "-16")
|
NORMALIZE_TARGET = os.environ.get("NORMALIZE_TARGET", "-16")
|
||||||
NORMALIZE_TRUE_PEAK = os.environ.get("NORMALIZE_TRUE_PEAK", "-1.5")
|
NORMALIZE_TRUE_PEAK = os.environ.get("NORMALIZE_TRUE_PEAK", "-1.5")
|
||||||
@@ -1271,24 +1322,26 @@ def render_index():
|
|||||||
normalizing_filename=normalizing_filename,
|
normalizing_filename=normalizing_filename,
|
||||||
status_error=status_error,
|
status_error=status_error,
|
||||||
icecast_public_url=ICECAST_PUBLIC_URL,
|
icecast_public_url=ICECAST_PUBLIC_URL,
|
||||||
git_repo_url=GIT_REPO_URL,
|
|
||||||
editor_url=EDITOR_URL,
|
|
||||||
**branding_context(),
|
**branding_context(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/branding/logo")
|
@app.route("/branding/logo")
|
||||||
def branding_logo():
|
def branding_logo():
|
||||||
if LOGO_FILE is None or not LOGO_FILE.is_file():
|
settings = load_settings()
|
||||||
|
path = Path(settings["logo_file"]) if settings.get("logo_file") else None
|
||||||
|
if path is None or not path.is_file():
|
||||||
abort(404)
|
abort(404)
|
||||||
return send_file(LOGO_FILE)
|
return send_file(path)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/branding/favicon")
|
@app.route("/branding/favicon")
|
||||||
def branding_favicon():
|
def branding_favicon():
|
||||||
if FAVICON_FILE is None or not FAVICON_FILE.is_file():
|
settings = load_settings()
|
||||||
|
path = Path(settings["favicon_file"]) if settings.get("favicon_file") else None
|
||||||
|
if path is None or not path.is_file():
|
||||||
abort(404)
|
abort(404)
|
||||||
return send_file(FAVICON_FILE)
|
return send_file(path)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/")
|
@app.route("/")
|
||||||
|
|||||||
Reference in New Issue
Block a user