# Export as Media

Node CLI tool to export audio or video from a cx v2 pack using ffmpeg.
**https://contexts.live/cx/tools/cx-export.js**

---

## Prerequisites

- **Node.js** (ES modules — node 18+)
- **ffmpeg** installed and on PATH
- **canvas** npm package — required for `text` render only:
  ```
  npm install canvas
  ```
- **`.cx-config.json`** — one level above the script (`/cx/.cx-config.json`):
  ```json
  { "root": "/Volumes/Data/www/Sites/contexts.live" }
  ```

---

## Usage

```
node cx/tools/cx-export.js <slug> <pack> <triple> [<triple2> ...] [options]
```

| Argument | Description |
|----------|-------------|
| `slug` | Org slug (e.g. `new`) |
| `pack` | Pack name without `.json` (e.g. `grain`) |
| `triple` | `source.render[.language]` — see below |

### Options

| Option | Default | Description |
|--------|---------|-------------|
| `--out <file>` | auto | Output file path |
| `--lang <lang>` | from triple | Language override |
| `--width <px>` | 1920 | Canvas width (text only) |
| `--height <px>` | 1080 | Canvas height (text only) |
| `--fontsize <px>` | 72 | Font size (text only) |
| `--valign top\|middle\|bottom` | middle | Vertical alignment (text only) |
| `--bg <color>` | `#000000` | Background colour (text only) |
| `--fg <color>` | `#faebd7` | Text colour (text only) |

---

## Render types

### audio

Assembles individual audio clips (mp3/m4a) into a single timed soundtrack.
Each clip is placed at its `cueIn` timecode using ffmpeg `adelay`.
Output is `.m4a` by default.

```
node cx/tools/cx-export.js new grain ad.audio.en
# → org/new/exports/grain-ad.audio.en.m4a
```

Multiple triples can be mixed into one output:

```
node cx/tools/cx-export.js new grain ad.audio.en st.audio.en --out grain-combined.m4a
```

### image

Builds a slideshow video from score images, each held for the duration
between its `cueIn` and the next cue. Optionally mixes in audio clips
if additional audio triples are passed.

```
node cx/tools/cx-export.js new grain score.image
# → org/new/exports/grain-score.image.mp4

node cx/tools/cx-export.js new grain score.image ad.audio.en
# → video with AD audio mixed in at correct timecodes
```

Image dimensions are auto-detected from the first file via `ffprobe`.

### text

Renders subtitle text to PNG frames using the `canvas` package, then
assembles them into a video via ffmpeg. Each frame uses the `cueIn`/`cueOut`
timecodes to set duration; gaps between cues are filled with a blank frame.

Supports basic HTML in subtitle text: `<b>`, `<i>`, `<em>`, `<strong>`, `<br>`.
Italic text renders in EB Garamond; all other text in Lexend.

Font files are read from `cx/fonts/` — both fonts must be present.

```
node cx/tools/cx-export.js new grain st.text.en
# → org/new/exports/grain-st.text.en.mp4

node cx/tools/cx-export.js new grain st.text.en --valign bottom --fontsize 60
```

---

## Output location

All outputs default to `org/{slug}/exports/` — created automatically if
it doesn't exist. Override with `--out`.

---

## Config file

`/cx/.cx-config.json` tells the script where the server root is:

```json
{ "root": "/Volumes/Data/www/Sites/contexts.live" }
```

The script resolves all paths (pack JSON, media files, font files, exports dir)
relative to this root.

---

## Notes

- Pack must be v2 format (`meta.cx: "2.0"`). Use `cx-pack-migrate.html` to
  convert v1 units first.
- Missing media files are warned and skipped — the export continues.
- Temp files (concat lists, frame PNGs) are cleaned up after each run.
