davinci-resolve-mcp 2.39.0 → 2.41.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,76 @@
2
2
 
3
3
  Release history for the DaVinci Resolve MCP Server. The latest release is summarized in the root README; older entries live here to keep the README focused.
4
4
 
5
+ ## What's New in v2.41.0
6
+
7
+ DB-canonical clip analysis (C1) — Phase A of the analysis + edit-engine
8
+ program. The per-project SQLite DB is now the source of truth for clip
9
+ analysis; `analysis.json` becomes a derived export written in lockstep.
10
+
11
+ - **Added** schema v9 to the per-project timeline-brain DB: `clips`,
12
+ `clip_aliases`, `analysis_reports` (canonical full payload), `shots`,
13
+ `subjective_fields` + `field_changelog` (per-field provenance),
14
+ `transcript_segments`, `frames`, and `qc_observations`.
15
+ - **Added** `src/utils/analysis_store.py`: transactional ingest, export with
16
+ human-correction overlay (human rows always win and survive re-analysis),
17
+ alias-based clip lookup, shot ids stable under one-second boundary jitter,
18
+ and a round-trip guard verified against a real sample analysis root.
19
+ - **Added** `media_analysis` actions `db_status` (schema version + row
20
+ counts) and `db_ingest` (one-shot migration of existing JSON reports and
21
+ `corrections.json` sidecars into the DB).
22
+ - **Changed** the analysis write path: `execute_plan` and `commit_vision`
23
+ write DB rows first, then export the JSON. Panel clip/shot endpoints read
24
+ DB-first with JSON fallback for pre-v9 reports and job-linked report dirs.
25
+ - **Changed** `update_clip_field` / `update_shot_field` / `revert_field` to
26
+ mirror corrections into the DB as row-level provenance (the
27
+ `corrections.json` sidecar remains for compatibility).
28
+ - **Fixed** eight V2 actions that were unreachable from MCP dispatch since
29
+ v2.24.0 (`get_panel_state`, `set_panel_state`, `session_start_context`,
30
+ `update_shot_field`, `update_clip_field`, `get_field_history`,
31
+ `revert_field`, `list_corrections`): they were checked inside the
32
+ project-root dispatch block but missing from its membership set. The
33
+ control panel proxied to the helpers directly, which masked it.
34
+ - **Fixed** the action-list drift guard to inspect async tool functions
35
+ (media_analysis had drifted unchecked) and to fail on actions that are
36
+ unreachable inside membership blocks — the exact class above. Four
37
+ reachable-but-unadvertised actions (`coverage_report`,
38
+ `get_resolve_ai_usage`, `get_ai_governance`, `set_ai_governance`) are now
39
+ listed in the unknown-action error.
40
+ - **Validation**: full offline suite (1066 tests; 12 new), round-trip guard
41
+ on the real 2026-05-17 sample root, and a live headless pipeline run on
42
+ synthetic media verifying rows-then-export parity, row-level corrections,
43
+ and DB-first panel reads.
44
+
45
+ ## What's New in v2.40.0
46
+
47
+ Control panel UX overhaul + docs refresh, from a full live audit of every
48
+ panel view, with drift guards so neither can silently go stale again.
49
+
50
+ - **Added** a governance mode toggle (Advisory / Enforce) to the AI Console,
51
+ with corrected copy (the old text predated v2.39.0 enforce mode), and a
52
+ Recent-runs list on the AI-ops ledger showing each run's actor.
53
+ - **Added** chat-first onboarding: empty Overview, Review, and Inventory
54
+ states show plain-language guidance with a copyable suggested prompt
55
+ instead of zero-walls and MCP action names.
56
+ - **Added** History as a first-class Media menu item, deep-linkable at
57
+ `#analysis/review/history`; documented the full hash deep-link scheme.
58
+ - **Changed** the top-level "Analysis" menu to "Media" (it collided with the
59
+ Preferences page of the same name); "Analyze" is now "Inventory". Full
60
+ vocabulary pass: humanized readiness chips and frame-sampling labels, no
61
+ internal codenames, file names, or absolute paths in UI copy.
62
+ - **Fixed** the README's linked screenshot rendering broken in the Docs
63
+ reader: badge parsing no longer claims local linked images, and a new
64
+ `/api/doc_asset/` route serves repo doc images (path-constrained).
65
+ - **Fixed** poll hygiene: timers pause while the tab is hidden, panel-state
66
+ polling backs off when unfocused, and unchanged inventory polls return a
67
+ tiny 200 instead of a 304 that Chrome logged as an aborted request.
68
+ - **Docs**: `docs/guides/control-panel.md` fully rewritten for the current
69
+ IA with all screenshots regenerated from the live panel
70
+ (`scripts/regen_panel_screenshots.py`). A new drift-guard test fails the
71
+ suite — and the publish workflow, which now runs all three static guards
72
+ on every tag — when the guide drifts from the panel's navigation or
73
+ screenshots.
74
+
5
75
  ## What's New in v2.39.0
