Files
video-pipeline/modules/render_engine.py
2025-07-23 11:52:09 -07:00

67 lines
2.3 KiB
Python

import subprocess
from pathlib import Path
from modules.config import DEBUG
def render_montage_clip(
title_card_path: Path,
montage_path: Path,
output_path: Path,
intro_path: Path,
outro_path: Path,
music_path: Path,
is_vertical: bool = False,
):
"""
Combines intro (with title), montage, and outro into a final video.
Uses ffmpeg for concatenation and audio overlay.
"""
if not title_card_path.exists():
raise FileNotFoundError(f"[ERROR] Title card not found: {title_card_path}")
if not montage_path.exists():
raise FileNotFoundError(f"[ERROR] Montage clip not found: {montage_path}")
if not intro_path.exists():
raise FileNotFoundError(f"[ERROR] Intro file not found: {intro_path}")
if not outro_path.exists():
raise FileNotFoundError(f"[ERROR] Outro file not found: {outro_path}")
if not music_path.exists():
raise FileNotFoundError(f"[ERROR] Music track not found: {music_path}")
filter_complex = (
"[0:v:0]fps=30,setsar=1[v0];"
"[1:v:0]fps=30,setsar=1[v1];"
"[1:a:0]anull[a1];"
"[3:v:0]fps=30,setsar=1[v3];"
"[v0][v1][v3]concat=n=3:v=1:a=0[outv];"
"[a1][2:a:0]amix=inputs=2:duration=first[outa]"
)
ffmpeg_cmd = [
"ffmpeg",
"-y",
"-i", str(title_card_path), # 0 = intro with title baked in
"-i", str(montage_path), # 1 = montage content
"-i", str(music_path), # 2 = background music
"-i", str(outro_path), # 3 = static outro
"-filter_complex", filter_complex,
"-map", "[outv]",
"-map", "[outa]",
"-c:v", "libx264",
"-preset", "ultrafast",
"-crf", "23",
"-c:a", "aac",
"-b:a", "192k",
str(output_path)
]
if DEBUG:
print(f"[DEBUG] Starting render_montage_clip")
print(f"[DEBUG] Input files:")
print(f" title_card_path: {title_card_path}{title_card_path.exists()}")
print(f" montage_path: {montage_path}{montage_path.exists()}")
print(f" output_path: {output_path}")
print(f" output_dir exists? {output_path.parent.exists()}")
print(f"[DEBUG] subprocess command: {ffmpeg_cmd}")
subprocess.run(ffmpeg_cmd, check=True)