2026-01-07 16:24:01 +08:00

85 lines
2.6 KiB
Python
Executable File

import shutil
import sys
from pathlib import Path
import ast
import subprocess
import tempfile
def _get_class_names(py_file: str) -> list[str]:
file_path = Path(py_file)
source = file_path.read_text(encoding="utf-8")
tree = ast.parse(source, filename=str(file_path))
return [node.name for node in ast.walk(tree) if isinstance(node, ast.ClassDef)]
def render_manim(
script_path: str,
quality: str = "h",
preview: bool = True,
) -> Path:
output_dir = Path.cwd() / "media"
print("Clearing media folder:", output_dir)
shutil.rmtree(output_dir, ignore_errors=True)
script_path = Path(script_path).resolve()
if not script_path.exists():
raise FileNotFoundError(script_path, " does not exist.")
scene_name = _get_class_names(str(script_path))[0]
cmd = [
sys.executable, "-m", "manim",
f"-pq{quality}",
]
if preview:
cmd.insert(3, "-p")
cmd.extend([str(script_path), scene_name])
print("Running:", " ".join(cmd))
try:
subprocess.run(cmd, check=True, capture_output=True, text=True)
except subprocess.CalledProcessError as e:
print("Manim rendering failed:")
print("stdout:", e.stdout)
print("stderr:", e.stderr)
error_info = f"Error rendering {scene_name} from {script_path}: {e.stderr}"
# shutil.rmtree(output_dir, ignore_errors=True)
raise RuntimeError(error_info)
# Find valid mp4 files where no parent directory contains a partial_movie_files folder
video_file = None
for mp4_file in (Path.cwd() / "media" / "videos").parent.rglob("*.mp4"):
if mp4_file.name == f"{scene_name}.mp4":
video_file = mp4_file
break
target_path = script_path.parent / video_file.name
print(f"Copying video to {target_path}")
shutil.copy2(video_file, target_path)
shutil.rmtree(output_dir, ignore_errors=True)
return target_path
def concat_videos(video_paths: list[Path]) -> Path:
if not video_paths:
raise ValueError("No video files to concatenate")
video_paths = [Path(p).resolve() for p in video_paths]
output_path = video_paths[0].parent / "combined_video.mp4"
with tempfile.NamedTemporaryFile(mode="w", suffix=".txt", delete=False) as f:
for p in video_paths:
f.write(f"file '{p.as_posix()}'\n")
list_file = f.name
cmd = [
"ffmpeg",
"-y",
"-f", "concat",
"-safe", "0",
"-i", list_file,
"-c", "copy",
str(output_path)
]
subprocess.run(cmd, check=True)
return output_path