{"record":{"id":"f9dd359bea41208c","repo":"calesthio/OpenMontage","slug":"ffmpeg-is-required-to-render-preview-mp4","errorCode":null,"errorMessage":"ffmpeg is required to render preview MP4","messagePattern":"ffmpeg is required to render preview MP4","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"tools/character/character_animation.py","lineNumber":72,"sourceCode":"def _normalize_style(style: Any) -> dict[str, Any]:\n    if not isinstance(style, dict):\n        return {}\n    normalized: dict[str, Any] = {}\n    visual_style = style.get(\"visual_style\") or style.get(\"name\") or style.get(\"style\")\n    if visual_style:\n        normalized[\"visual_style\"] = str(visual_style)\n    palette = style.get(\"palette\")\n    if isinstance(palette, list):\n        normalized[\"palette\"] = [str(color) for color in palette]\n    for key in [\"line_style\", \"texture\"]:\n        if style.get(key):\n            normalized[key] = str(style[key])\n    return normalized\n\n\ndef _render_preview_mp4(preview_path: Path, video_path: Path, duration_seconds: float, fps: int) -> None:\n    if shutil.which(\"ffmpeg\") is None:\n        raise RuntimeError(\"ffmpeg is required to render preview MP4\")\n    try:\n        from playwright.sync_api import sync_playwright\n    except Exception as exc:  # pragma: no cover - dependency-specific branch\n        raise RuntimeError(\"Playwright is required to render preview MP4\") from exc\n\n    frame_dir = video_path.parent / f\"{video_path.stem}_frames\"\n    frame_dir.mkdir(parents=True, exist_ok=True)\n    frame_count = max(2, int(duration_seconds * fps))\n    with sync_playwright() as p:\n        browser = p.chromium.launch()\n        page = browser.new_page(viewport={\"width\": 1280, \"height\": 720})\n        page.goto(preview_path.resolve().as_uri(), wait_until=\"networkidle\")\n        for frame in range(frame_count):\n            if frame:\n                page.wait_for_timeout(int(1000 / fps))\n            page.screenshot(path=str(frame_dir / f\"frame_{frame:04d}.png\"))\n        browser.close()\n","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/calesthio/OpenMontage/blob/95e1c3d0ab93482159818560f6a8c8e866b9139f/tools/character/character_animation.py#L54-L90","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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.","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.","Verify with `python -c \"import shutil; print(shutil.which('ffmpeg'))\"` — it must print a path, not None, before re-running.","If you cannot install ffmpeg system-wide, point PATH at a static ffmpeg binary directory when launching the process."],"exampleFix":"# before: fails with RuntimeError('ffmpeg is required to render preview MP4')\n# Dockerfile\nFROM python:3.12-slim\nRUN pip install -r requirements.txt\n\n# after\nFROM python:3.12-slim\nRUN apt-get update && apt-get install -y --no-install-recommends ffmpeg \\\n    && rm -rf /var/lib/apt/lists/*\nRUN pip install -r requirements.txt","handlingStrategy":"validation","validationCode":"import shutil\n\ndef can_render_preview() -> bool:\n    return shutil.which(\"ffmpeg\") is not None","typeGuard":null,"tryCatchPattern":"try:\n    _render_preview_mp4(preview, video, duration, fps)\nexcept RuntimeError as e:\n    if \"ffmpeg is required\" in str(e):\n        raise SystemExit(\"Install ffmpeg (apt/brew/choco) and re-run\") from e\n    raise","preventionTips":["Add ffmpeg to Dockerfiles and CI images explicitly.","Run a startup dependency check (shutil.which) before long jobs that need previews.","Document ffmpeg as a system prerequisite next to pip requirements."],"tags":["ffmpeg","dependency","environment","character-animation","rendering"],"backgroundTag":null,"analyzedSha":"95e1c3d0ab93482159818560f6a8c8e866b9139f","analyzedAt":"2026-08-15T06:31:20.014Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}