davinci-resolve-mcp 2.86.4 → 2.87.1
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/AGENTS.md +7 -1
- package/CHANGELOG.md +81 -0
- package/README.md +5 -5
- package/README.zh-CN.md +6 -6
- package/docs/SKILL.md +16 -1
- package/docs/reference/api-coverage.md +28 -6
- package/docs/reference/resolve_scripting_api.txt +21 -1
- package/install.py +1 -1
- package/package.json +1 -1
- package/src/granular/common.py +1 -1
- package/src/server.py +124 -6
package/AGENTS.md
CHANGED
|
@@ -73,7 +73,13 @@ semantics.
|
|
|
73
73
|
`docs/reference/api-limitations.md` — when you document a new Resolve API
|
|
74
74
|
limitation, add a `submit`-tagged entry to `src/utils/api_truth.py` and
|
|
75
75
|
regenerate with `scripts/gen_api_limitations.py` (a drift guard enforces it)
|
|
76
|
-
- Bundled Resolve API text: `docs/reference/resolve_scripting_api.txt`
|
|
76
|
+
- Bundled Resolve API text: `docs/reference/resolve_scripting_api.txt` — refresh
|
|
77
|
+
it by copying the shipped `Developer/Scripting/README.txt` over it wholesale,
|
|
78
|
+
never by hand-adding the lines you already know about. The file's only value
|
|
79
|
+
is that a diff against it is trustworthy; a hand-patch carries a newer
|
|
80
|
+
`Last Updated:` header while still hiding everything you did not know to look
|
|
81
|
+
for. That is exactly how it sat eight weeks stale at 26 May 2026 while ten
|
|
82
|
+
documented 21.0.4 methods went unwired (PR #139)
|
|
77
83
|
|
|
78
84
|
## Key Paths
|
|
79
85
|
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,87 @@
|
|
|
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.87.1
|
|
6
|
+
|
|
7
|
+
Follow-up evidence from @legionsound on PR #139, plus the process fix for the
|
|
8
|
+
tooling failure that PR exposed.
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- **`import_user_preferences_preset` now says what the preset ended up called.**
|
|
13
|
+
The no-name path was the one branch in v2.87.0 with nothing behind it — it
|
|
14
|
+
calls the single-argument binding, which would have been a `TypeError` rather
|
|
15
|
+
than a graceful failure if the binding wanted two positionals. Round-tripped
|
|
16
|
+
on Studio 21.0.4.5 (export → delete → import both ways): the single-arg form
|
|
17
|
+
returns `True`, and the imported preset is **named after the file**. The
|
|
18
|
+
answer now carries that, so a caller who passed no name knows to read the
|
|
19
|
+
name back with `list_user_preferences_presets` instead of guessing.
|
|
20
|
+
|
|
21
|
+
### Documentation
|
|
22
|
+
|
|
23
|
+
- **`api-coverage.md`: the import/export rows carry the round-trip**, not
|
|
24
|
+
`dir()` membership. Still 🔬 and still 23 untested of 361 — the counting
|
|
25
|
+
convention is unchanged, because none of this was executed here. What changed
|
|
26
|
+
is the strength of the contributor's evidence behind two of the rows.
|
|
27
|
+
- **`AGENTS.md` now states how the API snapshot must be refreshed:** copy the
|
|
28
|
+
shipped `Developer/Scripting/README.txt` over it wholesale, never hand-add the
|
|
29
|
+
lines you already know about. A hand-patch carries a newer `Last Updated:`
|
|
30
|
+
header while hiding everything you did not know to look for — which is
|
|
31
|
+
precisely how the file sat eight weeks stale at 26 May 2026 while ten
|
|
32
|
+
documented 21.0.4 methods went unwired. The v2.87.0 snapshot was verified as a
|
|
33
|
+
wholesale replace (md5 `d732b3f6c1da08dc516bc8f80c5acd92`, byte-identical to
|
|
34
|
+
the 21.0.4.5 shipped file per the contributor).
|
|
35
|
+
|
|
36
|
+
## What's New in v2.87.0
|
|
37
|
+
|
|
38
|
+
The ten Resolve 21.0.4 scripting surfaces that the 24 Jul 2026 README refresh
|
|
39
|
+
documented beyond the three wired in v2.86.0. Contributed by @legionsound
|
|
40
|
+
(PR #139), who reported them as issues #135–#138 from Studio 21.0.4.5.
|
|
41
|
+
|
|
42
|
+
### Added
|
|
43
|
+
|
|
44
|
+
- **`layout_presets` gains `list`** (`Resolve.GetLayoutPresetList`). The tool
|
|
45
|
+
had six actions and every one of them took a preset name the caller had no
|
|
46
|
+
way to enumerate.
|
|
47
|
+
- **`render_presets` gains `list_burnin` and `delete_burnin`**
|
|
48
|
+
(`Resolve.GetBurnInPresetList` / `DeleteBurnInPreset`). `list_burnin` is the
|
|
49
|
+
missing half of the burn-in surface: the `DataBurnIn` render setting and both
|
|
50
|
+
`load_burnin_preset` actions take a name nothing could list.
|
|
51
|
+
- **`resolve_control` gains the six `*_user_preferences_preset` actions** —
|
|
52
|
+
`list`, `save`, `load`, `delete`, `import`, `export`. Two caveats are carried
|
|
53
|
+
in the docstrings and answers rather than left to be discovered:
|
|
54
|
+
`load_user_preferences_preset` is **session-wide** (it swaps the user's global
|
|
55
|
+
Resolve preferences, not a project setting), and
|
|
56
|
+
`import_user_preferences_preset` answers with the README's own caveat that the
|
|
57
|
+
imported preset is *not* auto-loaded, so a caller follows with `load` instead
|
|
58
|
+
of stopping early.
|
|
59
|
+
- **`project_manager` gains `list_attributes`**
|
|
60
|
+
(`ProjectManager.GetProjectAttributesInCurrentFolder`) — `lastModifiedDate`,
|
|
61
|
+
`creationDate`, `notes`, and `liveCollaborationMode` per project in the
|
|
62
|
+
current folder, without loading any project.
|
|
63
|
+
|
|
64
|
+
All ten are `_requires_method`-guarded at 21.0.4, advertised in the capability
|
|
65
|
+
and preset-lifecycle probes, and covered by 21 stub contract cases in
|
|
66
|
+
`tests/test_resolve2104_preset_actions.py` (answers, name-required errors,
|
|
67
|
+
pre-21.0.4 guards, `_unknown` listings, capability advertisement on both a
|
|
68
|
+
21.0.4 and a legacy stub).
|
|
69
|
+
|
|
70
|
+
### Documentation
|
|
71
|
+
|
|
72
|
+
- **`docs/reference/resolve_scripting_api.txt` was the 26 May 2026 README** —
|
|
73
|
+
which is exactly why this delta went unnoticed. It is now the 24 Jul 2026
|
|
74
|
+
text these wrappers came from.
|
|
75
|
+
- **`api-coverage.md`: 351 → 361 methods**, with the ten new rows marked 🔬 and
|
|
76
|
+
carrying the contributor's Studio 21.0.4.5 results as a contributor report,
|
|
77
|
+
not as validation of ours — this machine runs Studio 19.1.3 and free 21.0.3,
|
|
78
|
+
neither of which is 21.0.4. `LoadUserPreferencesPreset` is recorded as
|
|
79
|
+
deliberately unexecuted even by the reporter, same class as
|
|
80
|
+
`DisableBackgroundTasksForCurrentResolveSession`.
|
|
81
|
+
- **The README key stats were stale at 349** since the v2.86.0 surfaces landed;
|
|
82
|
+
both READMEs and the live-tested badge now read 361 covered / 338 live-tested
|
|
83
|
+
(93.6%), and the English tested-against row picks up the free 21.0.3 build the
|
|
84
|
+
coverage doc already listed.
|
|
85
|
+
|
|
5
86
|
## What's New in v2.86.4
|
|
6
87
|
|
|
7
88
|
The issue #132 follow-up, which turned out not to be a tool bug at all. No
|
package/README.md
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
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)
|
|
9
9
|
[-18%20tools-blueviolet.svg)](#server-modes)
|
|
10
|
-
[](docs/reference/api-coverage.md#test-results)
|
|
11
11
|
[](https://www.blackmagicdesign.com/products/davinciresolve)
|
|
12
12
|
[](https://www.python.org/downloads/)
|
|
13
13
|
[](https://opensource.org/licenses/MIT)
|
|
@@ -265,10 +265,10 @@ The default server is a local stdio process launched by your MCP client; it does
|
|
|
265
265
|
| MCP Tools | **34** compound / **341** granular (live server) |
|
|
266
266
|
| Advanced (offline) tools | **18** — .drp/.drt/.drx + DB authoring, no Resolve running |
|
|
267
267
|
| Kernel Actions | **136** guarded workflow actions across 9 compound tools |
|
|
268
|
-
| API Methods Covered | **
|
|
269
|
-
| Methods Live Tested | **338/
|
|
268
|
+
| API Methods Covered | **361/361** (100%) |
|
|
269
|
+
| Methods Live Tested | **338/361** (93.6%) |
|
|
270
270
|
| Live Test Pass Rate | **338/338** (100%) |
|
|
271
|
-
| Tested Against | DaVinci Resolve 19.1.3 Studio + Resolve 20.3.2 Studio + Resolve 21.0.2 Studio |
|
|
271
|
+
| Tested Against | DaVinci Resolve 19.1.3 Studio + Resolve 20.3.2 Studio + Resolve 21.0.2 Studio + Resolve 21.0.3 **free** (via the in-app bridge) |
|
|
272
272
|
|
|
273
273
|
For method-by-method status, see [API Coverage and Test Results](docs/reference/api-coverage.md). For current workflow support, see [Kernel Action Coverage](docs/kernels/README.md).
|
|
274
274
|
|
package/README.zh-CN.md
CHANGED
|
@@ -2,17 +2,17 @@
|
|
|
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)](#服务器模式)
|
|
9
9
|
[-18%20tools-blueviolet.svg)](#服务器模式)
|
|
10
|
-
[](docs/reference/api-coverage.md#test-results)
|
|
11
11
|
[](https://www.blackmagicdesign.com/products/davinciresolve)
|
|
12
12
|
[](https://www.python.org/downloads/)
|
|
13
13
|
[](https://opensource.org/licenses/MIT)
|
|
14
14
|
|
|
15
|
-
> 本翻译对应 v2.
|
|
15
|
+
> 本翻译对应 v2.87.1 版 README。如与英文原版有出入,以 [英文原版](README.md) 为准。
|
|
16
16
|
|
|
17
17
|
一个 Model Context Protocol (MCP) 服务器,让 AI 助手通过官方脚本 API 控制 DaVinci Resolve Studio(达芬奇)。它提供完整的 API 覆盖,外加带护栏的工作流助手,涵盖剪辑、媒体池整理、渲染设置、审阅标记、调色、Fusion、Fairlight、项目生命周期任务、扩展开发,以及不碰源媒体的媒体分析。
|
|
18
18
|
|
|
@@ -193,10 +193,10 @@ DRX 调色写入**针对 Resolve Studio 做过实机校准**:调色参数默
|
|
|
193
193
|
| MCP 工具 | **34** 复合 / **341** 细粒度(实时服务器) |
|
|
194
194
|
| Advanced(离线)工具 | **18**——.drp/.drt/.drx + 数据库创作,无需 Resolve 运行 |
|
|
195
195
|
| 内核 action | 9 个复合工具下 **136** 个带护栏的工作流 action |
|
|
196
|
-
| API 方法覆盖 | **
|
|
197
|
-
| 实机测试方法数 | **338/
|
|
196
|
+
| API 方法覆盖 | **361/361**(100%) |
|
|
197
|
+
| 实机测试方法数 | **338/361**(93.6%) |
|
|
198
198
|
| 实机测试通过率 | **338/338**(100%) |
|
|
199
|
-
| 测试环境 | DaVinci Resolve 19.1.3 Studio + 20.3.2 Studio + 21.0.2 Studio |
|
|
199
|
+
| 测试环境 | DaVinci Resolve 19.1.3 Studio + 20.3.2 Studio + 21.0.2 Studio + 21.0.3 **免费版**(经内置桥接) |
|
|
200
200
|
|
|
201
201
|
逐方法状态见 [API 覆盖与测试结果](docs/reference/api-coverage.md)。当前工作流支持见 [内核 action 覆盖](docs/kernels/README.md)。
|
|
202
202
|
|
package/docs/SKILL.md
CHANGED
|
@@ -498,11 +498,21 @@ Key actions:
|
|
|
498
498
|
- `get_keyframe_mode` / `set_keyframe_mode(mode)`
|
|
499
499
|
- `get_fairlight_presets` — Resolve 20.2.2+; returns available Fairlight
|
|
500
500
|
preset names
|
|
501
|
+
- `list/save/load/delete/import/export_user_preferences_preset` — Resolve
|
|
502
|
+
21.0.4+; user-preferences presets. `load_...` is SESSION-WIDE: it swaps the
|
|
503
|
+
user's global Resolve preferences, so only call it when the user asked for
|
|
504
|
+
the switch. `import_...` does not activate the imported preset — follow with
|
|
505
|
+
`load_user_preferences_preset`
|
|
501
506
|
- `quit` — terminates Resolve (destructive; confirm with user first)
|
|
502
507
|
|
|
503
508
|
**`layout_presets`** — Save, load, export, import, delete UI layout presets.
|
|
509
|
+
`list` (Resolve 21.0.4+) enumerates the saved preset names the other actions
|
|
510
|
+
take.
|
|
504
511
|
|
|
505
512
|
**`render_presets`** — Import and export render and burn-in presets.
|
|
513
|
+
`list_burnin` / `delete_burnin` (Resolve 21.0.4+) enumerate and remove burn-in
|
|
514
|
+
presets — `list_burnin` is the only way to discover the names the `DataBurnIn`
|
|
515
|
+
render setting and the `load_burnin_preset` actions expect.
|
|
506
516
|
|
|
507
517
|
---
|
|
508
518
|
|
|
@@ -510,11 +520,16 @@ Key actions:
|
|
|
510
520
|
|
|
511
521
|
**`project_manager`** — CRUD on projects.
|
|
512
522
|
|
|
513
|
-
Key actions: `list`, `
|
|
523
|
+
Key actions: `list`, `list_attributes`, `get_current`,
|
|
524
|
+
`create(name, media_location_path?)`,
|
|
514
525
|
`load(name)`, `save`, `close`,
|
|
515
526
|
`delete(name)`, `import_project(path)`, `export_project(name, path)`, `archive`,
|
|
516
527
|
`restore`
|
|
517
528
|
|
|
529
|
+
`list_attributes` (Resolve 21.0.4+) returns `lastModifiedDate`, `creationDate`,
|
|
530
|
+
`notes`, and `liveCollaborationMode` per project in the current folder without
|
|
531
|
+
loading any of them.
|
|
532
|
+
|
|
518
533
|
Project / Database / Archive kernel actions (v2.15.0+) add guarded project
|
|
519
534
|
lifecycle, settings, database, preset, and archive boundary helpers:
|
|
520
535
|
|
|
@@ -8,8 +8,8 @@ Complete Resolve scripting API coverage, live-test status, and method-by-method
|
|
|
8
8
|
|--------|-------|
|
|
9
9
|
| MCP Tools | **34** compound (default) / **341** granular |
|
|
10
10
|
| Kernel Actions | **136** guarded MCP workflow actions across 9 compound tools |
|
|
11
|
-
| API Methods Covered | **
|
|
12
|
-
| Methods Live Tested | **338/
|
|
11
|
+
| API Methods Covered | **361/361** (100%) |
|
|
12
|
+
| Methods Live Tested | **338/361** (93.6%) |
|
|
13
13
|
| Live Test Pass Rate | **338/338** (100%) |
|
|
14
14
|
| API Object Classes | 13 |
|
|
15
15
|
| Tested Against | DaVinci Resolve 19.1.3 Studio + Resolve 20.3.2 Studio + Resolve 21.0.2 Studio + Resolve 21.0.3 **free** (via the in-app bridge) |
|
|
@@ -109,7 +109,7 @@ source, these summaries are downstream of it:
|
|
|
109
109
|
| Phase 5 | 6/6 | 100% | Scene cuts, subtitles from audio, graph node cache/tools/enable |
|
|
110
110
|
| Resolve 20 delta | 12/12 | 100% | Resolve 20.0-20.2.2 scripting additions live-tested on 20.3.2 |
|
|
111
111
|
| Resolve 21 delta | 7/7 | 100% | Resolve 21.0 additions that could be executed, live-tested on Studio 21.0.2.4 (`tests/live_resolve21_validation.py`). The other 6 need an AI Extras pack or are unsafe to run — counted as untested, not as passes |
|
|
112
|
-
| **Total** | **338/338** | **100%** | **
|
|
112
|
+
| **Total** | **338/338** | **100%** | **93.6% of the 361 covered methods tested live** |
|
|
113
113
|
|
|
114
114
|
#### Resolve 21 delta detail
|
|
115
115
|
|
|
@@ -130,7 +130,7 @@ marked 🔬 rather than ⚠️.
|
|
|
130
130
|
| `Project.GenerateSpeech` | 🔬 | Requires AI Speech Generator; returned an error **string**, not a MediaPoolItem |
|
|
131
131
|
| `Resolve.DisableBackgroundTasksForCurrentResolveSession` | 🔬 | Present in `dir()`; **not executed** — session-wide, returns None, and has no `Enable...` counterpart, so there is no undo short of restarting Resolve |
|
|
132
132
|
|
|
133
|
-
### Untested Methods (
|
|
133
|
+
### Untested Methods (23 of 361)
|
|
134
134
|
|
|
135
135
|
Every ☁️ and 🔬 row from the reference tables, listed here so the count is
|
|
136
136
|
checkable rather than asserted.
|
|
@@ -150,12 +150,24 @@ checkable rather than asserted.
|
|
|
150
150
|
| `Resolve.DisableBackgroundTasksForCurrentResolveSession` | Deliberately not executed: session-wide, returns `None`, and has no `Enable...` counterpart, so there is no undo short of restarting Resolve | No |
|
|
151
151
|
| `MPI.GetTimeline` | Requires a Resolve 21.0.4 build; the validation machine runs Studio 19.1.3 and free 21.0.3 | Yes |
|
|
152
152
|
| `TL.GetSelectedClips` | Requires a Resolve 21.0.4 build; the validation machine runs 19.1.3 | Yes |
|
|
153
|
+
| `Resolve.GetLayoutPresetList` | Requires a Resolve 21.0.4 build; the validation machine runs Studio 19.1.3 and free 21.0.3 | Yes |
|
|
154
|
+
| `Resolve.GetBurnInPresetList` | Requires a Resolve 21.0.4 build; the validation machine runs Studio 19.1.3 and free 21.0.3 | Yes |
|
|
155
|
+
| `Resolve.DeleteBurnInPreset` | Requires a Resolve 21.0.4 build; also, no scripting API creates a burn-in preset to round-trip against | Yes |
|
|
156
|
+
| `Resolve.GetUserPreferencesPresetList` | Requires a Resolve 21.0.4 build; the validation machine runs Studio 19.1.3 and free 21.0.3 | Yes |
|
|
157
|
+
| `Resolve.SaveUserPreferencesPreset` | Requires a Resolve 21.0.4 build; the validation machine runs Studio 19.1.3 and free 21.0.3 | Yes |
|
|
158
|
+
| `Resolve.LoadUserPreferencesPreset` | Deliberately not executed even by the reporter: swaps the user's global preferences session-wide | No |
|
|
159
|
+
| `Resolve.DeleteUserPreferencesPreset` | Requires a Resolve 21.0.4 build; the validation machine runs Studio 19.1.3 and free 21.0.3 | Yes |
|
|
160
|
+
| `Resolve.ImportUserPreferencesPreset` | Requires a Resolve 21.0.4 build; round-tripped by the reporter on 21.0.4.5 in both call shapes (PR #139), not here | Yes |
|
|
161
|
+
| `Resolve.ExportUserPreferencesPreset` | Requires a Resolve 21.0.4 build; the validation machine runs Studio 19.1.3 and free 21.0.3 | Yes |
|
|
162
|
+
| `PM.GetProjectAttributesInCurrentFolder` | Requires a Resolve 21.0.4 build; the validation machine runs Studio 19.1.3 and free 21.0.3 | Yes |
|
|
153
163
|
|
|
154
164
|
The five AI Extras rows are untested for want of a downloadable pack, not because
|
|
155
165
|
the wrappers are suspect — a report from anyone who has the packs installed would
|
|
156
|
-
close them. The
|
|
166
|
+
close them. The twelve 21.0.4 rows are untested for want of the build: this machine
|
|
157
167
|
runs 19.1.3, so they are guarded and unit-tested against stubs and carry a
|
|
158
|
-
contributor's report rather than a result of ours
|
|
168
|
+
contributor's report rather than a result of ours (issue #131 for the first two;
|
|
169
|
+
issues #135–#138 for the ten from the 24 Jul 2026 README refresh, several with
|
|
170
|
+
save→list→delete round-trips on Studio 21.0.4.5). The remaining one is a
|
|
159
171
|
decision rather than a gap: it is reachable, and
|
|
160
172
|
running it during a validation sweep would disable background tasks for every
|
|
161
173
|
project open in that Resolve instance.
|
|
@@ -199,6 +211,15 @@ Every method in the DaVinci Resolve Scripting API and its test status. Methods a
|
|
|
199
211
|
| 21 | `SetKeyframeMode(keyframeMode)` | ⚠️ | API accepts; mode must match valid enum |
|
|
200
212
|
| 22 | `GetFairlightPresets()` | ✅ | Returns a preset map. Live on 20.3.2 Studio and 21.0.3 free; **absent below 20.2.2** — confirmed missing on 19.1.3, so the version floor is verified from both sides |
|
|
201
213
|
| 23 | `DisableBackgroundTasksForCurrentResolveSession()` | 🔬 | Resolve 21.0. Present in `dir()`; **not executed** — session-wide, returns `None`, no `Enable...` counterpart, so no undo short of restarting Resolve |
|
|
214
|
+
| 24 | `GetLayoutPresetList()` | 🔬 | Resolve 21.0.4 (24 Jul 2026 README). Not executed here — no 21.0.4 build; reporter's save→list→delete round-trip on Studio 21.0.4.5 in issue #137 |
|
|
215
|
+
| 25 | `GetBurnInPresetList()` | 🔬 | Resolve 21.0.4. Not executed here; reported returning a list on Studio 21.0.4.5 in issue #136 |
|
|
216
|
+
| 26 | `DeleteBurnInPreset(presetName)` | 🔬 | Resolve 21.0.4. `dir()`-present on 21.0.4.5 (issue #136); not executed anywhere — no scripting API creates a burn-in preset to round-trip against |
|
|
217
|
+
| 27 | `GetUserPreferencesPresetList()` | 🔬 | Resolve 21.0.4. Not executed here; reporter's save→list→delete round-trip on Studio 21.0.4.5 in issue #138 |
|
|
218
|
+
| 28 | `SaveUserPreferencesPreset(presetName)` | 🔬 | Resolve 21.0.4. Not executed here; part of the issue #138 round-trip on 21.0.4.5 |
|
|
219
|
+
| 29 | `LoadUserPreferencesPreset(presetName)` | 🔬 | Resolve 21.0.4. `dir()`-present on 21.0.4.5; **not executed** even by the reporter — swaps the user's global preferences session-wide (issue #138) |
|
|
220
|
+
| 30 | `DeleteUserPreferencesPreset(presetName)` | 🔬 | Resolve 21.0.4. Not executed here; part of the issue #138 round-trip on 21.0.4.5 |
|
|
221
|
+
| 31 | `ImportUserPreferencesPreset(presetFilePath, presetName)` | 🔬 | Resolve 21.0.4. Not executed here; reporter's export→delete→import round-trip on 21.0.4.5 (PR #139) covers **both** call shapes — single-arg returns `True` rather than a `TypeError`, and the imported preset is then named after the file; the two-arg form takes the given name. Per the README the imported preset is **not** auto-loaded, and the round-trip did not load either import |
|
|
222
|
+
| 32 | `ExportUserPreferencesPreset(presetName, exportPath)` | 🔬 | Resolve 21.0.4. Not executed here; reported writing the file at the given path with no extension appended, on 21.0.4.5 (PR #139) |
|
|
202
223
|
|
|
203
224
|
### ProjectManager
|
|
204
225
|
|
|
@@ -229,6 +250,7 @@ Every method in the DaVinci Resolve Scripting API and its test status. Methods a
|
|
|
229
250
|
| 23 | `LoadCloudProject({cloudSettings})` | ☁️ | Requires cloud infrastructure |
|
|
230
251
|
| 24 | `ImportCloudProject(filePath, {cloudSettings})` | ☁️ | Requires cloud infrastructure |
|
|
231
252
|
| 25 | `RestoreCloudProject(folderPath, {cloudSettings})` | ☁️ | Requires cloud infrastructure |
|
|
253
|
+
| 26 | `GetProjectAttributesInCurrentFolder()` | 🔬 | Resolve 21.0.4 (24 Jul 2026 README). Not executed here — no 21.0.4 build; reported live on Studio 21.0.4.5 in issue #135, returning `lastModifiedDate` / `creationDate` / `notes` / `liveCollaborationMode` for all 8 projects in the current folder |
|
|
232
254
|
|
|
233
255
|
### Project
|
|
234
256
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Last Updated:
|
|
1
|
+
Last Updated: 24 Jul 2026
|
|
2
2
|
-------------------------
|
|
3
3
|
In this package, you will find a brief introduction to the Scripting API for DaVinci Resolve Studio. Apart from this README.txt file, this package contains folders containing the basic import
|
|
4
4
|
modules for scripting access (DaVinciResolve.py) and some representative examples.
|
|
@@ -91,6 +91,7 @@ Resolve
|
|
|
91
91
|
GetProductName() --> string # Returns product name.
|
|
92
92
|
GetVersion() --> [version fields] # Returns list of product version fields in [major, minor, patch, build, suffix] format.
|
|
93
93
|
GetVersionString() --> string # Returns product version in "major.minor.patch[suffix].build" format.
|
|
94
|
+
GetLayoutPresetList() --> [presetNames...] # Returns list of layout preset names.
|
|
94
95
|
LoadLayoutPreset(presetName) --> Bool # Loads UI layout from saved preset named 'presetName'.
|
|
95
96
|
UpdateLayoutPreset(presetName) --> Bool # Overwrites preset named 'presetName' with current UI layout.
|
|
96
97
|
ExportLayoutPreset(presetName, presetFilePath) --> Bool # Exports preset named 'presetName' to path 'presetFilePath'.
|
|
@@ -100,8 +101,16 @@ Resolve
|
|
|
100
101
|
Quit() --> None # Quits the Resolve App.
|
|
101
102
|
ImportRenderPreset(presetPath) --> Bool # Import a preset from presetPath (string) and set it as current preset for rendering.
|
|
102
103
|
ExportRenderPreset(presetName, exportPath) --> Bool # Export a preset to a given path (string) if presetName(string) exists.
|
|
104
|
+
GetBurnInPresetList() --> [presetName...] # Returns a list of data burn in preset names.
|
|
105
|
+
DeleteBurnInPreset(presetName) --> Bool # Deletes the data burn in preset.
|
|
103
106
|
ImportBurnInPreset(presetPath) --> Bool # Import a data burn in preset from a given presetPath (string)
|
|
104
107
|
ExportBurnInPreset(presetName, exportPath) --> Bool # Export a data burn in preset to a given path (string) if presetName (string) exists.
|
|
108
|
+
GetUserPreferencesPresetList() --> [presetNames...] # Returns a list of user preferences preset names.
|
|
109
|
+
LoadUserPreferencesPreset(presetName) --> Bool # Loads the user preferences preset named 'presetName'.
|
|
110
|
+
SaveUserPreferencesPreset(presetName) --> Bool # Saves the current user preferences as a new preset named 'presetName'.
|
|
111
|
+
DeleteUserPreferencesPreset(presetName) --> Bool # Deletes the user preferences preset named 'presetName'.
|
|
112
|
+
ImportUserPreferencesPreset(presetFilePath, presetName) --> Bool # Imports user preferences preset from presetFilePath. Optional presetName argument to name the imported preset. The imported preset is not automatically loaded, use LoadUserPreferencesPreset(..) to load it.
|
|
113
|
+
ExportUserPreferencesPreset(presetName, exportPath) --> Bool # Exports the user preferences preset to exportPath.
|
|
105
114
|
GetKeyframeMode() --> keyframeMode # Returns the currently set keyframe mode (int). Refer to section 'Keyframe Mode information' below for details.
|
|
106
115
|
SetKeyframeMode(keyframeMode) --> Bool # Returns True when 'keyframeMode'(enum) is successfully set. Refer to section 'Keyframe Mode information' below for details.
|
|
107
116
|
GetFairlightPresets() --> [presetNames...]. # Returns a list of Fairlight presets by name
|
|
@@ -122,6 +131,8 @@ ProjectManager
|
|
|
122
131
|
CreateFolder(folderName) --> Bool # Creates a folder if folderName (string) is unique.
|
|
123
132
|
DeleteFolder(folderName) --> Bool # Deletes the specified folder if it exists. Returns True in case of success.
|
|
124
133
|
GetProjectListInCurrentFolder() --> [project names...] # Returns a list of project names in current folder.
|
|
134
|
+
GetProjectAttributesInCurrentFolder() --> {project name: {attributes}, ...} # Returns a dictionary of project names in the current folder to project attributes.
|
|
135
|
+
# Attributes include "lastModifiedDate", "creationDate", "notes", and "liveCollaborationMode".
|
|
125
136
|
GetFolderListInCurrentFolder() --> [folder names...] # Returns a list of folder names in current folder.
|
|
126
137
|
GotoRootFolder() --> Bool # Opens root folder in database.
|
|
127
138
|
GotoParentFolder() --> Bool # Opens parent folder of current folder in database if current folder has parent.
|
|
@@ -295,6 +306,7 @@ MediaPoolItem
|
|
|
295
306
|
# If no argument is specified, a dict of all set metadata properties is returned.
|
|
296
307
|
SetMetadata(metadataType, metadataValue) --> Bool # Sets the given metadata to metadataValue (string). Returns True if successful.
|
|
297
308
|
SetMetadata({metadata}) --> Bool # Sets the item metadata with specified 'metadata' dict. Returns True if successful.
|
|
309
|
+
GetTimeline() --> TimelineItem # Returns the timeline object if the mpItem is a timeline clip, or None if not.
|
|
298
310
|
GetThirdPartyMetadata(metadataType=None) --> string|dict # Returns the third party metadata value for the key 'metadataType'.
|
|
299
311
|
# If no argument is specified, a dict of all set third party metadata properties is returned.
|
|
300
312
|
SetThirdPartyMetadata(metadataType, metadataValue) --> Bool # Sets/Add the given third party metadata to metadataValue (string). Returns True if successful.
|
|
@@ -385,6 +397,7 @@ Timeline
|
|
|
385
397
|
DeleteClips([timelineItems], Bool) --> Bool # Deletes specified TimelineItems from the timeline, performing ripple delete if the second argument is True. Second argument is optional (The default for this is False)
|
|
386
398
|
SetClipsLinked([timelineItems], Bool) --> Bool # Links or unlinks the specified TimelineItems depending on second argument.
|
|
387
399
|
GetItemListInTrack(trackType, index) --> [items...] # Returns a list of timeline items on that track (based on trackType and index). 1 <= index <= GetTrackCount(trackType).
|
|
400
|
+
GetSelectedClips() --> [items...] # Returns the currently selected timeline items.
|
|
388
401
|
AddMarker(frameId, color, name, note, duration, --> Bool # Creates a new marker at given frameId position and with given marker information. 'customData' is optional and helps to attach user specific data to the marker.
|
|
389
402
|
customData)
|
|
390
403
|
GetMarkers() --> {markers...} # Returns a dict (frameId -> {information}) of all markers and dicts with their information.
|
|
@@ -693,6 +706,10 @@ Affects:
|
|
|
693
706
|
Affects:
|
|
694
707
|
• x = Project:GetSetting('timelineFrameRate') and Project:SetSetting('timelineFrameRate', x)
|
|
695
708
|
|
|
709
|
+
"timelineSampleRate" - the property value is one of the sample rates available to the user in project settings under "Audio sample rate" option in the Fairlight tab.
|
|
710
|
+
Affects:
|
|
711
|
+
• x = Project:GetSetting('timelineSampleRate') and Project:SetSetting('timelineSampleRate', x)
|
|
712
|
+
|
|
696
713
|
The following Clip properties have specifically enumerated values:
|
|
697
714
|
"Super Scale" - the property value is an enumerated integer between 1 and 4 with these meanings: 1=no scaling, and 2, 3 and 4 represent the Super Scale multipliers 2x, 3x and 4x.
|
|
698
715
|
for super scale multiplier '2x Enhanced', exactly 4 arguments must be passed as outlined below. If less than 4 arguments are passed, it will default to 2x.
|
|
@@ -844,6 +861,9 @@ The parameter setting is a dictionary containing the following keys:
|
|
|
844
861
|
- "ReplaceExistingFilesInPlace": Bool
|
|
845
862
|
- "ExportSubtitle": Bool
|
|
846
863
|
- "SubtitleFormat": string (options: "BurnIn", "EmbeddedCaptions", "SeparateFile")
|
|
864
|
+
- "UseFullExtents": Bool
|
|
865
|
+
- "AddFrameHandles": int (>= 0). Ignored if use full extents is enabled.
|
|
866
|
+
- "DataBurnIn": string. (example: "Same as project", "None")
|
|
847
867
|
|
|
848
868
|
Looking up timeline export properties
|
|
849
869
|
-------------------------------------
|
package/install.py
CHANGED
|
@@ -36,7 +36,7 @@ from src.utils.update_check import (
|
|
|
36
36
|
|
|
37
37
|
# ─── Version ──────────────────────────────────────────────────────────────────
|
|
38
38
|
|
|
39
|
-
VERSION = "2.
|
|
39
|
+
VERSION = "2.87.1"
|
|
40
40
|
# Only hard floor: mcp[cli] requires Python 3.10+. There is no upper bound —
|
|
41
41
|
# Resolve's scripting bridge loads into newer interpreters on recent builds
|
|
42
42
|
# (Python 3.14 verified against Resolve Studio 20.3.2). Older Resolve builds
|
package/package.json
CHANGED
package/src/granular/common.py
CHANGED
|
@@ -85,7 +85,7 @@ if not logging.getLogger().handlers:
|
|
|
85
85
|
handlers=[logging.StreamHandler()],
|
|
86
86
|
)
|
|
87
87
|
|
|
88
|
-
VERSION = "2.
|
|
88
|
+
VERSION = "2.87.1"
|
|
89
89
|
logger = logging.getLogger("davinci-resolve-mcp")
|
|
90
90
|
logger.info(f"Starting DaVinci Resolve MCP Server v{VERSION}")
|
|
91
91
|
logger.info(f"Detected platform: {get_platform()}")
|
package/src/server.py
CHANGED
|
@@ -11,7 +11,7 @@ Usage:
|
|
|
11
11
|
python src/server.py --full # Start the 341-tool granular server instead
|
|
12
12
|
"""
|
|
13
13
|
|
|
14
|
-
VERSION = "2.
|
|
14
|
+
VERSION = "2.87.1"
|
|
15
15
|
|
|
16
16
|
import base64
|
|
17
17
|
import os
|
|
@@ -13581,6 +13581,19 @@ def resolve_control(action: str, params: Optional[Dict[str, Any]] = None) -> Dic
|
|
|
13581
13581
|
get_fairlight_presets() -> {presets}
|
|
13582
13582
|
set_high_priority() -> {success}
|
|
13583
13583
|
disable_background_tasks_for_current_session() -> {success} — Resolve 21+
|
|
13584
|
+
list_user_preferences_presets() -> {presets} — Resolve 21.0.4+
|
|
13585
|
+
save_user_preferences_preset(name) -> {success} — Resolve 21.0.4+
|
|
13586
|
+
load_user_preferences_preset(name) -> {success} — Resolve 21.0.4+.
|
|
13587
|
+
SESSION-WIDE: swaps the user's global Resolve preferences, not a
|
|
13588
|
+
project setting. Only call when the user asked for the switch.
|
|
13589
|
+
delete_user_preferences_preset(name) -> {success} — Resolve 21.0.4+
|
|
13590
|
+
import_user_preferences_preset(path, name?) -> {success} — Resolve 21.0.4+.
|
|
13591
|
+
The imported preset is NOT auto-loaded; follow with
|
|
13592
|
+
load_user_preferences_preset to activate it. Omitting name is safe
|
|
13593
|
+
(reported on 21.0.4.5: single-arg returns True, not a TypeError) and
|
|
13594
|
+
the preset is then named after the file, so pass name when the preset
|
|
13595
|
+
should be called something the filename does not say.
|
|
13596
|
+
export_user_preferences_preset(name, path) -> {success} — Resolve 21.0.4+
|
|
13584
13597
|
open_control_panel(port?, host?, open_browser?) -> {success, url, pid, port, status}
|
|
13585
13598
|
— Launches the analysis control panel (src/analysis_dashboard.py) as a background process.
|
|
13586
13599
|
Idempotent: returns the existing URL if already running.
|
|
@@ -13816,7 +13829,59 @@ def resolve_control(action: str, params: Optional[Dict[str, Any]] = None) -> Dic
|
|
|
13816
13829
|
return missing
|
|
13817
13830
|
r.DisableBackgroundTasksForCurrentResolveSession()
|
|
13818
13831
|
return _ok()
|
|
13819
|
-
|
|
13832
|
+
elif action == "list_user_preferences_presets":
|
|
13833
|
+
missing = _requires_method(r, "GetUserPreferencesPresetList", "21.0.4")
|
|
13834
|
+
if missing:
|
|
13835
|
+
return missing
|
|
13836
|
+
return {"presets": _ser(r.GetUserPreferencesPresetList() or [])}
|
|
13837
|
+
elif action == "save_user_preferences_preset":
|
|
13838
|
+
missing = _requires_method(r, "SaveUserPreferencesPreset", "21.0.4")
|
|
13839
|
+
if missing:
|
|
13840
|
+
return missing
|
|
13841
|
+
if not p.get("name"):
|
|
13842
|
+
return _err("save_user_preferences_preset requires name")
|
|
13843
|
+
return {"success": bool(r.SaveUserPreferencesPreset(p["name"]))}
|
|
13844
|
+
elif action == "load_user_preferences_preset":
|
|
13845
|
+
missing = _requires_method(r, "LoadUserPreferencesPreset", "21.0.4")
|
|
13846
|
+
if missing:
|
|
13847
|
+
return missing
|
|
13848
|
+
if not p.get("name"):
|
|
13849
|
+
return _err("load_user_preferences_preset requires name")
|
|
13850
|
+
return {"success": bool(r.LoadUserPreferencesPreset(p["name"]))}
|
|
13851
|
+
elif action == "delete_user_preferences_preset":
|
|
13852
|
+
missing = _requires_method(r, "DeleteUserPreferencesPreset", "21.0.4")
|
|
13853
|
+
if missing:
|
|
13854
|
+
return missing
|
|
13855
|
+
if not p.get("name"):
|
|
13856
|
+
return _err("delete_user_preferences_preset requires name")
|
|
13857
|
+
return {"success": bool(r.DeleteUserPreferencesPreset(p["name"]))}
|
|
13858
|
+
elif action == "import_user_preferences_preset":
|
|
13859
|
+
missing = _requires_method(r, "ImportUserPreferencesPreset", "21.0.4")
|
|
13860
|
+
if missing:
|
|
13861
|
+
return missing
|
|
13862
|
+
if not p.get("path"):
|
|
13863
|
+
return _err("import_user_preferences_preset requires path")
|
|
13864
|
+
if p.get("name"):
|
|
13865
|
+
ok = bool(r.ImportUserPreferencesPreset(p["path"], p["name"]))
|
|
13866
|
+
else:
|
|
13867
|
+
ok = bool(r.ImportUserPreferencesPreset(p["path"]))
|
|
13868
|
+
note = "The imported preset is not auto-loaded; use load_user_preferences_preset to activate it."
|
|
13869
|
+
if not p.get("name"):
|
|
13870
|
+
note += (" No name was given, so the preset takes its name from the file — "
|
|
13871
|
+
"list_user_preferences_presets to read it back.")
|
|
13872
|
+
return {"success": ok, "note": note}
|
|
13873
|
+
elif action == "export_user_preferences_preset":
|
|
13874
|
+
missing = _requires_method(r, "ExportUserPreferencesPreset", "21.0.4")
|
|
13875
|
+
if missing:
|
|
13876
|
+
return missing
|
|
13877
|
+
err, clean = _validate_params(p, {
|
|
13878
|
+
"name": {"type": str, "required": True, "non_empty": True},
|
|
13879
|
+
"path": {"type": str, "required": True, "non_empty": True},
|
|
13880
|
+
})
|
|
13881
|
+
if err:
|
|
13882
|
+
return _err(err)
|
|
13883
|
+
return {"success": bool(r.ExportUserPreferencesPreset(clean["name"], clean["path"]))}
|
|
13884
|
+
return _unknown(action, ["launch","runtime_mode","get_version","api_truth","check_version_support","verification_stats","job_status","list_jobs","mcp_update_status","set_mcp_update_policy","ignore_mcp_update","snooze_mcp_update","clear_mcp_update_preferences","get_page","open_page","get_keyframe_mode","set_keyframe_mode","quit","get_fairlight_presets","set_high_priority","disable_background_tasks_for_current_session","list_user_preferences_presets","save_user_preferences_preset","load_user_preferences_preset","delete_user_preferences_preset","import_user_preferences_preset","export_user_preferences_preset","open_control_panel","control_panel_status","close_control_panel","save_state","restore_state"])
|
|
13820
13885
|
|
|
13821
13886
|
|
|
13822
13887
|
# ─── V2 C4: Per-field corrections with provenance + changelog ────────────────
|
|
@@ -14682,6 +14747,7 @@ def layout_presets(action: str, params: Optional[Dict[str, Any]] = None) -> Dict
|
|
|
14682
14747
|
"""Manage DaVinci Resolve UI layout presets.
|
|
14683
14748
|
|
|
14684
14749
|
Actions:
|
|
14750
|
+
list() -> {presets} — Resolve 21.0.4+; saved layout preset names
|
|
14685
14751
|
save(name) -> {success}
|
|
14686
14752
|
load(name) -> {success}
|
|
14687
14753
|
update(name) -> {success}
|
|
@@ -14694,7 +14760,12 @@ def layout_presets(action: str, params: Optional[Dict[str, Any]] = None) -> Dict
|
|
|
14694
14760
|
if r is None:
|
|
14695
14761
|
return _not_connected_error()
|
|
14696
14762
|
|
|
14697
|
-
if action == "
|
|
14763
|
+
if action == "list":
|
|
14764
|
+
missing = _requires_method(r, "GetLayoutPresetList", "21.0.4")
|
|
14765
|
+
if missing:
|
|
14766
|
+
return missing
|
|
14767
|
+
return {"presets": _ser(r.GetLayoutPresetList() or [])}
|
|
14768
|
+
elif action == "save":
|
|
14698
14769
|
if not p.get("name"):
|
|
14699
14770
|
return _err("save requires name")
|
|
14700
14771
|
return {"success": bool(r.SaveLayoutPreset(p["name"]))}
|
|
@@ -14716,7 +14787,7 @@ def layout_presets(action: str, params: Optional[Dict[str, Any]] = None) -> Dict
|
|
|
14716
14787
|
return {"success": bool(r.ImportLayoutPreset(p["path"]))}
|
|
14717
14788
|
elif action == "delete":
|
|
14718
14789
|
return {"success": bool(r.DeleteLayoutPreset(p["name"]))}
|
|
14719
|
-
return _unknown(action, ["save","load","update","export","import_preset","delete"])
|
|
14790
|
+
return _unknown(action, ["list","save","load","update","export","import_preset","delete"])
|
|
14720
14791
|
|
|
14721
14792
|
|
|
14722
14793
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
@@ -14733,6 +14804,10 @@ def render_presets(action: str, params: Optional[Dict[str, Any]] = None) -> Dict
|
|
|
14733
14804
|
export_render(name, path) -> {success}
|
|
14734
14805
|
import_burnin(path) -> {success}
|
|
14735
14806
|
export_burnin(name, path) -> {success}
|
|
14807
|
+
list_burnin() -> {presets} — Resolve 21.0.4+; burn-in preset names usable
|
|
14808
|
+
with the render tool's DataBurnIn setting and load_burnin_preset on
|
|
14809
|
+
project_settings / timeline_item
|
|
14810
|
+
delete_burnin(name) -> {success} — Resolve 21.0.4+
|
|
14736
14811
|
"""
|
|
14737
14812
|
p = _params(params)
|
|
14738
14813
|
r = get_resolve()
|
|
@@ -14747,7 +14822,19 @@ def render_presets(action: str, params: Optional[Dict[str, Any]] = None) -> Dict
|
|
|
14747
14822
|
return {"success": bool(r.ImportBurnInPreset(p["path"]))}
|
|
14748
14823
|
elif action == "export_burnin":
|
|
14749
14824
|
return {"success": bool(r.ExportBurnInPreset(p["name"], p["path"]))}
|
|
14750
|
-
|
|
14825
|
+
elif action == "list_burnin":
|
|
14826
|
+
missing = _requires_method(r, "GetBurnInPresetList", "21.0.4")
|
|
14827
|
+
if missing:
|
|
14828
|
+
return missing
|
|
14829
|
+
return {"presets": _ser(r.GetBurnInPresetList() or [])}
|
|
14830
|
+
elif action == "delete_burnin":
|
|
14831
|
+
missing = _requires_method(r, "DeleteBurnInPreset", "21.0.4")
|
|
14832
|
+
if missing:
|
|
14833
|
+
return missing
|
|
14834
|
+
if not p.get("name"):
|
|
14835
|
+
return _err("delete_burnin requires name")
|
|
14836
|
+
return {"success": bool(r.DeleteBurnInPreset(p["name"]))}
|
|
14837
|
+
return _unknown(action, ["import_render","export_render","import_burnin","export_burnin","list_burnin","delete_burnin"])
|
|
14751
14838
|
|
|
14752
14839
|
|
|
14753
14840
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
@@ -14783,6 +14870,7 @@ _PROJECT_MANAGER_METHODS = [
|
|
|
14783
14870
|
"CreateFolder",
|
|
14784
14871
|
"DeleteFolder",
|
|
14785
14872
|
"GetProjectListInCurrentFolder",
|
|
14873
|
+
"GetProjectAttributesInCurrentFolder",
|
|
14786
14874
|
"GetFolderListInCurrentFolder",
|
|
14787
14875
|
"GotoRootFolder",
|
|
14788
14876
|
"GotoParentFolder",
|
|
@@ -14938,6 +15026,7 @@ def _project_capabilities(pm=None, project=None, resolve_obj=None) -> Dict[str,
|
|
|
14938
15026
|
"kernel_actions": list(_PROJECT_KERNEL_ACTIONS),
|
|
14939
15027
|
"resolve": {
|
|
14940
15028
|
"layout_presets": {
|
|
15029
|
+
"list": _has_method(resolve_obj, "GetLayoutPresetList") if resolve_obj else True,
|
|
14941
15030
|
"save": _has_method(resolve_obj, "SaveLayoutPreset") if resolve_obj else True,
|
|
14942
15031
|
"load": _has_method(resolve_obj, "LoadLayoutPreset") if resolve_obj else True,
|
|
14943
15032
|
"update": _has_method(resolve_obj, "UpdateLayoutPreset") if resolve_obj else True,
|
|
@@ -14950,6 +15039,16 @@ def _project_capabilities(pm=None, project=None, resolve_obj=None) -> Dict[str,
|
|
|
14950
15039
|
"export_render": _has_method(resolve_obj, "ExportRenderPreset") if resolve_obj else True,
|
|
14951
15040
|
"import_burnin": _has_method(resolve_obj, "ImportBurnInPreset") if resolve_obj else True,
|
|
14952
15041
|
"export_burnin": _has_method(resolve_obj, "ExportBurnInPreset") if resolve_obj else True,
|
|
15042
|
+
"list_burnin": _has_method(resolve_obj, "GetBurnInPresetList") if resolve_obj else True,
|
|
15043
|
+
"delete_burnin": _has_method(resolve_obj, "DeleteBurnInPreset") if resolve_obj else True,
|
|
15044
|
+
},
|
|
15045
|
+
"user_preferences_presets": {
|
|
15046
|
+
"list": _has_method(resolve_obj, "GetUserPreferencesPresetList") if resolve_obj else True,
|
|
15047
|
+
"save": _has_method(resolve_obj, "SaveUserPreferencesPreset") if resolve_obj else True,
|
|
15048
|
+
"load": _has_method(resolve_obj, "LoadUserPreferencesPreset") if resolve_obj else True,
|
|
15049
|
+
"delete": _has_method(resolve_obj, "DeleteUserPreferencesPreset") if resolve_obj else True,
|
|
15050
|
+
"import": _has_method(resolve_obj, "ImportUserPreferencesPreset") if resolve_obj else True,
|
|
15051
|
+
"export": _has_method(resolve_obj, "ExportUserPreferencesPreset") if resolve_obj else True,
|
|
14953
15052
|
},
|
|
14954
15053
|
},
|
|
14955
15054
|
}
|
|
@@ -15248,6 +15347,7 @@ def _preset_lifecycle_probe(resolve_obj, project, p: Dict[str, Any]) -> Dict[str
|
|
|
15248
15347
|
"quick_export_presets": {"available": _has_method(project, "GetQuickExportRenderPresets")},
|
|
15249
15348
|
"fairlight_presets": {"available": _has_method(resolve_obj, "GetFairlightPresets")},
|
|
15250
15349
|
"layout_presets": {
|
|
15350
|
+
"list": _has_method(resolve_obj, "GetLayoutPresetList"),
|
|
15251
15351
|
"save": _has_method(resolve_obj, "SaveLayoutPreset"),
|
|
15252
15352
|
"load": _has_method(resolve_obj, "LoadLayoutPreset"),
|
|
15253
15353
|
"update": _has_method(resolve_obj, "UpdateLayoutPreset"),
|
|
@@ -15260,6 +15360,16 @@ def _preset_lifecycle_probe(resolve_obj, project, p: Dict[str, Any]) -> Dict[str
|
|
|
15260
15360
|
"export_render": _has_method(resolve_obj, "ExportRenderPreset"),
|
|
15261
15361
|
"import_burnin": _has_method(resolve_obj, "ImportBurnInPreset"),
|
|
15262
15362
|
"export_burnin": _has_method(resolve_obj, "ExportBurnInPreset"),
|
|
15363
|
+
"list_burnin": _has_method(resolve_obj, "GetBurnInPresetList"),
|
|
15364
|
+
"delete_burnin": _has_method(resolve_obj, "DeleteBurnInPreset"),
|
|
15365
|
+
},
|
|
15366
|
+
"user_preferences_presets": {
|
|
15367
|
+
"list": _has_method(resolve_obj, "GetUserPreferencesPresetList"),
|
|
15368
|
+
"save": _has_method(resolve_obj, "SaveUserPreferencesPreset"),
|
|
15369
|
+
"load": _has_method(resolve_obj, "LoadUserPreferencesPreset"),
|
|
15370
|
+
"delete": _has_method(resolve_obj, "DeleteUserPreferencesPreset"),
|
|
15371
|
+
"import": _has_method(resolve_obj, "ImportUserPreferencesPreset"),
|
|
15372
|
+
"export": _has_method(resolve_obj, "ExportUserPreferencesPreset"),
|
|
15263
15373
|
},
|
|
15264
15374
|
}
|
|
15265
15375
|
try:
|
|
@@ -15641,6 +15751,9 @@ def project_manager(action: str, params: Optional[Dict[str, Any]] = None) -> Dic
|
|
|
15641
15751
|
|
|
15642
15752
|
Actions:
|
|
15643
15753
|
list() -> {projects}
|
|
15754
|
+
list_attributes() -> {projects: {name: {lastModifiedDate, creationDate, notes, liveCollaborationMode}}}
|
|
15755
|
+
— Resolve 21.0.4+. Per-project attributes for the current folder without
|
|
15756
|
+
loading any project.
|
|
15644
15757
|
get_current() -> {name, id}
|
|
15645
15758
|
create(name, media_location_path?) -> {success, name}
|
|
15646
15759
|
load(name) -> {success}
|
|
@@ -15722,6 +15835,11 @@ def project_manager(action: str, params: Optional[Dict[str, Any]] = None) -> Dic
|
|
|
15722
15835
|
return _project_boundary_report(r, pm, proj, p)
|
|
15723
15836
|
elif action == "list":
|
|
15724
15837
|
return {"projects": pm.GetProjectListInCurrentFolder()}
|
|
15838
|
+
elif action == "list_attributes":
|
|
15839
|
+
missing = _requires_method(pm, "GetProjectAttributesInCurrentFolder", "21.0.4")
|
|
15840
|
+
if missing:
|
|
15841
|
+
return missing
|
|
15842
|
+
return {"projects": _ser(pm.GetProjectAttributesInCurrentFolder() or {})}
|
|
15725
15843
|
elif action == "get_current":
|
|
15726
15844
|
proj = pm.GetCurrentProject()
|
|
15727
15845
|
return {"name": proj.GetName(), "id": proj.GetUniqueId()} if proj else _err("No project open")
|
|
@@ -15776,7 +15894,7 @@ def project_manager(action: str, params: Optional[Dict[str, Any]] = None) -> Dic
|
|
|
15776
15894
|
if not p.get("path"):
|
|
15777
15895
|
return _err("restore requires path")
|
|
15778
15896
|
return {"success": bool(pm.RestoreProject(p["path"], p.get("name")))}
|
|
15779
|
-
return _unknown(action, ["list","get_current","create","load","save","close","delete","import_project","export_project","archive","restore","lint","diff_to_spec","plan_spec","apply_spec", *_PROJECT_KERNEL_ACTIONS])
|
|
15897
|
+
return _unknown(action, ["list","list_attributes","get_current","create","load","save","close","delete","import_project","export_project","archive","restore","lint","diff_to_spec","plan_spec","apply_spec", *_PROJECT_KERNEL_ACTIONS])
|
|
15780
15898
|
|
|
15781
15899
|
|
|
15782
15900
|
# ═══════════════════════════════════════════════════════════════════════════════
|