davinci-resolve-mcp 2.72.1 → 2.73.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 +51 -0
- package/README.md +1 -1
- package/install.py +1 -1
- package/package.json +1 -1
- package/resolve-advanced/README.md +19 -0
- package/resolve-advanced/server/aaf.mjs +12 -2
- package/src/granular/common.py +1 -1
- package/src/server.py +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,57 @@
|
|
|
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.73.0
|
|
6
|
+
|
|
7
|
+
The offline AAF reader could not read a multi-layer Avid timeline, and said so
|
|
8
|
+
in the worst possible way: `ok: true` with an empty event list, indistinguishable
|
|
9
|
+
from an empty timeline. Any caller gating on a successful parse would proceed to
|
|
10
|
+
conform nothing.
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **AAF: multi-layer timelines returned zero events while reporting success.**
|
|
15
|
+
Avid exports a multi-layer video timeline as a `NestedScope` segment, which the
|
|
16
|
+
offline reader never traversed — `NestedScope` carries `.slots`, not
|
|
17
|
+
`.components`, so the whole timeline fell through the walker's lone-`SourceClip`
|
|
18
|
+
fallback and emitted nothing. Two further drops sat on the same path and had to
|
|
19
|
+
be fixed with it: `OperationGroup.segments` holds a nested `Sequence` rather than
|
|
20
|
+
a direct `SourceClip`, so effect-wrapped clips — the majority of any real
|
|
21
|
+
turnover — were dropped even once `NestedScope` was traversed; and source-name
|
|
22
|
+
resolution stopped one mob hop short of the MasterMob, because Avid routes
|
|
23
|
+
timeline clips through an unnamed intermediate `CompositionMob`, resolving most
|
|
24
|
+
clips to `UNKNOWN`. `Selector` segments are now followed (via the AAF `Selected`
|
|
25
|
+
property — pyaaf2 does not expose it as an attribute), and non-editorial slots
|
|
26
|
+
are skipped by media kind rather than segment class, so `Pulldown`-wrapped
|
|
27
|
+
timecode tracks no longer leak through as editorial.
|
|
28
|
+
|
|
29
|
+
Verified against a real 83-minute Avid picture turnover: **0 → 878 events**
|
|
30
|
+
across 5 layers, **0 `UNKNOWN` sources** (779 distinct camera rolls), no
|
|
31
|
+
unhandled component classes, ~1.5 s.
|
|
32
|
+
|
|
33
|
+
### Added
|
|
34
|
+
|
|
35
|
+
- AAF probe and `listAafSequences` now report an `unhandled` map (component class
|
|
36
|
+
→ count) per sequence. A structural miss is visible instead of masquerading as
|
|
37
|
+
an empty timeline.
|
|
38
|
+
|
|
39
|
+
### Known limitations, stated deliberately
|
|
40
|
+
|
|
41
|
+
- **Motion Control retimes are flagged, not quantified.** They carry
|
|
42
|
+
`effect: "Motion Control"` with `speed: 100`, because the ratio is not
|
|
43
|
+
recoverable offline. A consumer reading `speed` alone will treat them as full
|
|
44
|
+
speed. Pre-existing behaviour, preserved on purpose — flag over fabricate.
|
|
45
|
+
- **Effect-only layers correctly produce no events.** Layers that wrap
|
|
46
|
+
`ScopeReference` (subtitle burns, blends, mattes) apply to what shows through
|
|
47
|
+
from below and reference no media of their own. OTIO reports such layers as
|
|
48
|
+
tracks of gaps, so its track count can exceed the number of layers with media;
|
|
49
|
+
"6 layers" is not "6 layers with media". Events were not fabricated to make the
|
|
50
|
+
counts match.
|
|
51
|
+
- **Only `NestedScope` layers are numbered `V1..Vn`.** Non-nested slots keep the
|
|
52
|
+
flat `V`/`A` label, a deliberate scope limit that keeps the blast radius off
|
|
53
|
+
simple AAFs — notably `editorial.mjs`'s `track === 'A'` audio-follows-video
|
|
54
|
+
heuristic, which reads that label.
|
|
55
|
+
|
|
5
56
|
## What's New in v2.72.1
|
|
6
57
|
|
|
7
58
|
Documentation only. The API coverage page stated its method counts in four
|
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/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.73.0"
|
|
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
|
@@ -173,6 +173,25 @@ status + install hints):
|
|
|
173
173
|
Missing features fail with a clear, actionable message rather than crashing; the server logs a one-line
|
|
174
174
|
"needs setup" summary to stderr at startup.
|
|
175
175
|
|
|
176
|
+
> **Pinning the AAF interpreter.** `aaf_probe.py` runs under `AAF_PROBE_PYTHON`, falling back to
|
|
177
|
+
> `PYTHON` and then plain `python3` on PATH. `install.py` sets that pin **only inside the `mcpServers`
|
|
178
|
+
> JSON entry it writes**, so a host that spawns this advanced server some other way must pin it itself —
|
|
179
|
+
> otherwise the reader resolves a `python3` without `pyaaf2` and honest-refuses every AAF.
|
|
180
|
+
|
|
181
|
+
### AAF multi-layer reading — what it does and does not tell you
|
|
182
|
+
Avid exports a multi-layer video timeline as a `NestedScope` segment holding one nested `Sequence` per
|
|
183
|
+
layer; those layers are traversed and numbered `V1..Vn`. Each parsed sequence also carries an
|
|
184
|
+
`unhandled` map (component class → count) so a structural miss is visible rather than arriving as a
|
|
185
|
+
plausible-looking short event list. Three limits are deliberate:
|
|
186
|
+
|
|
187
|
+
- **Motion Control retimes are flagged, not quantified** — `effect: "Motion Control"` with `speed: 100`,
|
|
188
|
+
because the ratio is not recoverable offline. Reading `speed` alone reports them as full speed.
|
|
189
|
+
- **Effect-only layers produce no events.** A layer wrapping `ScopeReference` (subtitle burns, blends,
|
|
190
|
+
mattes) applies to what shows through from below and references no media of its own. OTIO materializes
|
|
191
|
+
such layers as tracks of gaps, so its track count can exceed the number of layers with media.
|
|
192
|
+
- **Only `NestedScope` layers are numbered.** Non-nested slots keep the flat `V`/`A` label, which is what
|
|
193
|
+
`editorial.mjs`'s `track === 'A'` audio-follows-video heuristic reads.
|
|
194
|
+
|
|
176
195
|
## Provenance & license
|
|
177
196
|
Vendored libraries are clean offline format-interop and deterministic compute code: no secrets, no
|
|
178
197
|
external service coupling, no network calls, no LLM dependency. Where a feature can take a second
|
|
@@ -94,11 +94,21 @@ export async function parseAAF(contentOrPath) {
|
|
|
94
94
|
|
|
95
95
|
/**
|
|
96
96
|
* Enumerate the sequences inside an AAF for the picker.
|
|
97
|
+
*
|
|
98
|
+
* `unhandled` is carried through deliberately: a structural miss in the probe
|
|
99
|
+
* (a component class it cannot walk) otherwise looks identical to a genuinely
|
|
100
|
+
* empty timeline, and the caller would gate on a successful parse and conform
|
|
101
|
+
* nothing. A NestedScope — Avid's multi-layer video stack — used to land in
|
|
102
|
+
* exactly that hole: eventCount 0 alongside ok:true. Absent means clean.
|
|
97
103
|
* @param {string} contentOrPath absolute .aaf path
|
|
98
|
-
* @returns {Promise<Array<{id:string,name:string,eventCount:number}>>}
|
|
104
|
+
* @returns {Promise<Array<{id:string,name:string,eventCount:number,unhandled?:Object}>>}
|
|
99
105
|
*/
|
|
100
106
|
export async function listAafSequences(contentOrPath) {
|
|
101
107
|
const aafPath = resolveAafPath(contentOrPath);
|
|
102
108
|
const { sequences } = await runProbe(aafPath);
|
|
103
|
-
return (sequences || []).map((s) =>
|
|
109
|
+
return (sequences || []).map((s) => {
|
|
110
|
+
const out = { id: String(s.id), name: String(s.name), eventCount: Number(s.eventCount || 0) };
|
|
111
|
+
if (s.unhandled && Object.keys(s.unhandled).length) out.unhandled = s.unhandled;
|
|
112
|
+
return out;
|
|
113
|
+
});
|
|
104
114
|
}
|
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.73.0"
|
|
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()}")
|