"""Text-Einblendungen und Overlays via ffmpeg drawtext.""" from __future__ import annotations import subprocess from pathlib import Path def _run(cmd: list[str]) -> subprocess.CompletedProcess: return subprocess.run(cmd, capture_output=True, text=True, check=False) _POSITION_MAP = { "center": ("(w-text_w)/2", "(h-text_h)/2"), "top": ("(w-text_w)/2", "50"), "bottom": ("(w-text_w)/2", "h-text_h-50"), } def create_text_clip( output_path: Path, content: str, duration: float = 3.0, font_size: int = 72, font_color: str = "white", background_color: str = "black", position: str = "center", width: int = 1920, height: int = 1080, ) -> Path: """Text-Standbild-Clip erzeugen (schwarzer/farbiger Hintergrund).""" output_path.parent.mkdir(parents=True, exist_ok=True) x_expr, y_expr = _POSITION_MAP.get(position, _POSITION_MAP["center"]) # Hintergrund-Farbe: "transparent" → schwarzer Hintergrund mit alpha bg = "black" if background_color == "transparent" else background_color drawtext = ( f"drawtext=text='{_escape_text(content)}':" f"fontsize={font_size}:" f"fontcolor={font_color}:" f"x={x_expr}:y={y_expr}" ) cmd = [ "ffmpeg", "-y", "-f", "lavfi", "-i", f"color=c={bg}:size={width}x{height}:rate=25:duration={duration}", "-vf", drawtext, "-c:v", "libx264", "-pix_fmt", "yuv420p", str(output_path), ] result = _run(cmd) if result.returncode != 0: raise RuntimeError(f"ffmpeg drawtext Fehler: {result.stderr}") return output_path def add_text_overlay( input_path: Path, output_path: Path, text: str, position: str = "bottom", duration: float | None = None, font_size: int = 48, font_color: str = "white", ) -> Path: """Text-Overlay auf ein laufendes Video legen.""" output_path.parent.mkdir(parents=True, exist_ok=True) x_expr, y_expr = _POSITION_MAP.get(position, _POSITION_MAP["bottom"]) if duration is not None: enable = f"enable='between(t,0,{duration})'" else: enable = "enable=1" drawtext = ( f"drawtext=text='{_escape_text(text)}':" f"fontsize={font_size}:" f"fontcolor={font_color}:" f"x={x_expr}:y={y_expr}:" f"{enable}" ) cmd = [ "ffmpeg", "-y", "-i", str(input_path), "-vf", drawtext, "-c:a", "copy", str(output_path), ] result = _run(cmd) if result.returncode != 0: raise RuntimeError(f"ffmpeg overlay Fehler: {result.stderr}") return output_path def _escape_text(text: str) -> str: """Sonderzeichen für ffmpeg drawtext escapen.""" return text.replace("'", "\\'").replace(":", "\\:").replace("\\", "\\\\")