# HLC Clipper — render tools

**Requirements:** macOS + Homebrew + `ffmpeg-full` + Python 3 (Xcode CLT for the optional Swift matte tool) — full walkthrough in [SETUP.md](SETUP.md).

The render engines from 19Keys' personal AI video-clipping studio: the Python/ffmpeg
scripts that turn a raw talking-head video into styled vertical clips and quote cards,
plus the Swift/Vision tool used for masked (person-cutout) color grading.

This is the **tools/ layer** of a larger private studio (dashboard, job server, deploy
pipeline). What's here is everything you need to understand and reuse the actual render
logic — the ffmpeg filter chains, the ASS subtitle generation, the face-tracked
auto-reframe, and the Vision-based person matte. What's *not* here (and why) is in
"What's missing" below — read it before you expect a one-command render.

## What this actually does

Every script is a standalone CLI you can run directly against a source video. No
server, no database, no queue.

- **`tools/luxury_render.py`** — the signature "luxury" grade: filmic curves, soft
  bloom, vignette, fine grain, a thin gold frame, and a letter-spaced Didot caption.
  Two registers baked in as `cinematic` (rich, moody, A24-ish) and `quiet` (near-
  monochrome, extreme restraint). Applies one of three LUTs — `cool` / `warm` /
  `natural` — on top of the procedural grade. Can run masked (subject cut out from a
  separately-graded background) via `personmatte`.
- **`tools/personmatte.swift`** — Apple Vision (`VNGeneratePersonSegmentationRequest`)
  person-matte extractor. Streams a video in sequentially, reuses one segmentation
  request so the matte stays temporally stable, and writes raw grayscale matte frames
  to stdout for ffmpeg to composite. macOS + Vision only.
- **`tools/reframe.py`** — auto-reframe: OpenCV face tracking across a clip, returns
  the ffmpeg crop that keeps the speaker centered for a target aspect (9:16 by
  default). Falls back to a centered crop if no face is found — a render never breaks
  because a face wasn't detected.
- **`tools/quote_cards.py`, `tools/quotecard_render.py`** — freeze-frame quote cards
  (1080x1350, IG 4:5) with burned-in quote text and a brand watermark.
- **`tools/style_render.py`, `tools/batch_cards.py`** — the repost/commentary card
  styles (conversation, quote, X-post/"xpost", editorial, culture) — HTML template
  rendered transparent via Playwright, composited over the cut footage with ffmpeg.
  `batch_cards.py` runs a ranked quote bank through this in one pass.
- **`tools/quote_reel_overlay.py`** — a transparent overlay PNG (rounded quote box +
  cursive signature) to lay over a 9:16 video for a quote-reel look.
- **`tools/screenplay_prep.py`** — formats a word-level transcript slice into the
  JSON a "screenplay scroll" motion composition consumes.
- **`tools/word_captions.py`** — karaoke / word-by-word ASS captions from a
  transcript, remapped onto the output timeline after cuts.
- **`tools/whisper_to_words.py`** — converts local `whisper-cli` JSON output into the
  same word-timing format the rest of the pipeline expects (no API key needed).
- **`tools/motion_render.py`, `tools/hyperframes_render.py`** — drivers for two
  external motion-graphics engines (Remotion, HyperFrames) — not vendored here, these
  scripts just shell out to `npx`/`bunx` if you have them installed.
- **`tools/fonts.py`** — scans your local font folders, resolves each file's real
  font-family name (not the filename), classifies it, and writes `fonts.json` so
  render tools can offer your own type as presets.

`AGENTS.md` and `CLAUDE.md` are the original operating rules for this studio (session
harness, house render defaults, iron rules learned from real bugs). They reference
pieces of the full studio (`dashboard/`, `ops/`, `atomizer/`, a Mac Mini deployment)
that aren't part of this extract — read them for the *reasoning*, not as instructions
you can execute here.

## What's missing (read this before you run anything)

This is a scoped extract of `tools/`, not the whole studio. A few things won't work
out of the box:

