# Manifest-driven preview renderer for Shorts

Use this when a shorts automation project already has render manifests and needs the first video output without slipping into generic AI slideshow rendering.

## Durable pattern

Keep the renderer deterministic and manifest-driven:

```text
Render manifest JSON -> scene/card frames -> FFmpeg MP4 -> preview/approval
```

The LLM should not render frames. It may help create scripts, shot plans, and manifests, but the renderer should consume structured timeline fields and produce reproducible preview media.

## Renderer v1 architecture

A pragmatic first renderer can use:

- Pillow/PIL to draw vertical scene frames/cards.
- FFmpeg concat demuxer to turn frame images into an MP4.
- Render manifest fields for timing, text, asset type, visual direction, retention goal, resolution, aspect ratio, and fps.

Recommended module shape:

- `build_preview_frames(manifest, width, height)` or `build_local_preview_plan(manifest)`
  - side-effect-free plan/artifact list, one representative visual item per manifest timeline item
  - uses the manifest text, asset type, timing, and retention goal
  - records reduced preview settings such as `540x960`, `24fps` for fast timing review
- `write_frame_images(frames, output_dir)` or `write_local_preview_frames(plan, output_dir)`
  - first explicit artifact-writing boundary
  - writes numbered PNGs for inspection/debugging
  - returns metadata with `side_effects=("write_png",)` and `external_calls=()`
- `build_ffmpeg_concat_plan(preview_plan, frame_write_result, output_path)`
  - side-effect-free FFmpeg plan
  - validates frame count, `.png` paths, positive durations, `.mp4` output, `.concat.txt` path
  - creates concat-demuxer text but does not write it or call FFmpeg
- `render_preview_video(concat_plan)` / `render_ffmpeg_preview_video(plan)`
  - explicit FFmpeg boundary only after the concat plan is green
  - writes the `.concat.txt`, calls FFmpeg, and writes MP4
  - exports `yuv420p` MP4 with `+faststart`
  - returns metadata with `side_effects=("write_concat", "call_ffmpeg", "write_mp4")` and `external_calls=("ffmpeg",)`

This is a preview/timing renderer, not the final beauty renderer. It validates scene flow, text weight, timing, and approval review before spending time on polished assets. Keep the side-effect-free plan stages separate from artifact-writing/rendering stages so tests and review can catch bad timing before any heavy media step.

## TDD checks worth preserving

Write tests before implementation that assert:

- side-effect-free preview plans have no external calls and no file writes
- preview frames equal the number of visual scene items
- first frame starts at `0ms`
- frame durations are positive and match manifest timing
- image dimensions match requested vertical preview size
- first frame text matches the first visual timeline item
- PNG frame files are written and valid at the frame-writer boundary
- generated media paths stay under runtime/temp preview directories and are gitignored
- concat plans are side-effect-free, reference only `.png` frames, produce `.mp4` output paths, and contain positive durations
- renderer results explicitly report `write_concat`, `call_ffmpeg`, and `write_mp4` side effects
- MP4 output exists and is non-empty
- `ffprobe` verifies width, height, frame rate, and duration for real render smoke tests

## CLI pattern

Provide a small script such as `scripts/render_preview.py`:

```bash
python3 scripts/render_preview.py \
  data/render_manifests/<batch>/<video>.json \
  --out-dir data/previews/<batch> \
  --width 540 \
  --height 960 \
  --fps 24
```

Use reduced preview resolution like `540x960` for fast local review. Keep final `1080x1920` as a later renderer-quality target.

## Repo hygiene

- Keep generated MP4/PNG media out of Git via `.gitignore`.
- Commit renderer code, tests, scripts, manifests, and lightweight planning artifacts.
- Do not commit rendered previews unless explicitly needed as small fixtures.
- Add runtime dependencies explicitly, e.g. `Pillow>=10.0`; treat FFmpeg as an external system prerequisite checked at runtime.

## Pitfalls

- Do not let the renderer infer timing or visuals from prose when the manifest already encodes them.
- Do not start with AI video generation as the renderer; use it later only as an asset supplier.
- Do not over-polish the first renderer. Its job is approval/timing validation.
- Do not publish from this step. Preview rendering still requires human approval before posting.
- Watch typography consistency in early previews: fixed-size fonts can make some scenes look huge and others tiny. Add tests/metadata for fitted font sizes, max line counts, and minimum retention-goal readability before scaling to more videos.
- For this user's AutoShorts flow, approval/freigabe preview videos should use German copy by default so the concept is easy to review; final public videos can still be produced in English. Use pixel-width-based line wrapping rather than fixed character counts, especially for German text.