6
76
 
7
77
  Governance enforce mode and actor identity — the staged Phase 3 of the
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # DaVinci Resolve MCP Server
2
2
 
3
- [![Version](https://img.shields.io/badge/version-2.39.0-blue.svg)](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
3
+ [![Version](https://img.shields.io/badge/version-2.41.0-blue.svg)](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
4
4
  [![npm](https://img.shields.io/npm/v/davinci-resolve-mcp.svg?label=npm&color=CB3837)](https://www.npmjs.com/package/davinci-resolve-mcp)
5
5
  [![API Coverage](https://img.shields.io/badge/API%20Coverage-100%25-brightgreen.svg)](docs/reference/api-coverage.md)
6
6
  [![Tools](https://img.shields.io/badge/MCP%20Tools-33%20(341%20full)-blue.svg)](#server-modes)
package/docs/SKILL.md CHANGED
@@ -491,8 +491,24 @@ Key actions: `capabilities`, `install_guidance`, `resolve_output_root`, `plan`,
491
491
  `publish_clip_metadata`, `commit_vision`, `summarize`, `get_report`,
492
492
  `build_index`, `index_status`, `query_index`, `start_batch_job`,
493
493
  `run_batch_job_slice`, `batch_job_status`, `list_batch_jobs`,
494
- `cancel_batch_job`, `resume_batch_job`, `review_timeline_markers`, and
495
- `cleanup_artifacts`.
494
+ `cancel_batch_job`, `resume_batch_job`, `review_timeline_markers`,
495
+ `cleanup_artifacts`, `db_status`, `db_ingest`, `get_panel_state`,
496
+ `set_panel_state`, `session_start_context`, `update_clip_field`,
497
+ `update_shot_field`, `get_field_history`, `revert_field`, and
498
+ `list_corrections`.
499
+
500
+ **DB-canonical analysis store (v2.41.0+).** The per-project SQLite DB
501
+ (`_soul/timeline_brain.sqlite`, schema v9+) is the source of truth for clip
502
+ analysis; `analysis.json` is a derived export written in lockstep. Analysis
503
+ runs write rows first (clips, shots, per-field subjective provenance,
504
+ transcript segments, sampled frames, QC observations) and then export the
505
+ JSON. Human corrections recorded via `update_clip_field` / `update_shot_field`
506
+ live as row-level provenance and always survive re-analysis. Readers
507
+ (panel API, exports) load DB-first and fall back to `analysis.json` for
508
+ reports that predate v9. `db_status` reports schema version + row counts;
509
+ `db_ingest` migrates an existing project's JSON reports (and
510
+ `corrections.json` sidecars) into the DB — run it once on older analysis
511
+ roots.
496
512
  The tool never installs
497
513
  dependencies and validates that outputs stay under
498
514
  `davinci-resolve-mcp-analysis` project roots rather than beside source media.
@@ -1,9 +1,9 @@
1
1
  # Local Control Panel
2
2
 
3
- The DaVinci Resolve MCP ships with a local, single-user browser control panel for
4
- inspecting Resolve state, running source-safe media analysis, drilling into
5
- analyzed clips and shots, fixing analysis output inline, and managing
6
- preferences.
3
+ The DaVinci Resolve MCP ships with a local, single-user browser control panel
4
+ for inspecting Resolve state, running source-safe media analysis, drilling into
5
+ analyzed clips and shots, fixing analysis output inline, reviewing timeline
6
+ edit history, driving Resolve's local AI operations, and managing preferences.
7
7
 
8
8
  The panel is a local HTTP server (default `http://127.0.0.1:8765`). It does not
9
9
  expose a network listener beyond loopback and does not modify source media.
@@ -21,8 +21,29 @@ npx davinci-resolve-mcp control-panel
21
21
  resolve_control(action="open_control_panel")
22
22
  ```
23
23
 
24
- Once running, you can `resolve_control(action="control_panel_status")` to check
25
- the pidfile or `resolve_control(action="close_control_panel")` to stop it.
24
+ Once running, `resolve_control(action="control_panel_status")` checks the
25
+ pidfile and `resolve_control(action="close_control_panel")` stops it.
26
+
27
+ ## Navigation and deep links
28
+
29
+ Top-level sections: **Overview**, **Media** (Inventory / Review / History),
30
+ **AI Console**, **Setup** (Resolve / MCP / Storage / Tools / Media Pool
31
+ History), **Docs**, and **Preferences** (Analysis / Caps + Safety / Metadata
32
+ and Markers / Paths and Workflow / MCP Updates). The project selector on the
33
+ right scopes the panel to an analysis context; **View All Projects** opens a
34
+ read-only browser over the Resolve project database with confirm-gated
35
+ loading.
36
+
37
+ Every view is addressable by URL hash, so links can be bookmarked or pasted
38
+ into chat:
39
+
40
+ ```
41
+ #overview #aiconsole
42
+ #analysis/media #analysis/review
43
+ #analysis/review/history #analysis/review/clip/<clip_id>
44
+ #analysis/review/clip/<clip_id>/shot/<n> #analysis/review/clip/<clip_id>/transcript
45
+ #diagnostics/mcp #preferences/caps
46
+ ```
26
47
 
27
48
  ## Tour
28
49
 
@@ -30,78 +51,125 @@ the pidfile or `resolve_control(action="close_control_panel")` to stop it.
30
51
 
31
52
  ![Control panel overview](../images/control-panel/01-overview.png)
32
53
 
33
- The Overview tab is the at-a-glance summary for the active project: Resolve
34
- connection state, source clip counts, analysis progress, search index size,
35
- and the source-media safety status.
54
+ The at-a-glance summary for the active project: Resolve connection state,
55
+ source clip counts, analysis progress, search-index status, and the
56
+ source-media safety posture. When the project has no source clips yet, the
57
+ view shows a get-started card with a suggested chat prompt and a
58
+ **Copy prompt** button — the panel is summoned by conversation, and its empty
59
+ states point back into it.
36
60
 
37
- ### AnalysisAnalyze
61
+ ### MediaInventory
38
62
 
39
- ![Analyze view](../images/control-panel/06-analyze.png)
63
+ ![Inventory view](../images/control-panel/06-inventory.png)
40
64
 
41
- The Analyze view inventories the Resolve media pool read-only and lets you
42
- queue source-safe analysis jobs. Filters cover bin, media type, clip status,
43
- and analysis status. Resolve media stays read-only; analysis outputs land under
44
- the configured analysis root.
65
+ Inventory walks the Resolve Media Pool read-only and filters to source clips so
66
+ timelines, compounds, titles, and generated items stay out of analysis queues.
67
+ Filters cover bin, media type, clip status, and analysis status, with optional
68
+ auto-refresh polling. From here you can select analyzable clips, copy a
69
+ ready-made analysis prompt for your chat session, or launch a supported chat
70
+ client directly.
45
71
 
46
- ### Analysis → Review (bin grid)
72
+ ### Media → Review
47
73
 
48
74
  ![Review bin grid](../images/control-panel/02-review-bin-grid.png)
49
75
 
50
- The Review surface is the browser for analyzed clips. Each card shows a
51
- representative thumbnail, summary one-liner, primary use, shot count, and tags.
52
- Click into a card to drill down.
76
+ Review is the browser for analyzed clips. The readiness strip summarizes the
77
+ evidence base (analyzed / superseded / vision-pending / warnings) with
78
+ humanized source-trust and analysis-layer chips. Cards show a representative
79
+ thumbnail, duration, shot count, status, and summary one-liner; grid and list
80
+ layouts are available, and full-text search covers clips, summaries, tags, and
81
+ transcripts once the search index is built.
53
82
 
54
83
  ### Clip detail
55
84
 
56
85
  ![Clip detail with shot strip](../images/control-panel/03-clip-detail.png)
57
86
 
58
- The clip view shows the full summary, tags, editorial notes, and a strip of
59
- every detected shot with thumbnails. From here you can open the clip in
60
- Resolve, jump to the transcript, or click into any shot for the full V2 field
61
- breakdown.
87
+ The clip view shows the full summary, tags, star rating, editorial notes, and
88
+ a contact sheet of every detected shot. From here you can open the clip in
89
+ Resolve, jump to the transcript, or click into any shot.
62
90
 
63
91
  ### Transcript
64
92
 
65
93
  ![Transcript view with word-level timing](../images/control-panel/04-transcript.png)
66
94
 
67
- The transcript view is reachable from the clip detail (top-right `Transcript`
68
- button). Each segment shows its start timecode, the cleaned sentence, and the
69
- word-level transcription beneath. The segment filter narrows by text; clicking
70
- a segment can jump the Resolve playhead to that source time. `Re-transcribe
71
- with words` re-runs the configured local transcription backend (Whisper by
72
- default) without re-doing visual analysis, and `corrections.json` captures
73
- inline edits so they survive future re-runs.
95
+ Each segment shows its start time, the cleaned sentence, and the original
96
+ machine transcription beneath. Inline edits are preserved across re-analysis,
97
+ and `Re-transcribe with words` re-runs the configured local transcription
98
+ backend (Whisper by default) without re-doing visual analysis.
74
99
 
75
100
  ### Shot detail with inline correction
76
101
 
77
- ![Shot detail with V2 fields and frames](../images/control-panel/05-shot-detail.png)
102
+ ![Shot detail with fields and frames](../images/control-panel/05-shot-detail.png)
78
103
 
79
- The shot view shows every V2 schema field (shot size, framing, camera height,
80
- motion direction, lens character, lighting, composition notes, and more)
81
- alongside the frames the vision pass actually saw. Each subjective field is
82
- editable inline edits land in `corrections.json` with an append-only
83
- changelog and are merged on top of the next re-analysis so human notes survive
84
- fresh vision runs. `Open in Resolve` bounces you straight to the clip in the
85
- source viewer with the shot's mark in/out set.
104
+ The shot view shows the analysis fields for the shot alongside the frames the
105
+ vision pass actually sampled, each labeled with why it was chosen ("Shot
106
+ start", "After cut", "Flash frame", "Motion peak"). Subjective fields are
107
+ editable inline; edits are kept with the clip's analysis and merged on top of
108
+ future re-analysis so human notes survive fresh vision runs. `Open in Resolve`
109
+ jumps straight to the clip in the source viewer with the shot's mark in/out
110
+ set.
86
111
 
87
- ### SetupResolve diagnostic
112
+ ### MediaHistory
88
113
 
89
- ![Setup / Resolve diagnostic](../images/control-panel/07-diagnostics-resolve.png)
114
+ ![Timeline history](../images/control-panel/10-history.png)
90
115
 
91
- The Resolve diagnostic page reports the live connection, product/version,
92
- active page, project identity, and media pool inventory. Useful when chat
93
- sessions need to confirm Resolve is reachable before issuing scripted
94
- operations.
116
+ The timeline edit history: archived timeline versions and brain edits per
117
+ timeline. Begin/end labeled runs to group related edits, archive the current
118
+ timeline before risky work, and select any timeline to inspect its version
119
+ chain and the edits between versions. Every destructive timeline edit made
120
+ through the MCP archives a version here automatically.
121
+
122
+ ### AI Console
123
+
124
+ ![AI Console with governance](../images/control-panel/09-ai-console.png)
125
+
126
+ Runs Resolve's local AI operations (audio classification, IntelliSearch, slate
127
+ analysis, transcription, motion deblur, speech generation) on the current
128
+ Media Pool folder or a specific clip. Capability dots show what this Resolve
129
+ build exposes and which AI Extras are required. The **Governance** card sets
130
+ the per-session tier (off / lenient / standard / strict) for the two
131
+ media-creating ops and the governance **mode**: *Advisory* warns in the
132
+ confirm dialog; *Enforce* refuses an over-tier run until you raise the tier,
133
+ relax the mode, or consciously override. Usage gauges track runs, media
134
+ created, and render time against the active tier, and the AI-ops ledger
135
+ (Preferences → Caps + Safety) records every run with the acting instance.
136
+
137
+ ### Setup
138
+
139
+ ![Setup / Resolve diagnostic](../images/control-panel/07-diagnostics-resolve.png)
95
140
 
96
- ### Preferences Analysis
141
+ - **Resolve** live connection, product/version, active page, project
142
+ identity, and Media Pool inventory.
143
+ - **MCP** — server identity, detected Resolve scripting paths, transport
144
+ status (with a start button for the networked mode), and per-client install
145
+ status with one-click install/repair for every supported harness.
146
+ - **Storage** — the analysis root, search index, and jobs database paths and
147
+ sizes for the active project.
148
+ - **Tools** — runtime helpers (ffprobe, ffmpeg, Whisper backends, OpenCV)
149
+ with ready/missing status and copyable install commands.
150
+ - **Media Pool History** — the provenance log for destructive media-pool
151
+ operations (deletes, replaces, relinks), kept separate from timeline brain
152
+ edits.
153
+
154
+ ### Preferences
97
155
 
98
156
  ![Preferences / Analysis defaults](../images/control-panel/08-preferences-analysis.png)
99
157
 
100
- Server-wide analysis defaults: vision on/off, transcription default, slate
101
- detection, source-trust grading (`auto` / `filename` / `low` / `medium` /
102
- `high`), default analysis depth, default sample frame count, persistence
103
- behavior, summary style, and report format. These are the defaults the
104
- `analyze_media` prompt uses when not overridden per call.
158
+ Server-wide defaults for this MCP install (dashboard-only conveniences stay in
159
+ the browser):
160
+
161
+ - **Analysis** vision/transcription defaults, slate detection, source-trust
162
+ grading, depth, frame-sampling mode, summary style, report format.
163
+ - **Caps + Safety** — token and frame budget presets (minimal / standard /
164
+ generous / unlimited) with live usage gauges, per-clip/job/day caps, the
165
+ Resolve AI-ops ledger, and destructive-edit safety rails.
166
+ - **Metadata and Markers** — Resolve metadata write-back fields, timed-marker
167
+ defaults, marker types and colors, overwrite policy.
168
+ - **Paths and Workflow** — preferred analysis root, generated-media folder,
169
+ post-operation page behavior, and a read-only map of where files live.
170
+ - **MCP Updates** — update policy (prompt / notify / auto / never), release
171
+ channel (stable / beta / dev), check cadence, apply/rollback with update
172
+ history.
105
173
 
106
174
  ## Chat ↔ panel state sharing
107
175
 
@@ -115,8 +183,9 @@ active project's analysis root). MCP actions:
115
183
  - `media_analysis(action="session_start_context")` — bootstrap a chat session
116
184
  with the panel's current focus
117
185
 
118
- The dashboard polls `/api/panel_state` every 2 seconds, so changes from chat
119
- appear in the UI without a refresh.
186
+ The dashboard polls `/api/panel_state` every 2 seconds while it has focus,
187
+ backs off to roughly every 10 seconds when the window is unfocused, and stops
188
+ polling entirely while the tab is hidden.
120
189
 
121
190
  ## Open in Resolve
122
191
 
@@ -30,6 +30,11 @@ Every release bump must update all version surfaces:
30
30
  - README current stats or latest-release summary when they changed
31
31
  - `CHANGELOG.md` latest release entry
32
32
  - `docs/SKILL.md` when tool discovery, examples, or behavior changed
33
+ - `docs/guides/control-panel.md` when the control panel UI changed, plus
34
+ regenerated screenshots: start the panel against a project with analysis
35
+ data, then run `venv/bin/python scripts/regen_panel_screenshots.py`.
36
+ The `tests.test_panel_docs_drift` guard fails the suite (and the publish
37
+ workflow) when the guide drifts from the panel's navigation or screenshots.
33
38
  - Git tag, e.g. `v2.4.1`
34
39
  - GitHub Release notes
35
40
 
@@ -43,12 +48,18 @@ Always run static checks before release:
43
48
  ```bash
44
49
  venv/bin/python tests/test_import.py
45
50
  venv/bin/python scripts/audit_api_parity.py
51
+ venv/bin/python -m unittest tests.test_static_undefined_names tests.test_action_list_drift tests.test_panel_docs_drift
46
52
  node bin/davinci-resolve-mcp.mjs --help
47
53
  node bin/davinci-resolve-mcp.mjs --version
48
54
  npm pack --dry-run
49
55
  git diff --check
50
56
  ```
51
57
 
58
+ The three drift guards (undefined names in `src/`, tool action lists vs
59
+ dispatch, control-panel guide vs panel navigation/screenshots) also run in the
60
+ `Publish npm package` workflow on every `v*` tag, so a stale doc or drifted
61
+ action list fails the publish — fix the drift rather than bypassing the gate.
62
+
52
63
  Run focused unit tests for the changed surface. For recent timeline/marker
53
64
  helpers, this usually includes:
54
65
 
package/install.py CHANGED
@@ -35,7 +35,7 @@ from src.utils.update_check import (
35
35
 
36
36
  # ─── Version ──────────────────────────────────────────────────────────────────
37
37
 
38
- VERSION = "2.39.0"
38
+ VERSION = "2.41.0"
39
39
  # Only hard floor: mcp[cli] requires Python 3.10+. There is no upper bound —
40
40
  # Resolve's scripting bridge loads into newer interpreters on recent builds
41
41
  # (Python 3.14 verified against Resolve Studio 20.3.2). Older Resolve builds
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "davinci-resolve-mcp",
3
- "version": "2.39.0",
3
+ "version": "2.41.0",
4
4
  "description": "NPM bootstrapper for the DaVinci Resolve MCP Server.",
5
5
  "license": "MIT",
6
6
  "author": "Samuel Gursky <samgursky@gmail.com>",
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/env python3
2
+ """Regenerate the control-panel doc screenshots from a live panel.
3
+
4
+ Drives the panel's hash deep links headlessly and writes 1280x800 captures
5
+ into docs/images/control-panel/. Run whenever the panel UI changes visibly.
6
+
7
+ Setup:
8
+ 1. venv/bin/pip install playwright (chromium is fetched on first run if absent)
9
+ 2. Start the panel bound to a project that HAS an analyzed clip, e.g.:
10
+ venv/bin/python -m src.control_panel --no-open
11
+ (Bind to a rich analysis context if the live Resolve project is empty.)
12
+ 3. venv/bin/python scripts/regen_panel_screenshots.py [--clip-id <id>] [--port 8765]
13
+
14
+ The clip id defaults to the first analyzed clip reported by /api/clips.
15
+ """
16
+ from __future__ import annotations
17
+
18
+ import argparse
19
+ import json
20
+ import pathlib
21
+ import sys
22
+ import urllib.request
23
+
24
+ OUT_DIR = pathlib.Path(__file__).resolve().parent.parent / "docs" / "images" / "control-panel"
25
+
26
+
27
+ def first_clip_id(base: str) -> str:
28
+ with urllib.request.urlopen(f"{base}/api/clips", timeout=10) as resp:
29
+ payload = json.load(resp)
30
+ clips = payload.get("clips") or []
31
+ if not clips:
32
+ sys.exit("No analyzed clips in the panel's project — bind the panel to a project with analysis data first.")
33
+ return clips[0]["clip_id"]
34
+
35
+
36
+ def main() -> int:
37
+ parser = argparse.ArgumentParser(description=__doc__)
38
+ parser.add_argument("--port", type=int, default=8765)
39
+ parser.add_argument("--clip-id", default=None)
40
+ args = parser.parse_args()
41
+ base = f"http://127.0.0.1:{args.port}"
42
+
43
+ try:
44
+ from playwright.sync_api import sync_playwright
45
+ except ImportError:
46
+ sys.exit("playwright not installed: venv/bin/pip install playwright")
47
+
48
+ clip = args.clip_id or first_clip_id(base)
49
+ shots = [
50
+ ("#overview", "01-overview.png", 3000),
51
+ ("#analysis/review", "02-review-bin-grid.png", 3500),
52
+ (f"#analysis/review/clip/{clip}", "03-clip-detail.png", 4000),
53
+ (f"#analysis/review/clip/{clip}/transcript", "04-transcript.png", 4000),
54
+ (f"#analysis/review/clip/{clip}/shot/3", "05-shot-detail.png", 4500),
55
+ ("#analysis/media", "06-inventory.png", 3500),
56
+ ("#diagnostics/resolve", "07-diagnostics-resolve.png", 3000),
57
+ ("#preferences/analysis", "08-preferences-analysis.png", 3000),
58
+ ("#aiconsole", "09-ai-console.png", 3000),
59
+ ("#analysis/review/history", "10-history.png", 3500),
60
+ ]
61
+ with sync_playwright() as pw:
62
+ browser = pw.chromium.launch()
63
+ page = browser.new_page(viewport={"width": 1280, "height": 800})
64
+ for hash_, name, wait in shots:
65
+ page.goto(f"{base}/{hash_}")
66
+ page.wait_for_timeout(wait)
67
+ page.screenshot(path=str(OUT_DIR / name))
68
+ print("shot:", name)
69
+ browser.close()
70
+ print(f"done — {len(shots)} screenshots in {OUT_DIR}")
71
+ return 0
72
+
73
+
74
+ if __name__ == "__main__":
75
+ raise SystemExit(main())