davinci-resolve-mcp 2.95.2 → 2.96.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 +123 -0
- package/README.md +5 -5
- package/README.zh-CN.md +6 -6
- package/docs/SKILL.md +58 -8
- package/docs/contributing.md +1 -1
- package/docs/install.md +17 -1
- package/docs/reference/api-coverage.md +2 -2
- package/docs/reference/api-limitations.md +1 -1
- package/install.py +383 -4
- package/package.json +1 -1
- package/resolve-advanced/server/aaf_probe.py +117 -14
- package/resolve-advanced/server/tools/project_read.mjs +35 -2
- package/resolve-advanced/vendor/drp-format/__tests__/media-timemap.test.js +56 -0
- package/resolve-advanced/vendor/drp-format/media-timemap.js +61 -9
- package/src/granular/common.py +1 -1
- package/src/server.py +412 -15
- package/src/utils/api_truth.py +33 -3
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,129 @@
|
|
|
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.96.0
|
|
6
|
+
|
|
7
|
+
New tool **`timeline_frame`** — the assistant can look at what Resolve is
|
|
8
|
+
rendering instead of inferring it from metadata. Closes
|
|
9
|
+
[#146](https://github.com/samuelgursky/davinci-resolve-mcp/issues/146).
|
|
10
|
+
|
|
11
|
+
### `timeline_frame(action="capture")`
|
|
12
|
+
|
|
13
|
+
Returns the timeline frame as MCP image content — grade, Fusion, titles,
|
|
14
|
+
transitions, as composited. (For the raw camera file, `media_analysis(action=
|
|
15
|
+
"extract_frames")` is still the right call.)
|
|
16
|
+
|
|
17
|
+
- `timecode` / `frame` — capture anywhere, not just the playhead. Accepts
|
|
18
|
+
absolute (`01:00:15:12`) or elapsed (`00:00:15:12`) timecode, matching the
|
|
19
|
+
marker-parameter contract.
|
|
20
|
+
- `quality` — `preview` (Resolve's thumbnail; fast, writes nothing) or `full`
|
|
21
|
+
(full resolution via a Gallery still, removed again afterwards).
|
|
22
|
+
- `max_width` — bound the context cost. Preview downscales in-process with an
|
|
23
|
+
area average; `full` rescales with ffmpeg, and **fails rather than silently
|
|
24
|
+
returning a full-size frame** when ffmpeg is missing.
|
|
25
|
+
- `format` — `png` (default), `jpg`, or `tif` on the `full` path.
|
|
26
|
+
- `timeline_name` — capture from another timeline; it is made current for the
|
|
27
|
+
read and the original restored after.
|
|
28
|
+
|
|
29
|
+
A capture is a read: the Color page, playhead, current timeline, and Gallery all
|
|
30
|
+
come back as the caller left them.
|
|
31
|
+
|
|
32
|
+
### Why a separate tool rather than an action on `timeline`
|
|
33
|
+
|
|
34
|
+
FastMCP derives an output schema from a tool's return annotation and validates
|
|
35
|
+
returns against it, so a `-> Dict[str, Any]` tool cannot return image content.
|
|
36
|
+
`timeline_frame` is annotated `-> Any` for that reason — the same reason
|
|
37
|
+
`timeline_markers` already was. The issue asked for a top-level tool, and the
|
|
38
|
+
schema constraint independently forces one.
|
|
39
|
+
|
|
40
|
+
### Fixed — thumbnail reads no longer misreport a page problem as a missing frame
|
|
41
|
+
|
|
42
|
+
`GetCurrentClipThumbnailImage` returns `None` on every page except Color, with
|
|
43
|
+
nothing to distinguish that from "no frame here."
|
|
44
|
+
`timeline_markers(action="get_thumbnail")` and `get_thumbnail_image` called it
|
|
45
|
+
bare, so off the Color page they reported a missing thumbnail. Both now hold the
|
|
46
|
+
Color page for the read and restore the previous page — the mitigation
|
|
47
|
+
`thumbnail_contact_sheet` already used. When the switch genuinely cannot happen
|
|
48
|
+
(headless, page locked), the error names the Color-page requirement instead.
|
|
49
|
+
|
|
50
|
+
`get_thumbnail_image` now shares the `timeline_frame` capture path, so it picks
|
|
51
|
+
up the fix; its contract is unchanged.
|
|
52
|
+
|
|
53
|
+
### Fixed — the installer configures Codex CLI
|
|
54
|
+
|
|
55
|
+
The installer claimed Codex support but never wrote its config, so a successful
|
|
56
|
+
install left no `davinci-resolve` entry in `~/.codex/config.toml` — everything
|
|
57
|
+
else (venv, Resolve paths, bridge, server) was in place and only the
|
|
58
|
+
registration was missing. Codex keys MCP servers under a TOML
|
|
59
|
+
`[mcp_servers.<name>]` table, and the installer only knew how to write JSON.
|
|
60
|
+
Closes [#39](https://github.com/samuelgursky/davinci-resolve-mcp/issues/39).
|
|
61
|
+
|
|
62
|
+
`codex` is now a selectable client (included in `--clients all`), writing
|
|
63
|
+
`$CODEX_HOME/config.toml` (default `~/.codex/config.toml`), and `--manual`
|
|
64
|
+
prints a ready-to-paste TOML block.
|
|
65
|
+
|
|
66
|
+
The merge is a text splice, not a parse-and-rewrite, so comments and hand
|
|
67
|
+
formatting survive; an existing `[mcp_servers.davinci-resolve]` table has its
|
|
68
|
+
`command`/`args`/`env` replaced in place. Sub-tables of that entry are kept:
|
|
69
|
+
hand-written Codex configs put per-tool approval modes in
|
|
70
|
+
`[mcp_servers.davinci-resolve.tools.<tool>]`, and an installer that dropped them
|
|
71
|
+
would quietly widen what the agent may do without asking. An
|
|
72
|
+
`[mcp_servers.davinci-resolve.env]` sub-table is regenerated in that same shape
|
|
73
|
+
rather than replaced with an inline `env` — TOML rejects a file that spells one
|
|
74
|
+
key both ways.
|
|
75
|
+
|
|
76
|
+
Paths are escaped as TOML basic strings (Windows backslashes would otherwise
|
|
77
|
+
corrupt the file). The installer refuses to touch a config that is already
|
|
78
|
+
invalid TOML, one that defines the server as an inline key it cannot safely
|
|
79
|
+
rewrite, or a merge result that would not parse — the same
|
|
80
|
+
never-wipe-a-user-config policy the JSON clients follow. Writes are backed up to
|
|
81
|
+
`config.toml.backup` first.
|
|
82
|
+
|
|
83
|
+
## What's New in v2.95.3
|
|
84
|
+
|
|
85
|
+
Closes the retime entry's explicit `UNTESTED` warning: **reverse and
|
|
86
|
+
variable-speed ramps both work**, and fixes two decoder bugs found proving it.
|
|
87
|
+
|
|
88
|
+
### Measured — reverse lands through both import routes
|
|
89
|
+
|
|
90
|
+
On Studio 21.0.4.5. OTIO with a negative `time_scalar` (-1) placed a clip reading
|
|
91
|
+
`GetSourceStartFrame` 95 → `GetSourceEndFrame` 46; EDL with a negative `M2` rate
|
|
92
|
+
(-24.0 at 24fps) read 48 → 0. **The API does expose direction** — a reversed clip
|
|
93
|
+
reports source start GREATER than source end, i.e. a negative span.
|
|
94
|
+
|
|
95
|
+
### Measured — variable-speed ramps survive Resolve intact
|
|
96
|
+
|
|
97
|
+
They cannot be expressed in OTIO (`LinearTimeWarp` is a single `time_scalar`,
|
|
98
|
+
constant by construction), but they can be authored offline. A `Sm2TimeMap` built
|
|
99
|
+
with `media-timemap.buildTimemap` carrying two segments — 0–2s record at 0.5×,
|
|
100
|
+
2–4s at 2.0× — was patched into a clip's `MediaTimemapBA`, imported, and
|
|
101
|
+
**re-exported by Resolve with the segments unchanged**. Confirmed independently
|
|
102
|
+
from the API side: the clip read source `0..120` over a 96-frame record, and
|
|
103
|
+
2s@0.5× (24 source frames) + 2s@2.0× (96) is exactly 120.
|
|
104
|
+
|
|
105
|
+
### Fixed — the timemap decoder reported a reverse as speed 0
|
|
106
|
+
|
|
107
|
+
Two real bugs in `media-timemap.js`, both exposed by the first reversed map it
|
|
108
|
+
ever saw:
|
|
109
|
+
|
|
110
|
+
- **Fixed-offset keyframe reads.** Each point omits whichever of
|
|
111
|
+
`recordSec`/`sourceSec` is zero (protobuf default-omission), so
|
|
112
|
+
`readDoubleLE(1)`/`readDoubleLE(10)` threw *"offset out of range"* on every
|
|
113
|
+
reversed map. Forward maps only decoded because both values happened to be
|
|
114
|
+
non-zero.
|
|
115
|
+
- **A hardcoded (0,0) origin.** A reversed clip starts at the far end of the
|
|
116
|
+
source, and Resolve encodes that starting offset as a **top-level protobuf
|
|
117
|
+
field 2** double. Assuming (0,0) made a reverse decode as **speed 0** — a
|
|
118
|
+
plausible wrong number, which is worse than a crash. It now decodes as −1.
|
|
119
|
+
|
|
120
|
+
### Trap worth naming
|
|
121
|
+
|
|
122
|
+
`buildTimemap` returns a **Buffer**. Writing it into the XML without
|
|
123
|
+
`.toString('hex')` embeds mojibake, and the failure is silent: the clip imports
|
|
124
|
+
cleanly and reads `0..0` — indistinguishable from the degenerate-map signature
|
|
125
|
+
the ledger describes for xmeml imports, so it looks like Resolve rejected the
|
|
126
|
+
retime when it is a caller bug. It cost real time here before being caught.
|
|
127
|
+
|
|
5
128
|
## What's New in v2.95.2
|
|
6
129
|
|
|
7
130
|
Adds the live round-trip harness the offline `.drp` tier never had — the absence
|
package/README.md
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
English | [简体中文](README.zh-CN.md)
|
|
4
4
|
|
|
5
|
-
[](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
|
|
6
6
|
[](https://www.npmjs.com/package/davinci-resolve-mcp)
|
|
7
7
|
[](docs/reference/api-coverage.md)
|
|
8
|
-
[-blue.svg)](#server-modes)
|
|
9
9
|
[-18%20tools-blueviolet.svg)](#server-modes)
|
|
10
10
|
[](docs/reference/api-coverage.md#test-results)
|
|
11
11
|
[](https://www.blackmagicdesign.com/products/davinciresolve)
|
|
@@ -24,7 +24,7 @@ A local browser control panel ships with the server for inspecting Resolve state
|
|
|
24
24
|
npx davinci-resolve-mcp setup
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
Before connecting, open DaVinci Resolve Studio and set **Preferences > General > External scripting using** to **Local**. (On the **free edition** that preference does not help — see [Free edition](#free-edition-in-app-bridge) below.) The npm launcher installs a managed copy under your user application-data directory, then runs the universal Python installer. The installer creates a virtual environment, detects Resolve paths, and can configure Claude Desktop, Claude Code, Cursor, VS Code, Windsurf, Zed, Continue, Cline, Roo Code, OpenCode, and JetBrains IDEs.
|
|
27
|
+
Before connecting, open DaVinci Resolve Studio and set **Preferences > General > External scripting using** to **Local**. (On the **free edition** that preference does not help — see [Free edition](#free-edition-in-app-bridge) below.) The npm launcher installs a managed copy under your user application-data directory, then runs the universal Python installer. The installer creates a virtual environment, detects Resolve paths, and can configure Claude Desktop, Claude Code, Cursor, VS Code, Windsurf, Zed, Continue, Cline, Roo Code, OpenCode, Codex CLI, and JetBrains IDEs.
|
|
28
28
|
|
|
29
29
|
For source installs:
|
|
30
30
|
|
|
@@ -109,7 +109,7 @@ The command starts a localhost server and opens the control panel in your browse
|
|
|
109
109
|
|
|
110
110
|
| Mode | Entry point | Tools | Best for |
|
|
111
111
|
|------|-------------|-------|----------|
|
|
112
|
-
| Compound | `src/server.py` |
|
|
112
|
+
| Compound | `src/server.py` | 35 | Default mode for most assistants. Related Resolve operations are grouped behind action parameters to keep context usage low. |
|
|
113
113
|
| Full / granular | `src/server.py --full` or `src/resolve_mcp_server.py` | 353 | Power users who want one MCP tool per Resolve API method. |
|
|
114
114
|
|
|
115
115
|
The compound server is recommended unless you specifically need the granular one-tool-per-method surface.
|
|
@@ -272,7 +272,7 @@ The default server is a local stdio process launched by your MCP client; it does
|
|
|
272
272
|
|
|
273
273
|
| Metric | Value |
|
|
274
274
|
|--------|-------|
|
|
275
|
-
| MCP Tools | **
|
|
275
|
+
| MCP Tools | **35** compound / **353** granular (live server) |
|
|
276
276
|
| Advanced (offline) tools | **18** — .drp/.drt/.drx + DB authoring, no Resolve running |
|
|
277
277
|
| Kernel Actions | **136** guarded workflow actions across 9 compound tools |
|
|
278
278
|
| API Methods Covered | **361/361** (100%) |
|
package/README.zh-CN.md
CHANGED
|
@@ -2,17 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
[English](README.md) | 简体中文
|
|
4
4
|
|
|
5
|
-
[](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
|
|
6
6
|
[](https://www.npmjs.com/package/davinci-resolve-mcp)
|
|
7
7
|
[](docs/reference/api-coverage.md)
|
|
8
|
-
[-blue.svg)](#服务器模式)
|
|
9
9
|
[-18%20tools-blueviolet.svg)](#服务器模式)
|
|
10
10
|
[](docs/reference/api-coverage.md#test-results)
|
|
11
11
|
[](https://www.blackmagicdesign.com/products/davinciresolve)
|
|
12
12
|
[](https://www.python.org/downloads/)
|
|
13
13
|
[](https://opensource.org/licenses/MIT)
|
|
14
14
|
|
|
15
|
-
> 本翻译对应 v2.
|
|
15
|
+
> 本翻译对应 v2.96.0 版 README。如与英文原版有出入,以 [英文原版](README.md) 为准。
|
|
16
16
|
|
|
17
17
|
一个 Model Context Protocol (MCP) 服务器,让 AI 助手通过官方脚本 API 控制 DaVinci Resolve Studio(达芬奇)。它提供完整的 API 覆盖,外加带护栏的工作流助手,涵盖剪辑、媒体池整理、渲染设置、审阅标记、调色、Fusion、Fairlight、项目生命周期任务、扩展开发,以及不碰源媒体的媒体分析。
|
|
18
18
|
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
npx davinci-resolve-mcp setup
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
连接之前,先打开 DaVinci Resolve Studio,把 **Preferences > General > External scripting using** 设为 **Local**。(**免费版**上这个偏好设置不起作用——见下文 [免费版](#免费版应用内桥接)。)npm 启动器会在你的用户应用数据目录下安装一份托管副本,然后运行通用 Python 安装器。安装器会创建虚拟环境、检测 Resolve 路径,并可自动配置 Claude Desktop、Claude Code、Cursor、VS Code、Windsurf、Zed、Continue、Cline、Roo Code、OpenCode 和 JetBrains 系列 IDE。
|
|
29
|
+
连接之前,先打开 DaVinci Resolve Studio,把 **Preferences > General > External scripting using** 设为 **Local**。(**免费版**上这个偏好设置不起作用——见下文 [免费版](#免费版应用内桥接)。)npm 启动器会在你的用户应用数据目录下安装一份托管副本,然后运行通用 Python 安装器。安装器会创建虚拟环境、检测 Resolve 路径,并可自动配置 Claude Desktop、Claude Code、Cursor、VS Code、Windsurf、Zed、Continue、Cline、Roo Code、OpenCode、Codex CLI 和 JetBrains 系列 IDE。
|
|
30
30
|
|
|
31
31
|
从源码安装:
|
|
32
32
|
|
|
@@ -81,7 +81,7 @@ venv/bin/python -m src.control_panel
|
|
|
81
81
|
|
|
82
82
|
| 模式 | 入口 | 工具数 | 适合谁 |
|
|
83
83
|
|------|------|--------|--------|
|
|
84
|
-
| Compound(复合) | `src/server.py` |
|
|
84
|
+
| Compound(复合) | `src/server.py` | 35 | 大多数助手的默认模式。相关的 Resolve 操作按 action 参数分组,压低上下文占用。 |
|
|
85
85
|
| Full / granular(细粒度) | `src/server.py --full` 或 `src/resolve_mcp_server.py` | 353 | 想要"一个 Resolve API 方法 = 一个 MCP 工具"的重度用户。 |
|
|
86
86
|
|
|
87
87
|
除非你明确需要一方法一工具的细粒度界面,否则推荐复合模式。
|
|
@@ -196,7 +196,7 @@ DRX 调色写入**针对 Resolve Studio 做过实机校准**:调色参数默
|
|
|
196
196
|
|
|
197
197
|
| 指标 | 数值 |
|
|
198
198
|
|------|------|
|
|
199
|
-
| MCP 工具 | **
|
|
199
|
+
| MCP 工具 | **35** 复合 / **353** 细粒度(实时服务器) |
|
|
200
200
|
| Advanced(离线)工具 | **18**——.drp/.drt/.drx + 数据库创作,无需 Resolve 运行 |
|
|
201
201
|
| 内核 action | 9 个复合工具下 **136** 个带护栏的工作流 action |
|
|
202
202
|
| API 方法覆盖 | **361/361**(100%) |
|
package/docs/SKILL.md
CHANGED
|
@@ -162,7 +162,7 @@ before mutating Resolve state.
|
|
|
162
162
|
|
|
163
163
|
| Mode | Entry point | Tool count | Use when |
|
|
164
164
|
|---|---|---|---|
|
|
165
|
-
| Compound (default) | `src/server.py` |
|
|
165
|
+
| Compound (default) | `src/server.py` | 35 tools | Most workflows — keeps context lean |
|
|
166
166
|
| Granular (full) | `src/server.py --full` | 353 tools | Power users needing one tool per API method |
|
|
167
167
|
|
|
168
168
|
This skill document covers the **compound server** (the default). Each compound
|
|
@@ -393,7 +393,7 @@ the work done:
|
|
|
393
393
|
|
|
394
394
|
- `timeline(action="detect_gaps_overlaps")`
|
|
395
395
|
- `timeline(action="source_range_report")`
|
|
396
|
-
- `
|
|
396
|
+
- `timeline_frame(action="capture")` at important markers and cuts
|
|
397
397
|
- Compare each marker name against the Resolve-rendered frame; revise the marker
|
|
398
398
|
or edit if the image contradicts the plan.
|
|
399
399
|
|
|
@@ -1492,7 +1492,39 @@ applied — which is different from reading the source file directly.
|
|
|
1492
1492
|
|
|
1493
1493
|
Use `get_thumbnail_image` when the MCP client can display image content directly.
|
|
1494
1494
|
It converts the same Resolve thumbnail payload to PNG bytes without writing a
|
|
1495
|
-
file to disk.
|
|
1495
|
+
file to disk. Both actions hold the Color page for the read and restore the
|
|
1496
|
+
previous page. Prefer `timeline_frame(action="capture")` for new work — it is the
|
|
1497
|
+
same capture plus a timecode target, `max_width`, and a full-resolution mode.
|
|
1498
|
+
|
|
1499
|
+
**`timeline_frame`** — Capture a timeline frame as viewable image content.
|
|
1500
|
+
|
|
1501
|
+
Key actions: `capture(timecode?|frame?, quality?, max_width?, format?, timeline_name?)`,
|
|
1502
|
+
`capabilities`
|
|
1503
|
+
|
|
1504
|
+
Returns MCP image content, so a multimodal assistant can look at what Resolve is
|
|
1505
|
+
rendering — grade, Fusion, titles, transitions — rather than inferring it from
|
|
1506
|
+
metadata. (For the raw camera file instead, use
|
|
1507
|
+
`media_analysis(action="extract_frames")`.)
|
|
1508
|
+
|
|
1509
|
+
- `quality="preview"` (default) is Resolve's thumbnail: fast, small, writes
|
|
1510
|
+
nothing.
|
|
1511
|
+
- `quality="full"` grabs a Gallery still for a full-resolution frame, then
|
|
1512
|
+
removes it again. `format` is `png` (default), `jpg`, or `tif`.
|
|
1513
|
+
- `max_width` caps the width to conserve context. On `full` this needs ffmpeg;
|
|
1514
|
+
without it the call fails rather than quietly returning a full-size frame.
|
|
1515
|
+
- `timecode` accepts absolute (`01:00:15:12`) or elapsed (`00:00:15:12`) time;
|
|
1516
|
+
`frame` is the absolute timeline frame. Omit both to capture the playhead.
|
|
1517
|
+
|
|
1518
|
+
A capture is a read: the Color page, the playhead, the current timeline, and the
|
|
1519
|
+
Gallery are all restored afterwards.
|
|
1520
|
+
|
|
1521
|
+
```
|
|
1522
|
+
timeline_frame(action="capture", params={"timecode": "01:00:15:12", "max_width": 1280})
|
|
1523
|
+
```
|
|
1524
|
+
|
|
1525
|
+
This tool is separate from `timeline` because a tool that returns image content
|
|
1526
|
+
cannot declare a `Dict[str, Any]` output schema — FastMCP validates returns
|
|
1527
|
+
against it, and image content fails that validation.
|
|
1496
1528
|
|
|
1497
1529
|
**`timeline_ai`** — AI/ML analysis on the current timeline.
|
|
1498
1530
|
|
|
@@ -2046,15 +2078,33 @@ clip's comp, always pass `clip_id`, `timeline_item_id`, or `timeline_item`.
|
|
|
2046
2078
|
|
|
2047
2079
|
## Seeing What Resolve Sees (Visual Context)
|
|
2048
2080
|
|
|
2049
|
-
The server provides
|
|
2050
|
-
including color grading, effects, and compositing — not just the raw source
|
|
2081
|
+
The server provides several mechanisms to inspect a frame as Resolve has processed
|
|
2082
|
+
it, including color grading, effects, and compositing — not just the raw source
|
|
2083
|
+
file.
|
|
2084
|
+
|
|
2085
|
+
**Start here: `timeline_frame(action="capture")`** — Returns the frame at the
|
|
2086
|
+
playhead (or at any `timecode`/`frame` you name) as MCP image content, so a
|
|
2087
|
+
multimodal assistant can simply look at it. `quality="preview"` is fast and
|
|
2088
|
+
writes nothing; `quality="full"` is full resolution; `max_width` bounds the
|
|
2089
|
+
context cost. The Color page, playhead, current timeline, and Gallery are all
|
|
2090
|
+
restored afterwards.
|
|
2091
|
+
|
|
2092
|
+
```
|
|
2093
|
+
timeline_frame(action="capture", params={"timecode": "01:00:15:12", "max_width": 1280})
|
|
2094
|
+
```
|
|
2051
2095
|
|
|
2052
2096
|
**`timeline_markers(action="get_thumbnail")`** — Returns raw thumbnail data at
|
|
2053
2097
|
the current playhead position. The response is a dictionary with keys `data`,
|
|
2054
|
-
`format`, `width`, `height`, `noOfComponents`, and `depth`.
|
|
2098
|
+
`format`, `width`, `height`, `noOfComponents`, and `depth`. Use it when you need
|
|
2099
|
+
pixel data for tooling rather than an image to look at.
|
|
2100
|
+
|
|
2101
|
+
**`timeline_markers(action="get_thumbnail_image")`** — The original image-content
|
|
2102
|
+
action, equivalent to `timeline_frame(action="capture")` at default quality with
|
|
2103
|
+
no positional arguments. Kept for existing callers.
|
|
2055
2104
|
|
|
2056
|
-
**`
|
|
2057
|
-
|
|
2105
|
+
**`timeline(action="thumbnail_contact_sheet")`** — Many frames at once as a single
|
|
2106
|
+
labeled PNG written to the analysis root. Use it to review a stretch of cut rather
|
|
2107
|
+
than one moment.
|
|
2058
2108
|
|
|
2059
2109
|
**`gallery_stills(action="grab_and_export", params={...})`** — Grabs a still from
|
|
2060
2110
|
the current frame on the Color page and returns the image encoded as base64 in the
|
package/docs/contributing.md
CHANGED
|
@@ -63,7 +63,7 @@ This MCP server controls DaVinci Resolve via its Scripting API. Some tools perfo
|
|
|
63
63
|
davinci-resolve-mcp/
|
|
64
64
|
├── install.py # Universal installer (macOS/Windows/Linux)
|
|
65
65
|
├── src/
|
|
66
|
-
│ ├── server.py # Compound MCP server —
|
|
66
|
+
│ ├── server.py # Compound MCP server — 35 tools (default)
|
|
67
67
|
│ ├── resolve_mcp_server.py # Thin full-server entrypoint — 353 tools
|
|
68
68
|
│ ├── granular/ # Modular full-server implementation
|
|
69
69
|
│ └── utils/ # Platform detection, Resolve connection helpers
|
package/docs/install.md
CHANGED
|
@@ -88,6 +88,7 @@ The installer can automatically configure any of these clients:
|
|
|
88
88
|
| Zed | `~/.config/zed/settings.json` |
|
|
89
89
|
| Continue | `~/.continue/config.json` |
|
|
90
90
|
| OpenCode | `~/.config/opencode/opencode.json` (or project-root `opencode.json`) |
|
|
91
|
+
| Codex CLI | `~/.codex/config.toml` (or `$CODEX_HOME/config.toml`) — TOML `[mcp_servers.davinci-resolve]` |
|
|
91
92
|
| JetBrains IDEs | Manual (Settings > Tools > AI Assistant > MCP) |
|
|
92
93
|
|
|
93
94
|
You can configure multiple clients at once, or use `--clients manual` to get copy-paste config snippets.
|
|
@@ -132,7 +133,7 @@ The MCP server comes in two modes:
|
|
|
132
133
|
|
|
133
134
|
| Mode | File | Tools | Best For |
|
|
134
135
|
|------|------|-------|----------|
|
|
135
|
-
| **Compound** (default) | `src/server.py` |
|
|
136
|
+
| **Compound** (default) | `src/server.py` | 35 | Most users — fast, clean, low context usage |
|
|
136
137
|
| **Full** | `src/resolve_mcp_server.py` | 353 | Power users who want one tool per API method |
|
|
137
138
|
|
|
138
139
|
The compound server's `timeline_item` tool includes dedicated actions for common workflows:
|
|
@@ -196,6 +197,21 @@ If you prefer to set things up yourself, add to your MCP client config:
|
|
|
196
197
|
}
|
|
197
198
|
```
|
|
198
199
|
|
|
200
|
+
Codex CLI uses TOML instead. Add to `~/.codex/config.toml` (or
|
|
201
|
+
`$CODEX_HOME/config.toml`):
|
|
202
|
+
|
|
203
|
+
```toml
|
|
204
|
+
[mcp_servers.davinci-resolve]
|
|
205
|
+
command = "/path/to/venv/bin/python"
|
|
206
|
+
args = ["/path/to/davinci-resolve-mcp/src/server.py"]
|
|
207
|
+
env = { RESOLVE_SCRIPT_API = "/path/to/DaVinci Resolve/Developer/Scripting", RESOLVE_SCRIPT_LIB = "/path/to/fusionscript.so-or-dll", PYTHONPATH = "/path/to/DaVinci Resolve/Developer/Scripting/Modules" }
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Paths must be absolute — Codex does not expand `~`. Run
|
|
211
|
+
`python install.py --clients codex` to have the installer write this block for
|
|
212
|
+
you (it merges into an existing config and leaves your other servers and
|
|
213
|
+
comments alone).
|
|
214
|
+
|
|
199
215
|
On Windows, installer-generated configs also include `PYTHONHOME`. That scopes Resolve's Python binding to the selected interpreter and avoids the Resolve 20.3 multi-Python crash reported in [Issue #26](https://github.com/samuelgursky/davinci-resolve-mcp/issues/26).
|
|
200
216
|
|
|
201
217
|
For Resolve's **Network** scripting mode, add
|
|
@@ -6,7 +6,7 @@ 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 | **35** compound (default) / **353** granular |
|
|
10
10
|
| Kernel Actions | **136** guarded MCP workflow actions across 9 compound tools |
|
|
11
11
|
| API Methods Covered | **361/361** (100%) |
|
|
12
12
|
| Methods Live Tested | **338/361** (93.6%) |
|
|
@@ -17,7 +17,7 @@ Complete Resolve scripting API coverage, live-test status, and method-by-method
|
|
|
17
17
|
|
|
18
18
|
## API Coverage
|
|
19
19
|
|
|
20
|
-
Every non-deprecated method in the DaVinci Resolve Scripting API is covered. The default compound server exposes **
|
|
20
|
+
Every non-deprecated method in the DaVinci Resolve Scripting API is covered. The default compound server exposes **35 tools** that group related operations by action parameter, keeping LLM context windows lean. The full granular server provides **353 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
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
|
|
@@ -117,7 +117,7 @@ equivalent, blocking full automation.
|
|
|
117
117
|
### Clip speed / retime ratio and speed ramps
|
|
118
118
|
|
|
119
119
|
- **Object:** `TimelineItem`
|
|
120
|
-
- **Behavior:** SetProperty exposes only retime *quality* (RetimeProcess, MotionEstimation) and transform/crop/composite/opacity keys — not the speed value itself. There is no way to set a clip to a given % speed, reverse it, or author a speed ramp. Verified against the documented SetProperty key list AND by live mutating attempt on 21.0.0: SetProperty('Speed'|'PlaybackSpeed'|'RetimeSpeed'|'ClipSpeed', 50) all return False, while SetProperty('RetimeProcess', 1) returns True. THE READ SIDE IS AS DEAD AS THE WRITE SIDE, which is easy to miss: re-measured on Studio 19.1.3.7 against a placed item, GetProperty('Speed'), GetProperty('PlaybackSpeed'), GetProperty('RetimeSpeed') and GetProperty('ClipSpeed') ALL return None, and the keyless GetProperty() dict (26 keys on that item) carries no speed value at all — its only retime key is RetimeProcess, which is quality, not ratio. SetProperty('Speed', 1.75) returned False on 19.1.3.7 too, so the write refusal is not specific to 21.0.0. Note the 21.0.0 stamp above covers the SetProperty measurements only. THE SCRIPTING-API xmeml IMPORT BUILDS NO RETIME — and the way it fails is worse than a no-op. First, what Premiere actually writes, because having this backwards is what produced the wrong contract this entry published in 2.79.0–2.79.1 (see CORRECTION below). In an FCP7 XML a retimed clipitem's <in>/<out> live in the POST-RETIME (warped) domain and always span the RECORD duration; pproTicksIn/pproTicksOut carry the TRUE SOURCE position; and <duration> is the file length expressed in the warped domain. A real 200% clip at 24 fps: <in>1957</in> <out>1971</out> — span 14, EQUAL to its record span; pproTicksIn 41425776000000 and pproTicksOut 41722128000000, which at 254016000000/24 = 10584000000 ticks per frame are source frames 3914 and 3942, exactly 1957x2 and 1971x2, a 28-frame source span over a 14-frame record span; <duration>24292</duration> for a 48584-frame file; and a graphdict mapping warped to true source with the ratio as its slope (when 17910 -> value 35820). So for a retimed clip <in> and pproTicksIn are SUPPOSED to disagree, by exactly the ratio. The same relationship seen from the other side is already encoded in this repo: resolve-advanced/server/prproj.mjs derives Premiere speed from tick geometry as |srcSpan / recSpan| * 100, reversing when in > out. Against that convention, measured on 19.1.3/19.1.3.7: (a) the importer IGNORES the scalar Time Remap speed filter and the clips arrive at 100%; (b) `graphdict` is ignored too — re-tested in Premiere's exact convention with one 100% control clip and one 200% clip per timeline, a document carrying warped <in>/<out>, true-source pproTicks, <duration> = fileLen/ratio and a constant-slope graphdict imports cleanly, the control lands correct, and NO retime is built: recalibrated 2026-08-05, every xmeml-imported clip carries a DEGENERATE time map in Project.db (Sm2TimeMap with an empty source axis — five Time Remap shapes re-measured, 15/15 clips degenerate), so no speed exists in the project data, and the API source witness reads 0/0 on those clips (see WITNESS CALIBRATION below); emitting the identical document WITHOUT the graphdict gives the identical result; (c) `reverse` does not survive either; (d) THE HAZARD, and it is the part that bites: Resolve reads <in> LITERALLY as the true source frame, honouring neither the ticks nor the graphdict. Import a genuine Premiere XML that contains retimes and every retimed clip is placed at in / ratio — the 200% clip above lands on source frame 1957 instead of 3914. There is no error, the cut lengths are still correct, every clip is linked and online, and the timeline renders — so it reads as a good conform while sitting at the wrong moment of the right file. This is the same failure class as the Avid AAF camera-file link (docs/guides/conforming-an-avid-aaf.md): wrong in a way only a frame comparison against a reference can see. SCOPE: all of the above is the SCRIPTING-API import (ImportTimelineFromFile). Resolve's UI importer (File > Import > Timeline) has NOT been tested, and that is how editors usually conform a Premiere XML — do not read this as covering it. CORRECTION: this entry as published in 2.79.0–2.79.1 also claimed that any <in>/<pproTicksIn> inconsistency is silently REJECTED in both orientations. That claim was FALSE and has been removed — it came from an emitter writing ticks = in x ticks-per-frame at every speed, so what it observed was its own malformed files being refused. The graphdict evidence published with it (dead in FOUR shapes, 0 of 2 landed, a 200% clip emitted in 200 / out 296 'clamped' to out 248) described that same malformed input being normalized and is replaced by the re-test above. The conclusion is unchanged; only its evidence is. Placement is NOT the problem: the same route imported 573 clips with 572 of 573 matching by track and record position with source frames exact, and the importer BUILT a 59-frame dissolve. The retime gap is specific, not general. TRAP: Resolve's own FCP7 export cannot witness a speed. It writes a DEGENERATE Time Remap on every clip — `speed` value 0 (not 100) and a graphdict whose keyframe `value`s are all 0 while its `when`s carry the clip's source in/out — so anyone verifying a retime by round-tripping through EXPORT_FCP_7_XML is reading furniture, and the identity Time Remap blocks present on every clip are what make the route look like it should work. WITNESS CALIBRATION (2026-08-05, Studio 19.1.3.7) — the positive control this entry previously lacked now exists, and it RETRACTS the witness the 2.80.0 revision of this entry recommended. The rig removed every confound: the SAME clip placed twice, adjacent, in ONE timeline, the second copy hand-set to 200% in the UI (the only way to make one — see above). GetSourceStartFrame/GetSourceEndFrame separated the copies — 1822..1870 (span 48) at 100% vs 1822..1918 (span 96) at 200% — while GetLeftOffset/GetRightOffset did NOT: 1822..1870 at 100% vs 911..959 at 200%, which is exactly position / 2. GetLeftOffset reports the WARPED (record-side) domain — position / speed, the `In` column of Project.db's Sm2TiItem — so it is exact for PLACEMENT and blind for SPEED BY CONSTRUCTION: its span equals the record span at every speed. The speed itself lives in the item's Sm2TimeMap blob (keyframe slope = ratio; the hand-set 200% reads slope exactly 2.0), which is what GetSourceStart/EndFrame and EXPORT_EDL read. THE SPEED WITNESS is therefore the GetSourceStart/EndFrame span vs the record duration. CAVEAT: on xmeml-IMPORTED timelines those return 0/0 — the importer leaves the time map's source axis empty — and a 0/0 read is UNKNOWN, never 'no retime'. Cross-checks that work everywhere: the Sm2TimeMap slope read from a saved Project.db, and the EXPORT_EDL M2 rate (rate = fps x speed/100, so 048.0 = 200% at 24 fps; `M2 ... 000.0` on every clip is the degenerate-map furniture of an xmeml import — ignore it). TWO IMPORT ROUTES DO BUILD CONSTANT RETIMES (measured 2026-08-05, media linked, judged via the calibrated witnesses above): (1) OTIO LinearTimeWarp through ImportTimelineFromFile — 200% (src 200..296 over a 48-frame record) and 50% (src 300..324 over a 48-frame record) both landed with correct source in-points; the saved Project.db shows slope 2.0 and 0.5. Emission rules: the document must be Resolve-shaped with TIMECODE-ABSOLUTE source frames (see the ImportTimelineFromFile .otio entry), the effect is `LinearTimeWarp.1` with `time_scalar`, and `source_range.duration` is the RECORD span — OTIO semantics, the time_scalar handles source consumption; sending the source span as the duration builds a spec-correctly longer clip, not a retime. (2) EDL M2 — 200% landed (src 100..196 over a 48-frame record), linked. Author the shape Resolve's own EXPORT_EDL writes: the event line's source span EQUALS the record span even under M2; the `M2 <reel> <rate> <srcInTC>` line carries the play rate in fps (048.0 = 200% at 24); `* FROM CLIP NAME:` comments drive pool linking. UNTESTED
|
|
120
|
+
- **Behavior:** SetProperty exposes only retime *quality* (RetimeProcess, MotionEstimation) and transform/crop/composite/opacity keys — not the speed value itself. There is no way to set a clip to a given % speed, reverse it, or author a speed ramp. Verified against the documented SetProperty key list AND by live mutating attempt on 21.0.0: SetProperty('Speed'|'PlaybackSpeed'|'RetimeSpeed'|'ClipSpeed', 50) all return False, while SetProperty('RetimeProcess', 1) returns True. THE READ SIDE IS AS DEAD AS THE WRITE SIDE, which is easy to miss: re-measured on Studio 19.1.3.7 against a placed item, GetProperty('Speed'), GetProperty('PlaybackSpeed'), GetProperty('RetimeSpeed') and GetProperty('ClipSpeed') ALL return None, and the keyless GetProperty() dict (26 keys on that item) carries no speed value at all — its only retime key is RetimeProcess, which is quality, not ratio. SetProperty('Speed', 1.75) returned False on 19.1.3.7 too, so the write refusal is not specific to 21.0.0. Note the 21.0.0 stamp above covers the SetProperty measurements only. THE SCRIPTING-API xmeml IMPORT BUILDS NO RETIME — and the way it fails is worse than a no-op. First, what Premiere actually writes, because having this backwards is what produced the wrong contract this entry published in 2.79.0–2.79.1 (see CORRECTION below). In an FCP7 XML a retimed clipitem's <in>/<out> live in the POST-RETIME (warped) domain and always span the RECORD duration; pproTicksIn/pproTicksOut carry the TRUE SOURCE position; and <duration> is the file length expressed in the warped domain. A real 200% clip at 24 fps: <in>1957</in> <out>1971</out> — span 14, EQUAL to its record span; pproTicksIn 41425776000000 and pproTicksOut 41722128000000, which at 254016000000/24 = 10584000000 ticks per frame are source frames 3914 and 3942, exactly 1957x2 and 1971x2, a 28-frame source span over a 14-frame record span; <duration>24292</duration> for a 48584-frame file; and a graphdict mapping warped to true source with the ratio as its slope (when 17910 -> value 35820). So for a retimed clip <in> and pproTicksIn are SUPPOSED to disagree, by exactly the ratio. The same relationship seen from the other side is already encoded in this repo: resolve-advanced/server/prproj.mjs derives Premiere speed from tick geometry as |srcSpan / recSpan| * 100, reversing when in > out. Against that convention, measured on 19.1.3/19.1.3.7: (a) the importer IGNORES the scalar Time Remap speed filter and the clips arrive at 100%; (b) `graphdict` is ignored too — re-tested in Premiere's exact convention with one 100% control clip and one 200% clip per timeline, a document carrying warped <in>/<out>, true-source pproTicks, <duration> = fileLen/ratio and a constant-slope graphdict imports cleanly, the control lands correct, and NO retime is built: recalibrated 2026-08-05, every xmeml-imported clip carries a DEGENERATE time map in Project.db (Sm2TimeMap with an empty source axis — five Time Remap shapes re-measured, 15/15 clips degenerate), so no speed exists in the project data, and the API source witness reads 0/0 on those clips (see WITNESS CALIBRATION below); emitting the identical document WITHOUT the graphdict gives the identical result; (c) `reverse` does not survive either; (d) THE HAZARD, and it is the part that bites: Resolve reads <in> LITERALLY as the true source frame, honouring neither the ticks nor the graphdict. Import a genuine Premiere XML that contains retimes and every retimed clip is placed at in / ratio — the 200% clip above lands on source frame 1957 instead of 3914. There is no error, the cut lengths are still correct, every clip is linked and online, and the timeline renders — so it reads as a good conform while sitting at the wrong moment of the right file. This is the same failure class as the Avid AAF camera-file link (docs/guides/conforming-an-avid-aaf.md): wrong in a way only a frame comparison against a reference can see. SCOPE: all of the above is the SCRIPTING-API import (ImportTimelineFromFile). Resolve's UI importer (File > Import > Timeline) has NOT been tested, and that is how editors usually conform a Premiere XML — do not read this as covering it. CORRECTION: this entry as published in 2.79.0–2.79.1 also claimed that any <in>/<pproTicksIn> inconsistency is silently REJECTED in both orientations. That claim was FALSE and has been removed — it came from an emitter writing ticks = in x ticks-per-frame at every speed, so what it observed was its own malformed files being refused. The graphdict evidence published with it (dead in FOUR shapes, 0 of 2 landed, a 200% clip emitted in 200 / out 296 'clamped' to out 248) described that same malformed input being normalized and is replaced by the re-test above. The conclusion is unchanged; only its evidence is. Placement is NOT the problem: the same route imported 573 clips with 572 of 573 matching by track and record position with source frames exact, and the importer BUILT a 59-frame dissolve. The retime gap is specific, not general. TRAP: Resolve's own FCP7 export cannot witness a speed. It writes a DEGENERATE Time Remap on every clip — `speed` value 0 (not 100) and a graphdict whose keyframe `value`s are all 0 while its `when`s carry the clip's source in/out — so anyone verifying a retime by round-tripping through EXPORT_FCP_7_XML is reading furniture, and the identity Time Remap blocks present on every clip are what make the route look like it should work. WITNESS CALIBRATION (2026-08-05, Studio 19.1.3.7) — the positive control this entry previously lacked now exists, and it RETRACTS the witness the 2.80.0 revision of this entry recommended. The rig removed every confound: the SAME clip placed twice, adjacent, in ONE timeline, the second copy hand-set to 200% in the UI (the only way to make one — see above). GetSourceStartFrame/GetSourceEndFrame separated the copies — 1822..1870 (span 48) at 100% vs 1822..1918 (span 96) at 200% — while GetLeftOffset/GetRightOffset did NOT: 1822..1870 at 100% vs 911..959 at 200%, which is exactly position / 2. GetLeftOffset reports the WARPED (record-side) domain — position / speed, the `In` column of Project.db's Sm2TiItem — so it is exact for PLACEMENT and blind for SPEED BY CONSTRUCTION: its span equals the record span at every speed. The speed itself lives in the item's Sm2TimeMap blob (keyframe slope = ratio; the hand-set 200% reads slope exactly 2.0), which is what GetSourceStart/EndFrame and EXPORT_EDL read. THE SPEED WITNESS is therefore the GetSourceStart/EndFrame span vs the record duration. CAVEAT: on xmeml-IMPORTED timelines those return 0/0 — the importer leaves the time map's source axis empty — and a 0/0 read is UNKNOWN, never 'no retime'. Cross-checks that work everywhere: the Sm2TimeMap slope read from a saved Project.db, and the EXPORT_EDL M2 rate (rate = fps x speed/100, so 048.0 = 200% at 24 fps; `M2 ... 000.0` on every clip is the degenerate-map furniture of an xmeml import — ignore it). TWO IMPORT ROUTES DO BUILD CONSTANT RETIMES (measured 2026-08-05, media linked, judged via the calibrated witnesses above): (1) OTIO LinearTimeWarp through ImportTimelineFromFile — 200% (src 200..296 over a 48-frame record) and 50% (src 300..324 over a 48-frame record) both landed with correct source in-points; the saved Project.db shows slope 2.0 and 0.5. Emission rules: the document must be Resolve-shaped with TIMECODE-ABSOLUTE source frames (see the ImportTimelineFromFile .otio entry), the effect is `LinearTimeWarp.1` with `time_scalar`, and `source_range.duration` is the RECORD span — OTIO semantics, the time_scalar handles source consumption; sending the source span as the duration builds a spec-correctly longer clip, not a retime. (2) EDL M2 — 200% landed (src 100..196 over a 48-frame record), linked. Author the shape Resolve's own EXPORT_EDL writes: the event line's source span EQUALS the record span even under M2; the `M2 <reel> <rate> <srcInTC>` line carries the play rate in fps (048.0 = 200% at 24); `* FROM CLIP NAME:` comments drive pool linking. REVERSE AND VARYING-SPEED RAMPS ARE NOW MEASURED TOO (2026-08-12, Studio 21.0.4.5) — this entry previously warned they were UNTESTED; that warning is closed and both work. REVERSE lands through BOTH import routes: OTIO with a negative time_scalar (-1) placed a clip reading GetSourceStartFrame 95 -> GetSourceEndFrame 46, and EDL with a negative M2 rate (-24.0 at 24fps) read 48 -> 0. So THE API DOES EXPOSE DIRECTION, which is worth knowing: a reversed clip reports GetSourceStartFrame GREATER THAN GetSourceEndFrame (a negative span). VARYING-SPEED RAMPS cannot be expressed in OTIO (LinearTimeWarp is a single time_scalar — constant by construction) but CAN be authored offline and survive Resolve intact: a Sm2TimeMap built with media-timemap.buildTimemap carrying two segments (0-2s record at 0.5x, 2-4s at 2.0x) was patched into a clip's MediaTimemapBA, imported, and re-exported by Resolve with the segments UNCHANGED. Independent confirmation from the API side: the placed clip read GetSourceStart/EndFrame 0..120 over a 96-frame record, and 2s at 0.5x (24 source frames) + 2s at 2.0x (96) is exactly 120. TWO TRAPS worth naming. (1) buildTimemap returns a BUFFER; writing it into the XML without .toString('hex') embeds mojibake, and the failure is SILENT — the clip imports fine and reads 0..0, which is indistinguishable from the degenerate-map signature this entry describes for xmeml imports, so it reads as 'Resolve rejected the retime' when it is really a caller bug. (2) a REVERSED map does not start at (0,0): Resolve encodes the starting source offset as a TOP-LEVEL protobuf field 2 double, and each keyframe point omits whichever of record/source is zero (protobuf default-omission). Anything reading fixed offsets or assuming a (0,0) origin decodes a reverse as SPEED 0 rather than -1. drp-format's own decoder had both bugs and is fixed.
|
|
121
121
|
- **Workaround / current handling:** Set clip speed/retime in the Resolve UI, or BUILD it by import: OTIO LinearTimeWarp and EDL M2 both construct constant retimes through ImportTimelineFromFile (measured — emission rules in reality above); xmeml does not, in any Time Remap shape. To READ a retime back, judge speed by the GetSourceStart/EndFrame span vs the record duration — a 0/0 read (xmeml-imported timelines) is UNKNOWN, never 'no retime' — and cross-check with the Sm2TimeMap slope in a saved Project.db or the EXPORT_EDL M2 rate. Do NOT read speed with GetProperty (None), witness it via EXPORT_FCP_7_XML (degenerate), or judge it from GetLeftOffset/GetRightOffset — the 2.80.0 revision of this entry recommended that pair as the witness and it is blind by construction: it reads the warped domain (position / speed) and its span equals the record span at every speed. Keep it for PLACEMENT checks only. Reverse and varying-speed maps remain untested as import routes. And if you are importing a real Premiere XML that contains retimes, treat every retimed clip's source position as WRONG — placed at <in>, i.e. in / ratio — until it is checked against a reference; the lengths and the links will look right.
|
|
122
122
|
- **Tags:** missing-method, timeline, retime, speed, interchange, silent-failure, unreliable-return
|
|
123
123
|
|