calesthio/OpenMontage · error · RuntimeError

ffmpeg is required to render preview MP4

Error message

ffmpeg is required to render preview MP4

What it means

Raised by _render_preview_mp4 when shutil.which('ffmpeg') returns None, meaning no ffmpeg executable is on PATH. The character-animation preview pipeline needs ffmpeg to encode the captured PNG frames into an MP4. It fails fast before launching Playwright so the user gets an actionable dependency message instead of a cryptic subprocess error.

Source

Thrown at tools/character/character_animation.py:72

def _normalize_style(style: Any) -> dict[str, Any]:
    if not isinstance(style, dict):
        return {}
    normalized: dict[str, Any] = {}
    visual_style = style.get("visual_style") or style.get("name") or style.get("style")
    if visual_style:
        normalized["visual_style"] = str(visual_style)
    palette = style.get("palette")
    if isinstance(palette, list):
        normalized["palette"] = [str(color) for color in palette]
    for key in ["line_style", "texture"]:
        if style.get(key):
            normalized[key] = str(style[key])
    return normalized


def _render_preview_mp4(preview_path: Path, video_path: Path, duration_seconds: float, fps: int) -> None:
    if shutil.which("ffmpeg") is None:
        raise RuntimeError("ffmpeg is required to render preview MP4")
    try:
        from playwright.sync_api import sync_playwright
    except Exception as exc:  # pragma: no cover - dependency-specific branch
        raise RuntimeError("Playwright is required to render preview MP4") from exc

    frame_dir = video_path.parent / f"{video_path.stem}_frames"
    frame_dir.mkdir(parents=True, exist_ok=True)
    frame_count = max(2, int(duration_seconds * fps))
    with sync_playwright() as p:
        browser = p.chromium.launch()
        page = browser.new_page(viewport={"width": 1280, "height": 720})
        page.goto(preview_path.resolve().as_uri(), wait_until="networkidle")
        for frame in range(frame_count):
            if frame:
                page.wait_for_timeout(int(1000 / fps))
            page.screenshot(path=str(frame_dir / f"frame_{frame:04d}.png"))
        browser.close()

View on GitHub (pinned to 95e1c3d0ab)

Solutions

  1. Install ffmpeg and ensure it is on PATH: `brew install ffmpeg` (macOS), `apt-get install -y ffmpeg` (Debian/Ubuntu), `choco install ffmpeg` (Windows), or add your existing ffmpeg bin dir to PATH.
  2. If you use a virtualenv/conda env, install ffmpeg into it (e.g. `conda install -c conda-forge ffmpeg`) so it is guaranteed visible to the same interpreter.
  3. Verify with `python -c "import shutil; print(shutil.which('ffmpeg'))"` — it must print a path, not None, before re-running.
  4. If you cannot install ffmpeg system-wide, point PATH at a static ffmpeg binary directory when launching the process.

Example fix

# before: fails with RuntimeError('ffmpeg is required to render preview MP4')
# Dockerfile
FROM python:3.12-slim
RUN pip install -r requirements.txt

# after
FROM python:3.12-slim
RUN apt-get update && apt-get install -y --no-install-recommends ffmpeg \
    && rm -rf /var/lib/apt/lists/*
RUN pip install -r requirements.txt
Defensive patterns

Strategy: validation

Validate before calling

import shutil

def can_render_preview() -> bool:
    return shutil.which("ffmpeg") is not None

Try / catch

try:
    _render_preview_mp4(preview, video, duration, fps)
except RuntimeError as e:
    if "ffmpeg is required" in str(e):
        raise SystemExit("Install ffmpeg (apt/brew/choco) and re-run") from e
    raise

Prevention

When it happens

Trigger: Calling the character animation tool's preview/MP4 render path (any code path that reaches _render_preview_mp4) on a machine where `ffmpeg` is not installed or not on the PATH that Python's shutil.which sees.

Common situations: Fresh dev machines, minimal Docker containers (python:slim has no ffmpeg), macOS without `brew install ffmpeg`, Windows where ffmpeg was installed but not added to PATH, or CI runners that only install Python deps.

Related errors


AI-assisted analysis of calesthio/OpenMontage@95e1c3d0ab (2026-08-15). Data as JSON: /api/errors/f9dd359bea41208c. Report an issue: GitHub.