Skip to content

Commit db9576a

Browse files
committed
feat: v1.9.2 — context awareness, parallel batch, plugin system, structured logging, 150+ tests
v1.9.0: Contextual clip awareness (35-feature scoring), plugin system with example plugins, multicam XML export, SQLite FTS5 footage index, NDJSON response streaming, 96 routes hardened with TooManyJobsError→429, AI GPU rate limiting, secure tempfile usage. v1.9.1: Frontend context awareness with guidance banners, parallel batch processing (ThreadPoolExecutor, GPU/CPU worker isolation), clip notes plugin, 175+ endpoint smoke test suite. v1.9.2: JSON structured logging, CI coverage enforcement (50% threshold), structured error migration (zero bare 500s), smart tab reordering, frontend error code mapper, 150 additional core module tests, ExtendScript mock harness (38 functions, 35 assertions), pre-commit hooks, log level audit. Security: replaced all __import__() with importlib. Fixed: GPUContext memory leak, batch_id collisions, XSS in engine registry, 5 timer/interval leaks, ASS subtitle injection, reframe dimension DoS, 23 import sorting + 15 unused import + 4 unused variable lint violations.
1 parent ad2cd64 commit db9576a

115 files changed

Lines changed: 17699 additions & 1208 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/build.yml

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ name: Build
22

33
on:
44
workflow_dispatch:
5+
pull_request:
6+
branches: [main]
57
push:
8+
branches: [main]
69
tags:
710
- 'v*'
811

@@ -40,11 +43,12 @@ jobs:
4043
- name: Lint with ruff
4144
run: |
4245
pip install ruff
43-
ruff check opencut/ --select E,F,I --ignore E501
46+
ruff check opencut/ --select E,F,I --ignore E501,E402
4447
45-
- name: Run tests
48+
- name: Run tests with coverage
4649
run: |
47-
python -m pytest tests/ -v --tb=short
50+
pip install pytest-cov
51+
python -m pytest tests/ -v --tb=short --cov=opencut --cov-report=term-missing --cov-fail-under=50
4852
4953
- name: Smoke test imports
5054
run: |
@@ -56,9 +60,11 @@ jobs:
5660
python -c "from opencut.cli import cli; print('CLI OK')"
5761
5862
- name: Build with PyInstaller
63+
if: startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch'
5964
run: pyinstaller opencut_server.spec
6065

6166
- name: Archive build output
67+
if: startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch'
6268
uses: actions/upload-artifact@v4
6369
with:
6470
name: ${{ matrix.artifact }}

.pre-commit-config.yaml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ repos:
33
rev: v0.8.6
44
hooks:
55
- id: ruff
6-
args: [--fix, --select, "E,F,I", --ignore, "E501"]
6+
args: [--fix, --select, "E,F,I", --ignore, "E501,E402"]
77
- id: ruff-format
88

99
- repo: https://github.com/pre-commit/pre-commit-hooks
@@ -13,3 +13,13 @@ repos:
1313
- id: end-of-file-fixer
1414
- id: check-yaml
1515
- id: check-json
16+
17+
- repo: local
18+
hooks:
19+
- id: pytest-smoke
20+
name: pytest smoke tests
21+
entry: python -m pytest tests/test_route_smoke.py tests/test_core_modules.py -x -q --tb=short --no-header
22+
language: system
23+
pass_filenames: false
24+
always_run: true
25+
stages: [pre-push]

CHANGELOG.md

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,80 @@
11
# Changelog
22

