Releases: SysAdminDoc/OpenCut
v1.9.33 — Preflight checklist
Every long pipeline now runs a ~100ms preflight probe before kicking off. Missing Whisper, bad filepath, <500MB free — the modal spells it out before the user commits to a 10-minute wait. Currently wired into /interview-polish; /full and /shorts-pipeline definitions are present in PIPELINES, ready to enable.
v1.9.32 — Batch Interview Polish
Polish an entire shoot in one click. A 'Polish batch (N)' button appears on the Interview Polish card when there are 2+ files in the Batch picker; runs v1.9.29 sequentially across every file (serial, to keep Whisper from OOMing). Per-file progress toasts, final summary alert. Each run lands in job history as a normal job so Session Context / Journal / Apply-to-selection all work on batch results.
v1.9.31 — CLI `opencut polish`
New opencut polish <file> subcommand runs the full v1.9.29 Interview Polish pipeline against a locally running backend. Auto-detects the port (or use --port), submits the job, polls to completion, prints a Rich step table + output paths. Non-zero exit on any failure so it composes with shell scripts and task schedulers.
Flags: --no-repeats, --no-fillers, --no-chapters, --timeout, -o/--output.
No backend changes. See commit 83293b8.
v1.9.30 — Replay on selected clip
Job-history and Welcome-back rows gain Apply to selection alongside Re-run when the current clip differs from the original. Clones the stored payload, swaps filepath, fires the same job on the new clip. Turns the job store into a de-facto preset history.
Frontend-only feature; no new backend routes.
v1.9.29 — Interview Polish pipeline
OpenCut v1.9.29 — Interview Polish
Third of five feature releases. This is the marquee item — a real
one-click pipeline for interview / podcast content that composes six
existing OpenCut capabilities into a single button.
What's new
A new Interview Polish card sits at the top of the Cut tab. Hit
the big button on a selected clip and OpenCut runs the whole
interview-grade flow:
- Detect speech segments — silence removal
- Transcribe audio — Whisper (any installed backend)
- Find and cut repeated takes — Jaccard similarity on transcript
- Remove filler words — "um", "uh", "like", context-aware
- Speaker diarization — advisory; flags the step with a how-to-enable hint if WhisperX is available
- Generate captions + chapters — SRT, YouTube chapter markdown,
and a final Premiere XML with every cut applied
Three opt-out checkboxes let you skip repeat-detection, filler cut,
or chapter generation if you don't want them on a given run.
UX
- A real step checklist — not just a progress bar. Each of the 6
steps renders with:- A status icon (pending / ok / failed / skipped)
- Per-step stat detail: "3 fillers removed", "2 repeats cut",
"8 chapters generated", "kept 247 segments" - A clear "why skipped" reason when a step is opted out or the
backend dep is missing
- Result strip shows the compression ratio ("Compressed to 62% of
original (18:34)") plus three one-click actions:- Import to Premiere — pulls the XML into the active project,
journaled so you can revert from the v1.9.28 Journal tab - Open SRT — launches the captions file in its default app
- Open Chapters — opens the YouTube chapter markdown
- Import to Premiere — pulls the XML into the active project,
Under the hood
- New
POST /interview-polishendpoint inroutes/captions.py
(~230 lines). Composes existing core modules directly —
captions.transcribe,fillers.detect_fillers,
repeat_detect.detect_repeated_takes+
merge_repeat_ranges,chapter_gen.generate_chapters— and
carries intermediate results from one step to the next so the
transcript isn't re-run between stages. - Per-step status is captured in a
stepsarray in the job result.
The frontend renders each entry as a checklist row. - Range subtraction for repeat removal is inlined in the route
because it's the only caller; no speculative shared helper. - Diarization is advisory in this release — shipping speaker labels
requires an HF token in settings, so we report the step as skipped
with a clear "Run Captions → WhisperX with an HF token to label
speakers" hint instead of silently failing.
Tests
- 4 new cases in
TestInterviewPolish: route registered, CSRF
enforced, missing filepath → 400INVALID_INPUT, bad filepath
→ 400. - Full focused suite: 155 passed, 3 skipped.
Artifact
Windows x64 PyInstaller build attached as
OpenCut-Server-v1.9.29-win-x64.zip (~226 MB).
What's next
The planned v1.9.30 (live audio preview) and v1.10.0 (sequence
assistant) are in progress as part of the same feature-release
cycle. See the roadmap comment in v1.9.27 release notes.
v1.9.28 — Operation Journal + Rollback
OpenCut v1.9.28 — Operation Journal + Rollback
Second of five feature releases this cycle. This is the trust feature —
the thing that lets you commit to automated edits knowing you can take
them back without relying on Premiere's native undo stack.
What's new
Operation Journal. Every OpenCut action that writes to your
Premiere project is now recorded in a persistent SQLite journal at
~/.opencut/journal.db. A new Operation Journal card at the top
of the Settings tab lists recent operations in reverse-chronological
order with a one-click Revert button for actions that support it.
Instrumented forward operations
| Action | Revert behavior |
|---|---|
| Add sequence markers (e.g. beat markers) | Deletes exactly the markers OpenCut added by fingerprinting {time, comment} |
| Batch rename project items | Restores each item's previous name (by node id with a name-lookup fallback) |
| Import sequence from XML (silence remove, full pipeline, etc.) | Removes the sequence by name |
| Import overlay video | Deletes the imported project item by node id |
Recorded but not auto-revertible
Some actions are kept in the history for context, but the ExtendScript
API doesn't offer a reliable inverse. These show a "No auto-revert"
pill instead of a Revert button:
- Import captions (caption tracks don't have a clean deletion API)
- Create smart bins (item bin membership isn't always recoverable)
This is intentional: we'd rather be honest about what we can and can't
reverse than pretend to revert and silently fail.
UX
- Card lives at the top of Settings so it's always one click away.
- Empty state tells you plainly: "No operations recorded yet. Run an
action that writes to your Premiere project and it will appear
here." - Loading skeleton on first open; visible error with the server
message if the journal can't be read. - Refresh button re-queries; Clear button wipes the journal (with a
confirm prompt — it does not undo anything in Premiere). - Reverted entries are dimmed and swap their Revert button for a
"Reverted" pill. - Relative timestamps ("3 min ago", "yesterday").
Under the hood
Backend:
opencut/journal.py— SQLite store with WAL mode, thread-local
connections, andclose_all_connections()wired into the atexit
chain alongsidejob_storeandfootage_index_db.opencut/routes/journal.py— five endpoints:
GET /journal/list,POST /journal/record,
POST /journal/mark-reverted/<id>,DELETE /journal/<id>,
POST /journal/clear. All mutation endpoints CSRF-protected.
Unknown action types are rejected at record time so typos can't
corrupt the revert flow.VALID_ACTIONS/REVERTIBLE_ACTIONSfrozensets keep the contract
explicit and easy to extend when new revertible operations are added.
ExtendScript:
- Four new inverse helpers in
host/index.jsx:
ocRemoveSequenceMarkers,ocUnrenameItems,
ocRemoveImportedSequence,ocRemoveImportedItem. ES3-compliant,
with a_ocParsehelper for older runtimes that lackJSON.parse.
Frontend:
main.jsrecords after every instrumented forward op completes
successfully. Records never fail loudly — if the journal endpoint
is unreachable the user sees no warning because the forward op
already succeeded.PremiereBridgeextended with four new dispatch helpers.- New CSS (~100 lines) matching the existing Studio Graphite palette.
Tests
tests/test_journal.py— 14 new cases covering the store
(recordround-trip,mark_revertedidempotency,clear_all,
revertibleflag mapping, unknown-action rejection, reverted filter)
and the routes (CSRF requirement, 404/409/400 error paths,
delete, clear-empty).- Full suite: 151 passed, 3 skipped.
Artifact
Windows x64 PyInstaller build attached as
OpenCut-Server-v1.9.28-win-x64.zip (~226 MB). Unzip and run
OpenCut-Server\OpenCut-Server.exe or the standard launchers.
What's next
v1.9.29 (in progress): Interview Polish pipeline — a real 6-step
one-click chain that runs silence removal → repeated-take detection →
filler cut → speaker diarization → captions → chapters in one flow
with step-by-step progress and revertable final timeline apply.
v1.9.27 — Session Context: Where you left off
OpenCut v1.9.27 — Session Context
First of five feature releases landing this cycle. Small, zero-risk,
surfaces existing data the panel was already collecting.
What's new
"Where you left off" card. On the first successful connection
after launching the panel — or reconnecting after a server restart —
a dismissible card now appears above the workspace listing the last
5 completed OpenCut jobs, plus any jobs that were mid-flight when
the server died.
Each history row has three quick actions:
- Open — launches the output file in its default app (Premiere,
VLC, Photos, whatever) viaos.startfileon Windows,openon
macOS,xdg-openon Linux. - Reveal — shows the file in Explorer / Finder / the system file
manager (explorer /select,/open -R/xdg-openon parent
dir). - Re-run — replays the same job with the same params, using the
storedendpointandpayloadalready persisted in
~/.opencut/jobs.db.
Interrupted jobs get their own inline banner with a View history
jump that expands the existing job-history tray — no more generic
showAlert() popup that users couldn't act on.
Behavior
- Card appears once per panel session (the
_updateCheckDonegate).
Dismiss is sticky for the rest of the session. - If there's nothing to show (fresh machine, empty history), the
card silently stays hidden — no empty state noise. - Loading skeleton while fetching; clear error text if the history
endpoint is unreachable. - Responsive: rows collapse to a stacked layout below 720 px panel
width. Focus-visible rings on every action button.
Under the hood
- New route:
POST /system/open-path— CSRF-protected, validates
the path throughvalidate_filepath(no traversal, null bytes, or
UNC), dispatches to the OS file manager. Onlymode=openand
mode=revealare accepted. - No new data infrastructure. Uses the existing
/jobs/history?limit=5&status=completeand/jobs/interrupted
endpoints plus the SQLite job store that's been persisting
endpoint + payload since v1.5. - Frontend: one new card component in
main.jswith empty /
loading / error states, slide-in animation matching the alert
banner shell, and styles that respect the existing Studio
Graphite palette.
Tests
tests/test_new_features.TestOpenPath— 6 cases covering CSRF,
empty path, bad mode, missing file, traversal rejection, and a
platform-aware OS-dispatch mock.- Broader suite: 158 passed, 3 skipped.
Artifact
Windows x64 PyInstaller build attached as
OpenCut-Server-v1.9.27-win-x64.zip (~226 MB). Unzip and run
OpenCut-Server\OpenCut-Server.exe or the standard launchers.
What's next
v1.9.28 (in progress): Operation Journal + one-click Rollback —
every OpenCut action that modifies the Premiere project becomes
revertable from a Journal tab.
OpenCut v1.9.26 — Install rate limit 429 + structured filepath errors
v1.9.26 — Two bugs caught by deep behavioral smoke test
Rolling QA caught two more real bugs by exercising the actual running binary — neither were visible to static code review.
1. Install routes returned 200 + job_id on rate-limited retries, then failed async
make_install_route() was checking rate_limit("model_install") inside the @async_job body, so the rate limit violation surfaced only after a job record + worker thread were already created. The client got 200 OK + {"job_id": "..."} and had to poll /status/<id> to discover the failure.
Fix: moved the rate-limit check to the synchronous Flask handler layer — now returns 429 RATE_LIMITED immediately with no job spawned.
first call: 200 {"job_id": "..."} (install begins in worker)
second call: 429 {"code": "RATE_LIMITED", ← synchronous
"error": "A model_install operation is already running...",
"suggestion": "Wait for the current install to finish, then retry."}
2. @async_job filepath errors lacked code + suggestion
When a route rejected a filepath through @async_job, the response was {"error": "No file path provided"} with no structured code field. The CEP/UXP panels' ERROR_CODE_ACTIONS table relies on code to pick the right recovery hint — without it, users saw only the raw message.
Fix: @async_job now classifies filepath failures into INVALID_INPUT (empty, traversal, null byte, UNC prefix) vs FILE_NOT_FOUND (missing, not-a-file) and attaches a suggestion:
{"error": "No file path provided",
"code": "INVALID_INPUT",
"suggestion": "Select a clip in Premiere or pass `filepath` in the request body."}Applies to all 97 async routes that go through the @async_job decorator.
How this was found
A 9-step behavioral smoke test on the v1.9.25 exe that submitted real payloads and inspected the response shapes:
- CSRF enforcement (no-token / bad-token → 403) ✓
- Empty body → 400 (but
codewas None — bug!) - Path traversal → 400 (same bug)
- Non-existent file → 400 (same bug)
- Real silence-detect job submission
- Bad JSON body → 400 ✓
- 100 MB oversize payload → 413 ✓
- Double install call — expected 429 but got 200 twice (bug!)
- Shutdown → 200 ✓
Downloads
Windows
OpenCut-Server-v1.9.26-Windows.zip— 🆕 ready-to-run bundle, extract + double-clickOpenCut-Server.exeOpenCut-Server-Windows.tar.gz— same contents, tarball format
Linux
OpenCut-Server-Linux.tar.gz
macOS
OpenCut-Server-macOS.tar.gz
Built by GitHub Actions CI and uploaded automatically.
OpenCut v1.9.25 — /system/dependencies 6000x speedup
v1.9.25 — Settings tab load time: 6.5 s → 0 ms
Caught during the v1.9.24 binary smoke test: GET /system/dependencies took ~6.5 s on the first cold call because it runs 20+ importlib.import_module() probes (torch, mediapipe, audiocraft, pyannote, onnxruntime, rembg, realesrgan, gfpgan, insightface, ...) plus an ffmpeg -version subprocess plus an ollama HTTP probe — all synchronously in one request. This blocked the Settings tab render on first open.
Fix
- New module-level
_deps_cacheinopencut/routes/system.pywith athreading.Lockand a 60-second TTL. - Cold call still pays the ~5 s import tax once; every subsequent call within the window returns from memory in <1 ms.
- New
?fresh=1query param bypasses the cache so the frontend can force a re-check after an install/uninstall.
Measured (via compiled PyInstaller exe)
cold call: 5513 ms (20+ importlib + ffmpeg + ollama)
warm call: 87 ms (HTTP roundtrip overhead only — cached in memory)
fresh=1: 173 ms (bypasses cache; sys.modules warm)
~63x speedup on steady-state.
How this was found
Part of a rolling QA cycle: every tagged release gets an end-to-end binary smoke test against the PyInstaller exe (not just the Flask test client). That exercise caught v1.9.23's OPENCUT_PORT regression in v1.9.24, and now v1.9.25's dependency-check slowness.
Downloads
Windows
OpenCut-Server-v1.9.25-Windows.zip(225 MB) — 🆕 ready-to-run Windows bundle. Extract anywhere, double-clickOpenCut-Server.exe. Self-contained PyInstaller onedir build with Python 3.12 + all dependencies. This is the easiest download for Windows users.OpenCut-Server-Windows.tar.gz— same contents as the zip, tarball format for CI/WSL/Linux extraction
Linux
OpenCut-Server-Linux.tar.gz— PyInstaller onedir, Python 3.12, Ubuntu-built
macOS
OpenCut-Server-macOS.tar.gz— PyInstaller onedir, Python 3.12, macOS-built
Running it (Windows)
# Extract the zip
# Double-click OpenCut-Server.exe
# OR from a terminal:
cd OpenCut-Server
./OpenCut-Server.exe
# Env-var config (new in v1.9.24):
OPENCUT_PORT=5799 ./OpenCut-Server.exe
OPENCUT_HOST=0.0.0.0 OPENCUT_PORT=5679 ./OpenCut-Server.exe
Server listens on http://127.0.0.1:5679 by default. Point the CEP/UXP Adobe Premiere Pro panel at it or use the REST API directly.
OpenCut v1.9.24 — Env-var config fix
v1.9.24 — Binary smoke-test regression fix
Found during an end-to-end smoke test of the v1.9.23 Windows binary: running OPENCUT_PORT=5789 ./OpenCut-Server.exe silently ignored the env var and bound to 5679 anyway. CLAUDE.md had long advertised env-based config, but opencut/server.py::main() only read argparse flags with hardcoded defaults — nothing was actually reading OPENCUT_PORT / OPENCUT_HOST / OPENCUT_DEBUG.
Fix
main() now reads these env vars before parsing args and uses them as the argparse defaults, so:
- Docker-compose / the VBS launcher can configure the backend entirely via env
- CLI flags (
--host,--port,--debug) still override env when both are set - Values are sanity-checked: port must be 1–65535, non-integer values fall back to 5679, bad
OPENCUT_DEBUGstrings fall back to off OPENCUT_DEBUGaccepts1,true,yes,on(case-insensitive)
Verified with a two-process smoke test:
OPENCUT_PORT=5799 python -m opencut.server→ binds to 5799 ✓GET /health→ 200 with version 1.9.24 ✓GET /system/status→ 200 with CPU/RAM/GPU/jobs keys ✓POST /shutdown→ 200, clean exit ✓
Downloads
- Windows:
OpenCut-Server-Windows.tar.gz - Linux:
OpenCut-Server-Linux.tar.gz - macOS:
OpenCut-Server-macOS.tar.gz
Binaries built by GitHub Actions CI and uploaded automatically after the tag push. Refresh in a few minutes if the download links show "(no assets yet)".