davinci-resolve-mcp 2.73.1 → 2.73.2

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 CHANGED
@@ -2,6 +2,38 @@
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.2
6
+
7
+ Two honesty fixes in the conform path, both found by running a real 83-minute
8
+ Avid AAF turnover end to end. Neither adds tool surface; `detect_missing_media`
9
+ gains two additive response fields.
10
+
11
+ ### Fixed
12
+
13
+ - **`detect_missing_media` counted an unknown item as a present one.** A timeline
14
+ item with no media pool item has no file path and no offline marker, so it fell
15
+ through to `present` — absence of information reported as presence. An AAF
16
+ imported with `importSourceClips=false` yields 882 such items, and the probe
17
+ answered `present_count: 882, missing_count: 0` while every one of them returned
18
+ `None` from `GetMediaPoolItem()`. The payload contradicted itself: the diagnosis
19
+ already said `unique_media_pool_item_count: 0`.
20
+
21
+ Those items now report under `unlinked` / `unlinked_count` and never inflate
22
+ `present_count`. They are deliberately **not** folded into `missing`: those rows
23
+ drive relink plans keyed on `media_pool_item_id`, and there is no pool item here
24
+ to relink. When a timeline is entirely unlinked the diagnosis says so, instead of
25
+ "No offline media detected" — technically true and completely misleading.
26
+
27
+ - **The "Resolve created no timeline" remediation named the wrong fix.** It advised
28
+ converting the file to FCP7 XML / FCPXML. The far more common cause is that
29
+ `importSourceClips` defaults to `True`, so Resolve tries to pull in the sequence's
30
+ source clips and fails the entire import when those paths do not resolve — the
31
+ normal state of a turnover, whose paths belong to the offline editor.
32
+ `import_source_clips=false` lands the timeline offline and now leads the
33
+ remediation. Note `sourceClipsPath` does not rescue it: Resolve matches source
34
+ clips by the filenames recorded in the sequence, so an AAF referencing Avid MXF
35
+ finds nothing in a folder of differently-named finishing media.
36
+
5
37
  ## What's New in v2.73.1
6
38
 
7
39
  Packaging fix. The npm package shipped the AAF reader's Node half without its
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # DaVinci Resolve MCP Server
2
2
 
