Skip to content

Architecture

prpr looks like a normal Python library, but under the hood it drives JavaScript running inside Premiere Pro. Three ideas make that feel seamless.

1. The connection is inverted

DaVinci Resolve exposes a scripting API an external process can import directly. Premiere does not: its UXP API is JavaScript that runs inside a plugin panel, and UXP plugins can only open outgoing network connections. There is no socket to dial into.

So prpr turns the relationship around. The Python side (CLI, library, or MCP server) hosts a WebSocket server on 127.0.0.1:8855. The bundled prpr bridge panel, running inside Premiere, dials out to it and waits for work.

┌─────────────────────────┐         ws://127.0.0.1:8855         ┌────────────────────────┐
│ prpr (Python)            │ ◀───────── dials in ──────────────  │ prpr bridge panel (UXP) │
│  hosts the WS server    │ ───────── RPC requests ──────────▶  │  runs premierepro JS   │
└─────────────────────────┘                                     └────────────────────────┘

Consequences that shape everything else:

  • Premiere must be running with the panel open. There is no headless mode. The panel auto-reconnects, so this is a one-time dock.
  • One port, one owner. Only one Python process can host the bridge. When the daemon runs, every CLI command forwards to it rather than binding a second server (see daemon).

2. The plugin is generic; semantics live in Python

The panel (plugin/main.js) is a thin, stable RPC executor. It knows how to call a method, get/set a property, eval a function body, subscribe to an event, and run a transaction — nothing about projects or sequences specifically. Live host objects are kept in a handle registry and referenced across the wire as {"$h": id}.

That means adding a Premiere capability almost never requires touching or reinstalling the plugin. New behavior is a JavaScript snippet in prpr/_js.py plus a typed Python wrapper. The snippet is the body of an async (ppro, uxp, H, args) => {...} function; it runs against the real premierepro module and returns JSON-able data.

3. Mutations are transactions; reads may need a lock

Premiere's write model is unusual. You don't set properties directly — you build Action objects and commit them inside project.executeTransaction(...), which makes the whole batch a single undoable step. Some synchronous DOM reads (getTrackItems, markers.getMarkers, component parameters) must run inside project.lockedAccess(...) to see a consistent state.

Every snippet uses a shared runTransaction(project, label, build) helper that wraps both concerns. This is why a prpr mutation shows up as one tidy entry in Premiere's Edit → Undo menu.

Time

Premiere measures time in ticks: 254,016,000,000 per second. The bridge protocol converts everything to seconds at the boundary, and frame counts are derived from the sequence's getTimebase() fps. You never deal with ticks in Python.

Failure decoding

The raw API fails quietly: promises reject with one-line messages, importFiles() returns a bare false, actions silently no-op. Each wrapper decodes those into a PrprError subclass carrying a cause, a fix, and a state snapshot — the same structured shape dvr uses, so agents can branch on error type and humans can read the fix.

Operations Premiere's API simply cannot do (render-queue enumeration, page switching, interchange import) raise NotSupportedError — they still exist in the CLI/MCP surface for cross-app parity, but fail loudly with a pointer to the closest alternative. See parity.

Is a plugin really required? (yes)

DaVinci Resolve ships an external scripting API — an outside process imports DaVinciResolveScript and talks to a socket Resolve listens on. Premiere has no equivalent. Its only automation surface, UXP, runs inside the app, so something has to run in-process. The bridge panel is that something.

The historical escape hatch — driving ExtendScript from outside via AppleScript DoScript (macOS) / BridgeTalk — has been removed in Premiere Pro 26. Its AppleScript dictionary now exposes only capture and editoriginal; DoScript is gone (verified on 26.5 beta and 2026 release). ExtendScript itself is deprecated and being sunset. So on Premiere 26 there is no supported no-plugin path, and prpr's bridge is not merely the best option — it's the only one.

Headless — no panel to open

The bridge is a headless UXP plugin: instead of a dockable panel, it uses a command entrypoint whose main.js opens the WebSocket connection at module load. A UPIA-installed command plugin's main.js runs automatically at Premiere startup (verified on 26.5: it connects with no panel, no menu, no developer tools). So setup is just:

prpr plugin install     # UPIA installs the .ccx
# restart Premiere once so it registers the plugin

After that the bridge starts with Premiere every launch and reconnects to the daemon on its own — there is nothing to open or keep open. prpr plugin check confirms it's connected. (A status-panel variant lives in plugin-panel/ for anyone who wants a visible readout.)