davinci-resolve-mcp 4.5.1 → 4.6.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 +164 -0
- package/README.md +1 -1
- package/README.zh-CN.md +2 -2
- package/docs/SKILL.md +16 -2
- package/docs/kernels/color-grade-kernel.md +3 -0
- package/docs/reference/api-coverage.md +1 -1
- package/docs/reference/api-limitations.md +9 -1
- package/docs/reference/project-archive.md +81 -0
- package/install.py +1 -1
- package/package.json +1 -1
- package/resolve-advanced/README.md +10 -0
- package/resolve-advanced/server/drp-node-graph-relayout.mjs +567 -0
- package/resolve-advanced/server/tools/drp.mjs +47 -2
- package/resolve-advanced/server/tools/drx.mjs +6 -2
- package/resolve-advanced/server/tools/project_db.mjs +3 -1
- package/resolve-advanced/vendor/drx-codec/node-layout.js +214 -13
- package/src/granular/common.py +1 -1
- package/src/granular/project.py +31 -9
- package/src/server.py +26 -7
- package/src/utils/api_truth.py +43 -0
- package/src/utils/archive_guard.py +97 -0
- package/src/utils/destructive_hook.py +93 -13
- package/src/utils/execution_lifecycle.py +6 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,170 @@
|
|
|
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 v4.6.0 — a default project archive no longer crashes Resolve 21.1
|
|
6
|
+
|
|
7
|
+
The archive coverage measured by @legionsound in [#233](https://github.com/samuelgursky/davinci-resolve-mcp/pull/233), plus the offline node-graph relayout work that was sitting unreleased on `main`.
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **`project_manager` `archive` defaulted source media and render cache ON, and on
|
|
12
|
+
Resolve 21.1.0.14 a default call crashes Resolve.** ([#233](https://github.com/samuelgursky/davinci-resolve-mcp/pull/233), @legionsound)
|
|
13
|
+
Measured on Studio 21.1.0.14, one isolated call per case on a disposable project:
|
|
14
|
+
with source media and proxies off, `ProjectManager.ArchiveProject` returns `False`
|
|
15
|
+
instantly and writes nothing, open or closed, render cache on or off. With either
|
|
16
|
+
`isArchiveSrcMedia` or `isArchiveProxyMedia` on it creates an empty directory at the
|
|
17
|
+
target and Resolve SIGSEGVs in the same second (4 of 4; flags-off 0 of 5), the call
|
|
18
|
+
comes back through the bridge as `None`, and unsaved work in the open project is
|
|
19
|
+
lost. A file or folder already at the target survived every case byte for byte, so
|
|
20
|
+
the destination is never the casualty. 19.1.3.7 agrees where it was measured: this
|
|
21
|
+
repo's 2026-08-02 mode matrix recorded `False` for `.dra` and folder paths with
|
|
22
|
+
every flag off. **No scriptable call on either build has produced an archive.**
|
|
23
|
+
- New `src/utils/archive_guard.py`, shared by the compound `archive`,
|
|
24
|
+
`safe_project_archive`, and the granular `archive_project`: every flag defaults
|
|
25
|
+
off; only real booleans are accepted (`bool("false")` is `True`, and two of these
|
|
26
|
+
flags crash); `src_media` and `proxy_media` are refused unless
|
|
27
|
+
`acknowledge_trap=true`; the result reports the native return as observed —
|
|
28
|
+
`True`, `False` ("wrote nothing"), or `None` ("likely crashed; check Resolve is
|
|
29
|
+
running") — instead of a bare bool.
|
|
30
|
+
- `safe_project_archive` keeps `allow_media_archive` as the size guard and now also
|
|
31
|
+
needs `acknowledge_trap` for the crashing flags. One guards size, the other the
|
|
32
|
+
crash.
|
|
33
|
+
- Granular `archive_project` now carries the `DESTRUCTIVE_TOOL` annotation and
|
|
34
|
+
`@granular_destructive_op()`. It previously fell through to plain `WRITE_TOOL`
|
|
35
|
+
(`archive_` is in no verb table) with no gate.
|
|
36
|
+
- Both compound actions are in the `destructive_hook` registry and rated MEDIUM in
|
|
37
|
+
the risk sets, off the ratchet backlog; `safe_project_archive` honours `dry_run`
|
|
38
|
+
natively and is in `NATIVE_DRY_RUN_ACTIONS`. The refusal is at parameter level
|
|
39
|
+
rather than a symbol-level `destroys_prior_work`, because a flags-off call is a
|
|
40
|
+
harmless no-op that should not need acknowledgement.
|
|
41
|
+
- `api_truth`: new `ProjectManager.ArchiveProject` entry (`verified_on:
|
|
42
|
+
21.1.0.14`, tagged `reported` — the contributor's measurement) and
|
|
43
|
+
`ACTION_SYMBOLS` for both actions, so the fact rides on every result as a
|
|
44
|
+
`known_limitation`. `docs/reference/api-coverage.md` row corrected from "API
|
|
45
|
+
accepts; archiving is slow"; evidence in `docs/reference/project-archive.md` and
|
|
46
|
+
`docs/reference/evidence/project-archive-21.1.json`.
|
|
47
|
+
- Not measured: `RestoreProject` round-trip (nothing produced an archive to
|
|
48
|
+
restore), render cache on a project that has one, headless 21.1, builds after
|
|
49
|
+
21.1.0.14, Windows/Linux, and whether the media flags crash 19.1.3.7 — this
|
|
50
|
+
machine's Resolve was left alone rather than risked on it.
|
|
51
|
+
|
|
52
|
+
### Added
|
|
53
|
+
|
|
54
|
+
- **`drp` `relayout_node_graphs` — whole-project Cleanup Node Graph on an exported `.drp`.**
|
|
55
|
+
The UI command has no scripting API and the two existing paths each had a gap: `drx`
|
|
56
|
+
`relayout` is one clip at a time, and `project_db` `relayout_node_graphs` needs the
|
|
57
|
+
project closed plus a full Resolve quit. This one runs on `ExportProject` output and
|
|
58
|
+
round-trips through `ImportProject` as a sibling, so the open project is never touched.
|
|
59
|
+
- Indexes **every** node graph the export carries: every LOCAL version of every timeline
|
|
60
|
+
clip (a clip with three versions is three graphs — the earlier `extract_node_graphs` /
|
|
61
|
+
`inject_grades` only ever saw the first `<Body>`), remote versions on media-pool clips,
|
|
62
|
+
group pre/post graphs (`project.xml`), timeline-level graphs.
|
|
63
|
+
- Scope by anything the `.drp` can name: timeline globs, video track, clip id, clip name /
|
|
64
|
+
media globs, absolute frame range (overlap), clip position range, color group (a group
|
|
65
|
+
= its clips + its own graphs), graph kind, active-only / version-name globs, node count,
|
|
66
|
+
node label. Several scopes union. Unknown selectors are refused (zod `.strict()`), never
|
|
67
|
+
ignored — an ignored selector would silently widen the sweep to the whole project.
|
|
68
|
+
- Byte-preserving: only the position varints move (node-layout.js); `HasCorrection` is
|
|
69
|
+
left exactly as found (this is not a grade change). Resolve's 0x80 **STORED** body
|
|
70
|
+
container (how it serialises small/default graphs in exports) is now decoded and
|
|
71
|
+
re-wrapped in kind — before this every ungraded clip read as "not a grade Body".
|
|
72
|
+
- The default body carries no node message at all (Resolve creates the node lazily in
|
|
73
|
+
the UI): reported as `empty`, not skipped, not an error. Measured on a 737-clip
|
|
74
|
+
export: 746 empty, 3 real graphs, 0 skipped.
|
|
75
|
+
- Write path re-indexes the written file from scratch: same graph count, every rewritten
|
|
76
|
+
body at the target positions with its node count intact, every untouched body
|
|
77
|
+
byte-identical, every flag unchanged — a failed read-back deletes the output and throws.
|
|
78
|
+
- Tests: `test/drp-node-graph-relayout.test.mjs` — fixture in the measured Resolve 19.1.3
|
|
79
|
+
shapes with a two-version clip, a remote version, group graphs, an empty default, an
|
|
80
|
+
undecodable body; the full scope matrix incl. a null control; write + idempotence +
|
|
81
|
+
layout tuning.
|
|
82
|
+
- **Topology-aware relayout (node-layout.js, all three relayout paths).** Positions now
|
|
83
|
+
come from the graph's own wiring: F8 connection messages (from/to node id, ports —
|
|
84
|
+
64 = RGB path, 16 = key link — input slot, creation order) and the F9/F10 markers
|
|
85
|
+
naming the chain's first and last node. Nodes are ranked by longest RGB path (x), a
|
|
86
|
+
fan-out stacks its branches into lanes ordered by the mixer input slot they feed (y),
|
|
87
|
+
a merge returns to its lowest input lane, key links never move a node, and an
|
|
88
|
+
unrankable graph (cycle) falls back to a row in index order and says so in `meta`.
|
|
89
|
+
- Fixes a latent bug in the row layout: it placed nodes in LIST order, and Resolve
|
|
90
|
+
lists nodes by id, so any chain with a node inserted mid-way (every traced KICK
|
|
91
|
+
graph: index order 4,3,6,5,2,7,8,9,1) was laid out scrambled. A chain now lands on
|
|
92
|
+
the measured row in chain order; generator chains are byte-identical to before.
|
|
93
|
+
- New options everywhere (`drx` `relayout`, `drp` `relayout_node_graphs`,
|
|
94
|
+
`project_db` `relayout_node_graphs`): `spacingY` (lane pitch, default 178 —
|
|
95
|
+
Resolve's own vertical placement grid seen in every stacked export) and
|
|
96
|
+
`layout`/`mode` = `topology` (default) | `row` (the old behaviour). Every result
|
|
97
|
+
carries `layout: {kind: chain|dag|row, ranks, lanes, keyLinks}`; the sweep report
|
|
98
|
+
counts `stackedGraphs`.
|
|
99
|
+
- NOT yet measured: native Cleanup Node Graph on a graph with a parallel/layer
|
|
100
|
+
mixer (the lane pitch and how Resolve orders branches). The x row is measured;
|
|
101
|
+
the lane pitch is a documented default until a mixer graph is cleaned natively
|
|
102
|
+
and read back. `test/node-layout-topology.test.mjs` pins the planner on rewired
|
|
103
|
+
real bodies (fan-out + merge, slot order, key link, cycle, row mode, single node).
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
## What's New in v4.5.2 — granular safety stops guessing, and the audit log stops lying
|
|
107
|
+
|
|
108
|
+
Two findings from a review of the v4.5.0 enforcement hook, both measured before and
|
|
109
|
+
after rather than reasoned about.
|
|
110
|
+
|
|
111
|
+
### Fixed
|
|
112
|
+
|
|
113
|
+
- **The verb table was the sole authority on risk, and it disagreed with the
|
|
114
|
+
compound server on 20 tools.** The same operation is exposed on both servers under
|
|
115
|
+
the same action name, and the compound tables are where someone actually assessed
|
|
116
|
+
it. Three granular tools were rated **below** their compound assessment — the
|
|
117
|
+
direction that matters, because safe mode then lets them through:
|
|
118
|
+
|
|
119
|
+
| tool | was | now |
|
|
120
|
+
|---|---|---|
|
|
121
|
+
| `ti_copy_grades` | medium | **high** |
|
|
122
|
+
| `timeline_delete_clips` | high | **critical** |
|
|
123
|
+
| `timeline_detect_scene_cuts` | medium | **high** |
|
|
124
|
+
|
|
125
|
+
`ti_copy_grades` is the tool this entire effort began with — the one that reaches
|
|
126
|
+
`TimelineItem.CopyGrades` and replaces a node graph with no recovery version. The
|
|
127
|
+
verb table called it MEDIUM, because `copy` appears in no table, so safe mode did
|
|
128
|
+
not stop it.
|
|
129
|
+
|
|
130
|
+
The other seventeen were rated **above** their compound assessment: `clear_*` and
|
|
131
|
+
`set_*` tools called HIGH here while compound rates them LOW. That is not the safe
|
|
132
|
+
direction either — `_safe_mode_allows` documents why at length. A gate that refuses
|
|
133
|
+
work the compound server considers low-risk teaches people to switch safe mode off,
|
|
134
|
+
and a setting left off protects nothing.
|
|
135
|
+
|
|
136
|
+
Rating order is now: a symbol the ledger marks `destroys_prior_work`, then the
|
|
137
|
+
compound server's established rating for the same action name, then the verb.
|
|
138
|
+
Most-severe-wins where two compound tools rate one name differently, because a gate
|
|
139
|
+
should resolve ambiguity by refusing more rather than less.
|
|
140
|
+
|
|
141
|
+
- **The audit log misreported two of its three outcomes.** It is this surface's only
|
|
142
|
+
record of what ran — there is no archive behind it — so a row that overstates, or
|
|
143
|
+
is simply absent, is the whole artifact failing:
|
|
144
|
+
|
|
145
|
+
- a first call that only minted a confirm token was recorded `allowed`, claiming a
|
|
146
|
+
mutation that had not happened. It is now `pending_confirmation`, matching what
|
|
147
|
+
the compound hook records for the same case.
|
|
148
|
+
- **an exception wrote no row at all**, so the log went silent exactly when
|
|
149
|
+
something broke. It is now `failed`, carrying the exception type. The hook is a
|
|
150
|
+
witness, not a handler: the exception is re-raised untouched.
|
|
151
|
+
|
|
152
|
+
### Changed
|
|
153
|
+
|
|
154
|
+
- Four existing tests asserted the old verb ratings. `ti_clear_flags` is LOW now
|
|
155
|
+
because compound rates `clear_flags` LOW — the change working, not a regression —
|
|
156
|
+
so the two behavioural tests moved to `ti_delete_version` as their HIGH exemplar,
|
|
157
|
+
and namespace stripping is asserted directly rather than through a rating that may
|
|
158
|
+
now come from the compound tables.
|
|
159
|
+
|
|
160
|
+
### Validation
|
|
161
|
+
|
|
162
|
+
- Full offline suite: **3,693 passed, 1 skipped, 0 failed**, 1,419 subtests.
|
|
163
|
+
- Each fix was reverted in turn to confirm its guard fails rather than passing
|
|
164
|
+
vacuously — 9, 1 and 1 failures respectively, all green on restore.
|
|
165
|
+
- A guard now walks every decorated tool and fails if any is rated *below* the
|
|
166
|
+
compound server's established assessment, so the class cannot return one tool at a
|
|
167
|
+
time. A trap-symbol tool may still be raised above it.
|
|
168
|
+
|
|
5
169
|
## What's New in v4.5.1 — the safe-mode refusal reaches the caller on 27 more tools
|
|
6
170
|
|
|
7
171
|
v4.5.0 gave the granular server a working safe-mode gate. On 27 tools it then threw
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
English | [简体中文](README.zh-CN.md)
|
|
4
4
|
|
|
5
|
-
[](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
|
|
6
6
|
[](https://www.npmjs.com/package/davinci-resolve-mcp)
|
|
7
7
|
[](docs/reference/api-coverage.md)
|
|
8
8
|
[-blue.svg)](#server-modes)
|
package/README.zh-CN.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[English](README.md) | 简体中文
|
|
4
4
|
|
|
5
|
-
[](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
|
|
6
6
|
[](https://www.npmjs.com/package/davinci-resolve-mcp)
|
|
7
7
|
[](docs/reference/api-coverage.md)
|
|
8
8
|
[-blue.svg)](#服务器模式)
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
[](https://www.python.org/downloads/)
|
|
13
13
|
[](https://opensource.org/licenses/MIT)
|
|
14
14
|
|
|
15
|
-
> 本翻译对应 v4.
|
|
15
|
+
> 本翻译对应 v4.6.0 版 README。如与英文原版有出入,以 [英文原版](README.md) 为准。
|
|
16
16
|
|
|
17
17
|
一个 Model Context Protocol (MCP) 服务器,让 AI 助手通过官方脚本 API 控制 DaVinci Resolve Studio(达芬奇)。它提供完整的 API 覆盖,外加带护栏的工作流助手,涵盖剪辑、媒体池整理、渲染设置、审阅标记、调色、Fusion、Fairlight、项目生命周期任务、扩展开发,以及不碰源媒体的媒体分析。
|
|
18
18
|
|
package/docs/SKILL.md
CHANGED
|
@@ -403,7 +403,17 @@ Operating rules an agent must know:
|
|
|
403
403
|
has no API). Single clip, live: `gallery_stills.grab_and_export` → advanced
|
|
404
404
|
`drx(action="relayout")` → `graph.reset_all_grades` → `safe_apply_drx` with
|
|
405
405
|
EXPLICIT item indices (the reset is required — a same-structure apply keeps
|
|
406
|
-
the old layout). Whole project, offline: `project_db(action="relayout_node_graphs")
|
|
406
|
+
the old layout). Whole project, offline: `project_db(action="relayout_node_graphs")`
|
|
407
|
+
(closed project + quit/relaunch). Whole project or ANY SUBSET **without closing it**:
|
|
408
|
+
`project_manager.export_project` → advanced `drp(action="relayout_node_graphs")`
|
|
409
|
+
(scope by timeline/track/clip id/name/media/frames/clip position/group/version/node
|
|
410
|
+
count/node label; covers every LOCAL version of every clip, remote versions, group
|
|
411
|
+
pre/post and timeline graphs; dry-run first, read-back verified on write) →
|
|
412
|
+
`project_manager.import_project` as a sibling `<name>_CLEANED`, then re-export THAT
|
|
413
|
+
and dry-run again to prove the effect. BPA's "Node Graph Cleanup" job is this loop.
|
|
414
|
+
Layout is topology-aware (rank by RGB wiring, branches stacked into lanes at
|
|
415
|
+
`spacingY`, key links untouched); the lane pitch is NOT measured against native
|
|
416
|
+
Cleanup on a mixer graph yet — the x row is.
|
|
407
417
|
- **project_db patches** require the project CLOSED in Resolve plus
|
|
408
418
|
`iConfirmProjectClosed:true`; every write auto-backs-up and read-back
|
|
409
419
|
verifies. Resolve caches open projects in memory: after patching, fully QUIT
|
|
@@ -820,7 +830,11 @@ lifecycle, settings, database, preset, and archive boundary helpers:
|
|
|
820
830
|
- `safe_project_create(name, media_location_path?, dry_run?)`
|
|
821
831
|
- `safe_project_export(name, path, with_stills_and_luts?, dry_run?)`
|
|
822
832
|
- `safe_project_import(path, name, dry_run?)`
|
|
823
|
-
- `safe_project_archive(name, path, src_media=false, render_cache=false, proxy_media=false, dry_run?)`
|
|
833
|
+
- `safe_project_archive(name, path, src_media=false, render_cache=false, proxy_media=false, allow_media_archive?, acknowledge_trap?, dry_run?)`
|
|
834
|
+
— `src_media` and `proxy_media` crash Resolve 21.1.0.14 (reported, #233); they are refused
|
|
835
|
+
unless `acknowledge_trap=true`, and every flag defaults off on `archive` too. No
|
|
836
|
+
scriptable call has produced an archive on 19.1.3.7 or 21.1.0.14; see
|
|
837
|
+
`docs/reference/project-archive.md`.
|
|
824
838
|
- `safe_project_restore(path, name, dry_run?)`
|
|
825
839
|
- `safe_project_delete(name, close_current?, dry_run?)`
|
|
826
840
|
- `safe_set_project_settings(settings, restore?, dry_run?)`
|
|
@@ -155,6 +155,9 @@ Cross-server rules an agent must know:
|
|
|
155
155
|
`drx(action="relayout")` → `graph.reset_all_grades` → `safe_apply_drx` with
|
|
156
156
|
explicit indices (a same-structure apply keeps the OLD layout — the reset is
|
|
157
157
|
required). Whole project, offline: `project_db(action="relayout_node_graphs")`.
|
|
158
|
+
Any scope, open project: export → `drp(action="relayout_node_graphs")` (every
|
|
159
|
+
version of every clip + group/timeline graphs; dry-run, read-back verify) →
|
|
160
|
+
import as a sibling → re-export the sibling and dry-run again.
|
|
158
161
|
- **Deps.** The grading catalog needs `sharp`; call the advanced `capabilities`
|
|
159
162
|
tool for live status and install hints.
|
|
160
163
|
|
|
@@ -251,7 +251,7 @@ Every method in the DaVinci Resolve Scripting API and its test status. Methods a
|
|
|
251
251
|
|
|
252
252
|
| # | Method | Status | Test Result / Notes |
|
|
253
253
|
|---|--------|--------|---------------------|
|
|
254
|
-
| 1 | `ArchiveProject(projectName, filePath, ...)` | ⚠️ |
|
|
254
|
+
| 1 | `ArchiveProject(projectName, filePath, ...)` | ⚠️ | Never produces an archive by script: returns `False` and writes nothing with source media and proxies off; either on crashes Resolve 21.1.0.14. Wrappers default every flag off and refuse the crashing two unless `acknowledge_trap`. See [project archive](project-archive.md). |
|
|
255
255
|
| 2 | `CreateProject(projectName, mediaLocationPath)` | ✅ | Creates new project; optional media location added in Resolve 20.2.2 |
|
|
256
256
|
| 3 | `DeleteProject(projectName)` | ⚠️ | Returns `False` if project is open |
|
|
257
257
|
| 4 | `LoadProject(projectName)` | ✅ | Returns Project object |
|
|
@@ -12,7 +12,7 @@ that none exists).
|
|
|
12
12
|
|
|
13
13
|
**Verified on:** DaVinci Resolve Studio 21.0.2
|
|
14
14
|
|
|
15
|
-
**Totals:** 41 missing capabilities,
|
|
15
|
+
**Totals:** 41 missing capabilities, 54 bugs / unreliable behaviors.
|
|
16
16
|
|
|
17
17
|
The authoritative source is the runtime-queryable `api_truth` ledger
|
|
18
18
|
(`resolve_control api_truth "<query>"`); this document is generated from
|
|
@@ -776,3 +776,11 @@ values, or automation-hostile modal prompts.
|
|
|
776
776
|
- **Behavior:** Gated on the Color page. Measured on all six pages: returns False from media, edit, fusion, fairlight and deliver, and True only from color. The refusal is a bare False with no reason. It does at least fail cleanly - no file is written on the failing pages, so there is no stale-file trap here.
|
|
777
777
|
- **Workaround / current handling:** resolve.OpenPage('color') before the call and restore the prior page afterwards. Treat a False as 'you were on the wrong page' before suspecting the path.
|
|
778
778
|
- **Tags:** page-gated, silent-failure, lut
|
|
779
|
+
|
|
780
|
+
### ProjectManager.ArchiveProject
|
|
781
|
+
|
|
782
|
+
- **Object:** `ProjectManager`
|
|
783
|
+
- **Signature:** `(projectName, filePath, isArchiveSrcMedia=True, isArchiveRenderCache=True, isArchiveProxyMedia=False) -> bool`
|
|
784
|
+
- **Behavior:** No scriptable call produces an archive. With source media and proxies off it returns False instantly and writes nothing, for an open or a closed project and with render cache on or off. With isArchiveSrcMedia or isArchiveProxyMedia on it creates an empty directory at the target and Resolve crashes (SIGSEGV) in the same second; the call comes back through the bridge as None and every later handle is dead. Four crashes, one of them in a Blackmagic Cloud library, share identical top stack frames in Fusion script-symbol teardown on the UI thread; a separate crash in the same session during DeleteProject/LoadProject had a different stack, so the signature belongs to the archive calls. Media-flag calls crashed 4 of 4, flags-off calls 0 of 5. A file already at the target survived every case byte for byte, including a crash, so the destination is never overwritten; unsaved work in the open project is what is lost. The native defaults turn source media on, so a default call crashes Resolve. Resolve logs nothing about the False returns.
|
|
785
|
+
- **Workaround / current handling:** Keep isArchiveSrcMedia and isArchiveProxyMedia off unless you have verified the build, and save every open project first. Treat False as 'nothing archived', not as a path problem: a .dra and a folder-style path fail identically. Archive from the Project Manager UI when you need a real archive.
|
|
786
|
+
- **Tags:** crash, unreliable-return, silent-failure, project, reported
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Project archive on Resolve 21.1
|
|
2
|
+
|
|
3
|
+
`ProjectManager.ArchiveProject` has no `api_truth` entry until now, and both of
|
|
4
|
+
this server's archive actions sat in the ratchet's unrated backlog. The native
|
|
5
|
+
defaults, `isArchiveSrcMedia=True` and `isArchiveRenderCache=True`, were
|
|
6
|
+
inherited by both the compound `project_manager archive` action and the granular
|
|
7
|
+
`archive_project` tool. On Studio 21.1.0.14 a default call crashes Resolve.
|
|
8
|
+
|
|
9
|
+
## Measured
|
|
10
|
+
|
|
11
|
+
Studio 21.1.0.14, local disk database, one disposable project with one synthetic
|
|
12
|
+
2-second clip on one timeline, archived by name. One isolated call per row, with
|
|
13
|
+
breadcrumbs written to disk before and after so a crash cannot hide which call
|
|
14
|
+
caused it. Presence was checked with `dir()` membership against a known-present
|
|
15
|
+
control, not `hasattr`, which Resolve answers True for any name.
|
|
16
|
+
|
|
17
|
+
| Case | Returns | Effect |
|
|
18
|
+
| --- | --- | --- |
|
|
19
|
+
| Open project, all flags off | `False` | nothing written, instantly |
|
|
20
|
+
| Closed project, all flags off | `False` | nothing written, instantly |
|
|
21
|
+
| Closed project, render cache only | `False` | nothing written, instantly |
|
|
22
|
+
| Closed project, source media | `None` | empty directory at the target, then Resolve crashes |
|
|
23
|
+
| Closed project, proxy media | `None` | empty directory at the target, then Resolve crashes |
|
|
24
|
+
| Unrelated file at target, flags off | `False` | file byte-identical |
|
|
25
|
+
| Populated directory at target, flags off | `False` | untouched |
|
|
26
|
+
| Source media onto an existing file | `None` | Resolve crashes; file byte-identical |
|
|
27
|
+
|
|
28
|
+
The crash is SIGSEGV, stamped the same second as the call. A crashing call comes
|
|
29
|
+
back through the bridge as `None`, not a bool, and every handle after it is dead.
|
|
30
|
+
Resolve logs nothing about the `False` returns.
|
|
31
|
+
|
|
32
|
+
**Media-flag calls crashed 4 of 4; flags-off calls crashed 0 of 5.** The four
|
|
33
|
+
crashes, one of them in a Blackmagic Cloud library on an earlier attempt, share
|
|
34
|
+
identical top stack frames in Fusion script-symbol teardown on the UI thread. A
|
|
35
|
+
fifth crash in the same session, during `DeleteProject` then `LoadProject` 44
|
|
36
|
+
seconds after a relaunch, has a different stack past the generic crash-handler
|
|
37
|
+
frames, so the archive signature is not a general instability. Crash uptimes were
|
|
38
|
+
1:45, 14:16, 1:31 and 0:45.
|
|
39
|
+
|
|
40
|
+
**The destination is never the casualty.** A file or folder already at the target
|
|
41
|
+
survived every case byte for byte, including the crash. Resolve cannot create its
|
|
42
|
+
bundle folder over an existing file and crashes anyway, so the crash is in starting
|
|
43
|
+
the archive job, not in writing the destination. What is lost is unsaved work in
|
|
44
|
+
the open project.
|
|
45
|
+
|
|
46
|
+
19.1.3.7 agrees on the part it measured: this repo's mode matrix (2026-08-02, GUI
|
|
47
|
+
and headless) and the project-lifecycle kernel recorded `False` for both a `.dra`
|
|
48
|
+
and a folder-style path with every flag off. No scriptable call on either build has
|
|
49
|
+
produced an archive.
|
|
50
|
+
|
|
51
|
+
## What changed
|
|
52
|
+
|
|
53
|
+
- `src/utils/archive_guard.py`: every flag defaults off; only real booleans are
|
|
54
|
+
accepted, since `bool("false")` is `True` and two flags crash; source media and
|
|
55
|
+
proxy media are refused unless `acknowledge_trap=true`; the result reports the
|
|
56
|
+
native return as observed (`True`, `False` with "wrote nothing", or `None` with
|
|
57
|
+
"likely crashed") rather than a bare bool.
|
|
58
|
+
- Compound `project_manager archive` and `safe_project_archive` use it.
|
|
59
|
+
`safe_project_archive` keeps its existing `allow_media_archive` size guard and
|
|
60
|
+
now also needs `acknowledge_trap` for the crashing flags: one guards size, the
|
|
61
|
+
other the crash.
|
|
62
|
+
- Granular `archive_project`: defaults off, `acknowledge_trap` for the crashing
|
|
63
|
+
flags, `DESTRUCTIVE_TOOL` annotation and `@granular_destructive_op()`.
|
|
64
|
+
- Both compound actions are registered, rated MEDIUM, and removed from the ratchet
|
|
65
|
+
backlog. `safe_project_archive` honours `dry_run` natively and is listed as such.
|
|
66
|
+
- `api_truth` gains a `ProjectManager.ArchiveProject` entry and `ACTION_SYMBOLS`
|
|
67
|
+
maps both compound actions to it, so the measured fact rides on every result.
|
|
68
|
+
|
|
69
|
+
## A design question for the maintainer
|
|
70
|
+
|
|
71
|
+
The entry deliberately does **not** set `destroys_prior_work`. That flag is
|
|
72
|
+
symbol-level, so every mapped action would refuse until acknowledged, including
|
|
73
|
+
the flags-off call, which is a harmless no-op. The refusal here is parameter-level
|
|
74
|
+
instead, in the wrappers. If the symbol-level trap is preferred, it is a one-line
|
|
75
|
+
change plus the granular confirmation tier.
|
|
76
|
+
|
|
77
|
+
## Not tested
|
|
78
|
+
|
|
79
|
+
No scriptable call produced an archive, so a `RestoreProject` round-trip could not
|
|
80
|
+
be measured. Also untested: render cache on a project that has one, headless mode
|
|
81
|
+
on 21.1, builds after 21.1.0.14, and Windows or Linux.
|
package/install.py
CHANGED
|
@@ -37,7 +37,7 @@ from src.utils.update_check import (
|
|
|
37
37
|
|
|
38
38
|
# ─── Version ──────────────────────────────────────────────────────────────────
|
|
39
39
|
|
|
40
|
-
VERSION = "4.
|
|
40
|
+
VERSION = "4.6.0"
|
|
41
41
|
# Only hard floor: mcp[cli] requires Python 3.10+. There is no upper bound —
|
|
42
42
|
# Resolve's scripting bridge loads into newer interpreters on recent builds
|
|
43
43
|
# (Python 3.14 verified against Resolve Studio 20.3.2). Older Resolve builds
|
package/package.json
CHANGED
|
@@ -79,6 +79,16 @@ Each dispatches on an `action`. Highlights:
|
|
|
79
79
|
- **`drx`** — per-clip grade (`.drx`) codec: `parse`, `generate`, `generate_from_request`,
|
|
80
80
|
`export_cdl`, `merge`, plus the **grading/QC catalog** below.
|
|
81
81
|
- **`drp` / `drt`** — project / timeline file authoring + editing + grade injection + structural diff.
|
|
82
|
+
`drp` `relayout_node_graphs` is the whole-project **Cleanup Node Graph** (the UI command has
|
|
83
|
+
no API): every node graph an exported `.drp` carries — every LOCAL version of every clip,
|
|
84
|
+
remote versions, group pre/post, timeline-level — relaid out to Resolve's clean row with the
|
|
85
|
+
grade bytes untouched and `HasCorrection` as found; scoped by timeline / track / clip id /
|
|
86
|
+
name / media / frame range / clip position / color group / version / node count / node
|
|
87
|
+
label; dry-run report, read-back verify. Round trip: `export_project` → `relayout_node_graphs`
|
|
88
|
+
→ `import_project` as a sibling. Single `.drx`: `drx` `relayout`; closed `Project.db`:
|
|
89
|
+
`project_db` `relayout_node_graphs`. All three are topology-aware: nodes rank by their
|
|
90
|
+
RGB wiring (chain order, not list order), branches stack into lanes, key links stay put;
|
|
91
|
+
`layout: row` is the old single-row mode.
|
|
82
92
|
- **`conform`** — offline conform/relink QC engine (frame-oracle math, not filename matching),
|
|
83
93
|
reverse-clip DB repair, sequence lineage store + diff, per-cut frame QC (sampled clear of
|
|
84
94
|
transition windows; ingests Resolve's own FCP7 export — `-1` edges → junctions, no ticks needed).
|