davinci-resolve-mcp 2.120.0 → 2.121.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/install.py CHANGED
@@ -37,7 +37,7 @@ from src.utils.update_check import (
37
37
 
38
38
  # ─── Version ──────────────────────────────────────────────────────────────────
39
39
 
40
- VERSION = "2.120.0"
40
+ VERSION = "2.121.0"
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.120.0",
3
+ "version": "2.121.0",
4
4
  "description": "NPM bootstrapper for the DaVinci Resolve MCP Server.",
5
5
  "license": "MIT",
6
6
  "author": "Samuel Gursky <samgursky@gmail.com>",
@@ -141,6 +141,16 @@ export function parseOTIO(otio, opts = {}) {
141
141
  // First track of each kind keeps the bare letter (compat); higher tracks
142
142
  // are numbered ('V2', 'A2', …).
143
143
  const kind = isAudio ? (aNum === 1 ? 'A' : `A${aNum}`) : (vNum === 1 ? 'V' : `V${vNum}`);
144
+ // Track-level markers: marked_range is already in track (record) time.
145
+ for (const mk of track.markers || []) {
146
+ const mrStart = (mk.marked_range && mk.marked_range.start_time && mk.marked_range.start_time.value) || 0;
147
+ const mrRate = (mk.marked_range && mk.marked_range.start_time && mk.marked_range.start_time.rate) || opts.fps || 24;
148
+ events.push(evt({
149
+ index: idx++, track: 'MARKER', source: '',
150
+ recIn: mrStart, recOut: null,
151
+ name: mk.name || undefined, color: mk.color || undefined, fps: mrRate,
152
+ }));
153
+ }
144
154
  let rec = 0;
145
155
  for (const child of track.children || []) {
146
156
  const schema = child.OTIO_SCHEMA || '';
@@ -429,5 +439,20 @@ export function markerRoundtrip(markers, opts = {}) {
429
439
  throw new Error(`marker_roundtrip: ${markers.length} in, ${decoded.length} out — round-trip dropped markers`);
430
440
  const provenanceOk = decoded.every((m) => typeof m.provenance === 'string' && m.provenance.length);
431
441
  if (markers.length && !provenanceOk) throw new Error('marker_roundtrip: a marker lost its provenance tag');
432
- return { count: decoded.length, markers: decoded, provenanceOk, roundTrip: 'ok' };
442
+ // Binary round-trip through the REAL Sm2SequenceLockableBlob codec
443
+ // (byte-exact vs a live export): provenance rides in customData, so an
444
+ // authored .drt carries it. Colors outside the measured 16 refuse there —
445
+ // markerRoundtrip normalizes to 'Blue' above, so this cannot throw for
446
+ // valid input; if it ever does, that IS the failed round-trip.
447
+ let blobRoundTrip = 'skipped (no markers)';
448
+ if (normalized.length) {
449
+ const { encodeTimelineMarkersBlob, decodeTimelineMarkersBlob } = require('../vendor/drp-format/timeline-markers-blob.js');
450
+ const back = decodeTimelineMarkersBlob(encodeTimelineMarkersBlob(normalized.map((m) => ({
451
+ frame: m.frame, color: m.color, name: m.name, note: m.note, customData: m.provenance,
452
+ }))));
453
+ if (back.length !== normalized.length) throw new Error(`marker_roundtrip: blob codec ${normalized.length} in, ${back.length} out`);
454
+ if (!back.every((m) => m.customData && m.customData.length)) throw new Error('marker_roundtrip: provenance lost in the blob codec');
455
+ blobRoundTrip = 'ok';
456
+ }
457
+ return { count: decoded.length, markers: decoded, provenanceOk, roundTrip: 'ok', blobRoundTrip };
433
458
  }
@@ -338,9 +338,15 @@ function buildLockableBlobElement(markers, blobId, frameRate) {
338
338
  ]);
339
339
  }
340
340
 
341
- // Build marker protobuf (simplified - actual markers would need full encoding)
342
- // For now, return minimal structure that DaVinci can parse
343
- const fieldsBlobHex = buildMarkersFieldsBlob(markers, frameRate);
341
+ // Real Sm2SequenceLockableBlob encoding (byte-exact vs a live 19.1.3.7
342
+ // export; see timeline-markers-blob.js) replaces the old simplified
343
+ // marker-encoder blob, whose bytes never matched a real export.
344
+ const { encodeTimelineMarkersBlob } = require('./timeline-markers-blob');
345
+ const fieldsBlobHex = encodeTimelineMarkersBlob(markers.map((m) => ({
346
+ frame: Number(m.frame) || 0,
347
+ color: m.color, name: m.name, note: m.note ?? m.description,
348
+ duration: m.duration, customData: m.customData,
349
+ }))).toString('hex');
344
350
 
345
351
  return buildXmlElement('Sm2SequenceLockableBlob', { DbId: blobId }, [
346
352
  buildXmlElement('FieldsBlob', {}, fieldsBlobHex),
@@ -87,7 +87,7 @@ if not logging.getLogger().handlers:
87
87
  handlers=[logging.StreamHandler()],
88
88
  )
89
89
 
90
- VERSION = "2.120.0"
90
+ VERSION = "2.121.0"
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.120.0"
14
+ VERSION = "2.121.0"
15
15
 
16
16
  import base64
17
17
  import os