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.
@@ -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.0"
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "davinci-resolve-mcp",
3
- "version": "2.115.0",
3
+ "version": "2.115.1",
4
4
  "description": "NPM bootstrapper for the DaVinci Resolve MCP Server.",
5
5
  "license": "MIT",
6
6
  "author": "Samuel Gursky <samgursky@gmail.com>",
@@ -87,7 +87,7 @@ if not logging.getLogger().handlers:
87
87
  handlers=[logging.StreamHandler()],
88
88
  )
89
89
 
90
- VERSION = "2.115.0"
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()}")
package/src/server.py CHANGED
@@ -11,7 +11,7 @@ Usage:
11
11
  python src/server.py --full # Start the 353-tool granular server instead
12
12
  """
13
13
 
14
- VERSION = "2.115.0"
14
+ VERSION = "2.115.1"
15
15
 
16
16
  import base64
17
17
  import os