Files
auto-video-cut/auto_video_cut/audio.py
Christoph K. 267070ad52 Initial commit: auto-video-cut project
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 21:51:01 +02:00

117 lines
3.1 KiB
Python
Executable File

"""Hintergrundmusik-Mixing und Audio-Logik."""
from __future__ import annotations
import random
import subprocess
from pathlib import Path
from .config import MUSIC_EXTENSIONS, get_music_files
def _run(cmd: list[str]) -> subprocess.CompletedProcess:
return subprocess.run(cmd, capture_output=True, text=True, check=False)
def pick_music_file(
music_files: list[Path],
mode: str = "random",
) -> Path:
"""Musikdatei nach Modus auswählen."""
if not music_files:
raise FileNotFoundError("Keine Musikdateien gefunden.")
if mode == "random":
return random.choice(music_files)
elif mode in ("alphabetical", "loop"):
return sorted(music_files)[0]
else:
raise ValueError(f"Unbekannter Musik-Modus: {mode}")
def mix_music(
video_path: Path,
output_path: Path,
music_file: Path,
volume_original: float = 1.0,
volume_music: float = 0.3,
) -> Path:
"""Hintergrundmusik in Video mixen."""
output_path.parent.mkdir(parents=True, exist_ok=True)
# Prüfen ob Video Audio-Stream hat
probe_cmd = [
"ffprobe", "-v", "error",
"-select_streams", "a",
"-show_entries", "stream=codec_type",
"-of", "default=noprint_wrappers=1:nokey=1",
str(video_path),
]
probe = _run(probe_cmd)
has_audio = bool(probe.stdout.strip())
if has_audio:
filter_complex = (
f"[0:a]volume={volume_original}[v1];"
f"[1:a]volume={volume_music}[v2];"
f"[v1][v2]amix=inputs=2:duration=first[a]"
)
cmd = [
"ffmpeg", "-y",
"-i", str(video_path),
"-stream_loop", "-1",
"-i", str(music_file),
"-filter_complex", filter_complex,
"-c:v", "copy",
"-c:a", "aac",
"-map", "0:v:0",
"-map", "[a]",
"-shortest",
str(output_path),
]
else:
# Kein Original-Audio → Musik direkt als Track
cmd = [
"ffmpeg", "-y",
"-i", str(video_path),
"-stream_loop", "-1",
"-i", str(music_file),
"-filter_complex", f"[1:a]volume={volume_music}[a]",
"-c:v", "copy",
"-c:a", "aac",
"-map", "0:v:0",
"-map", "[a]",
"-shortest",
str(output_path),
]
result = _run(cmd)
if result.returncode != 0:
raise RuntimeError(f"ffmpeg Fehler beim Musik-Mixing: {result.stderr}")
return output_path
def add_music_from_config(
video_path: Path,
output_path: Path,
config: dict,
) -> Path:
"""Musik aus Konfiguration auswählen und mixen."""
music_files = get_music_files(config)
if not music_files:
raise FileNotFoundError(
f"Keine Musikdateien in: {config['resources']['folder']}/music/"
)
mode = config["music"]["mode"]
music_file = pick_music_file(music_files, mode)
return mix_music(
video_path=video_path,
output_path=output_path,
music_file=music_file,
volume_original=config["music"]["volume_original"],
volume_music=config["music"]["volume_music"],
)