davinci-resolve-mcp 4.8.24 → 4.8.26
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 +61 -0
- package/README.md +1 -1
- package/README.zh-CN.md +2 -2
- package/install.py +1 -1
- package/package.json +1 -1
- package/scripts/audit_api_parity.py +6 -4
- package/src/granular/common.py +1 -3
- package/src/granular/resolve_control.py +25 -12
- package/src/server.py +1 -1
- package/src/utils/api_truth.py +28 -0
- package/src/utils/app_control.py +64 -59
- package/src/utils/layout_presets.py +10 -69
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,67 @@
|
|
|
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 v4.8.26 — two dead layout-preset helpers removed
|
|
6
|
+
|
|
7
|
+
### Removed
|
|
8
|
+
|
|
9
|
+
- `src/utils/layout_presets.py` no longer carries `save_layout_preset` and
|
|
10
|
+
`load_layout_preset`. Both went through `Resolve.GetUIManager()` and then
|
|
11
|
+
`SaveUILayout` / `LoadUILayout`, none of which exist on any build measured
|
|
12
|
+
(Studio 19.1.3.7; see the `api_truth` entry added in v4.8.25). Nothing called
|
|
13
|
+
them: the granular `save_layout_preset_tool` and `load_layout_preset_tool`
|
|
14
|
+
use `Resolve.SaveLayoutPreset` / `LoadLayoutPreset` directly, and still do.
|
|
15
|
+
No tool, action or count changes. The module docstring now says what the
|
|
16
|
+
file actually does (preset files on disk) and where live save/load happens.
|
|
17
|
+
- `scripts/audit_api_parity.py` drops `LoadUILayout` and `SaveUILayout` from
|
|
18
|
+
its allowlist, since no source calls them any more.
|
|
19
|
+
|
|
20
|
+
### Validation
|
|
21
|
+
|
|
22
|
+
- No Resolve behavior changed; the removed functions had no callers. Live test
|
|
23
|
+
not required.
|
|
24
|
+
|
|
25
|
+
## What's New in v4.8.25 — open_settings and open_app_preferences say what Resolve cannot do
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
|
|
29
|
+
- **The granular `open_settings` and `open_app_preferences` tools could never
|
|
30
|
+
work, and reported that as an ordinary failure.** Both went through
|
|
31
|
+
`Resolve.GetUIManager()`, which does not exist. Measured on Studio 19.1.3.7:
|
|
32
|
+
`dir(resolve)` lists 23 methods and `GetUIManager` is not one of them;
|
|
33
|
+
`Fusion().UIManager` is real but has neither `OpenProjectSettings` nor
|
|
34
|
+
`OpenPreferences`; and none of the three names appears in the 21.1 typed API.
|
|
35
|
+
The call raised `'NoneType' object is not callable`, a broad `except`
|
|
36
|
+
swallowed it, an ERROR was logged, and the tool answered
|
|
37
|
+
`Failed to open Project Settings dialog` with no reason.
|
|
38
|
+
Both tools now answer `Not supported:` and name the call that is missing;
|
|
39
|
+
`open_settings` also names the tools that read and write project settings.
|
|
40
|
+
Nothing is logged as an error, because nothing went wrong.
|
|
41
|
+
- The route is now probed with `has_method` rather than `hasattr`, which is
|
|
42
|
+
true for every name on a Resolve object. If a future build does provide these
|
|
43
|
+
calls they are used, and their result is reported: the old code discarded the
|
|
44
|
+
return and answered success regardless, so a refusal would have read as a
|
|
45
|
+
dialog that opened.
|
|
46
|
+
|
|
47
|
+
### Documentation
|
|
48
|
+
|
|
49
|
+
- `api_truth` records the absence, with what `Fusion().UIManager` does expose.
|
|
50
|
+
Whether `UIManager.DoAction` or `QueueAction` can open these dialogs was not
|
|
51
|
+
tried: both dialogs are modal, and a modal dialog blocks the scripting API
|
|
52
|
+
until a person closes it.
|
|
53
|
+
- `scripts/audit_api_parity.py` no longer describes `GetUIManager` as a
|
|
54
|
+
documented API.
|
|
55
|
+
|
|
56
|
+
### Tests
|
|
57
|
+
|
|
58
|
+
- `tests/test_app_control_dialogs.py`: a fake that fabricates attributes the way
|
|
59
|
+
a Resolve object does (every `hasattr` true, a missing `getattr` is `None`).
|
|
60
|
+
Covers the measured build, a manager without the method, a build that has
|
|
61
|
+
the call, a refusal, an exception, and both granular tools. Eight of its
|
|
62
|
+
twelve tests fail against v4.8.24.
|
|
63
|
+
- `tests/test_discarded_resolve_returns.py` now covers every `Open*` call, not
|
|
64
|
+
only `OpenPage`.
|
|
65
|
+
|
|
5
66
|
## What's New in v4.8.24 — a frame capture leaves the render output folder and file name alone
|
|
6
67
|
|
|
7
68
|
### Fixed
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
English | [简体中文](README.zh-CN.md)
|
|
4
4
|
|
|
5
|
-
[](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
|
|
6
6
|
[](https://www.npmjs.com/package/davinci-resolve-mcp)
|
|
7
7
|
[](docs/reference/api-coverage.md)
|
|
8
8
|
[-blue.svg)](#server-modes)
|
package/README.zh-CN.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[English](README.md) | 简体中文
|
|
4
4
|
|
|
5
|
-
[](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
|
|
6
6
|
[](https://www.npmjs.com/package/davinci-resolve-mcp)
|
|
7
7
|
[](docs/reference/api-coverage.md)
|
|
8
8
|
[-blue.svg)](#服务器模式)
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
[](https://www.python.org/downloads/)
|
|
13
13
|
[](https://opensource.org/licenses/MIT)
|
|
14
14
|
|
|
15
|
-
> 本翻译对应 v4.8.
|
|
15
|
+
> 本翻译对应 v4.8.26 版 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 = "4.8.
|
|
40
|
+
VERSION = "4.8.26"
|
|
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
|
@@ -145,10 +145,12 @@ ALLOWLIST_UNDOCUMENTED: Set[str] = {
|
|
|
145
145
|
"AddKeyframe", "DeleteKeyframe", "ModifyKeyframe", "RemoveKeyFrame",
|
|
146
146
|
"GetKeyframeAtIndex", "GetKeyframeCount", "GetPropertyAtKeyframeIndex",
|
|
147
147
|
"SetKeyframeInterpolation", "Render", "StartUndo",
|
|
148
|
-
#
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
148
|
+
# Resolve app-control method, not in the scripting README this audit parses
|
|
149
|
+
"SetHighPriority",
|
|
150
|
+
# NOT a documented API: Resolve has no GetUIManager on any build measured
|
|
151
|
+
# (Studio 19.1.3.7; api_truth 'Resolve.GetUIManager ...'). Called only
|
|
152
|
+
# behind has_method in src/utils/app_control.py.
|
|
153
|
+
"GetUIManager",
|
|
152
154
|
# Lua-table iteration helper used as a fallback in object_inspection.py
|
|
153
155
|
"GetKeyList",
|
|
154
156
|
# Project metadata accessor used defensively (hasattr-guarded)
|
package/src/granular/common.py
CHANGED
|
@@ -45,8 +45,6 @@ from src.utils.layout_presets import (
|
|
|
45
45
|
export_layout_preset,
|
|
46
46
|
import_layout_preset,
|
|
47
47
|
list_layout_presets,
|
|
48
|
-
load_layout_preset,
|
|
49
|
-
save_layout_preset,
|
|
50
48
|
)
|
|
51
49
|
from src.utils.object_inspection import inspect_object, print_object_help
|
|
52
50
|
from src.utils.platform import get_platform, get_resolve_paths
|
|
@@ -93,7 +91,7 @@ if not logging.getLogger().handlers:
|
|
|
93
91
|
handlers=[logging.StreamHandler()],
|
|
94
92
|
)
|
|
95
93
|
|
|
96
|
-
VERSION = "4.8.
|
|
94
|
+
VERSION = "4.8.26"
|
|
97
95
|
logger = logging.getLogger("davinci-resolve-mcp")
|
|
98
96
|
logger.info(f"Starting DaVinci Resolve MCP Server v{VERSION}")
|
|
99
97
|
logger.info(f"Detected platform: {get_platform()}")
|
|
@@ -371,32 +371,45 @@ def restart_app(wait_seconds: int = 5) -> str:
|
|
|
371
371
|
|
|
372
372
|
@mcp.tool()
|
|
373
373
|
def open_settings() -> str:
|
|
374
|
-
"""Open the Project Settings dialog in DaVinci Resolve.
|
|
374
|
+
"""Open the Project Settings dialog in DaVinci Resolve.
|
|
375
|
+
|
|
376
|
+
Not available on any Resolve build measured so far: the scripting API has
|
|
377
|
+
no call that opens this dialog (Studio 19.1.3.7 measured; absent from the
|
|
378
|
+
21.1 typed API). The reply says so by name instead of a bare failure. To
|
|
379
|
+
read or change project settings use get_project_settings,
|
|
380
|
+
get_project_setting and set_project_setting.
|
|
381
|
+
"""
|
|
375
382
|
resolve = get_resolve()
|
|
376
383
|
if resolve is None:
|
|
377
384
|
return "Error: Not connected to DaVinci Resolve"
|
|
378
|
-
|
|
385
|
+
|
|
379
386
|
result = open_project_settings(resolve)
|
|
380
|
-
|
|
381
|
-
if result:
|
|
387
|
+
if result["success"]:
|
|
382
388
|
return "Project Settings dialog opened successfully"
|
|
383
|
-
|
|
384
|
-
return
|
|
389
|
+
if not result["supported"]:
|
|
390
|
+
return (
|
|
391
|
+
result["message"]
|
|
392
|
+
+ " Use get_project_settings, get_project_setting and set_project_setting instead."
|
|
393
|
+
)
|
|
394
|
+
return result["message"]
|
|
385
395
|
|
|
386
396
|
|
|
387
397
|
@mcp.tool()
|
|
388
398
|
def open_app_preferences() -> str:
|
|
389
|
-
"""Open the Preferences dialog in DaVinci Resolve.
|
|
399
|
+
"""Open the Preferences dialog in DaVinci Resolve.
|
|
400
|
+
|
|
401
|
+
Not available on any Resolve build measured so far: the scripting API has
|
|
402
|
+
no call that opens this dialog (Studio 19.1.3.7 measured; absent from the
|
|
403
|
+
21.1 typed API). The reply says so by name instead of a bare failure.
|
|
404
|
+
"""
|
|
390
405
|
resolve = get_resolve()
|
|
391
406
|
if resolve is None:
|
|
392
407
|
return "Error: Not connected to DaVinci Resolve"
|
|
393
|
-
|
|
408
|
+
|
|
394
409
|
result = open_preferences(resolve)
|
|
395
|
-
|
|
396
|
-
if result:
|
|
410
|
+
if result["success"]:
|
|
397
411
|
return "Preferences dialog opened successfully"
|
|
398
|
-
|
|
399
|
-
return "Failed to open Preferences dialog"
|
|
412
|
+
return result["message"]
|
|
400
413
|
|
|
401
414
|
|
|
402
415
|
@mcp.tool()
|
package/src/server.py
CHANGED
package/src/utils/api_truth.py
CHANGED
|
@@ -2749,6 +2749,34 @@ API_TRUTH: List[Dict[str, Any]] = [
|
|
|
2749
2749
|
"verified_on": "DaVinci Resolve Studio 19.1.3.7",
|
|
2750
2750
|
"mitigation": ["_render_target_dir", "_playhead_frame_render"],
|
|
2751
2751
|
},
|
|
2752
|
+
{
|
|
2753
|
+
"symbol": "Resolve.GetUIManager / UIManager.OpenProjectSettings / OpenPreferences (do not exist)",
|
|
2754
|
+
"object": "Resolve",
|
|
2755
|
+
"reality": "No scripting call opens the Project Settings or Preferences "
|
|
2756
|
+
"dialog. Measured 2026-09-30 on Studio 19.1.3.7, direct "
|
|
2757
|
+
"connection: dir(resolve) lists 23 methods and GetUIManager "
|
|
2758
|
+
"is not among them (getattr returns None; hasattr says True, "
|
|
2759
|
+
"as it does for every name). Fusion().UIManager is a real "
|
|
2760
|
+
"object with 15 names — AddNotify, Comp, Composition, "
|
|
2761
|
+
"DoAction, FindWindow, FindWindows, GetData, GetEvent, GetID, "
|
|
2762
|
+
"GetReg, QueueAction, QueueEvent, RemoveNotify, SetData, "
|
|
2763
|
+
"TriggerEvent — and none of OpenProjectSettings, "
|
|
2764
|
+
"OpenPreferences, SaveUILayout or LoadUILayout. None of those "
|
|
2765
|
+
"names, nor GetUIManager, appears in the 21.1 typed API "
|
|
2766
|
+
"either. Code written against them calls None and raises "
|
|
2767
|
+
"\"'NoneType' object is not callable\"; wrapped in a broad "
|
|
2768
|
+
"except, that reads as an ordinary failure. Whether "
|
|
2769
|
+
"UIManager.DoAction or QueueAction can open these dialogs was "
|
|
2770
|
+
"not tried: both dialogs are modal, and a modal dialog blocks "
|
|
2771
|
+
"the scripting API until a person closes it.",
|
|
2772
|
+
"recommended": "Do not offer to open these dialogs. Read and write "
|
|
2773
|
+
"project settings through Project.GetSetting/SetSetting. "
|
|
2774
|
+
"UI layouts go through Resolve.SaveLayoutPreset / "
|
|
2775
|
+
"LoadLayoutPreset, which do exist. Probe with "
|
|
2776
|
+
"resolve_probe.has_method, never hasattr.",
|
|
2777
|
+
"tags": ["ui", "unsupported", "dialog"],
|
|
2778
|
+
"verified_on": "DaVinci Resolve Studio 19.1.3.7",
|
|
2779
|
+
},
|
|
2752
2780
|
{
|
|
2753
2781
|
"symbol": "ProjectManager.SaveProject",
|
|
2754
2782
|
"object": "ProjectManager",
|
package/src/utils/app_control.py
CHANGED
|
@@ -16,6 +16,8 @@ import platform
|
|
|
16
16
|
import subprocess
|
|
17
17
|
from typing import Dict, Any, Optional, Union, List
|
|
18
18
|
|
|
19
|
+
from src.utils.resolve_probe import has_method
|
|
20
|
+
|
|
19
21
|
# Configure logging
|
|
20
22
|
logger = logging.getLogger("davinci-resolve-mcp.app_control")
|
|
21
23
|
APP_CONTROL_TIMEOUT_SECONDS = 10
|
|
@@ -267,65 +269,68 @@ def restart_resolve_app(resolve_obj, wait_seconds: int = 5) -> bool:
|
|
|
267
269
|
logger.error(f"Error restarting DaVinci Resolve: {str(e)}")
|
|
268
270
|
return False
|
|
269
271
|
|
|
270
|
-
def
|
|
271
|
-
"""
|
|
272
|
-
Open the Project Settings dialog in DaVinci Resolve.
|
|
273
|
-
|
|
274
|
-
Args:
|
|
275
|
-
resolve_obj: DaVinci Resolve API object
|
|
276
|
-
|
|
277
|
-
Returns:
|
|
278
|
-
True if successful, False otherwise
|
|
279
|
-
"""
|
|
280
|
-
try:
|
|
281
|
-
# Check if UI Manager is available
|
|
282
|
-
ui_manager = resolve_obj.GetUIManager()
|
|
283
|
-
if not ui_manager:
|
|
284
|
-
logger.error("Failed to get UI Manager")
|
|
285
|
-
return False
|
|
286
|
-
|
|
287
|
-
# Open Project Settings dialog
|
|
288
|
-
if hasattr(ui_manager, 'OpenProjectSettings') and callable(getattr(ui_manager, 'OpenProjectSettings')):
|
|
289
|
-
ui_manager.OpenProjectSettings()
|
|
290
|
-
return True
|
|
291
|
-
|
|
292
|
-
# Alternative method - send keyboard shortcut based on platform
|
|
293
|
-
current_page = resolve_obj.GetCurrentPage()
|
|
294
|
-
|
|
295
|
-
# Ensure we're on a page that supports project settings
|
|
296
|
-
if current_page not in ['media', 'cut', 'edit', 'fusion', 'color', 'fairlight', 'deliver']:
|
|
297
|
-
logger.error(f"Can't open settings from page: {current_page}")
|
|
298
|
-
return False
|
|
299
|
-
|
|
300
|
-
return False # Keyboard shortcuts not implemented yet
|
|
301
|
-
except Exception as e:
|
|
302
|
-
logger.error(f"Error opening project settings: {str(e)}")
|
|
303
|
-
return False
|
|
272
|
+
def _open_dialog(resolve_obj, method_name: str, label: str) -> Dict[str, Any]:
|
|
273
|
+
"""Ask Resolve to open a dialog, and say what actually happened.
|
|
304
274
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
275
|
+
Returns {"success", "supported", "message"}.
|
|
276
|
+
|
|
277
|
+
The route this module has always used is Resolve.GetUIManager() and then a
|
|
278
|
+
method on the manager. Neither half exists on any build measured so far.
|
|
279
|
+
On Studio 19.1.3.7 (2026-09-30): dir(resolve) lists 23 methods and
|
|
280
|
+
GetUIManager is not one of them; Fusion().UIManager is real but offers
|
|
281
|
+
neither OpenProjectSettings nor OpenPreferences; and none of the three
|
|
282
|
+
names appears in the 21.1 typed API. So the old code raised "'NoneType'
|
|
283
|
+
object is not callable" on its first line, caught it, logged an error and
|
|
284
|
+
returned a bare False — a tool that could never work, reporting that as an
|
|
285
|
+
ordinary failure with no reason.
|
|
286
|
+
|
|
287
|
+
`hasattr` cannot make this distinction: it is True for every name on a
|
|
288
|
+
Resolve object (see src/utils/resolve_probe.py). `has_method` can, so the
|
|
289
|
+
route is probed with it, and a build that does grow these calls is used
|
|
290
|
+
and its answer reported instead of assumed.
|
|
314
291
|
"""
|
|
292
|
+
if not has_method(resolve_obj, "GetUIManager"):
|
|
293
|
+
return {
|
|
294
|
+
"success": False,
|
|
295
|
+
"supported": False,
|
|
296
|
+
"message": (
|
|
297
|
+
f"Not supported: DaVinci Resolve's scripting API has no call that opens the "
|
|
298
|
+
f"{label} dialog. Resolve.GetUIManager does not exist on this build."
|
|
299
|
+
),
|
|
300
|
+
}
|
|
301
|
+
ui_manager = resolve_obj.GetUIManager()
|
|
302
|
+
if not has_method(ui_manager, method_name):
|
|
303
|
+
return {
|
|
304
|
+
"success": False,
|
|
305
|
+
"supported": False,
|
|
306
|
+
"message": (
|
|
307
|
+
f"Not supported: DaVinci Resolve's scripting API has no call that opens the "
|
|
308
|
+
f"{label} dialog. UIManager.{method_name} does not exist on this build."
|
|
309
|
+
),
|
|
310
|
+
}
|
|
315
311
|
try:
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
312
|
+
opened = getattr(ui_manager, method_name)()
|
|
313
|
+
except Exception as exc:
|
|
314
|
+
logger.error("UIManager.%s raised: %s", method_name, exc)
|
|
315
|
+
return {
|
|
316
|
+
"success": False,
|
|
317
|
+
"supported": True,
|
|
318
|
+
"message": f"Failed to open the {label} dialog: UIManager.{method_name} raised {exc}",
|
|
319
|
+
}
|
|
320
|
+
if opened is False:
|
|
321
|
+
return {
|
|
322
|
+
"success": False,
|
|
323
|
+
"supported": True,
|
|
324
|
+
"message": f"Failed to open the {label} dialog: UIManager.{method_name} returned False",
|
|
325
|
+
}
|
|
326
|
+
return {"success": True, "supported": True, "message": f"{label} dialog opened"}
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
def open_project_settings(resolve_obj) -> Dict[str, Any]:
|
|
330
|
+
"""Open the Project Settings dialog. See `_open_dialog` for the result shape."""
|
|
331
|
+
return _open_dialog(resolve_obj, "OpenProjectSettings", "Project Settings")
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
def open_preferences(resolve_obj) -> Dict[str, Any]:
|
|
335
|
+
"""Open the Preferences dialog. See `_open_dialog` for the result shape."""
|
|
336
|
+
return _open_dialog(resolve_obj, "OpenPreferences", "Preferences")
|
|
@@ -2,11 +2,17 @@
|
|
|
2
2
|
"""
|
|
3
3
|
DaVinci Resolve MCP Server - Layout Presets Utilities
|
|
4
4
|
|
|
5
|
-
This module
|
|
6
|
-
-
|
|
7
|
-
- Loading layout presets
|
|
5
|
+
This module works with DaVinci Resolve UI layout preset FILES on disk:
|
|
6
|
+
- Listing the presets in Resolve's preset folder
|
|
8
7
|
- Exporting/importing preset files
|
|
9
|
-
-
|
|
8
|
+
- Deleting a preset file
|
|
9
|
+
|
|
10
|
+
Saving and loading the live layout are not done here. They go through
|
|
11
|
+
Resolve.SaveLayoutPreset / LoadLayoutPreset, which the granular tools in
|
|
12
|
+
src/granular/resolve_control.py call directly. Two earlier helpers here tried
|
|
13
|
+
Resolve.GetUIManager().SaveUILayout / LoadUILayout instead; neither method
|
|
14
|
+
exists on any build measured (api_truth 'Resolve.GetUIManager ...'), nothing
|
|
15
|
+
called them, and they were removed in v4.8.26.
|
|
10
16
|
"""
|
|
11
17
|
|
|
12
18
|
import os
|
|
@@ -137,71 +143,6 @@ def list_layout_presets(layout_type: str = "ui") -> List[Dict[str, Any]]:
|
|
|
137
143
|
|
|
138
144
|
return presets
|
|
139
145
|
|
|
140
|
-
def save_layout_preset(resolve_obj, preset_name: str, layout_type: str = "ui") -> bool:
|
|
141
|
-
"""
|
|
142
|
-
Save the current layout as a preset.
|
|
143
|
-
|
|
144
|
-
Args:
|
|
145
|
-
resolve_obj: DaVinci Resolve API object
|
|
146
|
-
preset_name: Name for the saved preset
|
|
147
|
-
layout_type: Type of layout to save ('ui', 'window', 'workspace')
|
|
148
|
-
|
|
149
|
-
Returns:
|
|
150
|
-
True if successful, False otherwise
|
|
151
|
-
"""
|
|
152
|
-
try:
|
|
153
|
-
# Ensure preset name has no spaces or special characters
|
|
154
|
-
safe_name = preset_name.replace(" ", "_").replace("/", "_").replace("\\", "_")
|
|
155
|
-
|
|
156
|
-
# Different layout types have different save methods
|
|
157
|
-
if layout_type.lower() == "ui":
|
|
158
|
-
# For UI layouts, use the UI Manager
|
|
159
|
-
ui_manager = resolve_obj.GetUIManager()
|
|
160
|
-
if not ui_manager:
|
|
161
|
-
logger.error("Failed to get UI Manager")
|
|
162
|
-
return False
|
|
163
|
-
|
|
164
|
-
# Save the current UI layout
|
|
165
|
-
return ui_manager.SaveUILayout(safe_name)
|
|
166
|
-
else:
|
|
167
|
-
# Other layout types would be handled here
|
|
168
|
-
logger.error(f"Unsupported layout type: {layout_type}")
|
|
169
|
-
return False
|
|
170
|
-
except Exception as e:
|
|
171
|
-
logger.error(f"Error saving layout preset: {str(e)}")
|
|
172
|
-
return False
|
|
173
|
-
|
|
174
|
-
def load_layout_preset(resolve_obj, preset_name: str, layout_type: str = "ui") -> bool:
|
|
175
|
-
"""
|
|
176
|
-
Load a layout preset.
|
|
177
|
-
|
|
178
|
-
Args:
|
|
179
|
-
resolve_obj: DaVinci Resolve API object
|
|
180
|
-
preset_name: Name of the preset to load
|
|
181
|
-
layout_type: Type of layout to load ('ui', 'window', 'workspace')
|
|
182
|
-
|
|
183
|
-
Returns:
|
|
184
|
-
True if successful, False otherwise
|
|
185
|
-
"""
|
|
186
|
-
try:
|
|
187
|
-
# Different layout types have different load methods
|
|
188
|
-
if layout_type.lower() == "ui":
|
|
189
|
-
# For UI layouts, use the UI Manager
|
|
190
|
-
ui_manager = resolve_obj.GetUIManager()
|
|
191
|
-
if not ui_manager:
|
|
192
|
-
logger.error("Failed to get UI Manager")
|
|
193
|
-
return False
|
|
194
|
-
|
|
195
|
-
# Load the specified UI layout
|
|
196
|
-
return ui_manager.LoadUILayout(preset_name)
|
|
197
|
-
else:
|
|
198
|
-
# Other layout types would be handled here
|
|
199
|
-
logger.error(f"Unsupported layout type: {layout_type}")
|
|
200
|
-
return False
|
|
201
|
-
except Exception as e:
|
|
202
|
-
logger.error(f"Error loading layout preset: {str(e)}")
|
|
203
|
-
return False
|
|
204
|
-
|
|
205
146
|
def export_layout_preset(preset_name: str, export_path: str, layout_type: str = "ui") -> bool:
|
|
206
147
|
"""
|
|
207
148
|
Export a layout preset to a file.
|