davinci-resolve-mcp 2.97.1 → 2.97.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,39 @@
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.97.2
6
+
7
+ **The panel's inventory snapshot survives a large project.** Follow-up to the
8
+ v2.97.1 fix: now that `inventory_limit` actually reaches the panel, the cache it
9
+ feeds has to cope with what arrives.
10
+
11
+ ### Fixed
12
+
13
+ - **The localStorage snapshot silently stopped working above ~5MB.** The panel
14
+ caches the last inventory so reopening it paints the previous view instantly
15
+ instead of "connection pending". That write was unbounded — and with
16
+ `inventory_limit` reaching 10000 clips, each carrying a file path, bin path,
17
+ status reasons, and the analysis overlay, it overruns the per-origin quota. The
18
+ write then threw, was caught, logged a `console.warn` no one reads, and the
19
+ instant-paint feature quietly did nothing on exactly the big projects where a
20
+ slow first fetch makes it worth having. The snapshot is now capped at 750
21
+ clips; on a quota failure the panel prunes cached inventories (including
22
+ snapshots for projects the user has moved on from) and retries once, and if
23
+ that still fails it removes its own key so a snapshot cannot outlive the walk
24
+ it described.
25
+ - **A truncated snapshot no longer reads as a complete inventory.** A capped
26
+ snapshot carries `snapshot_partial` and the true clip count, and both surfaces
27
+ that show a number say so — the header reads `(cached preview of 3015)` and the
28
+ poll status reads `cached first 750 of 3015`. Capping the cache without this
29
+ would have re-created the v2.97.1 bug one layer down: a number presented as the
30
+ inventory that isn't.
31
+
32
+ ### Added
33
+
34
+ - `tests/test_dashboard_inventory_snapshot.py` — guards that the inventory fetch
35
+ sends no `limit` of its own (the v2.97.1 regression), that the snapshot stays
36
+ capped, and that a partial snapshot declares itself.
37
+
5
38
  ## What's New in v2.97.1
6
39
 
7
40
  **The control panel honors `media_analysis.inventory_limit` again.** Thanks to
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  English | [简体中文](README.zh-CN.md)
4
4
 
