# adom-video

Hydrogen-webview video player. Plays `.webm` / `.mp4` from disk with frame-accurate scrub, A-B loop, pixel zoom up to 8×, gallery thumbnails, and frame-extract / trim.

CLI- and HTTP-drivable so Claude can `seek`, `pause`, `extract-frame`, and `trim` without simulating clicks.

## Demo

![Player + native controls + custom toolbar](apps/adom-video/hero.png)

Watch the 6-section feature tour: [final.webm](apps/adom-video/final.webm)

The tour covers:

1. **Player + native controls + custom toolbar** — a clip plays with the full HTML5 controls bar (timeline, volume, speed menu, fullscreen, picture-in-picture) AND the custom toolbar for everything the native bar doesn't give you.
2. **Folder gallery** — every clip auto-probed for codec, resolution, frame count, recorded-at time parsed from the filename.
3. **Scrub + A-B loop** — frame-step with `,` `.`, set markers with `I` `O`, instant loop fires.
4. **Pixel zoom + pan** — zoom up to 8× with pixel-perfect rendering, drag to pan.
5. **Extract frame → reveal in VS Code** — files land next to the source clip in `extracts/` and auto-reveal in the VS Code Explorer.
6. **CLI + HTTP** — every action exposed as a CLI subcommand and an HTTP endpoint.

## Why this exists

`adom-desktop` produces screen and tab recordings as WebM. Until this tool, every demo round-tripped through the desktop ("send the file home, open it manually, scrub manually, screenshot a frame manually"). `adom-video play <file>` opens a tab next to VS Code, plays in a loop, and exposes every action as a CLI/HTTP call.

## Quick use

```bash
adom-video play /tmp/recording.webm        # one-clip player
adom-video play /tmp/recordings/           # gallery for the directory
adom-video gallery /tmp/recordings/        # explicit gallery view
adom-video probe /tmp/recording.webm       # ffprobe → JSON
adom-video extract-frame v.webm 3.5 -o frame.png
adom-video trim v.webm 1.0 5.0 -o clip.webm
```

Extracts and trims land in `<clip-folder>/extracts/<stem>-frame-<MMmSSsMMM>.png` and `<clip-folder>/trims/<stem>-trim-<in>-<out>.<ext>` — sortable filenames, sane location, auto-revealed in the VS Code Explorer sidebar (sidebar is forced visible too).

## Drive an open player from outside (port auto-discovered via `/tmp/adom-video.port`)

```bash
adom-video seek 12.5      # jump to t=12.5s in the open tab
adom-video pause
adom-video resume
adom-video set-rate 0.5   # half speed
adom-video status         # current state JSON
adom-video stop           # close tab + shut down server
```

## HTTP API

| Method · Path | Purpose |
|---|---|
| `GET  /api/library` | List clips with metadata + filename hints (`rec-desktop-*`, `rec-tab-*`) |
| `GET  /api/state` | Current player state (file, currentTime, paused, rate, in/out points, zoom) |
| `POST /api/seek` `{t}` | Jump to time |
| `POST /api/pause` `{}` · `POST /api/resume` `{}` | |
| `POST /api/rate` `{rate}` | 0.25 → 4.0 |
| `POST /api/zoom` `{zoom}` | 1.0 → 8.0 |
| `POST /api/loop` `{t_in, t_out}` | Set A-B loop range |
| `POST /api/load` `{path}` | Load a different clip into the open player |
| `POST /api/extract-frame` `{path, t, out?}` | Write PNG, auto-reveal, return path |
| `POST /api/trim` `{path, t_in, t_out, output?}` | Stream-copy webm/mp4, auto-reveal |
| `POST /api/reveal` `{path}` | Reveal any path in the VS Code Explorer (forces sidebar visible) |
| `GET  /api/console` | Recent JS console output (forwarded from the UI) |
| `GET  /api/thumb?path=&t=&w=` | JPEG thumbnail (cached) |

## Keyboard shortcuts

| Key | Action |
|---|---|
| Space / K | Play / pause |
| J / L | Slow down / speed up (0.25× → 4×) |
| ← / → | −5s / +5s · Shift = ±1s · Alt = ±0.1s |
| , / . | Previous / next frame (uses fps from probe) |
| 0–9 | Jump to 0%, 10% … 90% |
| I / O | Set A / B loop point · Shift+I/O = clear |
| + / − | Zoom in/out (1× → 8×) · 0 = reset · drag to pan when zoomed |
| F | Fullscreen |
| M | Mute |
| G | Toggle gallery view |
| ? | Show shortcut help |

## Filename hints

Recordings produced by `adom-desktop` are auto-detected:

- `rec-desktop-<unix-ms>.webm` → tagged `desktop`, recorded-at parsed from filename
- `rec-tab-<session>-<tab>-<unix-ms>.webm` → tagged `tab`

The Inspector panel surfaces this plus codec / dimensions / FPS / frame count / duration / bit rate / file size / encoder / **clickable Folder + Path rows** that reveal in the VS Code Explorer.

## How the FPS is computed

Chrome MediaRecorder writes `r_frame_rate=1000/1` and `avg_frame_rate=1000/1` as placeholder values — both useless. `adom-video probe` falls back to `ffprobe -count_packets` and divides by duration when the reported FPS is implausible (>120 fps). For typical desktop screen recordings this gives the real capture rate (15–30 fps).

## Architecture

- Rust + `tiny_http`. Single static binary, ~1.6 MB.
- HTTP byte-range support — required for `<video>` element seek (the native scrubber works because of this).
- ffprobe + ffmpeg must be on PATH.
- Server writes `/tmp/adom-video.port` so subcommands find a running instance.
- Always opens on a **non-VS-Code** Hydrogen pane — never covers Claude Code chat.
- Container webview URL uses `$VSCODE_PROXY_URI` substitution (no `localhost:` baked in).
- HTML uses `<base href="./">` so relative URLs work in the proxied iframe.
- Reveal-in-VS-Code via `adom-vscode reveal` + `adom-vscode sidebar show` (sidebar forced visible so the highlighted entry is in view).

## Brand

Adom dark + teal. Familjen Grotesk (headlines), Satoshi (body), JetBrains Mono (timecode + paths). Monochrome white SVG icons. No emoji.

## License

AGPL-3.0-or-later.
