116 lines
3.3 KiB
Python
Executable File
116 lines
3.3 KiB
Python
Executable File
"""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
|
|
)
|