- **`tools/luts/{cool,warm,natural}.cube`** — the three LUT files `luxury_render.py`
  loads are not included. Bring your own `.cube` LUTs at those paths, or strip the
  `--grade` step and rely on the procedural curves alone.
- **`dashboard/styles/*.html`** — the actual visual templates for the quote-card and
  repost-style tools (`quote_cards.py`, `quotecard_render.py`, `style_render.py`,
  `batch_cards.py`) live in the dashboard layer, which is out of scope for this repo.
  These scripts will run but fail to find their template HTML until you supply your
  own — the Python here shows you the full render pipeline (ffmpeg composite, ASS
  captioning, Playwright screenshot) to build templates against.
- **`fonts.json`** — checked in as a working example, but every path in it is
  absolute to the machine it was generated on (`/Users/<user>/Library/Fonts/...`).
  Regenerate it for your own machine with `python3 tools/fonts.py`.
- **`tools/personmatte`** (the compiled binary) is intentionally not included —
  compile it yourself (one command, see below).

## Requirements

- macOS (Vision-based masking and `swiftc` are Apple-only; everything else is
  portable Python/ffmpeg)
- **`ffmpeg-full`**, not the slim Homebrew formula — subtitle burn-in needs `libass`,
  which the slim build doesn't ship. `brew install ffmpeg-full` (keg-only).
- Python 3 with `opencv-python-headless` (for `reframe.py`) and `playwright` (for the
  card-style renderers, if you supply your own templates)
- Node 22+ if you want the Remotion/HyperFrames motion-graphics drivers
- An ElevenLabs API key **only if** you use hosted transcription — the local-whisper
  path (`whisper_to_words.py`) needs no key

## Quickstart

```bash
# 1. ffmpeg-full (subtitle burn-in requires libass; the slim formula lacks it)
brew install ffmpeg-full

# 2. compile the Vision matte tool (macOS + Xcode CLT)
cd tools && swiftc -O -o personmatte personmatte.swift && cd ..

# 3. python deps for face-tracked reframing
python3 -m venv tools/reframe-venv
tools/reframe-venv/bin/pip install "opencv-python-headless<5" numpy

# 4. copy the env template and fill in a key if you're using hosted transcription
#    (named env.example, not .env.example, so it doesn't get swept up by dotfile
#    secret-scanners in CI — same idea, just a plain filename)
cp env.example .env

# 5. run a grade directly against a source clip
/opt/homebrew/opt/ffmpeg-full/bin/ffmpeg -version   # confirm libass is present
python3 tools/luxury_render.py --video source.mp4 --start 12 --end 20 \
  --title "YOUR CAPTION HERE" --out out.mp4
```

`init.sh` and `setup.sh` are the original studio's verification/bootstrap scripts —
`init.sh` in particular checks for files outside this extract (`dashboard/server.py`,
`atomizer/`) and will fail as shipped. Treat both as reference for what a complete
setup checks, not as a script you can run unmodified against this repo. `setup.sh`
also references `.env.example`; this repo ships that same template as `env.example`
(see the quickstart) — copy it to `.env` by hand rather than relying on `setup.sh`'s
auto-copy step.

## Making it your own

- Swap `GOLD`, `CREAM`, and the Didot font reference in `luxury_render.py` for your
  own brand marks and palette.
- Build your own `.cube` LUTs (DaVinci Resolve, or any grading tool that exports
  3D LUTs) and point `GRADES` in `luxury_render.py` at them.
- Write your own `dashboard/styles/*.html` templates for `style_render.py` /
  `quote_cards.py` — they're plain HTML/CSS rendered headless via Playwright, so any
  web design skill transfers directly.
- Run `tools/fonts.py` against your own font folders to build a `fonts.json` that
  reflects your own type library instead of the shipped example.

## License / attribution

This is 19Keys' own tooling, shared with the community to build from. The two
external engines referenced (`video-use`, `hyperframes`) are separate open-source
projects — see `setup.sh` for their upstream repos and install their own licenses
before vendoring them.