3
- [![Version](https://img.shields.io/badge/version-2.73.1-blue.svg)](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
3
+ [![Version](https://img.shields.io/badge/version-2.73.2-blue.svg)](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
4
4
  [![npm](https://img.shields.io/npm/v/davinci-resolve-mcp.svg?label=npm&color=CB3837)](https://www.npmjs.com/package/davinci-resolve-mcp)
5
5
  [![API Coverage](https://img.shields.io/badge/API%20Coverage-100%25-brightgreen.svg)](docs/reference/api-coverage.md)
6
6
  [![Tools](https://img.shields.io/badge/MCP%20Tools-34%20(341%20full)-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.73.1"
39
+ VERSION = "2.73.2"
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "davinci-resolve-mcp",
3
- "version": "2.73.1",
3
+ "version": "2.73.2",
4
4
  "description": "NPM bootstrapper for the DaVinci Resolve MCP Server.",
5
5
  "license": "MIT",
6
6
  "author": "Samuel Gursky <samgursky@gmail.com>",
@@ -85,7 +85,7 @@ if not logging.getLogger().handlers:
85
85
  handlers=[logging.StreamHandler()],
86
86
  )
87
87
 
88
- VERSION = "2.73.1"
88
+ VERSION = "2.73.2"
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()}")
package/src/server.py CHANGED
@@ -11,7 +11,7 @@ Usage:
11
11
  python src/server.py --full # Start the 341-tool granular server instead
12
12
  """
13
13
 
14
- VERSION = "2.73.1"
14
+ VERSION = "2.73.2"
15
15
 
16
16
  import base64
17
17
  import os
@@ -6048,9 +6048,23 @@ def _import_timeline_checked(proj, mp, p: Dict[str, Any]):
6048
6048
  imported = t
6049
6049
  if not imported:
6050
6050
  if is_binary:
6051
+ # importSourceClips defaults to True, so Resolve tries to pull in the
6052
+ # sequence's source clips during import — and when those paths do not
6053
+ # resolve it fails the whole import rather than creating an offline
6054
+ # timeline. That is the normal case for a turnover (the paths belong
6055
+ # to the offline editor), so it is by far the most common reason a
6056
+ # valid AAF "creates no timeline", and the flag fixes it. Naming a
6057
+ # format conversion first sent people down a much longer road than
6058
+ # the one-flag retry that actually works. Note sourceClipsPath does
6059
+ # NOT rescue it: Resolve matches source clips by the filenames
6060
+ # recorded in the sequence, so an AAF referencing Avid MXF finds
6061
+ # nothing in a folder of differently-named finishing media.
6051
6062
  remediation = (
6052
- f"Resolve created no timeline from this {ext}. Verify it exports/opens in "
6053
- "Resolve directly, or convert to FCP7 XML / FCPXML upstream and import that."
6063
+ f"Resolve created no timeline from this {ext}. Most often the source clips "
6064
+ "could not be resolved: retry with import_source_clips=false to land the "
6065
+ "timeline offline, then add the media to the media pool and relink. "
6066
+ "Otherwise verify it exports/opens in Resolve directly, or convert to "
6067
+ "FCP7 XML / FCPXML upstream and import that."
6054
6068
  )
6055
6069
  elif sanitize:
6056
6070
  remediation = None
@@ -6583,6 +6597,7 @@ def _missing_media_diagnosis(missing_rows: List[Dict[str, Any]]) -> Dict[str, An
6583
6597
  def _detect_missing_media_from_snapshot(snapshot: Dict[str, Any]):
6584
6598
  missing = []
6585
6599
  present = []
6600
+ unlinked = []
6586
6601
  for track_type, type_payload in (snapshot.get("tracks") or {}).items():
6587
6602
  for track in type_payload.get("tracks", []):
6588
6603
  for item in track.get("items", []):
@@ -6603,13 +6618,40 @@ def _detect_missing_media_from_snapshot(snapshot: Dict[str, Any]):
6603
6618
  }
6604
6619
  if is_missing:
6605
6620
  missing.append(row)
6621
+ elif not file_path and not item.get("media_pool_item_id"):
6622
+ # No path AND no media pool item: the timeline item has nothing
6623
+ # behind it at all. It is not "present" — we simply know nothing
6624
+ # about it — and counting it as present is how an entirely
6625
+ # offline timeline reported full coverage (an AAF imported with
6626
+ # importSourceClips=false yields 882 such items and used to
6627
+ # report present_count 882 / missing_count 0, while every one of
6628
+ # them returned None from GetMediaPoolItem()).
6629
+ #
6630
+ # Kept out of `missing` deliberately: those rows drive relink
6631
+ # plans keyed on media_pool_item_id, and there is no pool item
6632
+ # here to relink. This is a third state, so it gets its own.
6633
+ unlinked.append(row)
6606
6634
  else:
6607
6635
  present.append(row)
6608
6636
  diagnosis = _missing_media_diagnosis(missing)
6637
+ if unlinked and not missing:
6638
+ # Nothing to relink, so the generic "no offline media detected" advice
6639
+ # would send the caller away satisfied from a timeline with no media.
6640
+ diagnosis = dict(diagnosis)
6641
+ diagnosis["primary_cause"] = "no_media_pool_items"
6642
+ diagnosis["recommended_next_step"] = (
6643
+ f"{len(unlinked)} timeline items have no media pool item at all — the timeline was "
6644
+ "imported without its source clips. Add the media to the media pool, then relink; "
6645
+ "there is nothing here for a path-based relink to act on."
6646
+ )
6609
6647
  return {
6610
6648
  "missing": missing,
6611
6649
  "present_count": len(present),
6612
6650
  "missing_count": len(missing),
6651
+ # Timeline items with neither a file path nor a media pool item. Never
6652
+ # folded into present_count — see above.
6653
+ "unlinked": unlinked,
6654
+ "unlinked_count": len(unlinked),
6613
6655
  "diagnosis": diagnosis,
6614
6656
  }
6615
6657