davinci-resolve-mcp 2.115.0 → 2.115.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/guides/native-drt-authoring.md +109 -0
- package/install.py +1 -1
- package/package.json +1 -1
- package/src/granular/common.py +1 -1
- package/src/server.py +1 -1
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Native DRT Authoring — offline timelines that import AND render
|
|
2
|
+
|
|
3
|
+
The `drt` tool in the advanced server (`drt.assemble`,
|
|
4
|
+
`drt.assemble_from_interchange`) writes a **native-schema `.drt`** — a real
|
|
5
|
+
Resolve project archive that `ImportTimelineFromFile` accepts — entirely
|
|
6
|
+
offline. Unlike XML/AAF/OTIO interchange (which lands offline and needs a
|
|
7
|
+
relink), a correctly assembled `.drt` arrives **linked and rendering**,
|
|
8
|
+
because it carries native media-pool descriptors captured from a live Resolve.
|
|
9
|
+
|
|
10
|
+
Everything below was measured live on Studio 19.1.3.7 and render-verified
|
|
11
|
+
(frame luma via ffmpeg signalstats; audio via rendered RMS). Version numbers
|
|
12
|
+
mark the release where each capability shipped.
|
|
13
|
+
|
|
14
|
+
## The core doctrine: readback is blind, render is truth
|
|
15
|
+
|
|
16
|
+
The recurring bug class of this entire subsystem: a structure that imports
|
|
17
|
+
fine, **reads back fine through every API**, and renders black or silent.
|
|
18
|
+
Structural readback cannot detect any of these:
|
|
19
|
+
|
|
20
|
+
- media clips without native pool descriptors (black)
|
|
21
|
+
- Fusion title comps whose bytes were edited offline (black — see below)
|
|
22
|
+
- audio clips cloned from a donor with the donor's identity (silent)
|
|
23
|
+
- audio tracks added by cloning (silent — no Fairlight strip)
|
|
24
|
+
- a timemap in the wrong generation's encoding (plays at 100%, no warning)
|
|
25
|
+
|
|
26
|
+
Always verify with a render: video by frame luma, audio by RMS in the target
|
|
27
|
+
window. `render.verify_output` covers the container-level checks.
|
|
28
|
+
|
|
29
|
+
## Prerequisites
|
|
30
|
+
|
|
31
|
+
- **Per media file, once**: `media_pool.capture_media_template` (Python
|
|
32
|
+
server, live Resolve required once). It caches the file's native pool
|
|
33
|
+
`<Element>` + MediaRef under `~/.config/davinci-resolve-mcp/media-templates/`.
|
|
34
|
+
After that, all assembly for that file is fully offline.
|
|
35
|
+
- Pass `targetAppVersion` (e.g. `"19.1.3"`) when the importing host is
|
|
36
|
+
pre-21: it selects the r19-generation templates and version stamps.
|
|
37
|
+
|
|
38
|
+
## What you can author (all render-verified on 19.1.3)
|
|
39
|
+
|
|
40
|
+
| Capability | Spec surface | Since |
|
|
41
|
+
|---|---|---|
|
|
42
|
+
| Media cuts, multi-source | `media: [{mediaFilePath, spec, cuts}]` | v2.106–2.107 |
|
|
43
|
+
| Multi-track video (V2+ stacking) | `cuts[].track` (video-only above V1) | v2.112 |
|
|
44
|
+
| Cross-dissolves | `transitions: [{track, atFrame, durationFrames}]` | v2.111 |
|
|
45
|
+
| Constant retimes, forward | `cuts[].speed` (e.g. `0.5`) | v2.113 |
|
|
46
|
+
| Constant retimes, reverse | `cuts[].reverse` | v2.114 |
|
|
47
|
+
| Audio placements, A1–A8 | `cuts[].audioOnly + track` | v2.115 |
|
|
48
|
+
| Built-in generators | `elements: [{type:'generator', generatorName}]` | v2.110 |
|
|
49
|
+
| Fusion titles | `elements: [{type:'title', text}]` — **21-gen hosts only** | v2.108 |
|
|
50
|
+
|
|
51
|
+
`assemble_from_interchange` drives the same engine from an EDL / OTIO /
|
|
52
|
+
FCP7-XML / AAF plus a `sourceMap`, and returns an honesty ledger
|
|
53
|
+
(`authoredTransitions`, `droppedTransitions` with reasons, `authoredRetimes`,
|
|
54
|
+
`flattenedRetimes`, `authoredAudioEvents`, `upperTrackCutsVideoOnly`).
|
|
55
|
+
|
|
56
|
+
## The laws (why the constraints are what they are)
|
|
57
|
+
|
|
58
|
+
**Fusion comp cache law (titles).** On 19.x, an imported Fusion comp renders
|
|
59
|
+
only via the machine's disk cache, keyed to the comp blob's *exact
|
|
60
|
+
compressed bytes*. Any offline byte change — even an identity recompression —
|
|
61
|
+
misses the cache and renders black; live Fusion render of imported comps
|
|
62
|
+
produces no frames on 19. Therefore offline title *text* cannot be authored
|
|
63
|
+
for pre-21 hosts. The working flow: assemble everything else offline, then
|
|
64
|
+
set title text post-import with `timeline.set_title_text` (live-verified on
|
|
65
|
+
19.1.3). Generators are exempt — plain `Sm2TiGenerator`, no comp — and
|
|
66
|
+
render everywhere (Solid Color, SMPTE Color Bar, Grey Scale verified).
|
|
67
|
+
|
|
68
|
+
**Fairlight strip law (audio).** Audio tracks cannot be grown offline: the
|
|
69
|
+
per-timeline Fairlight model (`FLStudioModelBA` in the pool's
|
|
70
|
+
`Sm2Sequence.FieldsBlob`) holds one mixer strip per audio track, and a
|
|
71
|
+
cloned track without a strip is mute. The r19 media template is captured
|
|
72
|
+
with **8 mono audio tracks** (strips included); audio placements beyond
|
|
73
|
+
track 8 refuse with instructions to re-capture a bigger template. Explicit
|
|
74
|
+
`audioOnly` cuts suppress the A1 convenience mirror (which otherwise
|
|
75
|
+
mirrors track-1 video cuts).
|
|
76
|
+
|
|
77
|
+
**Timemap generation split (retimes).** `Sm2TimeMap` keyframes are protobuf
|
|
78
|
+
points on 21 but keyed-dicts on 19, and 19 *silently ignores* the protobuf
|
|
79
|
+
form (clip plays 100%). The r19 keyed encoder is byte-exact against
|
|
80
|
+
Resolve's own output. The map spans the whole source stretched by 1/speed;
|
|
81
|
+
the clip's `<In>`/`<Duration>` window into it in record-domain frames.
|
|
82
|
+
Reverse is the same map with the Y endpoints swapped, and `In` then measures
|
|
83
|
+
from the source end.
|
|
84
|
+
|
|
85
|
+
**Timeline origin.** Template timelines start at frame 86400
|
|
86
|
+
(01:00:00:00 @ 24fps). Clips placed before the origin are silently dropped
|
|
87
|
+
by Resolve on import — `startFrame` is timeline-absolute.
|
|
88
|
+
|
|
89
|
+
**Dissolve geometry.** A transition is authored only when the predecessor
|
|
90
|
+
ends exactly at the cut and both sides have handle media for the centered
|
|
91
|
+
span (incoming `srcIn ≥ dur/2`; outgoing `srcIn + dur + dur/2 ≤ frameCount`).
|
|
92
|
+
Everything else stays in `droppedTransitions` with the reason.
|
|
93
|
+
|
|
94
|
+
## Verification checklist for a delivered .drt
|
|
95
|
+
|
|
96
|
+
1. `timeline.import_timeline_checked` — expect `linked == total` for media
|
|
97
|
+
(generators legitimately count as offline).
|
|
98
|
+
2. Render a probe range; check frame luma at cut boundaries, dissolve
|
|
99
|
+
midpoints (expect the blend average), and retime windows.
|
|
100
|
+
3. For audio: RMS per window (silence = -inf is a failed placement).
|
|
101
|
+
4. Never trust `created_new: false` — a same-named timeline already in the
|
|
102
|
+
project is returned as "success" (internal-name-wins law).
|
|
103
|
+
|
|
104
|
+
## References
|
|
105
|
+
|
|
106
|
+
- `docs/reference/api-limitations.md` — the measured laws in report form
|
|
107
|
+
- `resolve-advanced/vendor/drp-format/` — the codec layer (each module's
|
|
108
|
+
header documents its measured ground truth)
|
|
109
|
+
- Guides: `conforming-an-avid-aaf.md` for the AAF ingest side
|
package/install.py
CHANGED
|
@@ -37,7 +37,7 @@ from src.utils.update_check import (
|
|
|
37
37
|
|
|
38
38
|
# ─── Version ──────────────────────────────────────────────────────────────────
|
|
39
39
|
|
|
40
|
-
VERSION = "2.115.
|
|
40
|
+
VERSION = "2.115.1"
|
|
41
41
|
# Only hard floor: mcp[cli] requires Python 3.10+. There is no upper bound —
|
|
42
42
|
# Resolve's scripting bridge loads into newer interpreters on recent builds
|
|
43
43
|
# (Python 3.14 verified against Resolve Studio 20.3.2). Older Resolve builds
|
package/package.json
CHANGED
package/src/granular/common.py
CHANGED
|
@@ -87,7 +87,7 @@ if not logging.getLogger().handlers:
|
|
|
87
87
|
handlers=[logging.StreamHandler()],
|
|
88
88
|
)
|
|
89
89
|
|
|
90
|
-
VERSION = "2.115.
|
|
90
|
+
VERSION = "2.115.1"
|
|
91
91
|
logger = logging.getLogger("davinci-resolve-mcp")
|
|
92
92
|
logger.info(f"Starting DaVinci Resolve MCP Server v{VERSION}")
|
|
93
93
|
logger.info(f"Detected platform: {get_platform()}")
|