Premiere Pro Bridge
Public Made by Adomby adom
Let the AI edit video in Adobe Premiere Pro on your desktop (import, cut, trim, razor, effects, audio, previews, export) with no mouse or keyboard. Check adom-bridge bridge_list for 'premiere'; install with bridge_install + adom-wiki pkg install adom/premiere-bridge, then start with premiere_status.
32fb583
10d ago
name: premiere-bridge-dev description: Maintainer guide for the Premiere bridge source (Rust server + UXP plugin + CEP helper) — architecture, the verb table, the dev loop on a real Mac, and every premierepro API behavior measured on Premiere 26.5 (time bases, selection lifetime, item types, exporter quirks). Read before changing any premiere_* verb. user-invocable: false
Premiere bridge — developer guide
THE RULE: the AI reads verb OUTPUT, not this file
Every verb response gets _hint / _next / pitfalls stamped from src/catalog.rs. When you add or change a verb, update its catalog row in the same commit. Failure hints come from error_hint() keyed by errorCode, so throw fail(code, message) in the plugin with a stable code.
Layout
| Path | What |
|---|---|
src/main.rs |
axum server: /health, POST /command (JSON-only, 415/405 guards, no ACAO), /ws (plugin sockets, token in query). Routes verbs by runtime. |
src/catalog.rs |
The verb table: runtime (Local/Uxp/Cep/PreferCep), timeout, summary, args, hint, next, pitfalls. Drives premiere_describe and decoration. |
src/host.rs |
Local verbs: status/readiness/launch/quit, preview_frame + contact_sheet (inline base64, waits for async file writes), export-preset discovery, monitor_snapshot. |
src/plugin_pkg.rs |
Embeds plugin/ with include_str!, builds the .ccx, installs via UPIA; copies the CEP helper + sets PlayerDebugMode. |
src/ab.rs |
Minimal HTTP client for Adom Bridge's direct API (ADOM_DIRECT_API_URL or ~/.adom/direct-api-port). |
plugin/uxp/index.js |
Every UXP verb (handlers), helpers, connection loop. |
plugin/cep/ |
Invisible CEP extension + host.jsx (ES3) for QE razor, speed, tracks, raw ExtendScript. |
Protocol (both runtimes): plugin → {type:"hello", runtime:"uxp"|"cep", …}; bridge → {type:"call", id, verb, args}; plugin → {type:"result", id, ok, result|error, errorCode}; plugin → {type:"event", kind} (export progress, project opened). The bridge pings every 15 s.
The plugin finds the bridge by reading ~/.adom/premiere-bridge/connection.json (port + token) on every reconnect attempt, with a copy baked into the .ccx as a fallback.
Dev loop (needs the desktop connected over adom-bridge)
scripts/dev-sync.sh # tar → send_files → cargo build --release on the Mac → restart test server :47911
scripts/pb.sh premiere_install_plugin '{"force":true}' # reinstall plugin; Premiere hot-reloads it (no restart)
scripts/pb.sh premiere_status
scripts/seq.sh # compact timeline dump
scripts/pb.sh premiere_eval '{"code":"return Object.keys(ppro.Constants)"}' # probe the live API
Test edits in a scratch project (premiere_new_project under ~/adom-dev), never in the user's project; close it with close_project {save:false} after checking get_project.path.
Measured API facts (Premiere 26.5.2): don't relearn these
- Actions must be built inside
project.lockedAccess, synchronously. Do all async reads first, thentx(p, label, () => [actions]). TrackItemSelectionis only valid insidecreateEmptySelection's callback. Using it later throws "The script object is no longer valid". UsewithSelection(clips, sel => …).ppro.Constants.ProjectItemTypedoesn't exist. Useppro.ProjectItem.TYPE_*(CLIP=1, BIN=2, ROOT=3, FILE=4).TickTime.timeToTimecode/timecodeToTimedon't exist. The plugin computes timecode itself (drop-frame at 29.97/59.94).Constants.MediaTypevalues are GUID strings;TrackItemType.CLIP= 1;TransitionPositionSTART=1, END=2.createSequenceFromMedia(name, items)wants plain ProjectItems. Passingundefinedas the bin throws "Illegal Parameter type", so omit the argument.- Trim semantics:
createSetStartActionandcreateSetInPointActionare both head trims that preserve (start − in).createSetEndAction/createSetOutPointActionare tail trims.createMoveAction(delta)shifts. A cloned item (createCloneTrackItemAction) is offset like a move, which breaks sync. That's why razor is implemented as tail-trim + temporary source in/out + overwrite. - Keyframe positions are in the clip's media time (timeline − clip start + in point). The bridge converts from sequence time (
kfTime). Enabling time-varying adds a key at the clip's in point. - Volume:
Volume > Levelis linear; 0 dB = 0.17783 (10^((dB-15)/20)). - Motion > Position is normalized
[0..1, 0..1]. Exporter.exportSequenceFrameresolves before the file is fully written (the bridge waits for a stable size). It does NOT evaluate keyframes between two keys. Pass the filename WITH its extension; no doubled extension on 26.5.EncoderManager.exportSequence(…, IMMEDIATELY, …)returns after the render, with no encoder events.- A UPIA install of the
.ccxhot-loads into a running Premiere, and the plugin's JS runs without the panel being opened. - UPIA rejects a manifest with
hostas an array and noicons("uxp manifest file is invalid", status −267). Keephostan object and ship icons. - CEP helper needs
PlayerDebugMode=1(CSXS 9–13) and only loads at Premiere start.
Adding a verb
- Add the handler in
plugin/uxp/index.js(orADOM_VERBSinhost.jsxfor CEP). - Add its row to
VERBSinsrc/catalog.rswith a real hint/next/pitfalls. - Re-generate
bridge.jsonverbs(see the publish skill), rebuild, reinstall the plugin, and test on a scratch project. - Bump
PLUGIN_VERSION/BUNDLED_VERSIONand the manifest version together when the plugin changes.
---
name: premiere-bridge-dev
description: Maintainer guide for the Premiere bridge source (Rust server + UXP plugin + CEP helper) — architecture, the verb table, the dev loop on a real Mac, and every premierepro API behavior measured on Premiere 26.5 (time bases, selection lifetime, item types, exporter quirks). Read before changing any premiere_* verb.
user-invocable: false
---
# Premiere bridge — developer guide
## THE RULE: the AI reads verb OUTPUT, not this file
Every verb response gets `_hint` / `_next` / `pitfalls` stamped from `src/catalog.rs`. When you add or change a verb, update its catalog row in the same commit. Failure hints come from `error_hint()` keyed by `errorCode`, so throw `fail(code, message)` in the plugin with a stable code.
## Layout
| Path | What |
|---|---|
| `src/main.rs` | axum server: `/health`, `POST /command` (JSON-only, 415/405 guards, no ACAO), `/ws` (plugin sockets, token in query). Routes verbs by runtime. |
| `src/catalog.rs` | The verb table: runtime (Local/Uxp/Cep/PreferCep), timeout, summary, args, hint, next, pitfalls. Drives `premiere_describe` and decoration. |
| `src/host.rs` | Local verbs: status/readiness/launch/quit, preview_frame + contact_sheet (inline base64, waits for async file writes), export-preset discovery, monitor_snapshot. |
| `src/plugin_pkg.rs` | Embeds `plugin/` with `include_str!`, builds the `.ccx`, installs via UPIA; copies the CEP helper + sets PlayerDebugMode. |
| `src/ab.rs` | Minimal HTTP client for Adom Bridge's direct API (`ADOM_DIRECT_API_URL` or `~/.adom/direct-api-port`). |
| `plugin/uxp/index.js` | Every UXP verb (`handlers`), helpers, connection loop. |
| `plugin/cep/` | Invisible CEP extension + `host.jsx` (ES3) for QE razor, speed, tracks, raw ExtendScript. |
Protocol (both runtimes): plugin → `{type:"hello", runtime:"uxp"|"cep", …}`; bridge → `{type:"call", id, verb, args}`; plugin → `{type:"result", id, ok, result|error, errorCode}`; plugin → `{type:"event", kind}` (export progress, project opened). The bridge pings every 15 s.
The plugin finds the bridge by reading `~/.adom/premiere-bridge/connection.json` (port + token) on every reconnect attempt, with a copy baked into the `.ccx` as a fallback.
## Dev loop (needs the desktop connected over adom-bridge)
```bash
scripts/dev-sync.sh # tar → send_files → cargo build --release on the Mac → restart test server :47911
scripts/pb.sh premiere_install_plugin '{"force":true}' # reinstall plugin; Premiere hot-reloads it (no restart)
scripts/pb.sh premiere_status
scripts/seq.sh # compact timeline dump
scripts/pb.sh premiere_eval '{"code":"return Object.keys(ppro.Constants)"}' # probe the live API
```
Test edits in a **scratch project** (`premiere_new_project` under ~/adom-dev), never in the user's project; close it with `close_project {save:false}` after checking `get_project.path`.
## Measured API facts (Premiere 26.5.2): don't relearn these
- **Actions must be built inside `project.lockedAccess`, synchronously.** Do all async reads first, then `tx(p, label, () => [actions])`.
- **`TrackItemSelection` is only valid inside `createEmptySelection`'s callback.** Using it later throws "The script object is no longer valid". Use `withSelection(clips, sel => …)`.
- **`ppro.Constants.ProjectItemType` doesn't exist.** Use `ppro.ProjectItem.TYPE_*` (CLIP=1, BIN=2, ROOT=3, FILE=4).
- **`TickTime.timeToTimecode` / `timecodeToTime` don't exist.** The plugin computes timecode itself (drop-frame at 29.97/59.94).
- `Constants.MediaType` values are GUID strings; `TrackItemType.CLIP` = 1; `TransitionPosition` START=1, END=2.
- **`createSequenceFromMedia(name, items)`** wants plain ProjectItems. Passing `undefined` as the bin throws "Illegal Parameter type", so omit the argument.
- **Trim semantics:** `createSetStartAction` and `createSetInPointAction` are both head trims that preserve (start − in). `createSetEndAction` / `createSetOutPointAction` are tail trims. `createMoveAction(delta)` shifts. A cloned item (`createCloneTrackItemAction`) is offset like a move, which breaks sync. That's why razor is implemented as tail-trim + temporary source in/out + overwrite.
- **Keyframe positions are in the clip's media time** (timeline − clip start + in point). The bridge converts from sequence time (`kfTime`). Enabling time-varying adds a key at the clip's in point.
- **Volume:** `Volume > Level` is linear; 0 dB = 0.17783 (`10^((dB-15)/20)`).
- **Motion > Position** is normalized `[0..1, 0..1]`.
- **`Exporter.exportSequenceFrame`** resolves before the file is fully written (the bridge waits for a stable size). It does NOT evaluate keyframes between two keys. Pass the filename WITH its extension; no doubled extension on 26.5.
- `EncoderManager.exportSequence(…, IMMEDIATELY, …)` returns after the render, with no encoder events.
- A UPIA install of the `.ccx` hot-loads into a running Premiere, and the plugin's JS runs without the panel being opened.
- UPIA rejects a manifest with `host` as an array and no `icons` ("uxp manifest file is invalid", status −267). Keep `host` an object and ship icons.
- CEP helper needs `PlayerDebugMode=1` (CSXS 9–13) and only loads at Premiere start.
## Adding a verb
1. Add the handler in `plugin/uxp/index.js` (or `ADOM_VERBS` in `host.jsx` for CEP).
2. Add its row to `VERBS` in `src/catalog.rs` with a real hint/next/pitfalls.
3. Re-generate `bridge.json` `verbs` (see the publish skill), rebuild, reinstall the plugin, and test on a scratch project.
4. Bump `PLUGIN_VERSION` / `BUNDLED_VERSION` and the manifest version together when the plugin changes.