davinci-resolve-mcp 2.71.1 → 2.72.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/CHANGELOG.md +93 -0
- package/README.md +7 -7
- package/docs/contributing.md +2 -1
- package/docs/reference/api-coverage.md +77 -12
- package/docs/reference/api-limitations.md +35 -4
- package/docs/reference/resolve_scripting_api.txt +116 -10
- package/install.py +1 -1
- package/package.json +1 -1
- package/src/granular/common.py +1 -1
- package/src/server.py +96 -37
- package/src/utils/api_truth.py +135 -10
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,99 @@
|
|
|
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.72.1
|
|
6
|
+
|
|
7
|
+
Documentation only. The API coverage page stated its method counts in four
|
|
8
|
+
places and they disagreed; the cause turned out to be structural rather than
|
|
9
|
+
clerical.
|
|
10
|
+
|
|
11
|
+
### The Resolve 21 surface was never counted
|
|
12
|
+
|
|
13
|
+
None of the Resolve 21 methods appeared in the Complete API Reference tables —
|
|
14
|
+
the tables the `API Methods Covered` denominator counts — although the server
|
|
15
|
+
has wrapped, released and live-tested them since v2.28.1. So `337/337 (100%)`
|
|
16
|
+
described a surface that excluded nine methods across four classes, and the
|
|
17
|
+
`336 → 337` bump for `ResetIntellisearchAnalysis` added one to a count whose
|
|
18
|
+
table did not list it.
|
|
19
|
+
|
|
20
|
+
Thirteen rows added, one per method per object class, since a wrapper on
|
|
21
|
+
`Folder` and one on `MediaPoolItem` can fail independently. Signatures taken
|
|
22
|
+
from the bundled 21.0.2 scripting reference. `TranscribeAudio` was already
|
|
23
|
+
listed — its Resolve 21 change is the optional `useSpeakerDetection` argument,
|
|
24
|
+
accepted but producing identical transcripts either way — so those rows are
|
|
25
|
+
annotated rather than duplicated.
|
|
26
|
+
|
|
27
|
+
Every summary figure is now derived from the tables: **349 covered, 338 live
|
|
28
|
+
tested, 11 untested**.
|
|
29
|
+
|
|
30
|
+
### The counting convention is now written down
|
|
31
|
+
|
|
32
|
+
Two of the three disagreements came from it being implicit:
|
|
33
|
+
|
|
34
|
+
- A method that could not be executed is **not** counted as a pass. The old
|
|
35
|
+
"Resolve 21 delta 8/9" counted Extras-blocked methods as passes, contradicting
|
|
36
|
+
the prose directly above it and overstating coverage exactly where the risk is
|
|
37
|
+
highest.
|
|
38
|
+
- The phase table counts **methods, not assertions**, which is why its Total
|
|
39
|
+
equals Methods Live Tested. That ambiguity is what made two figures look
|
|
40
|
+
independently wrong.
|
|
41
|
+
|
|
42
|
+
`tests/test_api_coverage_arithmetic.py` derives all four figures from the
|
|
43
|
+
reference tables and fails if any disagrees, so the tables stay the single
|
|
44
|
+
source. It was checked against both drift shapes: a hand-edited summary figure,
|
|
45
|
+
and a row removed from a table.
|
|
46
|
+
|
|
47
|
+
## What's New in v2.72.0
|
|
48
|
+
|
|
49
|
+
Resolve 21's AI methods report a missing Extras pack as an error *string*, not
|
|
50
|
+
the documented bool — and a non-empty string is truthy. Live-validated against
|
|
51
|
+
Studio 21.0.2.4 by @AghisSs in #107.
|
|
52
|
+
|
|
53
|
+
### The trap
|
|
54
|
+
|
|
55
|
+
The methods do not agree on how they refuse. With only AI Motion Deblur
|
|
56
|
+
installed:
|
|
57
|
+
|
|
58
|
+
| Method | Return when the pack is absent |
|
|
59
|
+
|---|---|
|
|
60
|
+
| `AnalyzeForSlate` | `False` |
|
|
61
|
+
| `AnalyzeForIntellisearch` | `"Required package 'AI Intellisearch - Faster' is not installed."` |
|
|
62
|
+
| `GenerateSpeech` | `"Required Package, 'AI Speech Generator' is not Installed."` |
|
|
63
|
+
|
|
64
|
+
So `bool(result)` reported **success for analysis that never ran** across eight
|
|
65
|
+
call sites, and `generate_speech` let the string past its guard into
|
|
66
|
+
`.GetName()`, raising `AttributeError: 'str' object has no attribute 'GetName'`.
|
|
67
|
+
|
|
68
|
+
`_ai_result` / `_ai_result_payload` now treat any string as a failure and
|
|
69
|
+
surface its text as the error. That message is the only machine-readable signal
|
|
70
|
+
that a pack is missing, since nothing in the scripting API enumerates installed
|
|
71
|
+
Extras.
|
|
72
|
+
|
|
73
|
+
`remove_motion_blur` is routed through the same helper. It needs the AI Motion
|
|
74
|
+
Deblur Extra like its siblings and reproduced *both* failures — the
|
|
75
|
+
`AttributeError` on the clip path, and a silent `success: true` with
|
|
76
|
+
`created: []` in the confirm-gated folder path that renders new media. Both were
|
|
77
|
+
live-tested with the Extra installed, so the absent-pack return was never
|
|
78
|
+
observable.
|
|
79
|
+
|
|
80
|
+
### Also
|
|
81
|
+
|
|
82
|
+
- `project_settings("reset_intellisearch_analysis")` — documented in the 21.0.2
|
|
83
|
+
scripting README and present in `dir(project)`, but absent from the copy the
|
|
84
|
+
repo bundled, so it was never wrapped.
|
|
85
|
+
- A live validation harness for the Resolve 21 delta, source-safe: synthetic
|
|
86
|
+
media in a temp dir, disposable project, teardown that restores the
|
|
87
|
+
originally-open project.
|
|
88
|
+
- `api_truth` entries for the string-return bug, the undiscoverable Extras gap,
|
|
89
|
+
and `AnalyzeForSlate`'s documented `resolve.MARKER_*` constants, which do not
|
|
90
|
+
exist on the handle at all.
|
|
91
|
+
- The `hasattr` attribute-fabrication entry is scoped as **unresolved**. The
|
|
92
|
+
21.0.2.4 control probe used an invented name, while the 21.0.0 evidence it
|
|
93
|
+
overturns used real method names borrowed from other object types — so it does
|
|
94
|
+
not refute the original record. `_has_method` is what every `_requires_method`
|
|
95
|
+
version gate is built on, and a gate that silently passes is the failure this
|
|
96
|
+
ledger exists to prevent.
|
|
97
|
+
|
|
5
98
|
## What's New in v2.71.1
|
|
6
99
|
|
|
7
100
|
`Timeline.DeleteClips` can lie about whether it worked. #111 recorded four
|
package/README.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# DaVinci Resolve MCP Server
|
|
2
2
|
|
|
3
|
-
[](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
|
|
4
4
|
[](https://www.npmjs.com/package/davinci-resolve-mcp)
|
|
5
5
|
[](docs/reference/api-coverage.md)
|
|
6
6
|
[-blue.svg)](#server-modes)
|
|
7
7
|
[-18%20tools-blueviolet.svg)](#server-modes)
|
|
8
|
-
[](docs/reference/api-coverage.md#test-results)
|
|
9
9
|
[](https://www.blackmagicdesign.com/products/davinciresolve)
|
|
10
10
|
[](https://www.python.org/downloads/)
|
|
11
11
|
[](https://opensource.org/licenses/MIT)
|
|
@@ -252,10 +252,10 @@ The default server is a local stdio process launched by your MCP client; it does
|
|
|
252
252
|
| MCP Tools | **34** compound / **341** granular (live server) |
|
|
253
253
|
| Advanced (offline) tools | **18** — .drp/.drt/.drx + DB authoring, no Resolve running |
|
|
254
254
|
| Kernel Actions | **136** guarded workflow actions across 9 compound tools |
|
|
255
|
-
| API Methods Covered | **
|
|
256
|
-
| Methods Live Tested | **
|
|
257
|
-
| Live Test Pass Rate | **
|
|
258
|
-
| Tested Against | DaVinci Resolve 19.1.3 Studio + Resolve 20.3.2 Studio |
|
|
255
|
+
| API Methods Covered | **349/349** (100%) |
|
|
256
|
+
| Methods Live Tested | **338/349** (96.8%) |
|
|
257
|
+
| Live Test Pass Rate | **338/338** (100%) |
|
|
258
|
+
| Tested Against | DaVinci Resolve 19.1.3 Studio + Resolve 20.3.2 Studio + Resolve 21.0.2 Studio |
|
|
259
259
|
|
|
260
260
|
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).
|
|
261
261
|
|
|
@@ -289,7 +289,7 @@ Extension authoring references live in [docs/authoring](docs/authoring/). Resolv
|
|
|
289
289
|
preference has no effect — use the [in-app bridge](#free-edition-in-app-bridge)
|
|
290
290
|
instead.
|
|
291
291
|
|
|
292
|
-
Resolve 19.1.3 remains the compatibility baseline. Resolve 20.x scripting calls are additive, version-guarded, and live-tested on 20.3.2. Resolve 21.0 scripting additions (audio classification, speaker-detection transcription, IntelliSearch, slate analysis, motion-deblur, speech generation, session background-task control) are exposed behind runtime capability detection, so they stay inert on older builds and activate automatically on Resolve 21+.
|
|
292
|
+
Resolve 19.1.3 remains the compatibility baseline. Resolve 20.x scripting calls are additive, version-guarded, and live-tested on 20.3.2. Resolve 21.0 scripting additions (audio classification, speaker-detection transcription, IntelliSearch, slate analysis, motion-deblur, speech generation, session background-task control) are exposed behind runtime capability detection, so they stay inert on older builds and activate automatically on Resolve 21+. They are live-tested on Studio 21.0.2.4 — see the [Resolve 21 delta](docs/reference/api-coverage.md#resolve-21-delta-detail). Note that `AnalyzeForIntellisearch`, `AnalyzeForSlate` and `GenerateSpeech` each require a separately-downloaded AI Extras pack, and Resolve reports a missing pack inconsistently (some return `False`, others an error string), so these actions report `success: false` with the Resolve-supplied reason rather than guessing.
|
|
293
293
|
|
|
294
294
|
## Development
|
|
295
295
|
|
package/docs/contributing.md
CHANGED
|
@@ -67,7 +67,8 @@ davinci-resolve-mcp/
|
|
|
67
67
|
│ ├── resolve_mcp_server.py # Thin full-server entrypoint — 341 tools
|
|
68
68
|
│ ├── granular/ # Modular full-server implementation
|
|
69
69
|
│ └── utils/ # Platform detection, Resolve connection helpers
|
|
70
|
-
├── tests/ #
|
|
70
|
+
├── tests/ # offline suite (test_*.py) + live harnesses (live_*.py):
|
|
71
|
+
│ # 5-phase live API suite + Resolve 20/21 deltas
|
|
71
72
|
├── docs/
|
|
72
73
|
│ ├── README.md # Documentation index
|
|
73
74
|
│ ├── SKILL.md # AI assistant operating reference
|
|
@@ -6,20 +6,20 @@ Complete Resolve scripting API coverage, live-test status, and method-by-method
|
|
|
6
6
|
|
|
7
7
|
| Metric | Value |
|
|
8
8
|
|--------|-------|
|
|
9
|
-
| MCP Tools | **
|
|
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 | **
|
|
13
|
-
| Live Test Pass Rate | **
|
|
11
|
+
| API Methods Covered | **349/349** (100%) |
|
|
12
|
+
| Methods Live Tested | **338/349** (96.8%) |
|
|
13
|
+
| Live Test Pass Rate | **338/338** (100%) |
|
|
14
14
|
| API Object Classes | 13 |
|
|
15
|
-
| Tested Against | DaVinci Resolve 19.1.3 Studio + Resolve 20.3.2 Studio |
|
|
16
|
-
| Compatibility Note | Resolve 19.1.3 remains the compatibility baseline; Resolve 20.x scripting calls are additive, version-guarded, and live-tested on 20.3.2; Resolve 21
|
|
15
|
+
| Tested Against | DaVinci Resolve 19.1.3 Studio + Resolve 20.3.2 Studio + Resolve 21.0.2 Studio |
|
|
16
|
+
| Compatibility Note | Resolve 19.1.3 remains the compatibility baseline; Resolve 20.x scripting calls are additive, version-guarded, and live-tested on 20.3.2; Resolve 21.0 additions are version-guarded and live-tested on 21.0.2 (see the Resolve 21 delta row below — five wrappers need AI Extras packs and stay untested without one, and one is deliberately not executed) |
|
|
17
17
|
|
|
18
18
|
## API Coverage
|
|
19
19
|
|
|
20
20
|
Every non-deprecated method in the DaVinci Resolve Scripting API is covered. The default compound server exposes **34 tools** that group related operations by action parameter, keeping LLM context windows lean. The full granular server provides **341 individual tools** for power users. Both modes cover all 13 API object classes. MCP-level kernel actions are tracked separately in [Kernel Action Coverage](../kernels/README.md).
|
|
21
21
|
|
|
22
|
-
The
|
|
22
|
+
The 34th compound tool is `timeline_versioning` (C6) — an MCP-level workflow
|
|
23
23
|
tool, not a wrapper around a Resolve API method. It surfaces the
|
|
24
24
|
version-on-mutate hook that auto-archives the working timeline before any
|
|
25
25
|
destructive op, plus rollback and brain-edit history. See [SKILL.md](../SKILL.md)
|
|
@@ -81,7 +81,24 @@ guard, so it never goes stale.
|
|
|
81
81
|
|
|
82
82
|
## Test Results
|
|
83
83
|
|
|
84
|
-
Baseline testing was performed against **DaVinci Resolve 19.1.3 Studio** on macOS with live API calls (no mocks). Resolve 20 additions were revalidated live against **DaVinci Resolve 20.3.2 Studio**.
|
|
84
|
+
Baseline testing was performed against **DaVinci Resolve 19.1.3 Studio** on macOS with live API calls (no mocks). Resolve 20 additions were revalidated live against **DaVinci Resolve 20.3.2 Studio**, Resolve 21 additions against **Studio 21.0.2.4**.
|
|
85
|
+
|
|
86
|
+
**Counting convention.** Every figure on this page is derived from the rows of
|
|
87
|
+
the [Complete API Reference](#complete-api-reference) tables — that table is the
|
|
88
|
+
source, these summaries are downstream of it:
|
|
89
|
+
|
|
90
|
+
- **API Methods Covered** = the number of rows. One row per method per object
|
|
91
|
+
class, so `PerformAudioClassification` on both `Folder` and `MediaPoolItem`
|
|
92
|
+
counts twice, because they are two wrappers that can fail independently.
|
|
93
|
+
- **Methods Live Tested** = rows marked ✅ or ⚠️ — the call was made against a
|
|
94
|
+
live Resolve and the result observed.
|
|
95
|
+
- **Untested** = rows marked ☁️ or 🔬. A method that could not be executed is
|
|
96
|
+
never counted as a pass: a missing Extras pack tells you nothing about the
|
|
97
|
+
wrapper, so counting it as tested would overstate coverage in exactly the
|
|
98
|
+
place the risk is highest.
|
|
99
|
+
- **The phase table below counts methods, not test cases**, which is why its
|
|
100
|
+
Total equals Methods Live Tested rather than the number of assertions run.
|
|
101
|
+
|
|
85
102
|
|
|
86
103
|
| Phase | Tests | Pass Rate | Scope |
|
|
87
104
|
|-------|-------|-----------|-------|
|
|
@@ -91,9 +108,32 @@ Baseline testing was performed against **DaVinci Resolve 19.1.3 Studio** on macO
|
|
|
91
108
|
| Phase 4 | 10/10 | 100% | AI/ML methods, Fusion clips, stereo, gallery stills |
|
|
92
109
|
| Phase 5 | 6/6 | 100% | Scene cuts, subtitles from audio, graph node cache/tools/enable |
|
|
93
110
|
| Resolve 20 delta | 12/12 | 100% | Resolve 20.0-20.2.2 scripting additions live-tested on 20.3.2 |
|
|
94
|
-
|
|
|
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%** | **96.8% of the 349 covered methods tested live** |
|
|
113
|
+
|
|
114
|
+
#### Resolve 21 delta detail
|
|
115
|
+
|
|
116
|
+
Run on Studio 21.0.2.4, macOS/Apple Silicon, with **only the AI Motion Deblur
|
|
117
|
+
Extra installed**. Three of these methods require an Extras pack that was
|
|
118
|
+
absent, so their result says nothing about the wrapper — that is why they are
|
|
119
|
+
marked 🔬 rather than ⚠️.
|
|
120
|
+
|
|
121
|
+
| Method | Result | Evidence |
|
|
122
|
+
|--------|--------|----------|
|
|
123
|
+
| `MediaPoolItem/Folder.PerformAudioClassification` | ✅ | Returned True; `Category` clip property went `""` → `Dialogue` |
|
|
124
|
+
| `MediaPoolItem/Folder.ClearAudioClassification` | ✅ | Returned True; `Category` reset to `Uncategorized` (not `""`) |
|
|
125
|
+
| `MediaPoolItem/Folder.TranscribeAudio(useSpeakerDetection)` | ⚠️ | Parameter accepted; `True` and `False` produced identical transcripts on a two-voice clip |
|
|
126
|
+
| `MediaPoolItem/Folder.RemoveMotionBlur` | ✅ | Returned a MediaPoolItem; source media path unchanged |
|
|
127
|
+
| `Project.ResetIntellisearchAnalysis` | ✅ | Returned True — new in the 21.0.2 scripting doc, previously unwrapped |
|
|
128
|
+
| `MediaPoolItem/Folder.AnalyzeForIntellisearch` | 🔬 | Requires AI IntelliSearch; returned an error **string**, not False — see api-limitations |
|
|
129
|
+
| `MediaPoolItem/Folder.AnalyzeForSlate` | 🔬 | Requires AI Slate ID; returned False. Documented `resolve.MARKER_*` constants do not exist on the handle |
|
|
130
|
+
| `Project.GenerateSpeech` | 🔬 | Requires AI Speech Generator; returned an error **string**, not a MediaPoolItem |
|
|
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
|
+
|
|
133
|
+
### Untested Methods (11 of 349)
|
|
95
134
|
|
|
96
|
-
|
|
135
|
+
Every ☁️ and 🔬 row from the reference tables, listed here so the count is
|
|
136
|
+
checkable rather than asserted.
|
|
97
137
|
|
|
98
138
|
| Method | Reason | Help Wanted |
|
|
99
139
|
|--------|--------|-------------|
|
|
@@ -102,6 +142,18 @@ Baseline testing was performed against **DaVinci Resolve 19.1.3 Studio** on macO
|
|
|
102
142
|
| `PM.ImportCloudProject` | Requires DaVinci Resolve cloud infrastructure | Yes |
|
|
103
143
|
| `PM.RestoreCloudProject` | Requires DaVinci Resolve cloud infrastructure | Yes |
|
|
104
144
|
| `TL.AnalyzeDolbyVision` | Requires HDR/Dolby Vision content | Yes |
|
|
145
|
+
| `Folder.AnalyzeForIntellisearch` | Requires the AI IntelliSearch Extra | Yes |
|
|
146
|
+
| `MPI.AnalyzeForIntellisearch` | Requires the AI IntelliSearch Extra | Yes |
|
|
147
|
+
| `Folder.AnalyzeForSlate` | Requires the AI Slate ID Extra | Yes |
|
|
148
|
+
| `MPI.AnalyzeForSlate` | Requires the AI Slate ID Extra | Yes |
|
|
149
|
+
| `Project.GenerateSpeech` | Requires the AI Speech Generator Extra | Yes |
|
|
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
|
+
|
|
152
|
+
The five AI Extras rows are untested for want of a downloadable pack, not because
|
|
153
|
+
the wrappers are suspect — a report from anyone who has the packs installed would
|
|
154
|
+
close them. The last one is a decision rather than a gap: it is reachable, and
|
|
155
|
+
running it during a validation sweep would disable background tasks for every
|
|
156
|
+
project open in that Resolve instance.
|
|
105
157
|
|
|
106
158
|
---
|
|
107
159
|
|
|
@@ -141,6 +193,7 @@ Every method in the DaVinci Resolve Scripting API and its test status. Methods a
|
|
|
141
193
|
| 20 | `GetKeyframeMode()` | ✅ | Returns keyframe mode |
|
|
142
194
|
| 21 | `SetKeyframeMode(keyframeMode)` | ⚠️ | API accepts; mode must match valid enum |
|
|
143
195
|
| 22 | `GetFairlightPresets()` | ✅ | Resolve 20.3.2 live test returns preset map |
|
|
196
|
+
| 23 | `DisableBackgroundTasksForCurrentResolveSession()` | 🔬 | Resolve 21.0. Present in `dir()`; **not executed** — session-wide, returns `None`, no `Enable...` counterpart, so no undo short of restarting Resolve |
|
|
144
197
|
|
|
145
198
|
### ProjectManager
|
|
146
199
|
|
|
@@ -219,6 +272,8 @@ Every method in the DaVinci Resolve Scripting API and its test status. Methods a
|
|
|
219
272
|
| 41 | `AddColorGroup(groupName)` | ✅ | Returns ColorGroup object |
|
|
220
273
|
| 42 | `DeleteColorGroup(colorGroup)` | ✅ | Deletes color group |
|
|
221
274
|
| 43 | `ApplyFairlightPresetToCurrentTimeline(presetName)` | ⚠️ | Resolve 20.3.2 live test accepts call; returns `False` without a named preset |
|
|
275
|
+
| 44 | `GenerateSpeech({speechGenerationSettings}, timecode)` | 🔬 | Resolve 21.0. Requires the AI Speech Generator Extra; without it returns an error **string**, not a MediaPoolItem |
|
|
276
|
+
| 45 | `ResetIntellisearchAnalysis()` | ✅ | Resolve 21.0.2 live test returns `True` |
|
|
222
277
|
|
|
223
278
|
### MediaStorage
|
|
224
279
|
|
|
@@ -274,8 +329,13 @@ Every method in the DaVinci Resolve Scripting API and its test status. Methods a
|
|
|
274
329
|
| 4 | `GetIsFolderStale()` | ✅ | Returns `False` |
|
|
275
330
|
| 5 | `GetUniqueId()` | ✅ | Returns UUID string |
|
|
276
331
|
| 6 | `Export(filePath)` | ✅ | Exports DRB file |
|
|
277
|
-
| 7 | `TranscribeAudio()` | ✅ | Starts audio transcription |
|
|
332
|
+
| 7 | `TranscribeAudio({useSpeakerDetection})` | ✅ | Starts audio transcription. Resolve 21.0 added the optional `useSpeakerDetection` argument: it is accepted, but `True` and `False` produced identical transcripts on a deliberately two-voice clip (21.0.2.4) — the method passes, the parameter has no observable effect |
|
|
278
333
|
| 8 | `ClearTranscription()` | ✅ | Clears transcription |
|
|
334
|
+
| 9 | `PerformAudioClassification()` | ✅ | Resolve 21.0.2 live test returns `True`; `Category` clip property goes `""` → `Dialogue` |
|
|
335
|
+
| 10 | `ClearAudioClassification()` | ✅ | Resolve 21.0.2 live test returns `True`; `Category` resets to `Uncategorized`, not `""` |
|
|
336
|
+
| 11 | `AnalyzeForIntellisearch(identifyFaces, isBetterMode)` | 🔬 | Resolve 21.0. Requires the AI IntelliSearch Extra; without it returns an error **string**, not `False` |
|
|
337
|
+
| 12 | `AnalyzeForSlate(markerColor)` | 🔬 | Resolve 21.0. Requires the AI Slate ID Extra. Documented `resolve.MARKER_*` constants do not exist on the handle |
|
|
338
|
+
| 13 | `RemoveMotionBlur({deblurOption})` | ✅ | Resolve 21.0.2 live test returns original→new pairs; source media unchanged. Requires the AI Motion Deblur Extra |
|
|
279
339
|
|
|
280
340
|
### MediaPoolItem
|
|
281
341
|
|
|
@@ -307,7 +367,7 @@ Every method in the DaVinci Resolve Scripting API and its test status. Methods a
|
|
|
307
367
|
| 24 | `UnlinkProxyMedia()` | ✅ | Unlinks proxy media |
|
|
308
368
|
| 25 | `ReplaceClip(filePath)` | ✅ | Replaces clip source |
|
|
309
369
|
| 26 | `GetUniqueId()` | ✅ | Returns UUID string |
|
|
310
|
-
| 27 | `TranscribeAudio()` | ✅ | Starts audio transcription |
|
|
370
|
+
| 27 | `TranscribeAudio({useSpeakerDetection})` | ✅ | Starts audio transcription. Resolve 21.0 added the optional `useSpeakerDetection` argument: it is accepted, but `True` and `False` produced identical transcripts on a deliberately two-voice clip (21.0.2.4) — the method passes, the parameter has no observable effect |
|
|
311
371
|
| 28 | `ClearTranscription()` | ✅ | Clears transcription |
|
|
312
372
|
| 29 | `GetAudioMapping()` | ✅ | Returns JSON audio mapping |
|
|
313
373
|
| 30 | `GetMarkInOut()` | ✅ | Returns mark in/out dict |
|
|
@@ -317,6 +377,11 @@ Every method in the DaVinci Resolve Scripting API and its test status. Methods a
|
|
|
317
377
|
| 34 | `LinkFullResolutionMedia(filePath)` | ⚠️ | Resolve 20.3.2 live test accepts call; full-res relink returns `False` without a matching proxy/full-res fixture |
|
|
318
378
|
| 35 | `ReplaceClipPreserveSubClip(filePath)` | ✅ | Resolve 20.3.2 live test replaces clip while preserving subclip metadata |
|
|
319
379
|
| 36 | `MonitorGrowingFile()` | ✅ | Resolve 20.3.2 live test enables growing-file monitoring |
|
|
380
|
+
| 37 | `PerformAudioClassification()` | ✅ | Resolve 21.0.2 live test returns `True`; `Category` clip property goes `""` → `Dialogue` |
|
|
381
|
+
| 38 | `ClearAudioClassification()` | ✅ | Resolve 21.0.2 live test returns `True`; `Category` resets to `Uncategorized`, not `""` |
|
|
382
|
+
| 39 | `AnalyzeForIntellisearch(identifyFaces, isBetterMode)` | 🔬 | Resolve 21.0. Requires the AI IntelliSearch Extra; without it returns an error **string**, not `False` |
|
|
383
|
+
| 40 | `AnalyzeForSlate(markerColor)` | 🔬 | Resolve 21.0. Requires the AI Slate ID Extra. Documented `resolve.MARKER_*` constants do not exist on the handle |
|
|
384
|
+
| 41 | `RemoveMotionBlur({deblurOption})` | ✅ | Resolve 21.0.2 live test returns the new MediaPoolItem; source media path unchanged. Requires the AI Motion Deblur Extra |
|
|
320
385
|
|
|
321
386
|
### Timeline
|
|
322
387
|
|
|
@@ -10,9 +10,9 @@ submission to Blackmagic Design's developer feedback. Every item was
|
|
|
10
10
|
observed against live Resolve; each entry notes the current workaround (or
|
|
11
11
|
that none exists).
|
|
12
12
|
|
|
13
|
-
**Verified on:** DaVinci Resolve Studio 21.0.
|
|
13
|
+
**Verified on:** DaVinci Resolve Studio 21.0.2
|
|
14
14
|
|
|
15
|
-
**Totals:**
|
|
15
|
+
**Totals:** 25 missing capabilities, 24 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
|
|
@@ -192,6 +192,21 @@ equivalent, blocking full automation.
|
|
|
192
192
|
- **Workaround / current handling:** Delete and recreate the folder with the desired name, or rename in the Resolve UI.
|
|
193
193
|
- **Tags:** missing-method, media-pool, folder
|
|
194
194
|
|
|
195
|
+
### Installed AI Extras packs are not discoverable from scripting
|
|
196
|
+
|
|
197
|
+
- **Object:** `Resolve`
|
|
198
|
+
- **Behavior:** AnalyzeForIntellisearch, AnalyzeForSlate, GenerateSpeech and RemoveMotionBlur each require a separately-downloaded Extras pack, but nothing in the scripting API reports which packs are installed. A caller cannot distinguish 'the Extra is missing' from 'the analysis ran and found nothing' ahead of time; on 21.0.2.4 two of the four leak the reason only as free text in the return value, and AnalyzeForSlate's bare False carries no reason at all.
|
|
199
|
+
- **Workaround / current handling:** Until an API exists, treat a string return as the reason and read the pack names out of the Extras directory (Blackmagic Design/DaVinci Resolve/Extras/*/log.dpl1) for diagnostics only — that path is undocumented and may change.
|
|
200
|
+
- **Tags:** ai, extras, introspection, resolve-21
|
|
201
|
+
|
|
202
|
+
### Resolve.DisableBackgroundTasksForCurrentResolveSession
|
|
203
|
+
|
|
204
|
+
- **Object:** `Resolve`
|
|
205
|
+
- **Signature:** `() -> None`
|
|
206
|
+
- **Behavior:** Returns None, so a caller cannot tell whether it took effect, and there is no Enable... counterpart anywhere in the shipped 21.0.2 scripting README — the only documented way back is restarting Resolve. The scope is the whole session, so a script disables background tasks for every project open in that instance, not just its own. Present in dir(resolve) on Studio 21.0.2.4; deliberately not executed during validation for exactly that reason.
|
|
207
|
+
- **Workaround / current handling:** Treat as irreversible within a session. server returns _ok() unconditionally because there is nothing to check.
|
|
208
|
+
- **Tags:** resolve-21, unreliable-return, irreversible, session-wide
|
|
209
|
+
|
|
195
210
|
### MediaPool.ImportMedia (current-folder destination only)
|
|
196
211
|
|
|
197
212
|
- **Object:** `MediaPool`
|
|
@@ -322,10 +337,26 @@ values, or automation-hostile modal prompts.
|
|
|
322
337
|
### hasattr() / getattr() on Resolve API objects (attribute fabrication)
|
|
323
338
|
|
|
324
339
|
- **Object:** `(all Resolve scripting objects)`
|
|
325
|
-
- **Behavior:**
|
|
326
|
-
- **Workaround / current handling:**
|
|
340
|
+
- **Behavior:** UNRESOLVED — the two measurements do not test the same thing. On 21.0.0 the bridge was recorded as returning a callable for ANY attribute name, making capability detection by hasattr impossible; the evidence was REAL API method names borrowed from other object types (SetStart, Razor, AddNode, GenerateProxy, AddSmartBin reported present on objects that do not have them). A 21.0.2.4 control probe of the invented name 'TotallyMadeUpMethod_xyz123' returned getattr-callable False on all eight object types, matching dir() in every case. That does NOT refute the 21.0.0 record: if the bridge resolves any name known to the RemoteObject method table rather than literally any string, an invented name is correctly rejected on both builds and the probe never exercised the failing case. Re-running the probe with those five real names is what would settle it; until then, assume fabrication is possible.
|
|
341
|
+
- **Workaround / current handling:** Use dir(obj) membership for capability probes. It is correct on every build measured, and it is the only form not affected by whichever way this resolves. server._has_method uses hasattr/getattr and so may over-report on builds where fabrication is live — that is the case _requires_method gates guard, so it matters most exactly where it is least tested. Calling a fabricated method typically returns None/False with no error.
|
|
327
342
|
- **Tags:** bridge, introspection, silent-failure
|
|
328
343
|
|
|
344
|
+
### Resolve 21 AI methods (AnalyzeForIntellisearch, GenerateSpeech, AnalyzeForSlate) — inconsistent failure return type
|
|
345
|
+
|
|
346
|
+
- **Object:** `MediaPoolItem / Folder / Project`
|
|
347
|
+
- **Signature:** `-> Bool (documented)`
|
|
348
|
+
- **Behavior:** When the required Extras pack is not installed, these methods do not agree on how they say so, and the documented Bool is not what you get. Verified live on Studio 21.0.2.4 with only AI Motion Deblur installed: AnalyzeForSlate returned False, but AnalyzeForIntellisearch returned the STRING "Required package 'AI Intellisearch - Faster' is not installed." and GenerateSpeech returned the STRING "Required Package, 'AI Speech Generator' is not Installed.". A non-empty string is truthy in Python, so bool(result) reports SUCCESS for a call that definitively did not run, and treating GenerateSpeech's return as a MediaPoolItem raises AttributeError: 'str' object has no attribute 'GetName'.
|
|
349
|
+
- **Workaround / current handling:** Never bool() an AI-method return directly. Route it through server._ai_result / _ai_result_payload, which treat any string as a failure and surface its text as the error — the message is the only machine-readable signal that an Extras pack is missing, since there is no scripting API to enumerate installed Extras.
|
|
350
|
+
- **Tags:** ai, extras, unreliable-return, silent-failure, resolve-21
|
|
351
|
+
|
|
352
|
+
### Folder.AnalyzeForSlate / MediaPoolItem.AnalyzeForSlate markerColor
|
|
353
|
+
|
|
354
|
+
- **Object:** `MediaPoolItem / Folder`
|
|
355
|
+
- **Signature:** `(markerColor) -> Bool`
|
|
356
|
+
- **Behavior:** The shipped 21.0.2 scripting README says markerColor must be one of the resolve.MARKER_* constants (resolve.MARKER_BLUE etc.). Those constants do not exist: on Studio 21.0.2.4, [c for c in dir(resolve) if c.startswith('MARKER_')] is empty. There is therefore no documented-correct way to call this method. The plain colour string the server passes is the only option available, and it returns False here — though with AI Slate ID absent, a string-rejection bug cannot be distinguished from the missing pack on this machine.
|
|
357
|
+
- **Workaround / current handling:** Keep passing the plain colour name (server._MARKER_COLORS) — the documented constants are unavailable. Re-test on a machine with the AI Slate ID Extra installed before concluding the string form is rejected.
|
|
358
|
+
- **Tags:** ai, extras, missing-constant, documentation, resolve-21
|
|
359
|
+
|
|
329
360
|
### MediaPoolItem.SetClipProperty('Reel Name', ...)
|
|
330
361
|
|
|
331
362
|
- **Object:** `MediaPoolItem`
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Last Updated:
|
|
1
|
+
Last Updated: 26 May 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.
|
|
@@ -86,8 +86,8 @@ Resolve
|
|
|
86
86
|
Fusion() --> Fusion # Returns the Fusion object. Starting point for Fusion scripts.
|
|
87
87
|
GetMediaStorage() --> MediaStorage # Returns the media storage object to query and act on media locations.
|
|
88
88
|
GetProjectManager() --> ProjectManager # Returns the project manager object for currently open database.
|
|
89
|
-
OpenPage(pageName) --> Bool # Switches to indicated page in DaVinci Resolve. Input can be one of ("media", "cut", "edit", "fusion", "color", "fairlight", "deliver").
|
|
90
|
-
GetCurrentPage() --> String # Returns the page currently displayed in the main window. Returned value can be one of ("media", "cut", "edit", "fusion", "color", "fairlight", "deliver", None).
|
|
89
|
+
OpenPage(pageName) --> Bool # Switches to indicated page in DaVinci Resolve. Input can be one of ("media", "photo", "cut", "edit", "fusion", "color", "fairlight", "deliver").
|
|
90
|
+
GetCurrentPage() --> String # Returns the page currently displayed in the main window. Returned value can be one of ("media", "photo", "cut", "edit", "fusion", "color", "fairlight", "deliver", None).
|
|
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.
|
|
@@ -174,13 +174,13 @@ Project
|
|
|
174
174
|
SaveAsNewRenderPreset(presetName) --> Bool # Creates new render preset by given name if presetName(string) is unique.
|
|
175
175
|
DeleteRenderPreset(presetName) --> Bool # Delete render preset by provided name.
|
|
176
176
|
SetRenderSettings({settings}) --> Bool # Sets given settings for rendering. Settings is a dict, with support for the keys:
|
|
177
|
-
# Refer to "Looking up render settings"
|
|
177
|
+
# Refer to section "Looking up render settings" for information on supported settings
|
|
178
178
|
GetRenderJobStatus(jobId) --> {status info} # Returns a dict with job status and completion percentage of the job by given jobId (string).
|
|
179
179
|
GetQuickExportRenderPresets() --> [preset_name..] # Returns a list of Quick Export render presets by name
|
|
180
180
|
RenderWithQuickExport(preset_name, {param_dict})--> {status info} # Starts a quick export render for the current active timeline. preset_name from GetQuickExportRenderPresets list. param_dict supports render settings keys "TargetDir", "CustomName", "VideoQuality", and "EnableUpload".
|
|
181
181
|
# "EnableUpload" key enables direct upload for supported web presets.
|
|
182
182
|
# Returns a dict with job status and time taken to render, or an error string if render has failed or not attempted
|
|
183
|
-
# Refer to "Looking up Render Settings"
|
|
183
|
+
# Refer to section "Looking up Render Settings" for information on the above supported settings
|
|
184
184
|
GetSetting(settingName) --> string # Returns value of project setting (indicated by settingName, string). Check the section below for more information.
|
|
185
185
|
SetSetting(settingName, settingValue) --> Bool # Sets the project setting (indicated by settingName, string) to the value (settingValue, string). Check the section below for more information.
|
|
186
186
|
GetRenderFormats() --> {render formats..} # Returns a dict (format -> file extension) of available render formats.
|
|
@@ -200,7 +200,11 @@ Project
|
|
|
200
200
|
AddColorGroup(groupName) --> ColorGroup # Creates a new ColorGroup. groupName must be a unique string.
|
|
201
201
|
DeleteColorGroup(colorGroup) --> Bool # Deletes the given color group and sets clips to ungrouped.
|
|
202
202
|
ApplyFairlightPresetToCurrentTimeline(name) --> Bool # Apply Fairlight Preset of given name to the current timeline, returns True if successful, False otherwise.
|
|
203
|
+
ResetIntellisearchAnalysis() --> Bool # Clears Intellisearch analysis data. Returns True if successful.
|
|
204
|
+
# Refer to section "Studio and AI Scripting APIs" for prerequisites.
|
|
203
205
|
GenerateSpeech({speechGenerationSettings}, timecode) --> MediaPoolItem # Generates an audio MediaPoolItem based on the given speechGenerationSettings and adds it to the timeline at the stated timecode if "AddToTimeline" is True. Returns the newly generated MediaPoolItem.
|
|
206
|
+
# Refer to section "Speech Generation Settings" for information on supported settings
|
|
207
|
+
# Refer to section "Studio and AI Scripting APIs" for prerequisites.
|
|
204
208
|
|
|
205
209
|
MediaStorage
|
|
206
210
|
GetMountedVolumeList() --> [paths...] # Returns list of folder paths corresponding to mounted volumes displayed in Resolve’s Media Storage.
|
|
@@ -253,6 +257,7 @@ MediaPool
|
|
|
253
257
|
GetUniqueId() --> string # Returns a unique ID for the media pool
|
|
254
258
|
CreateStereoClip(LeftMediaPoolItem,
|
|
255
259
|
RightMediaPoolItem) --> MediaPoolItem # Takes in two existing media pool items and creates a new 3D stereoscopic media pool entry replacing the input media in the media pool.
|
|
260
|
+
# Refer to section "Studio and AI Scripting APIs" for prerequisites.
|
|
256
261
|
AutoSyncAudio([MediaPoolItems], {audioSyncSettings}) --> Bool # Syncs audio for specified [MediaPoolItems] (list). The list must contain a minimum of two MediaPoolItems - at least one video and one audio clip.
|
|
257
262
|
# Returns True if successful. Refer to 'Audio Sync Settings' section for details.
|
|
258
263
|
GetSelectedClips() --> [MediaPoolItems] # Returns the current selected MediaPoolItems
|
|
@@ -266,12 +271,22 @@ Folder
|
|
|
266
271
|
GetUniqueId() --> string # Returns a unique ID for the media pool folder
|
|
267
272
|
Export(filePath) --> bool # Returns true if export of DRB folder to filePath is successful, false otherwise
|
|
268
273
|
TranscribeAudio(useSpeakerDetection=None) --> Bool # Transcribes audio of the MediaPoolItems within the folder and nested folders. Returns True if successful; False otherwise
|
|
274
|
+
# Accepts an optional boolean argument to use speaker detection when transcribing. If no argument is specified, use the project's setting.
|
|
275
|
+
# Refer to section "Studio and AI Scripting APIs" for prerequisites.
|
|
269
276
|
ClearTranscription() --> Bool # Clears audio transcription of the MediaPoolItems within the folder and nested folders. Returns True if successful; False otherwise.
|
|
277
|
+
# Refer to section "Studio and AI Scripting APIs" for prerequisites.
|
|
270
278
|
PerformAudioClassification() --> Bool # Analyzes and classifies the audio of the MediaPoolItems within the folder and nested folders into categories and subcategories.
|
|
279
|
+
# Refer to section "Studio and AI Scripting APIs" for prerequisites.
|
|
271
280
|
ClearAudioClassification() --> Bool # Clears audio classification of the MediaPoolItems within the folder and nested folders.
|
|
281
|
+
# Refer to section "Studio and AI Scripting APIs" for prerequisites.
|
|
272
282
|
RemoveMotionBlur({deblurOption}) --> [[MediaPoolItem, MediaPoolItem]...] # Applies motion deblur on MediaPoolItems in the folder. Returns a list of pairs mapping original to newly created MediaPoolItems.
|
|
283
|
+
# Refer to section "Motion Deblur Settings" for information on supported settings
|
|
284
|
+
# Refer to section "Studio and AI Scripting APIs" for prerequisites.
|
|
273
285
|
AnalyzeForIntellisearch(identifyFaces, isBetterMode) --> Bool # Performs Intellisearch analysis on all MediaPoolItems in the folder. identifyFaces specifies whether to identify faces; isBetterMode specifies whether to use Better mode. Returns True if required packages are installed and analysis is successful.
|
|
286
|
+
# Refer to section "Studio and AI Scripting APIs" for prerequisites.
|
|
274
287
|
AnalyzeForSlate(markerColor) --> Bool # Performs Slate analysis on all MediaPoolItems in the folder using the current settings and specified markerColor. Returns True if required packages are installed and analysis is successful.
|
|
288
|
+
# Refer to section "Analyze Slate Settings" for information on markerColor
|
|
289
|
+
# Refer to section "Studio and AI Scripting APIs" for prerequisites.
|
|
275
290
|
|
|
276
291
|
MediaPoolItem
|
|
277
292
|
GetName() --> string # Returns the clip name.
|
|
@@ -312,18 +327,28 @@ MediaPoolItem
|
|
|
312
327
|
ReplaceClipPreserveSubClip(filePath) --> Bool # Replaces the underlying asset and metadata of a video or audio clip with the specified absolute clip path, preserving original sub clip extents.
|
|
313
328
|
GetUniqueId() --> string # Returns a unique ID for the media pool item
|
|
314
329
|
TranscribeAudio(useSpeakerDetection=None) --> Bool # Transcribes audio of the MediaPoolItem. Returns True if successful; False otherwise
|
|
330
|
+
# Accepts an optional boolean argument to use speaker detection when transcribing. If no argument is specified, use the project's setting.
|
|
331
|
+
# Refer to section "Studio and AI Scripting APIs" for prerequisites.
|
|
315
332
|
ClearTranscription() --> Bool # Clears audio transcription of the MediaPoolItem. Returns True if successful; False otherwise.
|
|
333
|
+
# Refer to section "Studio and AI Scripting APIs" for prerequisites.
|
|
316
334
|
PerformAudioClassification() --> Bool # Analyzes and classifies the audio of a MediaPoolItem into categories and subcategories.
|
|
335
|
+
# Refer to section "Studio and AI Scripting APIs" for prerequisites.
|
|
317
336
|
ClearAudioClassification() --> Bool # Clears audio classification of the MediaPoolItem.
|
|
318
|
-
|
|
319
|
-
AnalyzeForIntellisearch(identifyFaces, isBetterMode) --> Bool # Performs Intellisearch analysis on the MediaPoolItem. identifyFaces specifies whether to identify faces; isBetterMode specifies whether to use Better mode. Returns True if required packages are installed and analysis is successful.
|
|
320
|
-
AnalyzeForSlate(markerColor) --> Bool # Performs Slate analysis on the MediaPoolItem using the current settings and specified markerColor. Returns True if required packages are installed and analysis is successful.
|
|
337
|
+
# Refer to section "Studio and AI Scripting APIs" for prerequisites.
|
|
321
338
|
GetAudioMapping() --> json formatted string # Returns a string with MediaPoolItem's audio mapping information. Check 'Audio Mapping' section below for more information.
|
|
322
339
|
GetMarkInOut() --> {mark} # Returns dict of in/out marks set (keys omitted if not set), example:
|
|
323
340
|
# {'video': {'in': 0, 'out': 134}, 'audio': {'in': 0, 'out': 134}}
|
|
324
341
|
SetMarkInOut(in, out, type="all") --> Bool # Sets mark in/out of type "video", "audio" or "all" (default).
|
|
325
342
|
ClearMarkInOut(type="all") --> Bool # Clears mark in/out of type "video", "audio" or "all" (default).
|
|
326
343
|
MonitorGrowingFile() --> Bool # Monitor a file as long as it keeps growing (stops if the file does not grow for some time).
|
|
344
|
+
RemoveMotionBlur({deblurOption}) --> MediaPoolItem # Applies motion deblur on the MediaPoolItem. Returns the newly created MediaPoolItem.
|
|
345
|
+
# Refer to section "Motion Deblur Settings" for information on supported settings.
|
|
346
|
+
# Refer to section "Studio and AI Scripting APIs" for prerequisites.
|
|
347
|
+
AnalyzeForIntellisearch(identifyFaces, isBetterMode) --> Bool # Performs Intellisearch analysis on the MediaPoolItem. identifyFaces specifies whether to identify faces; isBetterMode specifies whether to use Better mode. Returns True if required packages are installed and analysis is successful.
|
|
348
|
+
# Refer to section "Studio and AI Scripting APIs" for prerequisites.
|
|
349
|
+
AnalyzeForSlate(markerColor) --> Bool # Performs Slate analysis on the MediaPoolItem using the current settings and specified markerColor. Returns True if required packages are installed and analysis is successful.
|
|
350
|
+
# Refer to section "Analyze Slate Settings" for information on markerColor
|
|
351
|
+
# Refer to section "Studio and AI Scripting APIs" for prerequisites.
|
|
327
352
|
|
|
328
353
|
Timeline
|
|
329
354
|
GetName() --> string # Returns the timeline name.
|
|
@@ -407,12 +432,15 @@ Timeline
|
|
|
407
432
|
CreateSubtitlesFromAudio({autoCaptionSettings}) --> Bool # Creates subtitles from audio for the timeline.
|
|
408
433
|
# Takes in optional dictionary {autoCaptionSettings}. Check 'Auto Caption Settings' subsection below for more information.
|
|
409
434
|
# Returns True on success, False otherwise.
|
|
435
|
+
# Refer to section "Studio and AI Scripting APIs" for prerequisites.
|
|
410
436
|
DetectSceneCuts() --> Bool # Detects and makes scene cuts along the timeline. Returns True if successful, False otherwise.
|
|
411
437
|
ConvertTimelineToStereo() --> Bool # Converts timeline to stereo. Returns True if successful; False otherwise.
|
|
438
|
+
# Refer to section "Studio and AI Scripting APIs" for prerequisites.
|
|
412
439
|
GetNodeGraph() --> Graph # Returns the timeline's node graph object.
|
|
413
440
|
AnalyzeDolbyVision([timelineItems]=[], --> Bool # Analyzes Dolby Vision on clips present on the timeline. Returns True if analysis start is successful; False otherwise.
|
|
414
441
|
analysisType=NONE) # if [timelineItems] is empty, analysis performed on all items. Else, analysis performed on [timelineItems] only.
|
|
415
442
|
# set analysisType to resolve.DLB_BLEND_SHOTS for blend setting
|
|
443
|
+
# Refer to section "Studio and AI Scripting APIs" for prerequisites.
|
|
416
444
|
GetMediaPoolItem() --> MediaPoolItem # Returns the media pool item corresponding to the timeline
|
|
417
445
|
GetMarkInOut() --> {mark} # Returns dict of in/out marks set (keys omitted if not set), example:
|
|
418
446
|
# {'video': {'in': 0, 'out': 134}, 'audio': {'in': 0, 'out': 134}}
|
|
@@ -420,6 +448,7 @@ Timeline
|
|
|
420
448
|
ClearMarkInOut(type="all") --> Bool # Clears mark in/out of type "video", "audio" or "all" (default).
|
|
421
449
|
GetVoiceIsolationState(trackIndex) --> {VoiceIsolationState} # Returns the Voice Isolation State as a dict {isEnabled, amount}, of the given track index
|
|
422
450
|
SetVoiceIsolationState(trackIndex, {VoiceIsolationState}) --> Bool # Sets Voice Isolation state of audio track with given trackIndex (int) to the given VoiceIsolationState of {isEnabled (bool), amount (int)}. amount is in range of [0, 100] and 1 <= trackIndex <= GetTrackCount("audio"). Returns True if successful.
|
|
451
|
+
# Refer to section "Studio and AI Scripting APIs" for prerequisites.
|
|
423
452
|
|
|
424
453
|
TimelineItem
|
|
425
454
|
GetName() --> string # Returns the item name.
|
|
@@ -438,7 +467,7 @@ TimelineItem
|
|
|
438
467
|
GetSourceStartFrame() --> int # Returns the start frame position of the media pool clip in the timeline clip.
|
|
439
468
|
GetSourceStartTime() --> float # Returns the start time position of the media pool clip in the timeline clip.
|
|
440
469
|
SetProperty(propertyKey, propertyValue) --> Bool # Sets the value of property "propertyKey" to value "propertyValue"
|
|
441
|
-
# Refer to "Looking up Timeline item properties" for more information
|
|
470
|
+
# Refer to section "Looking up Timeline item properties" for more information
|
|
442
471
|
GetProperty(propertyKey) --> int/[key:value] # returns the value of the specified key
|
|
443
472
|
# if no key is specified, the method returns a dictionary(python) or table(lua) for all supported keys
|
|
444
473
|
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.
|
|
@@ -471,8 +500,11 @@ TimelineItem
|
|
|
471
500
|
GetVersionNameList(versionType) --> [names...] # Returns a list of all color versions for the given versionType (0 - local, 1 - remote).
|
|
472
501
|
GetMediaPoolItem() --> MediaPoolItem # Returns the media pool item corresponding to the timeline item if one exists.
|
|
473
502
|
GetStereoConvergenceValues() --> {keyframes...} # Returns a dict (offset -> value) of keyframe offsets and respective convergence values.
|
|
503
|
+
# Refer to section "Studio and AI Scripting APIs" for prerequisites.
|
|
474
504
|
GetStereoLeftFloatingWindowParams() --> {keyframes...} # For the LEFT eye -> returns a dict (offset -> dict) of keyframe offsets and respective floating window params. Value at particular offset includes the left, right, top and bottom floating window values.
|
|
505
|
+
# Refer to section "Studio and AI Scripting APIs" for prerequisites.
|
|
475
506
|
GetStereoRightFloatingWindowParams() --> {keyframes...} # For the RIGHT eye -> returns a dict (offset -> dict) of keyframe offsets and respective floating window params. Value at particular offset includes the left, right, top and bottom floating window values.
|
|
507
|
+
# Refer to section "Studio and AI Scripting APIs" for prerequisites.
|
|
476
508
|
SetCDL([CDL map]) --> Bool # Keys of map are: "NodeIndex", "Slope", "Offset", "Power", "Saturation", where 1 <= NodeIndex <= total number of nodes.
|
|
477
509
|
# Example python code - SetCDL({"NodeIndex" : "1", "Slope" : "0.5 0.4 0.2", "Offset" : "0.4 0.3 0.2", "Power" : "0.6 0.7 0.8", "Saturation" : "0.65"})
|
|
478
510
|
AddTake(mediaPoolItem, startFrame, endFrame) --> Bool # Adds mediaPoolItem as a new take. Initializes a take selector for the timeline item if needed. By default, the full clip extents is added. startFrame (int) and endFrame (int) are optional arguments used to specify the extents.
|
|
@@ -489,9 +521,12 @@ TimelineItem
|
|
|
489
521
|
GetUniqueId() --> string # Returns a unique ID for the timeline item
|
|
490
522
|
LoadBurnInPreset(presetName) --> Bool # Loads user defined data burn in preset for clip when supplied presetName (string). Returns true if successful.
|
|
491
523
|
CreateMagicMask(mode) --> Bool # Returns True if magic mask was created successfully, False otherwise. mode can "F" (forward), "B" (backward), or "BI" (bidirection)
|
|
524
|
+
# Refer to section "Studio and AI Scripting APIs" for prerequisites.
|
|
492
525
|
RegenerateMagicMask() --> Bool # Returns True if magic mask was regenerated successfully, False otherwise.
|
|
526
|
+
# Refer to section "Studio and AI Scripting APIs" for prerequisites.
|
|
493
527
|
Stabilize() --> Bool # Returns True if stabilization was successful, False otherwise
|
|
494
528
|
SmartReframe() --> Bool # Performs Smart Reframe. Returns True if successful, False otherwise.
|
|
529
|
+
# Refer to section "Studio and AI Scripting APIs" for prerequisites.
|
|
495
530
|
GetNodeGraph(layerIdx) --> Graph # Returns the clip's node graph object at layerIdx (int, optional). Returns the first layer if layerIdx is skipped. 1 <= layerIdx <= project.GetSetting("nodeStackLayers").
|
|
496
531
|
GetColorGroup() --> ColorGroup # Returns the clip's color group if one exists.
|
|
497
532
|
AssignToColorGroup(ColorGroup) --> Bool # Returns True if TiItem to successfully assigned to given ColorGroup. ColorGroup must be an existing group in the current project.
|
|
@@ -510,6 +545,7 @@ TimelineItem
|
|
|
510
545
|
SetFusionOutputCache(cache_value) --> Bool # Sets caching to auto, enabled or disabled. Equivalent to clip context menu action 'Render Cache Fusion Output'.
|
|
511
546
|
GetVoiceIsolationState() --> {VoiceIsolationState} # Returns the Voice Isolation State as a dict {isEnabled, amount}, of the timelineItem
|
|
512
547
|
SetVoiceIsolationState({VoiceIsolationState}) --> Bool # Sets Voice Isolation state of the timelineItem to the given VoiceIsolationState of {isEnabled (bool), amount (int)}. amount is in range of [0, 100]. Returns True if successful.
|
|
548
|
+
# Refer to section "Studio and AI Scripting APIs" for prerequisites.
|
|
513
549
|
ResetAllNodeColors() --> Bool # Reset node color for all nodes in the active version of the clip. Returns True if successful.
|
|
514
550
|
|
|
515
551
|
Gallery
|
|
@@ -538,7 +574,7 @@ Graph
|
|
|
538
574
|
# The lutPath can be an absolute path, or a relative path (based off custom LUT paths or the master LUT path).
|
|
539
575
|
# The operation is successful for valid lut paths that Resolve has already discovered (see Project.RefreshLUTList).
|
|
540
576
|
GetLUT(nodeIndex) --> String # Gets relative LUT path based on the node index provided, 1 <= nodeIndex <= total number of nodes.
|
|
541
|
-
SetNodeCacheMode(nodeIndex, cache_value) --> Bool # Sets the cache mode type on the node mapping the node index provided. Refer to "Cache Mode"
|
|
577
|
+
SetNodeCacheMode(nodeIndex, cache_value) --> Bool # Sets the cache mode type on the node mapping the node index provided. Refer to section "Cache Mode" below to find the possible values of cache_value.
|
|
542
578
|
GetNodeCacheMode(nodeIndex) --> cache_value # Returns the cache mode type on the node mapping the node index provided.
|
|
543
579
|
GetNodeLabel(nodeIndex) --> string # Returns the label of the node at nodeIndex.
|
|
544
580
|
GetToolsInNode(nodeIndex) --> [toolsList] # Returns toolsList (list of strings) of the tools used in the node indicated by given nodeIndex (int).
|
|
@@ -960,6 +996,39 @@ as a single argument.
|
|
|
960
996
|
|
|
961
997
|
Getting the values for the keys that uses constants will return the number which is in the constant
|
|
962
998
|
|
|
999
|
+
Studio and AI Scripting APIs
|
|
1000
|
+
----------------------------
|
|
1001
|
+
The DaVinci Resolve scripting APIs cover a common superset of functions for both the Free and Studio versions of the application.
|
|
1002
|
+
|
|
1003
|
+
API calls can return with a False status (or an appropriate error status) when:
|
|
1004
|
+
* the function references a Studio function from the free DaVinci Resolve version.
|
|
1005
|
+
* the minimum system requirements of the function are not satisfied. To check if your system is capable, invoke the function from the GUI and check for error dialogs.
|
|
1006
|
+
* the requisite Extras have not been downloaded.
|
|
1007
|
+
|
|
1008
|
+
The following functions require one or more Extras downloads:
|
|
1009
|
+
* AnalyzeForIntellisearch(identifyFaces, isBetterMode=False) requires AI IntelliSearch - Faster.
|
|
1010
|
+
* AnalyzeForIntellisearch(identifyFaces, isBetterMode=True) requires AI IntelliSearch - Better.
|
|
1011
|
+
* AnalyzeForSlate(markerColor) requires AI Slate ID.
|
|
1012
|
+
* Transcription workflows with extended language models. Languages from built in models will be used as a fallback if unavailable.
|
|
1013
|
+
* GenerateSpeech({speechGenerationSettings}, timecode) requires AI Speech Generator.
|
|
1014
|
+
|
|
1015
|
+
For a successful API call, the required package will need to be installed before script invocation. Go to the DaVinci Resolve Studio application menu, open the Extras Download Manager and install the required package.
|
|
1016
|
+
|
|
1017
|
+
Motion Deblur Settings
|
|
1018
|
+
-----------------------------------
|
|
1019
|
+
This section covers the supported settings for the method RemoveMotionBlur({deblurOption})
|
|
1020
|
+
|
|
1021
|
+
The deblurOption setting is a dictionary containing the following keys:
|
|
1022
|
+
- "FileName": string
|
|
1023
|
+
- "Format": string (example: "mov", "mp4").
|
|
1024
|
+
- "Codec": string (example: "H264", "ProRes422")
|
|
1025
|
+
- "EncodingProfile": string (example: "Main10"). Can only be set for H.264 and H.265.
|
|
1026
|
+
- "UseExtremeMode": bool
|
|
1027
|
+
- "UseMarkInMarkOut": bool
|
|
1028
|
+
- "RenderAtSourceRes": bool
|
|
1029
|
+
- "UseMoreGpuMemory": bool
|
|
1030
|
+
- "Encoder" : string (Native or MainConcept). Can only be set for H.265
|
|
1031
|
+
|
|
963
1032
|
ExportLUT notes
|
|
964
1033
|
---------------
|
|
965
1034
|
The following section covers additional notes for TimelineItem.ExportLUT(exportType, path).
|
|
@@ -970,6 +1039,43 @@ Supported values for 'exportType' (enum) are:
|
|
|
970
1039
|
- resolve.EXPORT_LUT_65PTCUBE
|
|
971
1040
|
- resolve.EXPORT_LUT_PANASONICVLUT
|
|
972
1041
|
|
|
1042
|
+
Analyze Slate Settings
|
|
1043
|
+
-----------------------------------
|
|
1044
|
+
This section covers the supported settings for the method AnalyzeForSlate(markerColor)
|
|
1045
|
+
markerColor can be one of the following constants:
|
|
1046
|
+
- resolve.MARKER_BLUE
|
|
1047
|
+
- resolve.MARKER_CYAN
|
|
1048
|
+
- resolve.MARKER_GREEN
|
|
1049
|
+
- resolve.MARKER_YELLOW
|
|
1050
|
+
- resolve.MARKER_RED
|
|
1051
|
+
- resolve.MARKER_PINK
|
|
1052
|
+
- resolve.MARKER_PURPLE
|
|
1053
|
+
- resolve.MARKER_FUCHSIA
|
|
1054
|
+
- resolve.MARKER_ROSE
|
|
1055
|
+
- resolve.MARKER_LAVENDER
|
|
1056
|
+
- resolve.MARKER_SKY
|
|
1057
|
+
- resolve.MARKER_MINT
|
|
1058
|
+
- resolve.MARKER_LEMON
|
|
1059
|
+
- resolve.MARKER_SAND
|
|
1060
|
+
- resolve.MARKER_COCOA
|
|
1061
|
+
- resolve.MARKER_CREAM
|
|
1062
|
+
|
|
1063
|
+
Speech Generation Settings
|
|
1064
|
+
-----------------------------------
|
|
1065
|
+
This section covers the supported settings for the method GenerateSpeech({speechGenerationSettings}, timecode)
|
|
1066
|
+
|
|
1067
|
+
The speechGenerationSettings is a dictionary containing the following keys:
|
|
1068
|
+
- "TextInput": string # Max 350 chars
|
|
1069
|
+
- "VoiceModel": string (example: "Female 1", "Male 1", "Custom Voice").
|
|
1070
|
+
- "CustomVoiceFile": string "Full Path of Voice File"
|
|
1071
|
+
- "Speed": int
|
|
1072
|
+
- "Variation": int
|
|
1073
|
+
- "Pitch": int
|
|
1074
|
+
- "GenerationID": int
|
|
1075
|
+
- "Filename" : string
|
|
1076
|
+
- "AddToTimeline" : bool
|
|
1077
|
+
- "AudioTrack": int
|
|
1078
|
+
|
|
973
1079
|
Deprecated Resolve API Functions
|
|
974
1080
|
--------------------------------
|
|
975
1081
|
The following API functions are deprecated.
|
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.72.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.72.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.72.1"
|
|
15
15
|
|
|
16
16
|
import base64
|
|
17
17
|
import os
|
|
@@ -1784,6 +1784,36 @@ def _requires_method(obj, method_name, min_version):
|
|
|
1784
1784
|
return None
|
|
1785
1785
|
return _err(f"{method_name} requires DaVinci Resolve {min_version}+")
|
|
1786
1786
|
|
|
1787
|
+
def _ai_result(returned):
|
|
1788
|
+
"""Normalize a Resolve 21 AI-method return into (ok, message).
|
|
1789
|
+
|
|
1790
|
+
The AI methods do not agree on how they report a missing Extras pack, and
|
|
1791
|
+
one of the two shapes is a trap. Verified live on Studio 21.0.2.4 with only
|
|
1792
|
+
AI Motion Deblur installed:
|
|
1793
|
+
|
|
1794
|
+
- `AnalyzeForSlate` -> False
|
|
1795
|
+
- `AnalyzeForIntellisearch` -> "Required package 'AI Intellisearch -
|
|
1796
|
+
Faster' is not installed."
|
|
1797
|
+
- `GenerateSpeech` -> "Required Package, 'AI Speech Generator' is not
|
|
1798
|
+
Installed."
|
|
1799
|
+
|
|
1800
|
+
A non-empty string is truthy, so `bool(returned)` reports success for a call
|
|
1801
|
+
that definitively did not run, and treating the return as a MediaPoolItem
|
|
1802
|
+
raises AttributeError. Route every AI return through here instead: a string
|
|
1803
|
+
is always a failure, and its text is the reason worth surfacing.
|
|
1804
|
+
"""
|
|
1805
|
+
if isinstance(returned, str):
|
|
1806
|
+
return False, returned.strip() or None
|
|
1807
|
+
return bool(returned), None
|
|
1808
|
+
|
|
1809
|
+
def _ai_result_payload(returned):
|
|
1810
|
+
"""`{"success": ...}` plus the Resolve-supplied reason when there is one."""
|
|
1811
|
+
ok, message = _ai_result(returned)
|
|
1812
|
+
payload = {"success": ok}
|
|
1813
|
+
if message:
|
|
1814
|
+
payload["error"] = message
|
|
1815
|
+
return payload
|
|
1816
|
+
|
|
1787
1817
|
def _is_truncated(text):
|
|
1788
1818
|
"""True if a transcription preview was cut off.
|
|
1789
1819
|
|
|
@@ -15459,6 +15489,7 @@ def project_settings(action: str, params: Optional[Dict[str, Any]] = None) -> Di
|
|
|
15459
15489
|
delete_color_group(name) -> {success}
|
|
15460
15490
|
apply_fairlight_preset(preset_name) -> {success}
|
|
15461
15491
|
generate_speech(speech_generation_settings, timecode?) -> {success, new, new_id} — Resolve 21+, AI Speech Generator; creates new audio media (confirm-gated)
|
|
15492
|
+
reset_intellisearch_analysis() -> {success} — Resolve 21+; clears the project's IntelliSearch analysis data
|
|
15462
15493
|
"""
|
|
15463
15494
|
p = _params(params)
|
|
15464
15495
|
_, proj, err = _check()
|
|
@@ -15557,16 +15588,29 @@ def project_settings(action: str, params: Optional[Dict[str, Any]] = None) -> Di
|
|
|
15557
15588
|
return blocked
|
|
15558
15589
|
with _ai_ledger_timed("generate_speech") as _rec:
|
|
15559
15590
|
new_item = proj.GenerateSpeech(settings, timecode)
|
|
15560
|
-
|
|
15561
|
-
|
|
15591
|
+
# GenerateSpeech returns an error STRING when the AI Speech Generator
|
|
15592
|
+
# Extra is absent (verified on Studio 21.0.2.4), not a MediaPoolItem.
|
|
15593
|
+
# A bare truthiness test lets that string through to .GetName() and
|
|
15594
|
+
# raises AttributeError, so normalize before touching the result.
|
|
15595
|
+
ok, message = _ai_result(new_item)
|
|
15596
|
+
_rec.success = ok
|
|
15597
|
+
if ok:
|
|
15562
15598
|
path, nbytes = _clip_file_size(new_item)
|
|
15563
15599
|
_rec.output_path = path
|
|
15564
15600
|
_rec.output_bytes = nbytes
|
|
15565
|
-
if not
|
|
15566
|
-
return {"success": False}
|
|
15601
|
+
if not ok:
|
|
15602
|
+
return {"success": False, "error": message} if message else {"success": False}
|
|
15567
15603
|
return {"success": True, "new": new_item.GetName(), "new_id": new_item.GetUniqueId(),
|
|
15568
15604
|
"output_path": _rec.output_path, "output_bytes": _rec.output_bytes}
|
|
15569
|
-
|
|
15605
|
+
elif action == "reset_intellisearch_analysis":
|
|
15606
|
+
missing = _requires_method(proj, "ResetIntellisearchAnalysis", "21.0")
|
|
15607
|
+
if missing:
|
|
15608
|
+
return missing
|
|
15609
|
+
with _ai_ledger_timed("reset_intellisearch_analysis") as _rec:
|
|
15610
|
+
result = _ai_result_payload(proj.ResetIntellisearchAnalysis())
|
|
15611
|
+
_rec.success = result["success"]
|
|
15612
|
+
return result
|
|
15613
|
+
return _unknown(action, ["get_name","set_name","get_setting","set_setting","get_unique_id","get_presets","set_preset","refresh_luts","get_gallery","export_frame_as_still","project_summary","load_burnin_preset","insert_audio","get_color_groups","add_color_group","delete_color_group","apply_fairlight_preset","generate_speech","reset_intellisearch_analysis"])
|
|
15570
15614
|
|
|
15571
15615
|
|
|
15572
15616
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
@@ -17006,17 +17050,17 @@ def folder(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[str, An
|
|
|
17006
17050
|
if missing:
|
|
17007
17051
|
return missing
|
|
17008
17052
|
with _ai_ledger_timed("perform_audio_classification") as _rec:
|
|
17009
|
-
|
|
17010
|
-
_rec.success =
|
|
17011
|
-
return
|
|
17053
|
+
result = _ai_result_payload(f.PerformAudioClassification())
|
|
17054
|
+
_rec.success = result["success"]
|
|
17055
|
+
return result
|
|
17012
17056
|
elif action == "clear_audio_classification":
|
|
17013
17057
|
missing = _requires_method(f, "ClearAudioClassification", "21.0")
|
|
17014
17058
|
if missing:
|
|
17015
17059
|
return missing
|
|
17016
17060
|
with _ai_ledger_timed("clear_audio_classification") as _rec:
|
|
17017
|
-
|
|
17018
|
-
_rec.success =
|
|
17019
|
-
return
|
|
17061
|
+
result = _ai_result_payload(f.ClearAudioClassification())
|
|
17062
|
+
_rec.success = result["success"]
|
|
17063
|
+
return result
|
|
17020
17064
|
elif action == "analyze_for_intellisearch":
|
|
17021
17065
|
missing = _requires_method(f, "AnalyzeForIntellisearch", "21.0")
|
|
17022
17066
|
if missing:
|
|
@@ -17024,9 +17068,9 @@ def folder(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[str, An
|
|
|
17024
17068
|
identify_faces = bool(_first_param(p, "identify_faces", "identifyFaces", default=False))
|
|
17025
17069
|
is_better_mode = bool(_first_param(p, "is_better_mode", "isBetterMode", default=False))
|
|
17026
17070
|
with _ai_ledger_timed("analyze_for_intellisearch") as _rec:
|
|
17027
|
-
|
|
17028
|
-
_rec.success =
|
|
17029
|
-
return
|
|
17071
|
+
result = _ai_result_payload(f.AnalyzeForIntellisearch(identify_faces, is_better_mode))
|
|
17072
|
+
_rec.success = result["success"]
|
|
17073
|
+
return result
|
|
17030
17074
|
elif action == "analyze_for_slate":
|
|
17031
17075
|
missing = _requires_method(f, "AnalyzeForSlate", "21.0")
|
|
17032
17076
|
if missing:
|
|
@@ -17035,9 +17079,9 @@ def folder(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[str, An
|
|
|
17035
17079
|
if marker_color not in _MARKER_COLORS:
|
|
17036
17080
|
return _err(f"Invalid marker_color {marker_color!r}. Valid colors: {', '.join(_MARKER_COLORS)}")
|
|
17037
17081
|
with _ai_ledger_timed("analyze_for_slate") as _rec:
|
|
17038
|
-
|
|
17039
|
-
_rec.success =
|
|
17040
|
-
return
|
|
17082
|
+
result = _ai_result_payload(f.AnalyzeForSlate(marker_color))
|
|
17083
|
+
_rec.success = result["success"]
|
|
17084
|
+
return result
|
|
17041
17085
|
elif action == "remove_motion_blur":
|
|
17042
17086
|
missing = _requires_method(f, "RemoveMotionBlur", "21.0")
|
|
17043
17087
|
if missing:
|
|
@@ -17062,11 +17106,18 @@ def folder(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[str, An
|
|
|
17062
17106
|
if blocked:
|
|
17063
17107
|
return blocked
|
|
17064
17108
|
with _ai_ledger_timed("remove_motion_blur") as _rec:
|
|
17109
|
+
# RemoveMotionBlur needs the AI Motion Deblur Extra, so it belongs to
|
|
17110
|
+
# the same family as the methods above: absent the pack, the return
|
|
17111
|
+
# can be an error STRING rather than the documented list. Iterating a
|
|
17112
|
+
# string yields characters, the pair-unpack raises, `except Exception`
|
|
17113
|
+
# swallows it, and the action reported success:true with created:[]
|
|
17114
|
+
# — a silent lie in the confirm-gated path that renders new media.
|
|
17065
17115
|
result = f.RemoveMotionBlur(deblur)
|
|
17066
|
-
|
|
17116
|
+
ok, message = _ai_result(result)
|
|
17117
|
+
_rec.success = ok
|
|
17067
17118
|
created = []
|
|
17068
17119
|
total_bytes = 0
|
|
17069
|
-
for pair in (result or []):
|
|
17120
|
+
for pair in (result or []) if ok else []:
|
|
17070
17121
|
try:
|
|
17071
17122
|
orig, new = pair
|
|
17072
17123
|
path, nbytes = _clip_file_size(new)
|
|
@@ -17080,7 +17131,10 @@ def folder(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[str, An
|
|
|
17080
17131
|
if created:
|
|
17081
17132
|
_rec.output_path = created[0].get("output_path")
|
|
17082
17133
|
_rec.output_bytes = total_bytes or None
|
|
17083
|
-
|
|
17134
|
+
payload = {"success": ok, "created": created}
|
|
17135
|
+
if message:
|
|
17136
|
+
payload["error"] = message
|
|
17137
|
+
return payload
|
|
17084
17138
|
return _unknown(action, ["get_clips","get_name","get_subfolders","is_stale","get_unique_id","export","transcribe_audio","clear_transcription","perform_audio_classification","clear_audio_classification","analyze_for_intellisearch","analyze_for_slate","remove_motion_blur"])
|
|
17085
17139
|
|
|
17086
17140
|
|
|
@@ -17404,17 +17458,17 @@ def media_pool_item(action: str, params: Optional[Dict[str, Any]] = None) -> Dic
|
|
|
17404
17458
|
if missing:
|
|
17405
17459
|
return missing
|
|
17406
17460
|
with _ai_ledger_timed("perform_audio_classification", clip_id=p.get("clip_id")) as _rec:
|
|
17407
|
-
|
|
17408
|
-
_rec.success =
|
|
17409
|
-
return
|
|
17461
|
+
result = _ai_result_payload(clip.PerformAudioClassification())
|
|
17462
|
+
_rec.success = result["success"]
|
|
17463
|
+
return result
|
|
17410
17464
|
elif action == "clear_audio_classification":
|
|
17411
17465
|
missing = _requires_method(clip, "ClearAudioClassification", "21.0")
|
|
17412
17466
|
if missing:
|
|
17413
17467
|
return missing
|
|
17414
17468
|
with _ai_ledger_timed("clear_audio_classification", clip_id=p.get("clip_id")) as _rec:
|
|
17415
|
-
|
|
17416
|
-
_rec.success =
|
|
17417
|
-
return
|
|
17469
|
+
result = _ai_result_payload(clip.ClearAudioClassification())
|
|
17470
|
+
_rec.success = result["success"]
|
|
17471
|
+
return result
|
|
17418
17472
|
elif action == "analyze_for_intellisearch":
|
|
17419
17473
|
missing = _requires_method(clip, "AnalyzeForIntellisearch", "21.0")
|
|
17420
17474
|
if missing:
|
|
@@ -17422,9 +17476,9 @@ def media_pool_item(action: str, params: Optional[Dict[str, Any]] = None) -> Dic
|
|
|
17422
17476
|
identify_faces = bool(_first_param(p, "identify_faces", "identifyFaces", default=False))
|
|
17423
17477
|
is_better_mode = bool(_first_param(p, "is_better_mode", "isBetterMode", default=False))
|
|
17424
17478
|
with _ai_ledger_timed("analyze_for_intellisearch", clip_id=p.get("clip_id")) as _rec:
|
|
17425
|
-
|
|
17426
|
-
_rec.success =
|
|
17427
|
-
return
|
|
17479
|
+
result = _ai_result_payload(clip.AnalyzeForIntellisearch(identify_faces, is_better_mode))
|
|
17480
|
+
_rec.success = result["success"]
|
|
17481
|
+
return result
|
|
17428
17482
|
elif action == "analyze_for_slate":
|
|
17429
17483
|
missing = _requires_method(clip, "AnalyzeForSlate", "21.0")
|
|
17430
17484
|
if missing:
|
|
@@ -17433,9 +17487,9 @@ def media_pool_item(action: str, params: Optional[Dict[str, Any]] = None) -> Dic
|
|
|
17433
17487
|
if marker_color not in _MARKER_COLORS:
|
|
17434
17488
|
return _err(f"Invalid marker_color {marker_color!r}. Valid colors: {', '.join(_MARKER_COLORS)}")
|
|
17435
17489
|
with _ai_ledger_timed("analyze_for_slate", clip_id=p.get("clip_id")) as _rec:
|
|
17436
|
-
|
|
17437
|
-
_rec.success =
|
|
17438
|
-
return
|
|
17490
|
+
result = _ai_result_payload(clip.AnalyzeForSlate(marker_color))
|
|
17491
|
+
_rec.success = result["success"]
|
|
17492
|
+
return result
|
|
17439
17493
|
elif action == "remove_motion_blur":
|
|
17440
17494
|
missing = _requires_method(clip, "RemoveMotionBlur", "21.0")
|
|
17441
17495
|
if missing:
|
|
@@ -17460,14 +17514,19 @@ def media_pool_item(action: str, params: Optional[Dict[str, Any]] = None) -> Dic
|
|
|
17460
17514
|
if blocked:
|
|
17461
17515
|
return blocked
|
|
17462
17516
|
with _ai_ledger_timed("remove_motion_blur", clip_id=p.get("clip_id")) as _rec:
|
|
17517
|
+
# Same shape as generate_speech: a MediaPoolItem return, and an error
|
|
17518
|
+
# STRING when the AI Motion Deblur Extra is absent. `_clip_file_size`
|
|
17519
|
+
# swallows its own AttributeError, so the string survived to
|
|
17520
|
+
# `.GetName()` and raised there instead.
|
|
17463
17521
|
new_clip = clip.RemoveMotionBlur(deblur)
|
|
17464
|
-
|
|
17465
|
-
|
|
17522
|
+
ok, message = _ai_result(new_clip)
|
|
17523
|
+
_rec.success = ok
|
|
17524
|
+
if ok:
|
|
17466
17525
|
path, nbytes = _clip_file_size(new_clip)
|
|
17467
17526
|
_rec.output_path = path
|
|
17468
17527
|
_rec.output_bytes = nbytes
|
|
17469
|
-
if not
|
|
17470
|
-
return {"success": False}
|
|
17528
|
+
if not ok:
|
|
17529
|
+
return {"success": False, "error": message} if message else {"success": False}
|
|
17471
17530
|
return {"success": True, "new": new_clip.GetName(), "new_id": new_clip.GetUniqueId(),
|
|
17472
17531
|
"output_path": _rec.output_path, "output_bytes": _rec.output_bytes}
|
|
17473
17532
|
elif action == "get_audio_mapping":
|
package/src/utils/api_truth.py
CHANGED
|
@@ -26,7 +26,7 @@ When you add or change a ``submit``-tagged entry, regenerate the report
|
|
|
26
26
|
"""
|
|
27
27
|
from typing import Any, Dict, List, Optional
|
|
28
28
|
|
|
29
|
-
VERIFIED_ON = "DaVinci Resolve Studio 21.0.
|
|
29
|
+
VERIFIED_ON = "DaVinci Resolve Studio 21.0.2"
|
|
30
30
|
|
|
31
31
|
# Each entry: symbol, object, reality, recommended, tags. `signature` optional.
|
|
32
32
|
API_TRUTH: List[Dict[str, Any]] = [
|
|
@@ -717,18 +717,143 @@ API_TRUTH: List[Dict[str, Any]] = [
|
|
|
717
717
|
{
|
|
718
718
|
"symbol": "hasattr() / getattr() on Resolve API objects (attribute fabrication)",
|
|
719
719
|
"object": "(all Resolve scripting objects)",
|
|
720
|
-
"reality": "
|
|
721
|
-
"
|
|
722
|
-
"
|
|
723
|
-
"impossible
|
|
724
|
-
"Razor, AddNode, GenerateProxy,
|
|
725
|
-
"
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
720
|
+
"reality": "UNRESOLVED — the two measurements do not test the same thing. "
|
|
721
|
+
"On 21.0.0 the bridge was recorded as returning a callable for "
|
|
722
|
+
"ANY attribute name, making capability detection by hasattr "
|
|
723
|
+
"impossible; the evidence was REAL API method names borrowed from "
|
|
724
|
+
"other object types (SetStart, Razor, AddNode, GenerateProxy, "
|
|
725
|
+
"AddSmartBin reported present on objects that do not have them). "
|
|
726
|
+
"A 21.0.2.4 control probe of the invented name "
|
|
727
|
+
"'TotallyMadeUpMethod_xyz123' returned getattr-callable False on "
|
|
728
|
+
"all eight object types, matching dir() in every case. That does "
|
|
729
|
+
"NOT refute the 21.0.0 record: if the bridge resolves any name "
|
|
730
|
+
"known to the RemoteObject method table rather than literally any "
|
|
731
|
+
"string, an invented name is correctly rejected on both builds and "
|
|
732
|
+
"the probe never exercised the failing case. Re-running the probe "
|
|
733
|
+
"with those five real names is what would settle it; until then, "
|
|
734
|
+
"assume fabrication is possible.",
|
|
735
|
+
"recommended": "Use dir(obj) membership for capability probes. It is correct "
|
|
736
|
+
"on every build measured, and it is the only form not affected "
|
|
737
|
+
"by whichever way this resolves. server._has_method uses "
|
|
738
|
+
"hasattr/getattr and so may over-report on builds where "
|
|
739
|
+
"fabrication is live — that is the case _requires_method gates "
|
|
740
|
+
"guard, so it matters most exactly where it is least tested. "
|
|
741
|
+
"Calling a fabricated method typically returns None/False with "
|
|
742
|
+
"no error.",
|
|
729
743
|
"tags": ["bridge", "introspection", "silent-failure"],
|
|
730
744
|
"submit": "bug",
|
|
731
745
|
},
|
|
746
|
+
{
|
|
747
|
+
"symbol": "Resolve 21 AI methods (AnalyzeForIntellisearch, GenerateSpeech, "
|
|
748
|
+
"AnalyzeForSlate) — inconsistent failure return type",
|
|
749
|
+
"object": "MediaPoolItem / Folder / Project",
|
|
750
|
+
"signature": "-> Bool (documented)",
|
|
751
|
+
"reality": "When the required Extras pack is not installed, these methods do "
|
|
752
|
+
"not agree on how they say so, and the documented Bool is not what "
|
|
753
|
+
"you get. Verified live on Studio 21.0.2.4 with only AI Motion "
|
|
754
|
+
"Deblur installed: AnalyzeForSlate returned False, but "
|
|
755
|
+
"AnalyzeForIntellisearch returned the STRING \"Required package 'AI "
|
|
756
|
+
"Intellisearch - Faster' is not installed.\" and GenerateSpeech "
|
|
757
|
+
"returned the STRING \"Required Package, 'AI Speech Generator' is "
|
|
758
|
+
"not Installed.\". A non-empty string is truthy in Python, so "
|
|
759
|
+
"bool(result) reports SUCCESS for a call that definitively did not "
|
|
760
|
+
"run, and treating GenerateSpeech's return as a MediaPoolItem "
|
|
761
|
+
"raises AttributeError: 'str' object has no attribute 'GetName'.",
|
|
762
|
+
"recommended": "Never bool() an AI-method return directly. Route it through "
|
|
763
|
+
"server._ai_result / _ai_result_payload, which treat any string "
|
|
764
|
+
"as a failure and surface its text as the error — the message is "
|
|
765
|
+
"the only machine-readable signal that an Extras pack is "
|
|
766
|
+
"missing, since there is no scripting API to enumerate "
|
|
767
|
+
"installed Extras.",
|
|
768
|
+
"tags": ["ai", "extras", "unreliable-return", "silent-failure", "resolve-21"],
|
|
769
|
+
"submit": "bug",
|
|
770
|
+
"mitigation": ["_ai_result", "_ai_result_payload"],
|
|
771
|
+
},
|
|
772
|
+
{
|
|
773
|
+
"symbol": "Installed AI Extras packs are not discoverable from scripting",
|
|
774
|
+
"object": "Resolve",
|
|
775
|
+
"reality": "AnalyzeForIntellisearch, AnalyzeForSlate, GenerateSpeech and "
|
|
776
|
+
"RemoveMotionBlur each require a separately-downloaded Extras pack, "
|
|
777
|
+
"but nothing in the scripting API reports which packs are installed. "
|
|
778
|
+
"A caller cannot distinguish 'the Extra is missing' from 'the "
|
|
779
|
+
"analysis ran and found nothing' ahead of time; on 21.0.2.4 two of "
|
|
780
|
+
"the four leak the reason only as free text in the return value, and "
|
|
781
|
+
"AnalyzeForSlate's bare False carries no reason at all.",
|
|
782
|
+
"recommended": "Until an API exists, treat a string return as the reason and "
|
|
783
|
+
"read the pack names out of the Extras directory "
|
|
784
|
+
"(Blackmagic Design/DaVinci Resolve/Extras/*/log.dpl1) for "
|
|
785
|
+
"diagnostics only — that path is undocumented and may change.",
|
|
786
|
+
"tags": ["ai", "extras", "introspection", "resolve-21"],
|
|
787
|
+
"submit": "missing",
|
|
788
|
+
},
|
|
789
|
+
{
|
|
790
|
+
"symbol": "Folder.AnalyzeForSlate / MediaPoolItem.AnalyzeForSlate markerColor",
|
|
791
|
+
"object": "MediaPoolItem / Folder",
|
|
792
|
+
"signature": "(markerColor) -> Bool",
|
|
793
|
+
"reality": "The shipped 21.0.2 scripting README says markerColor must be one of "
|
|
794
|
+
"the resolve.MARKER_* constants (resolve.MARKER_BLUE etc.). Those "
|
|
795
|
+
"constants do not exist: on Studio 21.0.2.4, "
|
|
796
|
+
"[c for c in dir(resolve) if c.startswith('MARKER_')] is empty. "
|
|
797
|
+
"There is therefore no documented-correct way to call this method. "
|
|
798
|
+
"The plain colour string the server passes is the only option "
|
|
799
|
+
"available, and it returns False here — though with AI Slate ID "
|
|
800
|
+
"absent, a string-rejection bug cannot be distinguished from the "
|
|
801
|
+
"missing pack on this machine.",
|
|
802
|
+
"recommended": "Keep passing the plain colour name (server._MARKER_COLORS) — "
|
|
803
|
+
"the documented constants are unavailable. Re-test on a machine "
|
|
804
|
+
"with the AI Slate ID Extra installed before concluding the "
|
|
805
|
+
"string form is rejected.",
|
|
806
|
+
# Deliberately NOT tagged `enum`: that tag denotes the issue-#70 class,
|
|
807
|
+
# where plain strings are rejected and a resolver must translate them
|
|
808
|
+
# into live enum constants. Here the documented constants do not exist
|
|
809
|
+
# on the handle at all, so there is nothing to resolve — the defect is
|
|
810
|
+
# in the documentation, not in a missing resolver.
|
|
811
|
+
"tags": ["ai", "extras", "missing-constant", "documentation", "resolve-21"],
|
|
812
|
+
"submit": "bug",
|
|
813
|
+
},
|
|
814
|
+
{
|
|
815
|
+
"symbol": "Project.ResetIntellisearchAnalysis",
|
|
816
|
+
"object": "Project",
|
|
817
|
+
"signature": "() -> Bool",
|
|
818
|
+
"reality": "Documented in the scripting README shipped with Resolve 21.0.2 "
|
|
819
|
+
"(dated 26 May 2026) but absent from the 5 May 2026 copy the repo "
|
|
820
|
+
"bundled, so it was missing from the coverage tables. Present in "
|
|
821
|
+
"dir(project) and returns True on Studio 21.0.2.4.",
|
|
822
|
+
"recommended": "Exposed as project_settings('reset_intellisearch_analysis').",
|
|
823
|
+
"tags": ["resolve-21", "documentation"],
|
|
824
|
+
},
|
|
825
|
+
{
|
|
826
|
+
"symbol": "Resolve.DisableBackgroundTasksForCurrentResolveSession",
|
|
827
|
+
"object": "Resolve",
|
|
828
|
+
"signature": "() -> None",
|
|
829
|
+
"reality": "Returns None, so a caller cannot tell whether it took effect, and "
|
|
830
|
+
"there is no Enable... counterpart anywhere in the shipped 21.0.2 "
|
|
831
|
+
"scripting README — the only documented way back is restarting "
|
|
832
|
+
"Resolve. The scope is the whole session, so a script disables "
|
|
833
|
+
"background tasks for every project open in that instance, not just "
|
|
834
|
+
"its own. Present in dir(resolve) on Studio 21.0.2.4; deliberately "
|
|
835
|
+
"not executed during validation for exactly that reason.",
|
|
836
|
+
"recommended": "Treat as irreversible within a session. server returns _ok() "
|
|
837
|
+
"unconditionally because there is nothing to check.",
|
|
838
|
+
"tags": ["resolve-21", "unreliable-return", "irreversible", "session-wide"],
|
|
839
|
+
"submit": "missing",
|
|
840
|
+
},
|
|
841
|
+
{
|
|
842
|
+
"symbol": "MediaPoolItem.PerformAudioClassification / ClearAudioClassification",
|
|
843
|
+
"object": "MediaPoolItem / Folder",
|
|
844
|
+
"signature": "() -> Bool",
|
|
845
|
+
"reality": "Both work without any Extras pack and the effect is observable, "
|
|
846
|
+
"which is unusual for this family. Verified on Studio 21.0.2.4 "
|
|
847
|
+
"against a synthetic speech clip: PerformAudioClassification "
|
|
848
|
+
"returned True and set the clip property 'Category' from '' to "
|
|
849
|
+
"'Dialogue' (also surfacing Category/Subcategory in GetMetadata); "
|
|
850
|
+
"ClearAudioClassification returned True and reset 'Category' to "
|
|
851
|
+
"'Uncategorized' — note the cleared state is 'Uncategorized', NOT "
|
|
852
|
+
"the original empty string.",
|
|
853
|
+
"recommended": "Read back GetClipProperty('Category'); treat both '' and "
|
|
854
|
+
"'Uncategorized' as unclassified.",
|
|
855
|
+
"tags": ["ai", "audio", "resolve-21", "readback"],
|
|
856
|
+
},
|
|
732
857
|
{
|
|
733
858
|
"symbol": "subprocess inheriting stdin under the MCP stdio server",
|
|
734
859
|
"object": "(server runtime)",
|