5
- [![Version](https://img.shields.io/badge/version-2.97.1-blue.svg)](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
5
+ [![Version](https://img.shields.io/badge/version-2.97.2-blue.svg)](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
6
6
  [![npm](https://img.shields.io/npm/v/davinci-resolve-mcp.svg?label=npm&color=CB3837)](https://www.npmjs.com/package/davinci-resolve-mcp)
7
7
  [![API Coverage](https://img.shields.io/badge/API%20Coverage-100%25-brightgreen.svg)](docs/reference/api-coverage.md)
8
8
  [![Tools](https://img.shields.io/badge/MCP%20Tools-35%20(353%20full)-blue.svg)](#server-modes)
package/README.zh-CN.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [English](README.md) | 简体中文
4
4
 
5
- [![Version](https://img.shields.io/badge/version-2.97.1-blue.svg)](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
5
+ [![Version](https://img.shields.io/badge/version-2.97.2-blue.svg)](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
6
6
  [![npm](https://img.shields.io/npm/v/davinci-resolve-mcp.svg?label=npm&color=CB3837)](https://www.npmjs.com/package/davinci-resolve-mcp)
7
7
  [![API Coverage](https://img.shields.io/badge/API%20Coverage-100%25-brightgreen.svg)](docs/reference/api-coverage.md)
8
8
  [![Tools](https://img.shields.io/badge/MCP%20Tools-35%20(353%20full)-blue.svg)](#服务器模式)
@@ -12,7 +12,7 @@
12
12
  [![Python](https://img.shields.io/badge/python-3.10+-green.svg)](https://www.python.org/downloads/)
13
13
  [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT)
14
14
 
15
- > 本翻译对应 v2.97.1 版 README。如与英文原版有出入,以 [英文原版](README.md) 为准。
15
+ > 本翻译对应 v2.97.2 版 README。如与英文原版有出入,以 [英文原版](README.md) 为准。
16
16
 
17
17
  一个 Model Context Protocol (MCP) 服务器,让 AI 助手通过官方脚本 API 控制 DaVinci Resolve Studio(达芬奇)。它提供完整的 API 覆盖,外加带护栏的工作流助手,涵盖剪辑、媒体池整理、渲染设置、审阅标记、调色、Fusion、Fairlight、项目生命周期任务、扩展开发,以及不碰源媒体的媒体分析。
18
18
 
package/install.py CHANGED
@@ -37,7 +37,7 @@ from src.utils.update_check import (
37
37
 
38
38
  # ─── Version ──────────────────────────────────────────────────────────────────
39
39
 
40
- VERSION = "2.97.1"
40
+ VERSION = "2.97.2"
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.97.1",
3
+ "version": "2.97.2",
4
4
  "description": "NPM bootstrapper for the DaVinci Resolve MCP Server.",
5
5
  "license": "MIT",
6
6
  "author": "Samuel Gursky <samgursky@gmail.com>",
@@ -5981,7 +5981,10 @@ HTML = r"""<!doctype html>
5981
5981
  const clips = filteredResolveClips(allClips);
5982
5982
  const projectLabel = payload.project?.name || 'Current Resolve project';
5983
5983
  const visibleCounts = summarizeVisibleClips(clips);
5984
- project.textContent = `${projectLabel} · ${clipLabel(clips.length)}`;
5984
+ // A cached snapshot may hold only the first INVENTORY_SNAPSHOT_MAX_CLIPS
5985
+ // clips; say so rather than letting the prefix read as the clip count.
5986
+ const partialNote = payload.snapshot_partial ? ` (cached preview of ${payload.snapshot_total})` : '';
5987
+ project.textContent = `${projectLabel} · ${clipLabel(clips.length)}${partialNote}`;
5985
5988
  const hasAnalyzableClips = clips.some(clip => clip.analyzable);
5986
5989
  selectBtn.disabled = !hasAnalyzableClips;
5987
5990
  analyzeBtn.disabled = !hasAnalyzableClips;
@@ -6647,16 +6650,60 @@ HTML = r"""<!doctype html>
6647
6650
  // Persist the last good inventory so reopening the dashboard paints the
6648
6651
  // previous snapshot instantly (with a "refreshing" hint) instead of sitting
6649
6652
  // on "connection pending" until the first fetch returns.
6653
+ // localStorage gives us roughly 5MB per origin, and a clip record — file
6654
+ // path, bin path, status reasons, analysis overlay — runs several hundred
6655
+ // bytes. inventory_limit reaches 10000, so a large project overruns the
6656
+ // quota, and the write then throws and leaves the panel with no snapshot at
6657
+ // all. Cache a bounded prefix instead, and carry the real clip count so a
6658
+ // partial snapshot never presents itself as the whole inventory.
6659
+ const INVENTORY_SNAPSHOT_MAX_CLIPS = 750;
6660
+
6650
6661
  function inventorySnapshotKey() {
6651
6662
  return 'resolveMcpInventory:' + (state.activeContext?.project_root || state.boot?.project_root || state.boot?.project_name || 'default');
6652
6663
  }
6664
+ function inventorySnapshotPayload(payload) {
6665
+ const clips = payload.clips || [];
6666
+ if (clips.length <= INVENTORY_SNAPSHOT_MAX_CLIPS) return payload;
6667
+ return {
6668
+ ...payload,
6669
+ clips: clips.slice(0, INVENTORY_SNAPSHOT_MAX_CLIPS),
6670
+ snapshot_partial: true,
6671
+ snapshot_total: clips.length,
6672
+ };
6673
+ }
6674
+ // Drop every cached inventory, including this project's own stale value —
6675
+ // the panel is about to rewrite it, and snapshots for projects the user has
6676
+ // since moved on from are the usual reason the quota is full.
6677
+ function pruneInventorySnapshots() {
6678
+ try {
6679
+ const keys = [];
6680
+ for (let i = 0; i < localStorage.length; i += 1) {
6681
+ const key = localStorage.key(i);
6682
+ if (key && key.startsWith('resolveMcpInventory:')) keys.push(key);
6683
+ }
6684
+ keys.forEach(key => localStorage.removeItem(key));
6685
+ } catch (error) {
6686
+ console.warn('Could not prune inventory snapshots', error);
6687
+ }
6688
+ }
6653
6689
  function saveInventorySnapshot(payload) {
6654
6690
  if (!payload?.resolve_available) return;
6691
+ const key = inventorySnapshotKey();
6692
+ const raw = JSON.stringify({ saved_at: Date.now(), payload: inventorySnapshotPayload(payload) });
6655
6693
  try {
6656
- localStorage.setItem(inventorySnapshotKey(), JSON.stringify({ saved_at: Date.now(), payload }));
6694
+ localStorage.setItem(key, raw);
6657
6695
  } catch (error) {
6658
- // Quota or serialization failure is non-fatal the snapshot is a nicety.
6659
- console.warn('Could not cache inventory snapshot', error);
6696
+ // Out of quota: clear the cached inventories and try once more.
6697
+ pruneInventorySnapshots();
6698
+ try {
6699
+ localStorage.setItem(key, raw);
6700
+ } catch (retryError) {
6701
+ // Still no room — drop our key so a snapshot from an older walk can't
6702
+ // outlive the inventory it described. Non-fatal; the panel just paints
6703
+ // "connection pending" until the first fetch returns.
6704
+ try { localStorage.removeItem(key); } catch (removeError) { /* nothing left to do */ }
6705
+ console.warn('Could not cache inventory snapshot', retryError);
6706
+ }
6660
6707
  }
6661
6708
  }
6662
6709
  function loadInventorySnapshot() {
@@ -7170,7 +7217,11 @@ HTML = r"""<!doctype html>
7170
7217
  const poll = enabled ? `polling every ${Math.round(interval / 1000)}s` : 'polling off';
7171
7218
  const visible = state.resolveMedia?.clips ? clipLabel(filteredResolveClips(state.resolveMedia.clips).length) : 'no media snapshot';
7172
7219
  const prefix = state.mediaRefreshing ? 'refreshing' : poll;
7173
- const stale = state.resolveMediaStale ? 'cached · ' : '';
7220
+ const stale = state.resolveMediaStale
7221
+ ? (state.resolveMedia?.snapshot_partial
7222
+ ? `cached first ${state.resolveMedia.clips.length} of ${state.resolveMedia.snapshot_total} · `
7223
+ : 'cached · ')
7224
+ : '';
7174
7225
  el.textContent = `${stale}${prefix} · last ${last} · ${visible}${extra ? ` · ${extra}` : ''}`;
7175
7226
  }
7176
7227
 
@@ -87,7 +87,7 @@ if not logging.getLogger().handlers:
87
87
  handlers=[logging.StreamHandler()],
88
88
  )
89
89
 
90
- VERSION = "2.97.1"
90
+ VERSION = "2.97.2"
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.97.1"
14
+ VERSION = "2.97.2"
15
15
 
16
16
  import base64
17
17
  import os