davinci-resolve-mcp 2.69.3 → 2.70.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +85 -0
- package/README.md +1 -1
- package/docs/SKILL.md +78 -1
- package/docs/guides/headless-edit-loop.md +238 -0
- package/docs/reference/api-limitations.md +65 -4
- package/docs/reference/headless-capability-matrix.md +56 -0
- package/docs/reference/headless-cli.md +751 -0
- package/docs/reference/pixel-equality.md +11 -0
- package/docs/reference/roundtrip-fidelity.md +17 -0
- package/docs/reference/roundtrip-rich-fidelity.md +18 -0
- package/install.py +1 -1
- package/package.json +1 -1
- package/scripts/bisect_headless_hang.py +125 -0
- package/scripts/contact_sheet.py +209 -0
- package/scripts/headless_differential.py +578 -0
- package/scripts/mode_matrix.py +345 -0
- package/scripts/mode_matrix_worker.py +232 -0
- package/scripts/pixel_equality.py +402 -0
- package/scripts/relink_moved_media.py +267 -0
- package/scripts/render_stress.py +542 -0
- package/scripts/resolve_headless.py +225 -0
- package/scripts/roundtrip_matrix.py +434 -0
- package/scripts/roundtrip_rich.py +431 -0
- package/src/granular/common.py +1 -1
- package/src/server.py +92 -67
- package/src/utils/api_truth.py +302 -4
- package/src/utils/headless_differential.py +363 -0
- package/src/utils/mode_matrix.py +338 -0
- package/src/utils/probe_catalogue.py +796 -0
- package/src/utils/project_cleanup.py +61 -9
- package/src/utils/resolve_runtime.py +233 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,91 @@
|
|
|
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.70.0
|
|
6
|
+
|
|
7
|
+
Headless (`-nogui`) Resolve, measured rather than assumed — and the finding
|
|
8
|
+
reverses this project's working belief about it.
|
|
9
|
+
|
|
10
|
+
### The measurement
|
|
11
|
+
|
|
12
|
+
A GUI run and a `-nogui` run of the same Resolve Studio 19.1.3.7 were probed
|
|
13
|
+
identically and differenced: 139 read-only API probes plus 13 write scenarios
|
|
14
|
+
covering pages, media pool, timeline editing, colour, Fusion comps, Gallery,
|
|
15
|
+
render-to-disk, interchange export, layout presets, playhead and project
|
|
16
|
+
settings. 238 paired observations.
|
|
17
|
+
|
|
18
|
+
**Zero capabilities worked with a UI and failed without one.** Render to disk,
|
|
19
|
+
AAF/EDL/FCPXML/DRT/OTIO export, `ExportCurrentFrameAsStill`, `GrabStill`, Fusion
|
|
20
|
+
comp create/delete, colour groups, and even UI layout presets all behave
|
|
21
|
+
identically headless.
|
|
22
|
+
|
|
23
|
+
**Scope, stated up front:** that is a result about *capability* and about
|
|
24
|
+
modals. It is **not** a stability result. The probe's render was 2 seconds of
|
|
25
|
+
640×360 H.264; long renders, JPEG 2000 decode out of DCP MXF, ProRes-into-MXF
|
|
26
|
+
writes and repeated jobs in one session are all untested, and there is a field
|
|
27
|
+
report of headless crashing *on completion of writes* for exactly that class of
|
|
28
|
+
job. The docs and the runtime guidance say so rather than letting the matrix
|
|
29
|
+
be read as a blanket endorsement.
|
|
30
|
+
|
|
31
|
+
The one difference actually found runs the other way, and it is the one an agent
|
|
32
|
+
cares about: **a GUI Resolve can raise a modal dialog no script can dismiss;
|
|
33
|
+
headless cannot.** The trigger is project switching, and the obvious defence fails —
|
|
34
|
+
`ProjectManager.SaveProject()` returns `False` for the default never-saved
|
|
35
|
+
`Untitled Project`, which is exactly the project that raises the prompt.
|
|
36
|
+
Headless returns the same `False` and switches anyway.
|
|
37
|
+
|
|
38
|
+
Two earlier notes are corrected: `ExportCurrentFrameAsStill` was recorded as a
|
|
39
|
+
headless-only failure and works headless; `ExportStills` was recorded the same
|
|
40
|
+
way and fails in *both* modes (its documented cause is Gallery panel visibility,
|
|
41
|
+
which no headless session and no panel-closed GUI session can satisfy).
|
|
42
|
+
|
|
43
|
+
### Fixed
|
|
44
|
+
|
|
45
|
+
- **`timeline(action="get_items_in_track")` returned object reprs, not items.**
|
|
46
|
+
The handler passed `GetItemListInTrack`'s result to `_ser`, which has no
|
|
47
|
+
TimelineItem branch and falls through to `str(obj)`, so callers got
|
|
48
|
+
`["<PyRemoteObject at 0x…>", …]` instead of clip data. The docstring's claim of
|
|
49
|
+
"full serialization of each item" was never true. The sibling `get_items`
|
|
50
|
+
takes the same params through the same `_track_selector` and calls the same API
|
|
51
|
+
method, so the two are now one handler and `get_items_in_track` is documented
|
|
52
|
+
as its alias — there was no richer per-item serializer to preserve. Thanks
|
|
53
|
+
[@billcarroll](https://github.com/billcarroll) (#105).
|
|
54
|
+
|
|
55
|
+
### Added
|
|
56
|
+
|
|
57
|
+
- `resolve_control(action="runtime_mode")` — is Resolve up, and does it have a
|
|
58
|
+
UI? Answers with no connection needed, and carries the mode-specific guidance
|
|
59
|
+
with it. **There is no API tell**: a headless instance returns a real page from
|
|
60
|
+
`GetCurrentPage()` and identical product/version strings, so this reads the
|
|
61
|
+
process argv, which is the only place `-nogui` appears. `headless` is `null`
|
|
62
|
+
when undeterminable — never read that as `false`.
|
|
63
|
+
- `resolve_control(action="launch", params={"headless": true})`, plus
|
|
64
|
+
`DAVINCI_RESOLVE_HEADLESS=1` to make auto-launch headless. Launching the other
|
|
65
|
+
mode while an instance is running returns `RESOLVE_MODE_CONFLICT` instead of
|
|
66
|
+
starting a second one that would fight the singleton.
|
|
67
|
+
- `src/utils/resolve_runtime.py` — mode detection and launch-command
|
|
68
|
+
construction. Headless launch runs the binary inside the app bundle, because
|
|
69
|
+
`open -a` hands the argument list to LaunchServices, which discards `-nogui`
|
|
70
|
+
and gives you a window with no error.
|
|
71
|
+
- `scripts/resolve_headless.py` — batch/CI entry point: `status`, `guard`,
|
|
72
|
+
`start`, `stop`, and `run -- <cmd>` which only stops what it started.
|
|
73
|
+
- `scripts/headless_differential.py` + `src/utils/headless_differential.py` —
|
|
74
|
+
the harness that produced the above, rerunnable after a Resolve upgrade.
|
|
75
|
+
- `docs/reference/headless-cli.md` — verified flags, environment variables,
|
|
76
|
+
launch/teardown recipes, singleton rules, and the flags found in the
|
|
77
|
+
application binary but deliberately *not* exercised.
|
|
78
|
+
- `docs/reference/headless-capability-matrix.md` — the generated differential.
|
|
79
|
+
- Three `api_truth` entries: the `SaveProject`/modal trap, the absence of any
|
|
80
|
+
headless API tell, and the corrected `ExportStills` reality.
|
|
81
|
+
|
|
82
|
+
### Validation
|
|
83
|
+
|
|
84
|
+
- Offline suite 2280 → 2305 (`tests/test_headless_runtime.py`, 25 tests).
|
|
85
|
+
- Live: full differential recorded in both modes on Resolve Studio 19.1.3.7,
|
|
86
|
+
scratch project created and deleted per run, GUI session restored afterwards.
|
|
87
|
+
Measured teardown 2.1s via `Quit()`, headless boot to scriptable under 3s,
|
|
88
|
+
scripting listener on TCP 15000 in both modes.
|
|
89
|
+
|
|
5
90
|
## What's New in v2.69.3
|
|
6
91
|
|
|
7
92
|
Ships the v2.69.2 bundle, which never reached npm. Same fixes, plus the one that
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
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)
|
package/docs/SKILL.md
CHANGED
|
@@ -218,6 +218,81 @@ respect this hint pass it as `tool_choice={type:"tool", name:"media_analysis"}`
|
|
|
218
218
|
on the next API turn, hard-locking the agent into the correct next call. Hosts
|
|
219
219
|
that don't recognize the field ignore it — the flow is unchanged for them.
|
|
220
220
|
|
|
221
|
+
## Headless Resolve (`-nogui`)
|
|
222
|
+
|
|
223
|
+
Resolve runs without a UI, and it is **capability-identical** to a GUI session.
|
|
224
|
+
Measured across 238 paired observations on Studio 19.1.3.7 (see
|
|
225
|
+
`docs/reference/headless-cli.md` and the regenerable
|
|
226
|
+
`docs/reference/headless-capability-matrix.md`): **zero** capabilities work with
|
|
227
|
+
a UI and fail without one. Pages, render-to-disk, AAF/EDL/XML/DRT/OTIO export,
|
|
228
|
+
`ExportCurrentFrameAsStill`, Fusion comps, colour groups, layout presets and all
|
|
229
|
+
ordinary editorial behave identically.
|
|
230
|
+
|
|
231
|
+
**Capability is not stability, but stability is now partly measured too.** Ten
|
|
232
|
+
consecutive ProRes 422 HQ renders in one headless session, from a JPEG 2000 /
|
|
233
|
+
MXF OP1A source, completed with no crash, no death and no leak — marginally
|
|
234
|
+
faster and ~95 MB lighter than the same ten renders with a UI. What remains
|
|
235
|
+
untested is *sustained* encode over hours and the operator's own footage and
|
|
236
|
+
codec settings. Use headless freely for orchestration, edit, conform, analysis
|
|
237
|
+
and renders of that scale; qualify it on real footage before promising a
|
|
238
|
+
long-form or professional-container delivery, and keep a GUI fallback there.
|
|
239
|
+
|
|
240
|
+
**Headless is NOT immune to modal dialogs — it is worse.** It cannot *display* a
|
|
241
|
+
dialog, but Resolve still tries to raise one, and the call then never returns.
|
|
242
|
+
|
|
243
|
+
The canonical case, measured on a cold `-nogui` boot:
|
|
244
|
+
`ProjectManager.SaveProject()` on the default never-saved project named
|
|
245
|
+
`Untitled Project` **blocks forever** headless (no return after 45s, client
|
|
246
|
+
parked in `Fusion::RemoteApp::WaitPkt`), where the GUI merely returns `False`.
|
|
247
|
+
The project has no location and there is no `SaveProjectAs`, so Resolve wants a
|
|
248
|
+
Save-As dialog and waits for an answer that can never arrive. In the GUI a human
|
|
249
|
+
clears it in one click; headless nothing can.
|
|
250
|
+
|
|
251
|
+
**Never call `SaveProject()` without checking the project name first:**
|
|
252
|
+
|
|
253
|
+
```python
|
|
254
|
+
project = pm.GetCurrentProject()
|
|
255
|
+
if project is not None and project.GetName() != "Untitled Project":
|
|
256
|
+
pm.SaveProject() # safe: it has a location
|
|
257
|
+
# else: nothing to save, and headless this call blocks forever
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
`src/utils/project_cleanup.py:save_project_if_safe(pm)` does exactly this — use
|
|
261
|
+
it rather than calling `SaveProject` directly. And do **not** reach for headless
|
|
262
|
+
to dodge the GUI's save dialog; that trade makes a one-click interruption into a
|
|
263
|
+
dead session.
|
|
264
|
+
|
|
265
|
+
Rules:
|
|
266
|
+
|
|
267
|
+
- **Check the mode before any project switch.** `resolve_control(action="runtime_mode")`
|
|
268
|
+
→ `{running, headless, instances, database_attached, guidance}`. It needs no
|
|
269
|
+
connection.
|
|
270
|
+
- **`database_attached: false` means the instance is WEDGED — stop and restart it.**
|
|
271
|
+
Resolve can come up with no project database attached. It accepts connections
|
|
272
|
+
and answers product, version, page and current-project queries normally, so
|
|
273
|
+
every ordinary liveness check passes, while `CreateProject`/`LoadProject`
|
|
274
|
+
return False forever, `SaveProject` returns None, and some calls never return
|
|
275
|
+
at all. It does not recover on its own. Do not retry; quit and relaunch.
|
|
276
|
+
- **`headless` may be `null`.** That means "cannot be determined", not "has a UI".
|
|
277
|
+
Treat `null` like `false` — take the careful path — but do not report it as fact.
|
|
278
|
+
- **There is no API tell.** A headless instance returns a real page from
|
|
279
|
+
`GetCurrentPage()` and identical product/version strings. Anything that
|
|
280
|
+
inspects the `resolve` handle to guess the mode is guessing. `runtime_mode`
|
|
281
|
+
reads the process argv, which is the only place `-nogui` appears.
|
|
282
|
+
- **Launching:** `resolve_control(action="launch", params={"headless": true})`,
|
|
283
|
+
or set `DAVINCI_RESOLVE_HEADLESS=1` to make auto-launch headless. Launching
|
|
284
|
+
the other mode while an instance is already running returns
|
|
285
|
+
`RESOLVE_MODE_CONFLICT` rather than starting a second one — two Resolves fight
|
|
286
|
+
the singleton and have been observed to crash-loop rather than fail cleanly.
|
|
287
|
+
- **`instances` > 1 is a fault to report**, not a state to work around. Check for
|
|
288
|
+
a render node before starting anything.
|
|
289
|
+
- Teardown is `resolve_control(action="quit")`; it discards the open project
|
|
290
|
+
without prompting, which is what a batch process wants.
|
|
291
|
+
|
|
292
|
+
The one thing headless genuinely cannot do is anything that needs a *visible
|
|
293
|
+
panel* — `ExportStills` being the known case, and it fails in a panel-closed GUI
|
|
294
|
+
too.
|
|
295
|
+
|
|
221
296
|
## Local Control Panel
|
|
222
297
|
|
|
223
298
|
If the user asks to open, launch, or inspect the Resolve MCP control panel, run
|
|
@@ -1850,7 +1925,9 @@ timeline item returns `False` in Resolve. Use `get_node_graph` without a
|
|
|
1850
1925
|
|
|
1851
1926
|
**Gallery export requires the Gallery panel visible** — `ExportStills` only works
|
|
1852
1927
|
if the Gallery panel is open in the Resolve UI on the Color page. Instruct the
|
|
1853
|
-
user to open it via Workspace menu if export fails.
|
|
1928
|
+
user to open it via Workspace menu if export fails. Measured to fail in *both*
|
|
1929
|
+
GUI (panel closed) and headless sessions, so a failure is not a reason to switch
|
|
1930
|
+
modes. For pixels use `Project.ExportCurrentFrameAsStill`, which works in both.
|
|
1854
1931
|
|
|
1855
1932
|
**Python version** — the only hard requirement is Python **3.10+** (the MCP SDK
|
|
1856
1933
|
floor). There is no upper cap: 3.13/3.14 are accepted, and Python 3.14 is verified
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
# Building edits headless: the iterative loop
|
|
2
|
+
|
|
3
|
+
How to drive Resolve entirely from the command line to import media, link it,
|
|
4
|
+
assemble a cut, and then round-trip that cut repeatedly — with the format and
|
|
5
|
+
option choices justified by measurement rather than habit.
|
|
6
|
+
|
|
7
|
+
Everything here was measured on Resolve Studio 19.1.3.7 in **both** GUI and
|
|
8
|
+
`-nogui` sessions on 2026-08-01. Regenerate with `scripts/roundtrip_matrix.py`;
|
|
9
|
+
the raw runs are in `docs/reference/evidence/`.
|
|
10
|
+
|
|
11
|
+
## The short answer
|
|
12
|
+
|
|
13
|
+
**Mode does not matter.** Every result below is byte-identical between GUI and
|
|
14
|
+
`-nogui` — the flat round trip, the complex-cut round trip, and the moved-media
|
|
15
|
+
relink. Choose a format on its properties, never on whether you have a UI.
|
|
16
|
+
|
|
17
|
+
**There is no single best format.** Three measurements pull in different
|
|
18
|
+
directions, and the right choice depends on which one you are up against:
|
|
19
|
+
|
|
20
|
+
| what you need | format | why |
|
|
21
|
+
| --- | --- | --- |
|
|
22
|
+
| **Full fidelity**, media staying put | **DRT** | the only format that kept *everything*: transforms, colours, flags, item and timeline markers |
|
|
23
|
+
| **Iterative loop**, media staying put | **FCP7 XML** or AAF | no media duplication, controllable timeline name — DRT has neither |
|
|
24
|
+
| **Media has MOVED** (real conform) | **FCP7 XML**, AAF, FCPXML 1.10 | the only three that relink. **DRT, OTIO and EDL all fail** |
|
|
25
|
+
|
|
26
|
+
The trap is that DRT looks like the obvious choice — it is native and it is the
|
|
27
|
+
only format that survives a rich cut intact — and it is the *worst* choice for
|
|
28
|
+
both of the other two jobs. It re-imports its media every time, and it cannot
|
|
29
|
+
relink media that has moved at all.
|
|
30
|
+
|
|
31
|
+
**For an iterative loop, use FCP7 XML (or AAF) with:**
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
options = {
|
|
35
|
+
"timelineName": f"CUT_v{iteration:03d}", # MUST be unique per import
|
|
36
|
+
"importSourceClips": False, # reuse the pool, do not re-import
|
|
37
|
+
"sourceClipsFolders": [pool.GetRootFolder()],
|
|
38
|
+
}
|
|
39
|
+
timeline = pool.ImportTimelineFromFile(path, options)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Verified: five consecutive imports, all succeeded, source in-points preserved
|
|
43
|
+
exactly, and **no media duplicated** — the media pool grew by exactly one item
|
|
44
|
+
per import, which is the imported timeline itself, not another copy of the
|
|
45
|
+
footage.
|
|
46
|
+
|
|
47
|
+
## The traps, each measured
|
|
48
|
+
|
|
49
|
+
### A repeated `timelineName` silently stops working
|
|
50
|
+
|
|
51
|
+
Importing the same file three times with the *same* `timelineName`:
|
|
52
|
+
|
|
53
|
+
| import | result |
|
|
54
|
+
| --- | --- |
|
|
55
|
+
| 1 | succeeded, timeline `ITER_FIXED` |
|
|
56
|
+
| 2 | **returned None** |
|
|
57
|
+
| 3 | **returned None** |
|
|
58
|
+
|
|
59
|
+
No error, no exception — `ImportTimelineFromFile` just returns None once the
|
|
60
|
+
name is taken. An iterative loop that reuses one name works exactly once and
|
|
61
|
+
then quietly does nothing, which is the worst possible failure for an automated
|
|
62
|
+
edit cycle. Make the name unique per iteration.
|
|
63
|
+
|
|
64
|
+
### DRT ignores `timelineName` and re-imports the media
|
|
65
|
+
|
|
66
|
+
DRT is the native format and the obvious first choice, but it behaves
|
|
67
|
+
differently on import, in ways the documentation states but that are easy to
|
|
68
|
+
miss:
|
|
69
|
+
|
|
70
|
+
- `timelineName`, `importSourceClips` and `sourceClipsFolders` are **all invalid
|
|
71
|
+
for DRT**. Passing them gets you a failure that says nothing about the format.
|
|
72
|
+
- The imported timeline is named after the **file**, not the timeline inside it,
|
|
73
|
+
and repeated imports auto-uniquify: `iter`, `iter 2`, `iter 3`.
|
|
74
|
+
- Because `importSourceClips` cannot be set to False, each DRT import **adds
|
|
75
|
+
another copy of the source media** to the pool. Measured: three DRT imports
|
|
76
|
+
grew the pool by four items, against one per import for the XML route.
|
|
77
|
+
|
|
78
|
+
So DRT is excellent for a one-shot hand-off and wrong for a loop that runs a
|
|
79
|
+
hundred times.
|
|
80
|
+
|
|
81
|
+
### FCPXML 1.10 exports a *directory*, not a file
|
|
82
|
+
|
|
83
|
+
`Timeline.Export(path, EXPORT_FCPXML_1_10, EXPORT_NONE)` returns True and
|
|
84
|
+
creates a **bundle directory** at `path` containing `Info.fcpxml` — the
|
|
85
|
+
`.fcpxmld` shape Final Cut uses. Consequences:
|
|
86
|
+
|
|
87
|
+
- `path.stat().st_size` reports the directory inode (96 bytes here), so a
|
|
88
|
+
size check reads it as a near-empty export.
|
|
89
|
+
- `ImportTimelineFromFile(path)` fails, because the path is a directory.
|
|
90
|
+
|
|
91
|
+
Point the import at the member instead, and the format round-trips exactly:
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
inner = next(Path(export_path).glob("*.fcpxml")) # Info.fcpxml
|
|
95
|
+
pool.ImportTimelineFromFile(str(inner), options)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
This is why the first round-trip run reported FCPXML 1.10 as import-failed in
|
|
99
|
+
both modes. It was the harness, not the format.
|
|
100
|
+
|
|
101
|
+
### OTIO with `importSourceClips: False` arrives fully offline
|
|
102
|
+
|
|
103
|
+
Every other format relinks against the existing pool. OTIO does not: with
|
|
104
|
+
`reuse_pool` the timeline rebuilds with correct structure and **all four items
|
|
105
|
+
offline**, in both modes. With `importSourceClips: True` and a `sourceClipsPath`
|
|
106
|
+
it links correctly. If you want OTIO, re-import the sources.
|
|
107
|
+
|
|
108
|
+
## What each format actually keeps
|
|
109
|
+
|
|
110
|
+
The flat single-track round trip found every format frame-exact — because a flat
|
|
111
|
+
cut of untouched clips gives them nothing to disagree about. On a real cut (two
|
|
112
|
+
video tracks, audio, a per-item transform, clip colours, flags, item and timeline
|
|
113
|
+
markers) they diverge sharply. Identical in both modes:
|
|
114
|
+
|
|
115
|
+
| format | cut | tracks | transform | colour | flags | item markers | timeline markers |
|
|
116
|
+
| --- | --- | --- | --- | --- | --- | --- | --- |
|
|
117
|
+
| **drt** | kept | kept | **kept** | **kept** | kept | **kept** | kept |
|
|
118
|
+
| fcpxml_1_10 | kept | **lost an audio track** | kept | lost | kept | lost | lost |
|
|
119
|
+
| fcp7xml | kept | kept | lost **only FlipX** | lost | kept | lost | lost |
|
|
120
|
+
| aaf | kept | kept | **lost: all reset to defaults** | lost | kept | lost | kept |
|
|
121
|
+
| otio | **lost: source** | kept | kept | lost | lost | kept | kept |
|
|
122
|
+
| edl | **lost** | **1v1a** | n/a | lost | lost | lost | lost |
|
|
123
|
+
|
|
124
|
+
Two details worth having:
|
|
125
|
+
|
|
126
|
+
- **AAF resets the entire transform to defaults**, while **FCP7 XML preserves
|
|
127
|
+
zoom, pan and rotation exactly** and drops only `FlipX`. A whole-dict
|
|
128
|
+
comparison called both "lost", which is the same word for "carries geometry"
|
|
129
|
+
and "does not". Crop comes back as 39.999936 against 40.0 — that is unit
|
|
130
|
+
round-tripping, not loss.
|
|
131
|
+
- **FCPXML 1.10 silently drops an audio track** (2v2a in, 2v1a out).
|
|
132
|
+
|
|
133
|
+
## Relinking media that has moved
|
|
134
|
+
|
|
135
|
+
The test that matters for conform: build the cut, export it, **rename the media
|
|
136
|
+
directory** so every baked-in path is dead, then import in a fresh project with
|
|
137
|
+
`sourceClipsPath` pointing at the new location. Identical in both modes:
|
|
138
|
+
|
|
139
|
+
| format | relinked | cut still frame-exact |
|
|
140
|
+
| --- | --- | --- |
|
|
141
|
+
| fcp7xml | **3/3** | yes |
|
|
142
|
+
| aaf | **3/3** | yes |
|
|
143
|
+
| fcpxml_1_10 | **3/3** | yes |
|
|
144
|
+
| drt | **0/3** — items stay pointing at the dead path | yes (but offline) |
|
|
145
|
+
| edl | **0/3** | yes (but offline) |
|
|
146
|
+
| otio | **import returned None** — fails outright | — |
|
|
147
|
+
|
|
148
|
+
`sourceClipsPath` is not valid for DRT, so DRT has no mechanism to be told where
|
|
149
|
+
the media went. EDL carries reel names rather than paths and did not resolve
|
|
150
|
+
either. OTIO did not merely fail to relink — the import returned `None`.
|
|
151
|
+
|
|
152
|
+
Note that "frame-exact" stays true even where nothing relinked: the *cut*
|
|
153
|
+
survives offline. That is worth knowing, because it means a failed relink leaves
|
|
154
|
+
a correct timeline you can relink by other means, rather than a wrong one.
|
|
155
|
+
|
|
156
|
+
## Format selection
|
|
157
|
+
|
|
158
|
+
All are frame-exact in both modes; these are the tie-breakers. Payload sizes are
|
|
159
|
+
for the same four-item test timeline.
|
|
160
|
+
|
|
161
|
+
| format | size | links from pool | name controllable | notes |
|
|
162
|
+
| --- | --- | --- | --- | --- |
|
|
163
|
+
| **fcp7xml** | 42 KB | yes | yes | the recommended loop format |
|
|
164
|
+
| **aaf** | 315 KB | yes | yes | same behaviour, 7x the payload |
|
|
165
|
+
| **edl** | 512 B | yes | yes | astonishingly, frame-exact — see below |
|
|
166
|
+
| drt | 19 KB | n/a (always re-imports) | no | native; one-shot hand-off |
|
|
167
|
+
| otio | 85 KB | **no — offline** | yes | needs `importSourceClips: True` |
|
|
168
|
+
| fcpxml_1_10 | 3 KB | yes | yes | writes a bundle directory |
|
|
169
|
+
|
|
170
|
+
**On EDL:** 512 bytes round-tripped frame-exactly with media linked, which is not
|
|
171
|
+
what EDL's reputation suggests. It works here because the test media carries
|
|
172
|
+
embedded timecode (`-timecode 01:00:00:00`) and the sources relink by name. EDL
|
|
173
|
+
carries no effects, no multiple tracks, and no speed changes — so this result
|
|
174
|
+
means "EDL is sufficient for a flat single-track conform of timecoded media",
|
|
175
|
+
not "EDL is a good interchange format". Do not generalise it to a real cut.
|
|
176
|
+
|
|
177
|
+
## The whole loop, headless
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
# 1. one Resolve, no UI, and confirm nothing else holds the singleton
|
|
181
|
+
python scripts/resolve_headless.py guard
|
|
182
|
+
python scripts/resolve_headless.py start
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
```python
|
|
186
|
+
# 2. import media and build the initial cut through the API, not a file.
|
|
187
|
+
# AppendToTimeline with explicit clipInfo dicts is frame-exact and needs no
|
|
188
|
+
# interchange format at all.
|
|
189
|
+
clips = resolve.GetMediaStorage().AddItemListToMediaPool(paths)
|
|
190
|
+
timeline = pool.CreateEmptyTimeline("CUT_v001")
|
|
191
|
+
project.SetCurrentTimeline(timeline)
|
|
192
|
+
pool.AppendToTimeline([
|
|
193
|
+
{"mediaPoolItem": clips[0], "startFrame": 0, "endFrame": 191},
|
|
194
|
+
{"mediaPoolItem": clips[1], "startFrame": 24, "endFrame": 71},
|
|
195
|
+
])
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Two things about that call, both measured and both able to waste a session:
|
|
199
|
+
|
|
200
|
+
- **`endFrame` is inclusive-of-the-last-frame here** — pass `frames` rather than
|
|
201
|
+
`frames - 1` and you append nothing.
|
|
202
|
+
- **Do not build a repeated clip with `CreateTimelineFromClips([clip] * n)`.** It
|
|
203
|
+
deduplicates identical MediaPoolItem references: fifteen copies produced a
|
|
204
|
+
one-copy timeline, returned a valid Timeline, and reported no error. Use
|
|
205
|
+
`AppendToTimeline` with one dict per instance, and assert the resulting frame
|
|
206
|
+
count.
|
|
207
|
+
|
|
208
|
+
```python
|
|
209
|
+
# 3. iterate: export, mutate outside Resolve, re-import under a NEW name
|
|
210
|
+
timeline.Export(str(path), resolve.EXPORT_FCP_7_XML, resolve.EXPORT_NONE)
|
|
211
|
+
... # edit the XML however you like
|
|
212
|
+
pool.ImportTimelineFromFile(str(path), {
|
|
213
|
+
"timelineName": f"CUT_v{n:03d}",
|
|
214
|
+
"importSourceClips": False,
|
|
215
|
+
"sourceClipsFolders": [pool.GetRootFolder()],
|
|
216
|
+
})
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
# 4. done
|
|
221
|
+
python scripts/resolve_headless.py stop
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## Rules that apply to the whole loop
|
|
225
|
+
|
|
226
|
+
- **Never call `SaveProject()` without checking the project name.** On the
|
|
227
|
+
never-saved `Untitled Project` it blocks the entire application — in both
|
|
228
|
+
modes — and needs a force-kill. Use
|
|
229
|
+
`src/utils/project_cleanup.py:save_project_if_safe`.
|
|
230
|
+
- **Verify structure, not return values.** Every finding above involved a call
|
|
231
|
+
that returned success. After building or importing a cut, read the item count
|
|
232
|
+
and each item's `GetLeftOffset()` back and compare against what you intended.
|
|
233
|
+
- **Teardown order is `CloseProject` → `LoadProject(something named)` →
|
|
234
|
+
`DeleteProject`.** `CloseProject` is what releases the session lock, and
|
|
235
|
+
landing on the unsaveable default project is what raises the next modal.
|
|
236
|
+
- Capability is identical headless, but **stability under long renders is not
|
|
237
|
+
established** — see the limits section in
|
|
238
|
+
[headless-cli.md](../reference/headless-cli.md).
|
|
@@ -12,7 +12,7 @@ that none exists).
|
|
|
12
12
|
|
|
13
13
|
**Verified on:** DaVinci Resolve Studio 21.0.0
|
|
14
14
|
|
|
15
|
-
**Totals:**
|
|
15
|
+
**Totals:** 21 missing capabilities, 20 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
|
|
@@ -41,6 +41,13 @@ Python bridge fabricates a callable for any attribute name (see the
|
|
|
41
41
|
Functionality that exists in the Resolve UI but has no scripting API
|
|
42
42
|
equivalent, blocking full automation.
|
|
43
43
|
|
|
44
|
+
### Project.SetSetting('timelinePlaybackFrameRate')
|
|
45
|
+
|
|
46
|
+
- **Object:** `Project`
|
|
47
|
+
- **Behavior:** Returns False for every value form tried (string, int, float), both before and after a timeline exists, so the playback frame rate cannot be set from the API at all. Reported by a community contributor against Resolve Studio while assembling a vertical timeline (PR #99).
|
|
48
|
+
- **Workaround / current handling:** Ask the user to set it in Project Settings > Master Settings > Playback frame rate as a SETUP step, before any timeline exists. Read it back to confirm; do not report it as set on the strength of the call alone.
|
|
49
|
+
- **Tags:** project-settings, silent-failure, timeline
|
|
50
|
+
|
|
44
51
|
### Timeline.GetTimelineByName
|
|
45
52
|
|
|
46
53
|
- **Object:** `Project`
|
|
@@ -226,9 +233,47 @@ values, or automation-hostile modal prompts.
|
|
|
226
233
|
|
|
227
234
|
- **Object:** `ProjectManager`
|
|
228
235
|
- **Signature:** `(projectName) -> bool`
|
|
229
|
-
- **Behavior:** Returns False (no deletion) when the target project is, or recently was, the current project, and is flaky on the first attempt — so a single bool() call leaves the project undeleted with no useful error.
|
|
230
|
-
- **Workaround / current handling:**
|
|
231
|
-
- **Tags:** unreliable-return, project, flaky
|
|
236
|
+
- **Behavior:** Returns False (no deletion) when the target project is, or recently was, the current project, and is flaky on the first attempt — so a single bool() call leaves the project undeleted with no useful error. CloseProject reliably releases it: a delete that had failed six times in a row, a second apart, succeeded on the first attempt after one. Switching away with LoadProject is NOT reliable — it left the delete failing permanently after a heavily-used project, yet succeeded for a project that had only been created and loaded. Whatever distinguishes the two (modification? an open timeline?) is not established, so LoadProject-away cannot be depended on.
|
|
237
|
+
- **Workaround / current handling:** CloseProject the target FIRST (that is what releases it), then LoadProject some named project so the session is not left on the unsaveable 'Untitled Project' fallback, then delete. Use src/utils/project_cleanup.py:delete_project_safely, which does exactly that.
|
|
238
|
+
- **Tags:** unreliable-return, project, flaky, session-lock
|
|
239
|
+
|
|
240
|
+
### ProjectManager.GetCurrentDatabase
|
|
241
|
+
|
|
242
|
+
- **Object:** `ProjectManager`
|
|
243
|
+
- **Signature:** `() -> {DbType, DbName}`
|
|
244
|
+
- **Behavior:** Returns None when Resolve has come up without attaching to a project database — a state it reaches after an unclean shutdown, and does not recover from on its own. In that state the application looks entirely healthy: it accepts scripting connections, and GetProductName, GetVersionString, GetCurrentPage and GetCurrentProject all answer normally. But CreateProject and LoadProject return False indefinitely, SaveProject returns None, and some calls block forever in the scripting transport rather than returning at all. Observed headless on Studio 19.1.3.7 after force-killing a wedged instance; the replacement took 2m05s to become scriptable and came up with no database.
|
|
245
|
+
- **Workaround / current handling:** Treat a non-None GetCurrentDatabase() as the liveness check, not a successful connection — every cheaper check passes in the wedged state. `resolve_control runtime_mode` reports `database_attached` for exactly this. There is no repair: quit and restart Resolve.
|
|
246
|
+
- **Tags:** project, database, headless, silent-failure, startup
|
|
247
|
+
|
|
248
|
+
### MediaPool.ImportTimelineFromFile
|
|
249
|
+
|
|
250
|
+
- **Object:** `MediaPool`
|
|
251
|
+
- **Signature:** `(filePath, {importOptions}) -> Timeline`
|
|
252
|
+
- **Behavior:** Returns None — no error, no exception — when the requested `timelineName` already exists. Measured: importing one file three times with the same name succeeded once and returned None twice. An iterative loop that reuses a fixed name therefore works exactly once and then silently does nothing. DRT ignores importOptions entirely (timelineName, importSourceClips and sourceClipsFolders are all invalid for it): the timeline is named after the FILE, repeats auto-uniquify ('iter', 'iter 2', 'iter 3'), and because importSourceClips cannot be disabled each DRT import adds another copy of the source media to the pool. OTIO is the one format that will NOT relink from the pool — with importSourceClips=False its timeline rebuilds with correct structure and every item OFFLINE, in both GUI and headless.
|
|
253
|
+
- **Workaround / current handling:** For a repeatable loop use FCP7 XML or AAF with a UNIQUE timelineName per iteration plus importSourceClips=False and sourceClipsFolders=[root] — verified frame-exact over five consecutive imports with no media duplicated. Use DRT for one-shot hand-offs only. For OTIO, pass importSourceClips=True with a sourceClipsPath. See docs/guides/headless-edit-loop.md.
|
|
254
|
+
- **Tags:** timeline, import, interchange, silent-failure, conform
|
|
255
|
+
|
|
256
|
+
### Timeline.Export(EXPORT_FCPXML_1_10)
|
|
257
|
+
|
|
258
|
+
- **Object:** `Timeline`
|
|
259
|
+
- **Signature:** `(fileName, EXPORT_FCPXML_1_10, EXPORT_NONE) -> bool`
|
|
260
|
+
- **Behavior:** Returns True and creates a BUNDLE DIRECTORY at the given path containing `Info.fcpxml`, rather than a file — the `.fcpxmld` shape Final Cut uses. Two consequences bite immediately: a `stat().st_size` check reads the directory inode (96 bytes here) and concludes the export is empty, and ImportTimelineFromFile on that path fails because it is a directory. Every other EXPORT_* type in this build writes a plain file, so code that treats them uniformly gets this one wrong. Pointed at the inner member, the format round-trips frame-exactly in both modes.
|
|
261
|
+
- **Workaround / current handling:** After exporting, check `Path(p).is_dir()` and import `next(Path(p).glob('*.fcpxml'))` instead. Do not size-check the export path itself.
|
|
262
|
+
- **Tags:** timeline, export, interchange, fcpxml, silent-failure
|
|
263
|
+
|
|
264
|
+
### MediaPool.CreateTimelineFromClips
|
|
265
|
+
|
|
266
|
+
- **Object:** `MediaPool`
|
|
267
|
+
- **Behavior:** Fails with a bare 'Failed to create timeline from clip_infos' — naming nothing actionable — when the Media Pool's CURRENT folder is not the folder holding the clips, even though every clip_id passed is valid and resolvable. Reported against Resolve Studio in PR #99.
|
|
268
|
+
- **Workaround / current handling:** Call MediaPool.SetCurrentFolder() to the clips' bin before creating the timeline (media_pool set_current_folder). Valid ids are not sufficient.
|
|
269
|
+
- **Tags:** media-pool, timeline, unhelpful-error
|
|
270
|
+
|
|
271
|
+
### TimelineItem.AddFusionComp / LoadFusionCompByName
|
|
272
|
+
|
|
273
|
+
- **Object:** `TimelineItem (media-backed clip)`
|
|
274
|
+
- **Behavior:** A Fusion composition created on a media clip through the API is not applied at render WHEN MEDIAOUT HAS NO PATH FROM MEDIAIN. The original blanket form of this entry — 'never applied at render' — was too broad and was corrected on 2026-08-02: a comp wired MediaIn -> Blur -> MediaOut, created entirely through the API on an ordinary media clip, DOES render. PSNR between the plain and Fusion renders of the same timeline was 22.7 dB (identical would be infinite), the file shrank 22.5 MB -> 14.8 MB as a blur should, and the output was frame-for-frame identical in GUI and headless. A first attempt that wired ONLY MediaOut -> Blur, leaving the Blur with no source, made the render job come back 'Failed' with an 887-byte file — so an unrooted graph does not merely get bypassed, it can take the render down. What still stands is the original observation for the configuration it actually tested, which is retained below and has NOT been re-measured: AddFusionComp() returns the comp, AddTool/Connect/SetInput all succeed, and the whole graph reads back correctly (GetCompCount 1, MediaOut1.Input wired to the new tool, StyledText returning the value just set) — but the rendered output is byte-for-byte the untouched source media. Verified live on Studio 19.1.3.7 with the strongest form of the test: MediaOut1 fed ONLY by a Text+, with no path from MediaIn at all, still rendered the unmodified clip. LoadFusionCompByName on the sole comp does not activate it either. Contrast InsertFusionTitleIntoTimeline, whose comp DOES render — text set via SetInput('StyledText') appears in the output — so this is specific to comps attached to media-backed clips, not to Fusion through the API generally.
|
|
275
|
+
- **Workaround / current handling:** Wire the graph so MediaOut descends from MediaIn — that is the difference between a comp that renders and one that is silently bypassed, and it is what made this look like 'Fusion never renders from the API'. Never leave a tool unrooted: a MediaOut fed by a tool with no source failed the render job outright. For text or effects over picture, insert a Fusion title/generator as its own timeline clip and set its Text+ (fusion_comp set_text_plus), rather than attaching a comp to the media clip. Note the destination track cannot be chosen from the API (see the Track Selector entry), so overlaying onto an existing clip's track is not currently reachable end-to-end. Building the comp in the Fusion page UI works; only the API-created comp is ignored.
|
|
276
|
+
- **Tags:** fusion, silent-failure, render
|
|
232
277
|
|
|
233
278
|
### Composition.Paste
|
|
234
279
|
|
|
@@ -291,3 +336,19 @@ values, or automation-hostile modal prompts.
|
|
|
291
336
|
- **Workaround / current handling:** Normalize both arguments through the live maps before calling: src.utils.render_ids.render_format_id_from_formats and render_codec_id_from_codecs accept a description or an id and return the id.
|
|
292
337
|
- **Reference:** [issue #59](https://github.com/samuelgursky/davinci-resolve-mcp/issues/59)
|
|
293
338
|
- **Tags:** render, deliver, silent-failure, id-vs-label
|
|
339
|
+
|
|
340
|
+
### ProjectManager.SaveProject
|
|
341
|
+
|
|
342
|
+
- **Object:** `ProjectManager`
|
|
343
|
+
- **Signature:** `() -> bool`
|
|
344
|
+
- **Behavior:** On the default, never-saved project named 'Untitled Project' this call CANNOT succeed — the project has no location and there is no SaveProjectAs to give it one — and the two modes fail differently. In the GUI it returns False. HEADLESS IT BLOCKS FOREVER: measured on a cold -nogui boot with the database verified attached immediately beforehand, no return after 45s, the client parked in Fusion::RemoteApp::WaitPkt. Resolve wants a Save-As dialog and waits for an answer that cannot arrive. It also degrades the instance: after the hung call was interrupted, SaveProject began returning None instantly on that instance. On a named project it is fine in both modes.
|
|
345
|
+
- **Workaround / current handling:** Guard it: only call SaveProject when GetCurrentProject().GetName() != 'Untitled Project'. There is nothing to save on the default project and the call cannot succeed, so skipping it loses nothing. Do NOT reach for headless to dodge the GUI's save dialog — headless turns that dialog into an unbounded hang that no human can clear.
|
|
346
|
+
- **Tags:** project, modal, headless, hang, silent-failure, unreliable-return
|
|
347
|
+
|
|
348
|
+
### GalleryStillAlbum.ExportStills
|
|
349
|
+
|
|
350
|
+
- **Object:** `GalleryStillAlbum`
|
|
351
|
+
- **Signature:** `(galleryStills, folderPath, filePrefix, format) -> bool`
|
|
352
|
+
- **Behavior:** PANEL-dependent, not mode-dependent — and the distinction took three revisions of this entry to pin down, so the evidence is recorded rather than summarised. Across four controlled 92-probe sweeps (2 GUI, 2 headless, 2026-08-01) it returned False and wrote nothing in ALL FOUR, while Timeline.GrabStill() succeeded in all four — so it is not being handed an empty still. In one earlier GUI session it DID work, returning True and writing 2 files. The variable that differed is not the mode: it is whether the Gallery panel was visible on the Color page, which depends on the restored workspace layout and which the harness does not control. A headless session can never satisfy it, so in practice the call never works headless; a GUI session satisfies it only sometimes. Project.ExportCurrentFrameAsStill worked in all four runs in both modes.
|
|
353
|
+
- **Workaround / current handling:** Do not use ExportStills unattended in either mode — a GUI session is not sufficient, only a GUI session with the Gallery panel open. Use Project.ExportCurrentFrameAsStill for pixels (verified in both modes, four for four) or drp.extract_node_graphs for grades. If ExportStills must be used, have the user open Workspace > Gallery first and verify the written files rather than trusting the return.
|
|
354
|
+
- **Tags:** gallery, stills, headless, unreliable-return
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# GUI vs headless probe matrix
|
|
2
|
+
|
|
3
|
+
105 probes, 2 GUI run(s) x 2 headless run(s).
|
|
4
|
+
|
|
5
|
+
| verdict | count |
|
|
6
|
+
| --- | --- |
|
|
7
|
+
| parity | 86 |
|
|
8
|
+
| flaky | 0 |
|
|
9
|
+
| hang_headless | 0 |
|
|
10
|
+
| hang_gui | 0 |
|
|
11
|
+
| headless_degraded | 2 |
|
|
12
|
+
| gui_degraded | 0 |
|
|
13
|
+
| both_hang | 0 |
|
|
14
|
+
| both_failed | 14 |
|
|
15
|
+
| divergent | 3 |
|
|
16
|
+
| untested | 0 |
|
|
17
|
+
|
|
18
|
+
## Coverage by category
|
|
19
|
+
|
|
20
|
+
| category | probes | parity | hang_headless | headless_degraded | gui_degraded | divergent | untested |
|
|
21
|
+
| --- | --- | --- | --- | --- | --- | --- | --- |
|
|
22
|
+
| app | 8 | 6 | 0 | 0 | 0 | 0 | 0 |
|
|
23
|
+
| audio | 3 | 3 | 0 | 0 | 0 | 0 | 0 |
|
|
24
|
+
| color | 9 | 9 | 0 | 0 | 0 | 0 | 0 |
|
|
25
|
+
| editorial | 5 | 0 | 0 | 2 | 0 | 1 | 0 |
|
|
26
|
+
| fusion | 2 | 2 | 0 | 0 | 0 | 0 | 0 |
|
|
27
|
+
| gallery | 4 | 3 | 0 | 0 | 0 | 0 | 0 |
|
|
28
|
+
| interchange | 5 | 5 | 0 | 0 | 0 | 0 | 0 |
|
|
29
|
+
| layout | 4 | 4 | 0 | 0 | 0 | 0 | 0 |
|
|
30
|
+
| lifecycle | 3 | 3 | 0 | 0 | 0 | 0 | 0 |
|
|
31
|
+
| longop | 4 | 1 | 0 | 0 | 0 | 0 | 0 |
|
|
32
|
+
| media_pool | 8 | 5 | 0 | 0 | 0 | 0 | 0 |
|
|
33
|
+
| pages | 7 | 7 | 0 | 0 | 0 | 0 | 0 |
|
|
34
|
+
| project_manager | 8 | 8 | 0 | 0 | 0 | 0 | 0 |
|
|
35
|
+
| project_settings | 5 | 5 | 0 | 0 | 0 | 0 | 0 |
|
|
36
|
+
| projectlevel | 4 | 0 | 0 | 0 | 0 | 2 | 0 |
|
|
37
|
+
| render | 6 | 6 | 0 | 0 | 0 | 0 | 0 |
|
|
38
|
+
| timeline | 12 | 11 | 0 | 0 | 0 | 0 | 0 |
|
|
39
|
+
| timeline_item | 8 | 8 | 0 | 0 | 0 | 0 | 0 |
|
|
40
|
+
|
|
41
|
+
## Mode-dependent findings
|
|
42
|
+
|
|
43
|
+
> **Re-verify every row below in isolation before believing it.** Probes run in catalogue order against one shared fixture, so a probe near the end has ~90 destructive probes' worth of accumulated state behind it, and the two modes can drift apart for reasons that have nothing to do with the mode. Measured: five findings here — nested timelines, clip linking, take selectors, multi-job queues and render presets — all reproduced as mode differences in a full sweep and all came out **identical** when re-run with `--only`. Isolation is the test:
|
|
44
|
+
|
|
45
|
+
> ```
|
|
46
|
+
> python scripts/mode_matrix.py run --mode gui --out iso_gui.jsonl --only <probe>
|
|
47
|
+
> python scripts/mode_matrix.py run --mode headless --out iso_hl.jsonl --only <probe>
|
|
48
|
+
> ```
|
|
49
|
+
|
|
50
|
+
| probe | verdict | GUI | headless | note |
|
|
51
|
+
| --- | --- | --- | --- | --- |
|
|
52
|
+
| `editorial.nested_timeline` | headless_degraded | ok: 'True/1' | raised | succeeded with a UI, failed without one |
|
|
53
|
+
| `editorial.set_clips_linked` | headless_degraded | ok: True | none: None | succeeded with a UI, failed without one |
|
|
54
|
+
| `editorial.take_selector` | divergent | ok: 'False/0/None/None' | ok: 'None/None/None/None' | both returned, values differ |
|
|
55
|
+
| `projectlevel.multi_job_queue` | divergent | ok: '3/3' | ok: '0/0' | both returned, values differ |
|
|
56
|
+
| `projectlevel.render_preset_roundtrip` | divergent | ok: 'True/True' | ok: 'None/None' | both returned, values differ |
|