147 lines
5.6 KiB
Python
147 lines
5.6 KiB
Python
# 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
|