Initial commit: auto-video-cut project
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
115
auto_video_cut/config.py
Executable file
115
auto_video_cut/config.py
Executable file
@@ -0,0 +1,115 @@
|
||||
"""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
|
||||
)
|
||||
Reference in New Issue
Block a user