davinci-resolve-mcp 2.44.0 → 2.46.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.
@@ -83,6 +83,7 @@ class Spec:
83
83
  project: str
84
84
  color_preset: Optional[str] = None
85
85
  settings: Dict[str, str] = field(default_factory=dict)
86
+ bins: List[str] = field(default_factory=list)
86
87
  timelines: List[TimelineSpec] = field(default_factory=list)
87
88
  hooks: List[Hook] = field(default_factory=list)
88
89
 
@@ -145,6 +146,20 @@ def spec_from_dict(data: Dict[str, Any]) -> Spec:
145
146
  if not isinstance(settings, dict):
146
147
  raise SpecError("`settings` must be a mapping.")
147
148
 
149
+ bins: List[str] = []
150
+ for raw_bin in data.get("bins") or []:
151
+ if isinstance(raw_bin, str) and raw_bin.strip():
152
+ bin_path = raw_bin.strip().strip("/")
153
+ elif isinstance(raw_bin, dict) and raw_bin.get("path"):
154
+ bin_path = str(raw_bin["path"]).strip().strip("/")
155
+ else:
156
+ raise SpecError(f"Each bin needs a non-empty path: {raw_bin!r}")
157
+ # Live bin paths are always reported Master-prefixed; normalize here so
158
+ # an unprefixed spec bin doesn't read as a perpetually-pending change.
159
+ if bin_path != "Master" and not bin_path.startswith("Master/"):
160
+ bin_path = f"Master/{bin_path}"
161
+ bins.append(bin_path)
162
+
148
163
  timelines: List[TimelineSpec] = []
149
164
  for raw_tl in (data.get("timelines") or []):
150
165
  if not isinstance(raw_tl, dict) or not raw_tl.get("name"):
@@ -160,6 +175,7 @@ def spec_from_dict(data: Dict[str, Any]) -> Spec:
160
175
  project=str(project),
161
176
  color_preset=preset,
162
177
  settings={str(k): str(v) for k, v in settings.items()},
178
+ bins=bins,
163
179
  timelines=timelines,
164
180
  hooks=_parse_hooks(data.get("hooks")),
165
181
  )
@@ -252,6 +268,15 @@ def plan_spec(spec: Spec, live: Dict[str, Any]) -> Dict[str, Any]:
252
268
  f"{live_settings.get(key)!r} -> {want!r}", {"key": key, "value": want},
253
269
  ))
254
270
 
271
+ # Media Pool bins. Paths are slash-delimited from Master, e.g.
272
+ # "Master/Media/Scene_01"; creation is idempotent in the executor.
273
+ live_bins = set(live.get("bins") or [])
274
+ for bin_path in spec.bins:
275
+ if bin_path in live_bins:
276
+ actions.append(Action("noop", f"bin:{bin_path}", "exists"))
277
+ else:
278
+ actions.append(Action("ensure", f"bin:{bin_path}", "ensure media-pool bin", {"path": bin_path}))
279
+
255
280
  # Timelines + their settings + markers. NOTE: `fps` is a *creation-time*
256
281
  # property handled by ensure_timeline — Resolve refuses SetSetting on
257
282
  # timelineFrameRate after a timeline exists — so it is never emitted as a
@@ -300,6 +325,7 @@ def _spec_desired_state(spec: Spec) -> Dict[str, Any]:
300
325
  return {
301
326
  "project": spec.project,
302
327
  "settings": {k: _norm_setting_value(v) for k, v in effective_settings(spec).items()},
328
+ "bins": list(spec.bins),
303
329
  "timelines": [
304
330
  {
305
331
  "name": tl.name,
@@ -324,6 +350,7 @@ def _spec_normalized_state(live: Dict[str, Any], spec: Spec) -> Dict[str, Any]:
324
350
  return {
325
351
  "project": live.get("project"),
326
352
  "settings": {k: _norm_setting_value(live_settings.get(k)) for k in desired if k in live_settings},
353
+ "bins": [path for path in (live.get("bins") or []) if path in set(spec.bins)],
327
354
  "timelines": [
328
355
  {
329
356
  "name": name,
@@ -356,6 +383,7 @@ def apply_spec(
356
383
  ensure_timeline(name, fps) -> bool
357
384
  set_timeline_setting(timeline_name, key, value) -> bool
358
385
  add_marker(timeline_name, marker: dict) -> bool
386
+ ensure_bin(path) -> bool
359
387
 
360
388
  Hooks execute only when `run_hooks=True` AND a `run_hook` callable is given —
361
389
  arbitrary shell from a spec stays opt-in. Failures accumulate when
@@ -397,6 +425,9 @@ def apply_spec(
397
425
  _record(bool(executor.set_project_setting(key, desired[key])),
398
426
  f"project:{spec.project}/setting:{key}", str(desired[key]))
399
427
 
428
+ for bin_path in spec.bins:
429
+ _record(bool(executor.ensure_bin(bin_path)), f"bin:{bin_path}")
430
+
400
431
  # Timelines. `fps` is creation-time only (ensure_timeline); never set as a
401
432
  # post-creation timelineFrameRate setting — Resolve refuses that.
402
433
  live_tls = {tl.get("name"): tl for tl in (live.get("timelines") or [])}