# Setup

This repo is a **scoped extract** of `tools/` from a larger private video studio. It
is real, runnable render code — but three things it depends on are not shipped here
(LUT files, HTML card templates, the compiled Swift binary). That's explained below,
and again in README.md's "What's missing" section. Read this whole file before you
run anything; skipping steps here is the #1 way to get a confusing failure.

## What you need (checklist table)

| Requirement | Why | Have it already? |
|---|---|---|
| macOS (any recent version) | `tools/personmatte.swift` uses Apple's Vision framework (`VNGeneratePersonSegmentationRequest`) and compiles with `swiftc` — both Apple-only. Every other script is portable Python/ffmpeg and will run on Linux, but the masked/matte grade will not. | Check: `sw_vers` |
| Xcode Command Line Tools | Provides `swiftc`, needed only to build `personmatte`. | Check: `xcode-select -p` (installs with `xcode-select --install` if missing) |
| Homebrew | Package manager used to install ffmpeg-full, node, bun. | Check: `brew --version` |
| **`ffmpeg-full`** (not the slim `ffmpeg` formula) | Every render script shells out to a hardcoded path, `/opt/homebrew/opt/ffmpeg-full/bin/ffmpeg`. Subtitle burn-in (`tools/word_captions.py`'s ASS captions) needs `libass`, which Homebrew's plain `ffmpeg` formula does not include. | Check: `/opt/homebrew/opt/ffmpeg-full/bin/ffmpeg -hide_banner -filters \| grep -w subtitles` |
| Python 3 (any recent version) | All render tools are Python scripts. | Check: `python3 --version` |
| `opencv-python-headless` + `numpy` (Python packages) | `tools/reframe.py` imports `cv2` for face-tracked auto-reframe. | Check: `python3 -c "import cv2"` |
| `Pillow` (Python package) | `tools/quote_cards.py` and `tools/quote_reel_overlay.py` import `PIL` to draw quote cards. | Check: `python3 -c "import PIL"` |
| `playwright` (Python package) + Chromium browser | `tools/style_render.py` and `tools/quotecard_render.py` launch headless Chromium via `playwright.sync_api` to render HTML card templates transparently. | Check: `python3 -c "import playwright"` |
| Node.js 22 or newer | Only needed if you use `tools/motion_render.py` (Remotion) or `tools/hyperframes_render.py` (HyperFrames) — both shell out to `npx`. Everything else needs no Node. | Check: `node -v` |
| ElevenLabs API key | Only needed if you transcribe via ElevenLabs Scribe (hosted). `tools/whisper_to_words.py` is the free local alternative and needs no key at all. | Check: nothing to check until you use hosted transcription |
| **Docker — NOT needed.** Nothing here runs in a container. | — | — |
| **No paid API required to render.** ElevenLabs is optional and only for hosted transcription; every render/grade/caption tool runs on local ffmpeg/Python/Swift with no external service call. | — | — |
| **No GPU required.** All rendering is CPU (ffmpeg) plus Apple's on-device Vision framework for the matte tool — no CUDA, no cloud inference. | — | — |
| **No account/login required** except the optional ElevenLabs account for hosted transcription. | — | — |

Two things this repo depends on but does **not** ship (the README explains why —
this is a deliberate extract of the render logic, not the whole private studio):

- `tools/luts/{cool,warm,natural}.cube` — LUT files `luxury_render.py` loads for
  the `--grade` step. Bring your own `.cube` files, or skip `--grade` and rely on
  the procedural color curves alone.
- `dashboard/styles/*.html` — the HTML templates `style_render.py`, `quote_cards.py`,
  `quotecard_render.py`, and `batch_cards.py` render via Playwright. They live in
  the private dashboard layer, out of scope here. Those scripts will run but fail
  to find a template until you write your own (plain HTML/CSS).

## Install, step by step

Assumes a fresh Mac with nothing installed. Skip any step where the "check it
worked" command already passes.

### 1. Install Homebrew (if you don't have it)

```bash
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
```
Installs the Homebrew package manager, used for every other install step below.

Check it worked:
```bash
brew --version
```
Expected: a line like `Homebrew 4.x.x`.

### 2. Install Xcode Command Line Tools

```bash
xcode-select --install
```
Installs `swiftc` (the Swift compiler) and other Apple dev tools. A GUI prompt
will pop up — click Install and wait for it to finish.

Check it worked:
```bash
xcode-select -p
swiftc --version
```
Expected: a path like `/Library/Developer/CommandLineTools`, then a Swift version
banner.

### 3. Install ffmpeg-full (not plain ffmpeg)

```bash
brew install ffmpeg-full
```
Installs the keg-only, full-featured ffmpeg build with `libass` (subtitle burn-in)
compiled in. It will NOT put `ffmpeg` on your `PATH` (keg-only) — every script here
calls it by its full path instead: `/opt/homebrew/opt/ffmpeg-full/bin/ffmpeg`.

Check it worked:
```bash
/opt/homebrew/opt/ffmpeg-full/bin/ffmpeg -hide_banner -filters | grep -w subtitles
```
Expected: a line containing `subtitles` (confirms libass support). No output means
caption burn-in will fail later.

**Apple Silicon note:** the path above (`/opt/homebrew/...`) is correct for Apple
Silicon Macs. On an Intel Mac, Homebrew installs to `/usr/local`, so the path is
`/usr/local/opt/ffmpeg-full/bin/ffmpeg` — every script's hardcoded `FF`/`FFMPEG`
path will need that swap if you're on Intel.

### 4. Compile the Vision matte tool (macOS only — skip if you don't need masked grades)

```bash
cd tools && swiftc -O -o personmatte personmatte.swift && cd ..
```
Builds the person-segmentation binary `luxury_render.py` shells out to for masked
(subject-cutout) grading. If you skip this, everything except masked grades still
works — `luxury_render.py` falls back gracefully when the binary is missing.

Check it worked:
```bash
tools/personmatte 2>&1 | head -1
```
Expected: `personmatte: usage: personmatte <video> <outW> <outH> [fast|balanced|accurate]`
(it's erroring on purpose because you ran it with no video — that error message
is the proof the binary built and runs).

### 5. Create a Python virtual environment and install Python dependencies

```bash
python3 -m venv .venv
source .venv/bin/activate
pip install "opencv-python-headless<5" numpy pillow playwright
playwright install chromium
```
Creates an isolated Python environment, installs the packages the render scripts
import (`cv2`, `numpy`, `PIL`, `playwright`), and downloads the headless Chromium
browser Playwright drives.

Windows/Linux variant (same commands, different activate line):
```bash
python3 -m venv .venv
source .venv/bin/activate   # Windows: .venv\Scripts\activate
pip install "opencv-python-headless<5" numpy pillow playwright
playwright install chromium
```

Check it worked:
```bash
python3 -c "import cv2, numpy, PIL, playwright; print('ok')"
```
Expected: `ok`

### 6. (Optional) Install Node.js 22+ and bun — only if you'll use the motion-graphics drivers

```bash
brew install node bun
```
`tools/motion_render.py` and `tools/hyperframes_render.py` shell out to `npx`/`bunx`
for two external engines (Remotion, HyperFrames) that are not vendored in this repo.
Skip this step if you only need the ffmpeg-based render tools (luxury grade, quote
cards, captions, reframe).

Check it worked:
```bash
node -v
```
Expected: `v22.x.x` or newer.

## Configure

```bash
cp env.example .env
```

Then edit `.env`:

```
ELEVENLABS_API_KEY=YOUR_KEY_HERE  # keyzhub-allow
```

- Only needed if you use hosted transcription. Get a key at
  https://elevenlabs.io/app/settings/api-keys.
- If you use the local-whisper path instead (`tools/whisper_to_words.py`, converting
  `whisper-cli` JSON output), you need no key at all — leave `.env` unfilled or skip
  creating it.
- Note the filename is `env.example` (not `.env.example`) on purpose, so it isn't
  swept up by dotfile secret-scanners — same idea, just a plain filename. `setup.sh`
  (the original studio's bootstrap script, not meant to run unmodified here)
  references `.env.example`; use `env.example` as shown above instead.

## Run it

The scripts are standalone CLIs — no server to start. First real command:

```bash
source .venv/bin/activate
python3 tools/luxury_render.py --video source.mp4 --start 12 --end 20 \
  --title "YOUR CAPTION HERE" --out out.mp4
```

What success looks like: the command exits 0 and `out.mp4` exists in the current
directory. Verify it's a real render, not just a green exit code:

```bash
/opt/homebrew/opt/ffmpeg-full/bin/ffprobe -v error -show_entries \
  stream=width,height,r_frame_rate -of default=noprint_wrappers=1 out.mp4
```
Expected: width/height/frame-rate lines describing a real video stream (e.g.
`width=1080`, `height=1920`, `r_frame_rate=30/1`).

If you asked for `--grade`, this step will fail until you supply your own
`tools/luts/{cool,warm,natural}.cube` files (not included — see "What's missing"
above and in README.md).

## Troubleshooting

1. **`ffmpeg: command not found` or subtitle/caption burn-in fails silently.**
   You installed the slim `ffmpeg` formula instead of `ffmpeg-full`, or you're on
   Intel and the hardcoded `/opt/homebrew/...` path doesn't exist on your machine.
   Fix: `brew install ffmpeg-full`, and on Intel Macs, edit the `FF`/`FFMPEG`/`FFPROBE`
   constant near the top of the script you're running to point at
   `/usr/local/opt/ffmpeg-full/bin/...` instead.

2. **`ModuleNotFoundError: No module named 'cv2'` (or `PIL`, or `playwright`).**
   You're running the system `python3`, not the virtualenv's. Fix: run
   `source .venv/bin/activate` in the same shell before running any `tools/*.py`
   script, or call the interpreter directly: `.venv/bin/python3 tools/reframe.py ...`.

3. **`RuntimeError: personmatte unavailable (swiftc or source missing)` or masked
   grades silently fall back to the unmasked look.**
   Either you skipped step 4 (compiling `personmatte`), or you're not on macOS.
   Masked/matte grading is Apple Vision-only and has no cross-platform equivalent —
   compile it with `cd tools && swiftc -O -o personmatte personmatte.swift`, or
   accept the fallback and use the unmasked luxury grade.