3+
## [1.9.2] - 2026-03-26
4+
5+
### Added
6+
- **JSON Structured Logging** (Phase 0.3) — File handler now outputs JSON via `python-json-logger`. Each log line includes `timestamp`, `level`, `module`, `job_id`, `message`. Console handler stays plain text. Graceful fallback if `python-json-logger` not installed.
7+
- **CI Coverage Enforcement** (Phase 0.1) — `.github/workflows/build.yml` now runs `pytest-cov` with `--cov-fail-under=50` threshold. CI also triggers on PRs and pushes to main. PyInstaller build skipped for non-release runs.
8+
- **Structured Error Migration** (Phase 0.2) — All route error handlers migrated from bare `{"error": str(e)}` to `safe_error()` from `opencut/errors.py`. Zero bare 500 error patterns remain across all 13 route files. Every API error returns `{error, code, suggestion}`.
9+
- **Smart Tab Reordering** (Phase 3.2) — Sub-tabs now physically reorder in the DOM based on contextual relevance scores. Highest-scoring features move to front within each tab group. `resetTabOrder()` restores original order on clip deselection.
10+
- **Frontend Error Code Mapper** (Phase 0.2) — `enhanceError()` now reads `data.code` field before regex fallbacks. Code-to-action mapping for GPU_OOM, MISSING_DEPENDENCY, FILE_NOT_FOUND, etc. with navigable settings links.
11+
- **Core Module Unit Tests** (Phase 0.1) — 15 additional core modules tested (silence, fillers, scene_detect, auto_edit, highlights, workflow, speed_ramp, audio, face_reframe, chromakey, video_fx, export_presets, diarize, audio_duck, thumbnail).
12+
- **i18n String Extraction** (Phase 6.2) — ~200 additional `data-i18n` attributes added to buttons, headers, labels, and tabs in index.html with corresponding en.json keys.
13+
- **Pre-commit Hooks** (Phase 0.1) — `.pre-commit-config.yaml` now includes ruff lint/format + pytest smoke suite (on pre-push). `pre-commit` added to dev dependencies.
14+
- **Log Levels Audit** (Phase 0.3) — 22 log calls across 10 files corrected: verbose processing steps downgraded INFO→DEBUG, fallback/degraded paths upgraded INFO→WARNING. No secrets found in logging.
15+
- **Core Module Unit Tests Batch 2** (Phase 0.1) — 135 additional tests across 28 previously untested core modules (animated_captions, audio_enhance, audio_pro, audio_suite, caption_burnin, captions_enhanced, color_management, emotion_highlights, face_swap, face_tools, lut_library, motion_graphics, music_ai, music_gen, object_removal, particles, shorts_pipeline, style_transfer, styled_captions, transitions_3d, upscale_pro, video_ai, voice_gen, zoom, broll_insert, broll_generate, multimodal_diarize, social_post).
16+
- **ExtendScript Mock Harness** (Phase 0.1) — `tests/jsx_mock.js` provides fake Premiere Pro DOM (app.project, activeSequence, tracks, markers, ProjectItem). Tests 38 ExtendScript functions including ocApplySequenceCuts, ocBatchRenameProjectItems, ocAddSequenceMarkers, ocAddNativeCaptionTrack. 35 assertions pass under Node.js.
17+
18+
### Security
19+
- Replaced all `__import__()` calls with `importlib.import_module()` / `importlib.util.find_spec()` in helpers.py, system.py, engine_registry.py, plugins.py
20+
21+
### Fixed
22+
- **`_safe_error` undefined** — Fixed `_safe_error` reference in video.py multicam XML (F821 undefined name bug)
23+
- **`safe_int` missing import** — Added missing import in settings.py log tail endpoint (F821)
24+
- **Timecode watermark plugin**`start_tc` variable now used in FFmpeg drawtext filter (was extracted but unused)
25+
- **Version sync** — All version strings (pyproject.toml, __init__.py, CEP manifest, UXP manifest, server startup banner, installer AppConstants.cs) now read from single source. Server banner uses `__version__` instead of hardcoded string.
26+
- **TooManyJobsError handling** — 7 remaining `_new_job()` calls in system.py, search.py, timeline.py now properly catch `TooManyJobsError` and return HTTP 429 instead of 500.
27+
- **audio_separate cleanup crash**`temp_audio` variable moved above try block to prevent `NameError` in finally clause if `_resolve_output_dir()` fails.
28+
- **GPUContext memory leak**`__exit__` now calls `.cpu()` on registered models before clearing, actually freeing GPU VRAM instead of only deleting a loop variable.
29+
- **batch_id collision risk** — Batch IDs extended from 8 to 16 hex chars, reducing collision probability from 1-in-65K to 1-in-4-billion batches.
30+
- **XSS in engine registry** — All API-sourced values (domain, engine name, display_name, quality, speed) now wrapped in `esc()` before innerHTML insertion.
31+
- **elapsedTimer leak**`startJob()` now clears any existing elapsed timer before creating a new one, preventing interval accumulation on rapid re-starts.
32+
- **mediaScanTimer leak** — Periodic media scan `setInterval` now assigned to tracked variable and cleared on `beforeunload`.
33+
- **pollTimer leak**`trackJobPoll()` now clears any existing poll timer before creating a new one; SSE fallback no longer creates duplicate trackers.
34+
- **SSE parse errors silent** — SSE `onmessage` handler now logs JSON parse failures to console instead of silently swallowing them.
35+
- **Reframe dimension DoS**`/video/reframe` now enforces `min_val=16, max_val=7680` on width/height parameters, preventing absurd allocation requests.
36+
- **ASS subtitle injection** — Caption burn-in now strips backslash sequences from source text before ASS formatting, preventing style override injection via subtitle content.
37+
- **23 import sorting violations** fixed (I001)
38+
- **15 unused imports** removed (F401)
39+
- **4 unused variables** removed (F841)
40+
- Codebase at **0 lint warnings** matching CI config
41+
42+
## [1.9.1] - 2026-03-26
43+
44+
### Added
45+
- **Frontend Context Awareness** (Phase 3.2) — CEP panel now calls `POST /context/analyze` after clip selection. Guidance banner shows clip-specific recommendations. Sub-tabs for high-scoring features get visual highlights. Dismiss button hides banner.
46+
- **Parallel Batch Processing** (Phase 5.4) — `process_batch_parallel()` in `batch_process.py` uses `ThreadPoolExecutor` for concurrent multi-clip operations. GPU ops limited to 1 worker, CPU ops scale to core count. Partial failure isolation — one item crash doesn't kill the batch.
47+
- **Clip Notes Plugin** (Phase 6.1) — Second example plugin at `opencut/data/example_plugins/clip-notes/`. SQLite-backed per-clip notes with `POST /note`, `GET /notes`, `DELETE /note`, `GET /export` (text/CSV). Shipped alongside timecode-watermark as reference plugins.
48+
- **Route Smoke Tests** (Phase 0.1) — Comprehensive smoke test suite in `tests/test_route_smoke.py` covering all 175+ endpoints across 13 blueprints. Verifies routes don't crash with minimal payloads.
49+
50+
### Fixed
51+
- **Ruff lint cleanup** — Fixed unused imports in new test files (8 auto-fixed F401 violations)
52+
53+
## [1.9.0] - 2026-03-26
54+
55+
### Added
56+
- **Contextual Awareness** (Phase 3.2) — Clip type detection and feature relevance scoring. `POST /context/analyze` accepts clip metadata and returns scored features, guidance messages, and per-tab relevance scores. 35 features scored across 4 tabs based on clip tags (talking_head, audio_only, long_duration, etc.)
57+
- **Plugin System** (Phase 6.1) — Plugin loader discovers/validates/loads plugins from `~/.opencut/plugins/`. Each plugin has a `plugin.json` manifest and optional Flask Blueprint routes registered under `/plugins/<name>/`. Endpoints: `GET /plugins/list`, `GET /plugins/loaded`, `POST /plugins/install`, `POST /plugins/uninstall`. Includes example timecode-watermark plugin.
58+
- **Multicam XML Export** — Generate Premiere Pro compatible FCP XML from multicam diarization cut data. `POST /video/multicam-xml` endpoint. CLI `multicam` command now exports XML instead of showing placeholder message.
59+
- **Background Indexing with SQLite FTS5** (Phase 5.3) — Persistent footage index at `~/.opencut/footage_index.db` with full-text search. Incremental re-indexing via mtime comparison. Endpoints: `POST /search/auto-index`, `POST /search/db-search`, `GET /search/db-stats`, `POST /search/cleanup`.
60+
- **Response Streaming** (Phase 5.2) — NDJSON streaming utilities for progressively delivering large result sets. `GET /jobs/stream-result/<job_id>` streams completed job results (segments, scenes, thumbnails) in batches of 50.
61+
- **Context blueprint** (`routes/context.py`) — 2 new endpoints
62+
- **Plugins blueprint** (`routes/plugins.py`) — 4 new endpoints
63+
- **4 new search endpoints** — auto-index, db-search, db-stats, cleanup
64+
65+
### Fixed
66+
- **TooManyJobsError handling** — All 96 manual `_new_job()` calls across video.py (47), audio.py (23), and captions.py (13) now catch `TooManyJobsError` and return proper 429 responses instead of 500 errors
67+
- **AI GPU rate limiting** — Added `rate_limit("ai_gpu")` to 6 GPU-heavy routes: video AI upscale, background removal, shorts pipeline, and 3 music AI generation routes. Prevents concurrent GPU OOM crashes
68+
- **Settings import validation**`/settings/import` now validates workflow steps (requires endpoint + label per step) before saving, preventing malformed workflow injection
69+
- **Preview frame bounds**`width` parameter bounded to 32-3840, `detection_skip` bounded to 1-30
70+
- **Secure tempfile** — Preview frame extraction uses `tempfile.mkstemp()` instead of predictable path construction
71+
72+
### Security
73+
- 96 routes hardened against job limit bypass (TooManyJobsError → 429)
74+
- Input bounds added to prevent resource exhaustion via unbounded parameters
75+
- Workflow import step validation prevents malformed workflow injection
76+
- Fixed double rate-limit release in demucs/watermark install routes (decorator + explicit call)
77+
378
## [1.5.0] - 2026-03-23
479

580
### Added

0 commit comments

Comments
 (0)