mkcontrol-app/app/utils/helpers.py

164 lines
6.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# app/utils/helpers.py
import os
import json
import logging
try:
# Flask ist optional bei Aufruf außerhalb des App-Contexts fallbacken wir
from flask import current_app
except Exception: # pragma: no cover - nur wenn Flask nicht verfügbar ist
current_app = None
logger = logging.getLogger(__name__)
def _resolve_config_dir(config_dir=None):
"""
Ermittelt den zu nutzenden Config-Pfad:
1) Expliziter Parameter
2) Flask current_app.config['CONFIG_DIR']
3) Fallback: lokaler 'configs' Ordner
"""
if config_dir:
return config_dir
if current_app:
try:
return current_app.config.get('CONFIG_DIR', 'configs')
except Exception:
pass
return 'configs'
def load_configs(config_dir=None):
"""
Lädt alle .json-Konfigurationsdateien aus dem configs-Ordner,
außer default_sounds.json (die wird separat geladen).
"""
config_dir = _resolve_config_dir(config_dir)
configs = []
if not os.path.exists(config_dir):
logger.warning(f"Config-Verzeichnis nicht gefunden: {config_dir}")
return configs
for filename in os.listdir(config_dir):
if not filename.lower().endswith('.json'):
continue
if filename == 'default_sounds.json':
continue # globale Sounds separat laden
path = os.path.join(config_dir, filename)
try:
with open(path, 'r', encoding='utf-8') as f:
data = json.load(f)
data['filename'] = filename
data.setdefault('name', filename.replace('.json', '').replace('_', ' ').title())
configs.append(data)
except json.JSONDecodeError as e:
logger.error(f"Ungültiges JSON in {filename}: {e}")
except FileNotFoundError:
logger.error(f"Datei nicht gefunden (trotz listdir): {path}")
except Exception as e:
logger.error(f"Fehler beim Laden von {filename}: {e}")
return sorted(configs, key=lambda x: x.get('name', ''))
def load_default_sounds(config_dir=None):
"""
Lädt die globalen Standard-Sounds aus configs/default_sounds.json
Wird immer geladen, unabhängig von der aktuellen Lok-Konfiguration
"""
config_dir = _resolve_config_dir(config_dir)
path = os.path.join(config_dir, 'default_sounds.json')
if not os.path.exists(path):
logger.info("Keine default_sounds.json gefunden → keine globalen Sounds")
return []
try:
with open(path, 'r', encoding='utf-8') as f:
data = json.load(f)
# Flexibel: entweder 'global_sounds' oder direkt 'sounds'
sounds = data.get('global_sounds', data.get('sounds', []))
logger.info(f"Globale Default-Sounds geladen: {len(sounds)} Einträge")
return sounds
except json.JSONDecodeError as e:
logger.error(f"Ungültiges JSON in default_sounds.json: {e}")
return []
except Exception as e:
logger.error(f"Fehler beim Laden default_sounds.json: {e}")
return []
# ---------- Soundboard-Themen ----------
def load_soundboard_configs(config_dir=None):
"""
Lädt alle Soundboard-Themen aus einem separaten Ordner (z. B. soundboards/).
Unterstützt Unterordner. Jede *.json gilt als Thema (z. B. soundboards/station/theme.json).
Erwartet Schema mit keys wie backgrounds / random_pool / sounds.
"""
config_dir = config_dir or _resolve_config_dir(None)
configs = []
if not os.path.exists(config_dir):
logger.warning(f"Soundboard-Verzeichnis nicht gefunden: {config_dir}")
return configs
for root, _, files in os.walk(config_dir):
for filename in files:
if not filename.lower().endswith('.json'):
continue
rel_path = os.path.relpath(os.path.join(root, filename), config_dir)
try:
with open(os.path.join(config_dir, rel_path), 'r', encoding='utf-8') as f:
data = json.load(f)
data['filename'] = rel_path # relativer Pfad ab soundboards/
data.setdefault('name', filename.replace('.json', '').replace('_', ' ').title())
configs.append(data)
except Exception as e:
logger.error(f"Fehler beim Laden Soundboard {rel_path}: {e}")
return sorted(configs, key=lambda x: x.get('name', ''))
def load_soundboard_config(filename, config_dir, sounds_dir=None):
"""
Lädt ein Soundboard-Theme. Falls die Theme-Datei in Unterordnern liegt,
wird ein basis Sound-Pfad auf denselben relativen Unterordner unterhalb von sounds/ gesetzt.
Alle Einträge (backgrounds/sounds/random_pool) erhalten ein 'base_path'.
"""
sounds_dir = sounds_dir or 'sounds'
path = os.path.normpath(os.path.join(config_dir, filename))
if not path.startswith(os.path.abspath(config_dir)):
raise ValueError("Ungültiger Pfad")
rel_dir = os.path.dirname(filename) # z.B. "station" oder "urban/night"
base_sound_dir = os.path.normpath(os.path.join(sounds_dir, rel_dir)) if rel_dir else sounds_dir
with open(path, 'r', encoding='utf-8') as f:
data = json.load(f)
data['filename'] = filename # relative Angabe beibehalten
data.setdefault('name', filename.replace('.json', '').replace('_', ' ').title())
data['base_sound_dir'] = base_sound_dir
for key in ('backgrounds', 'sounds', 'random_pool'):
if key in data and isinstance(data[key], list):
for item in data[key]:
item.setdefault('base_path', base_sound_dir)
return data
def read_default_theme(path):
try:
if not os.path.exists(path):
return None
with open(path, 'r', encoding='utf-8') as f:
val = f.read().strip()
return val or None
except Exception:
return None
def write_default_theme(path, filename):
os.makedirs(os.path.dirname(path), exist_ok=True)
with open(path, 'w', encoding='utf-8') as f:
f.write(filename)