"""Konfiguration laden und validieren.""" from __future__ import annotations import os from pathlib import Path from typing import Any import yaml MUSIC_EXTENSIONS = {".mp3", ".wav", ".flac", ".aac", ".ogg"} VIDEO_EXTENSIONS = {".mp4", ".mov", ".avi", ".mkv"} IMAGE_EXTENSIONS = {".png", ".jpg", ".jpeg"} DEFAULTS: dict[str, Any] = { "resources": { "folder": "./resources", }, "music": { "mode": "random", "volume_original": 1.0, "volume_music": 0.3, }, "videos": { "intro": None, "outro": None, "transitions": False, }, "images": { "title_card": None, "duration": 3, }, "silence": { "threshold_db": -40, "min_duration": 0.5, }, "scenes": { "threshold": 27.0, }, "output": { "format": "mp4", "folder": "./output", }, } def _deep_merge(base: dict, override: dict) -> dict: """Rekursiv Dictionaries zusammenführen.""" result = dict(base) for key, value in override.items(): if key in result and isinstance(result[key], dict) and isinstance(value, dict): result[key] = _deep_merge(result[key], value) else: result[key] = value return result def load_config(config_path: str | Path | None = None) -> dict[str, Any]: """YAML-Konfiguration laden und mit Standardwerten zusammenführen.""" config = dict(DEFAULTS) if config_path is None: return config path = Path(config_path) if not path.exists(): raise FileNotFoundError(f"Konfigurationsdatei nicht gefunden: {path}") with open(path, encoding="utf-8") as fh: user_config = yaml.safe_load(fh) or {} return _deep_merge(config, user_config) def validate_config(config: dict[str, Any]) -> list[str]: """Konfiguration prüfen, Warnungen zurückgeben.""" warnings: list[str] = [] resources_folder = Path(config["resources"]["folder"]) if not resources_folder.exists(): warnings.append(f"Ressourcen-Ordner existiert nicht: {resources_folder}") else: music_folder = resources_folder / "music" if not music_folder.exists(): warnings.append(f"Musik-Ordner existiert nicht: {music_folder}") else: music_files = [ f for f in music_folder.iterdir() if f.suffix.lower() in MUSIC_EXTENSIONS ] if not music_files: warnings.append(f"Keine Musikdateien in: {music_folder}") vol_orig = config["music"]["volume_original"] vol_music = config["music"]["volume_music"] if not (0.0 <= vol_orig <= 1.0): warnings.append(f"volume_original muss zwischen 0.0 und 1.0 liegen (ist: {vol_orig})") if not (0.0 <= vol_music <= 1.0): warnings.append(f"volume_music muss zwischen 0.0 und 1.0 liegen (ist: {vol_music})") return warnings def get_resources_folder(config: dict[str, Any]) -> Path: return Path(config["resources"]["folder"]) def get_music_files(config: dict[str, Any]) -> list[Path]: """Alle Musikdateien aus dem konfigurierten Ordner zurückgeben.""" music_folder = get_resources_folder(config) / "music" if not music_folder.exists(): return [] return sorted( f for f in music_folder.iterdir() if f.suffix.lower() in MUSIC_EXTENSIONS )