Skip to content

Commit 7cdacf1

Browse files
committed
docs: update CLAUDE.md for v1.9.3 — video split, async_job, app factory
1 parent 6ea81d1 commit 7cdacf1

1 file changed

Lines changed: 31 additions & 15 deletions

File tree

CLAUDE.md

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@
1010
## Key Files
1111

1212
### Backend (Python)
13-
- `opencut/server.py` (~800 lines) - Flask app creation, startup, port management, download_models, `_setup_system_site_packages()` for frozen builds. JSON structured logging via python-json-logger (file handler; console stays plain text). `[job_id]` correlation via _JobLogFilter. `/logs/tail` endpoint for filtered log viewing.
13+
- `opencut/server.py` (~800 lines) - `create_app(config)` factory function for isolated Flask instances, startup, port management, download_models, `_setup_system_site_packages()` for frozen builds. JSON structured logging via python-json-logger (file handler; console stays plain text). `[job_id]` correlation via _JobLogFilter. `/logs/tail` endpoint for filtered log viewing.
14+
- `opencut/config.py``OpenCutConfig` dataclass centralizing env var reads (host, port, debug, cors_origins, log_level, data_dir). Used by `create_app(config)` for testable/isolated instances.
1415
- `opencut/security.py` (~280 lines) - Path validation, CSRF tokens, safe_pip_install (frozen-build aware via `_find_system_python()`), safe_float/safe_int (with range clamp + inf/nan rejection), validate_filepath, VALID_WHISPER_MODELS, rate_limit/require_rate_limit
15-
- `opencut/jobs.py` (~240 lines) - Job state, _new_job, _update_job, _kill_job_process, _get_job_copy, _list_jobs_copy, _unregister_job_process, TooManyJobsError, MAX_CONCURRENT_JOBS=10, async_job decorator. Thread-local job_id for log correlation (_thread_local.job_id set in _process, cleared in finally). _safe_error delegates to errors.safe_error for structured classification.
16+
- `opencut/jobs.py` (~240 lines) - Job state, _new_job, _update_job, _kill_job_process, _get_job_copy, _list_jobs_copy, _unregister_job_process, TooManyJobsError, MAX_CONCURRENT_JOBS=10, async_job decorator (with `filepath_required` and `filepath_param` parameters), `make_install_route()` factory for identical install endpoints. Thread-local job_id for log correlation (_thread_local.job_id set in _process, cleared in finally). _safe_error delegates to errors.safe_error for structured classification.
1617
- `opencut/helpers.py` (~530 lines) - _try_import, output paths, FFmpegCmd builder, FFmpeg progress runner, deferred temp cleanup, job time tracking, compute_estimate, `run_ffmpeg()`, `ensure_package()`, `get_video_info()`
1718
- `opencut/errors.py` (~230 lines) - Structured error taxonomy. OpenCutError exception class with code/message/suggestion. `error_response()` helper for routes. `safe_error(exc, context)` classifies exceptions (MemoryError→GPU_OUT_OF_MEMORY, TimeoutError→OPERATION_TIMEOUT, PermissionError→PERMISSION_DENIED, ImportError→MISSING_DEPENDENCY, etc.) and returns structured JSON with recovery suggestions. Factory constructors: missing_dependency, file_not_found, gpu_out_of_memory, invalid_input, invalid_model, operation_failed, rate_limited, queue_full, module_not_available, file_permission_denied, too_many_items, server_busy, install_failed. All errors return `{error, code, suggestion}` JSON.
1819
- `opencut/checks.py` (~90 lines) - Centralized dependency availability checks (demucs, watermark, pedalboard, audiocraft, edge_tts, rembg, upscale, scenedetect, auto-editor, transnetv2, resemble-enhance, ollama)
@@ -42,11 +43,15 @@
4243
- `nlp_command.py` - Natural language → API route mapping. COMMAND_MAP with 19 entries. parse_command_keyword(text) → {route, params, confidence, matched_keyword} or None. parse_command_llm(text, config, routes). parse_command(text, llm_config) tries LLM then keyword. extract_params_from_text(text) extracts numbers/language/intensity hints.
4344

4445
### Route Blueprints (`opencut/routes/`)
45-
- `__init__.py` - `register_blueprints(app)` registers all 11 Blueprints (added workflow_bp)
46-
- `system.py` (~1130 lines) - /health, /shutdown, /info, /gpu/*, /dependencies (includes color_match, auto_zoom, footage_search, loudness_match, deliverables, nlp_command checks), /file, /whisper/*, /llm/*
47-
- `audio.py` (~2175 lines) - /silence, /fillers, /audio/*, /audio/beat-markers (→ beat timestamps for ExtendScript markers), /audio/loudness-match (async, on_progress)
48-
- `captions.py` (~1590 lines) - /captions/*, /captions/chapters (LLMConfig object, not dict), /captions/repeat-detect (word-level timestamps → detect_repeated_takes)
49-
- `video.py` (~4021 lines) - /video/*, /video/color-match (async, on_progress), /video/auto-zoom (dynamic resolution via probe — no hardcoded hd1080), /video/multicam-cuts (result.get("cuts") — dict not list)
46+
- `__init__.py` - `register_blueprints(app)` registers all 17 Blueprints (system, audio, captions, video_core, video_fx, video_ai, video_editing, video_specialty, jobs, settings, timeline, search, deliverables, nlp, workflow, context, plugins) + dynamic plugin blueprints
47+
- `system.py` (~1130 lines) - /health, /shutdown, /info, /gpu/*, /dependencies, /file, /whisper/*, /llm/*. Install routes use `make_install_route()` factory.
48+
- `audio.py` (~2175 lines) - /silence, /fillers, /audio/*, /audio/beat-markers, /audio/loudness-match. All async routes use `@async_job` decorator.
49+
- `captions.py` (~1590 lines) - /captions/*, /captions/chapters (LLMConfig object, not dict), /captions/repeat-detect. All async routes use `@async_job`.
50+
- `video_core.py` (~1395 lines) - /video/export, /video/reframe, /video/trim, /video/merge, /video/concat, /video/pip, /video/blend, /video/stabilize, /video/speed, /video/letterbox, /video/preview-frame, /video/color-match, /video/auto-zoom, /video/multicam-cuts, /video/multicam-xml, /video/highlights
51+
- `video_editing.py` (~750 lines) - /video/transitions/*, /video/title/*, /video/scenes, /video/denoise, batch processing routes
52+
- `video_fx.py` (~687 lines) - /video/fx/*, /video/chromakey, /video/lut/*, /video/style/*, /video/particles
53+
- `video_specialty.py` (~489 lines) - /video/face/*, /video/object/*, /video/watermark, /video/shorts, /video/rembg
54+
- `video_ai.py` (~443 lines) - /video/ai/*, AI model install routes (depth, emotion, multimodal-diarize, broll-generate, crisper-whisper)
5055
- `jobs_routes.py` (~330 lines) - /status/*, /cancel/*, /cancel-all, /jobs, /stream/*, /queue/*, /jobs/history (SQLite-backed), /jobs/stats, /jobs/interrupted
5156
- `settings.py` (~440 lines) - /presets/*, /favorites/*, /workflows/*, /settings/import|export, /settings/llm (GET masks key, POST preserves masked), /settings/loudness-target, /settings/auto-zoom, /settings/chapters, /settings/multicam, /settings/footage-index, /logs/tail (filtered log viewer), /templates/list, /templates/save, /templates/apply
5257

@@ -101,7 +106,7 @@
101106
- `ROADMAP.md` - 7-phase implementation roadmap with priority matrix and success metrics
102107

103108
### Tests
104-
- `tests/conftest.py` - Flask test client + CSRF fixtures + test media generators
109+
- `tests/conftest.py` - Flask test client via `create_app()` factory + CSRF fixtures + test media generators
105110
- `tests/test_core.py` - Core module unit tests (silence, export, config)
106111
- `tests/test_integration.py` - Route integration tests (health, CSRF, search, NLP, settings, timeline)
107112
- `tests/test_new_modules.py` - v1.5 module tests (repeat_detect, chapter_gen, footage_search, deliverables, multicam, nlp_command, loudness_match, auto_zoom, color_match)
@@ -116,6 +121,11 @@
116121
- `tests/test_batch_parallel.py` - Parallel batch processing (ThreadPoolExecutor, GPU/CPU workers, error isolation, cancellation)
117122
- `tests/test_batch_executor.py` - BatchExecutor class tests (OperationSpec dispatch, combined progress, cancellation, partial failure)
118123
- `tests/test_clip_notes_plugin.py` - Clip Notes plugin tests (CRUD notes, export text/CSV)
124+
- `tests/test_core_modules.py` - Core module unit tests batch 1 (15 modules: silence, fillers, scene_detect, auto_edit, highlights, etc.)
125+
- `tests/test_core_modules_batch2.py` - Core module unit tests batch 2 (135 tests across 28 modules)
126+
- `tests/test_integration_ffmpeg.py` - FFmpeg integration tests
127+
- `tests/test_integration_whisper.py` - Whisper integration tests
128+
- `tests/jsx_mock.js` - ExtendScript mock harness (38 JSX functions, 35 assertions under Node.js)
119129

120130
### Example Plugins (`opencut/data/example_plugins/`)
121131
- `timecode-watermark/` - FFmpeg drawtext timecode overlay plugin
@@ -124,15 +134,17 @@
124134
## Architecture
125135
- Backend runs as standalone process (exe or `python -m opencut.server`)
126136
- Panel communicates via XHR to localhost:5679
127-
- **Blueprint-based route organization**: 13 Blueprints (system, audio, captions, video, jobs, settings, timeline, search, deliverables, nlp, workflow, context, plugins) + dynamically loaded plugin blueprints
137+
- **Blueprint-based route organization**: 17 Blueprints (system, audio, captions, video_core, video_fx, video_ai, video_editing, video_specialty, jobs, settings, timeline, search, deliverables, nlp, workflow, context, plugins) + dynamically loaded plugin blueprints
128138
- **Shared modules**: security.py (CSRF + path validation), jobs.py (job state), helpers.py (utilities + `run_ffmpeg` + `ensure_package` + `get_video_info`), user_data.py (thread-safe file I/O)
129139
- **CSRF protection**: Token generated at startup in security.py, returned via /health, sent as `X-OpenCut-Token` header on mutations. `@require_csrf` decorator applied to ALL POST routes.
130140
- **Path validation**: `validate_path()` checks realpath, null bytes, `..` components, symlinks. `validate_filepath()` adds isfile check. Applied to ALL routes accepting file paths.
131141
- **Input validation**: `safe_float()`/`safe_int()` with optional `min_val`/`max_val` range clamping and inf/nan rejection, `VALID_WHISPER_MODELS` frozenset for model name validation
132142
- **Rate limiting**: `require_rate_limit(key)` decorator prevents concurrent expensive ops (e.g. model installs share `"model_install"` key)
133143
- **Error taxonomy**: `OpenCutError` with typed codes (`MISSING_DEPENDENCY`, `GPU_OUT_OF_MEMORY`, etc.) — frontend `enhanceError()` adds actionable hints
134144
- **Job safety**: `TooManyJobsError` (429), `_get_job_copy()`/`_list_jobs_copy()` for thread-safe reads, `_unregister_job_process()` for cleanup
135-
- **Async job decorator**: `@async_job("type")` wraps routes in standard thread + try/catch + update pattern. Sets thread-local job_id for log correlation. Auto-persists terminal jobs to SQLite via _persist_job.
145+
- **Async job decorator**: `@async_job("type")` wraps routes in standard thread + try/catch + update pattern. Extended with `filepath_required` (auto-validates input file) and `filepath_param` (custom param name) parameters. Sets thread-local job_id for log correlation. Auto-persists terminal jobs to SQLite via _persist_job. All 97 async routes now use this decorator (no manual _new_job/Thread patterns remain).
146+
- **Install route factory**: `make_install_route(package, pip_name, check_fn, key)` in jobs.py generates identical install endpoint handlers. Used by 6 routes (depth, emotion, multimodal-diarize, broll-generate, face, crisper-whisper).
147+
- **App factory**: `create_app(config)` in server.py creates isolated Flask instances. `OpenCutConfig` dataclass in config.py centralizes env var reads. Tests use independent app instances. Module-level `app = create_app()` preserved for backward compat.
136148
- Job system: `_new_job()` creates job, thread pool processes, SSE/polling for status. SQLite persistence at `~/.opencut/jobs.db` (WAL mode). `mark_interrupted()` on startup recovers jobs from previous crashes.
137149
- **Workflow engine**: `core/workflow.py` chains steps sequentially via Flask test client, polls sub-jobs to completion, checks parent cancellation between steps. `routes/workflow.py` serves 6 built-in presets + user custom workflows.
138150
- **Request size limit**: 100 MB `MAX_CONTENT_LENGTH` with 413 error handler
@@ -160,19 +172,23 @@
160172
- Optional deps: `pip install -e ".[ai]"` (CPU), `pip install -e ".[ai-gpu]"` (GPU with onnxruntime-gpu), `pip install -e ".[all]"` (everything)
161173

162174
## Dev Tooling
163-
- `scripts/sync_version.py` - Syncs version from `__init__.py` to all 6 target locations (`python scripts/sync_version.py --set X.Y.Z`)
175+
- `scripts/sync_version.py` - Syncs version from `__init__.py` to all 19 targets (`python scripts/sync_version.py --set X.Y.Z`). `--check` flag for CI enforcement.
164176
- `.editorconfig` - Editor indent/encoding rules (4-space Python, 2-space JS/CSS/HTML)
165177
- `.pre-commit-config.yaml` - ruff lint+format, trailing whitespace, EOF fixer, YAML/JSON checks
166178
- `DEVELOPMENT.md` - Developer setup guide (backend, CEP, building, linting)
167179
- CI: `.github/workflows/build.yml` includes ruff lint (`--select E,F,I --ignore E501`) + import smoke tests before PyInstaller build
168180
- Lint: `ruff check opencut/` — codebase is fully clean, pre-commit enforces on every commit
169181

170182
## Version
171-
- Current: **v1.9.0**
172-
- All version strings: `pyproject.toml`, `__init__.py`, `CSXS/manifest.xml` (ExtensionBundleVersion + Version), `com.opencut.uxp/manifest.json`, `com.opencut.uxp/main.js` (VERSION const), `index.html` version display, README badge
173-
- Use `python scripts/sync_version.py --set X.Y.Z` to update all 18 targets at once (including UXP files)
183+
- Current: **v1.9.3**
184+
- All version strings: `pyproject.toml`, `__init__.py`, `CSXS/manifest.xml` (ExtensionBundleVersion + Version), `com.opencut.uxp/manifest.json`, `com.opencut.uxp/main.js` (VERSION const), `index.html` version display, README badge, `package.json`
185+
- Use `python scripts/sync_version.py --set X.Y.Z` to update all 19 targets at once (including UXP files and package.json)
186+
- Use `python scripts/sync_version.py --check` in CI to verify all targets match
174187

175188
## Gotchas
189+
- **video.py was split**`video.py` no longer exists. Routes are in `video_core.py`, `video_editing.py`, `video_fx.py`, `video_specialty.py`, `video_ai.py`. All URL paths unchanged. All 5 files share the `/video` URL prefix.
190+
- **No manual _new_job/Thread patterns** — All async routes MUST use `@async_job("type")` decorator. Never write manual `_new_job()` + `threading.Thread` + `_update_job(status="complete")`. The decorator handles job creation, thread spawning, error handling, cancellation race conditions, and SQLite persistence.
191+
- **Install routes use factory** — New install endpoints should use `make_install_route(package, pip_name, check_fn, key)` from jobs.py instead of duplicating the install handler pattern.
176192
- `subprocess.run` must use `_sp.run` (the alias) in route files
177193
- ExtendScript is ES3: no let/const, no arrow functions, no template literals
178194
- CEP Chromium needs `user-select: text` override for inputs
@@ -927,4 +943,4 @@ enhance = ["resemble-enhance>=0.0.1"]
927943
- **NDJSON response mime type**`application/x-ndjson`. Frontend must parse line-by-line, not `JSON.parse()` the whole body. Each line is a valid JSON object.
928944
- **Context awareness `score_features` capability check** — The scoring checks for "audio" and "video" capabilities by inferring from tags (e.g., `audio_only` implies has audio but no video). Tags like `talking_head` imply both audio and video are present.
929945
- **`rate_limit("ai_gpu")` scope** — Shared across all AI routes. If upscale is running, music generation returns 429. This is intentional to prevent GPU memory conflicts.
930-
- **13 Blueprints** — Route registration list in `__init__.py` is now: system, audio, captions, video, jobs, settings, timeline, search, deliverables, nlp, workflow, context, plugins. Plus dynamic plugin blueprints.
946+
- **17 Blueprints** — Route registration list in `__init__.py` is now: system, audio, captions, video_core, video_fx, video_ai, video_editing, video_specialty, jobs, settings, timeline, search, deliverables, nlp, workflow, context, plugins. Plus dynamic plugin blueprints.

0 commit comments

Comments
 (0)