davinci-resolve-mcp 2.87.0 → 2.87.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/AGENTS.md +7 -1
- package/CHANGELOG.md +65 -0
- package/README.md +1 -1
- package/README.zh-CN.md +2 -2
- package/docs/reference/api-coverage.md +3 -3
- package/docs/reference/api-limitations.md +2 -2
- package/install.py +1 -1
- package/package.json +1 -1
- package/src/granular/common.py +1 -1
- package/src/server.py +74 -8
- package/src/utils/api_truth.py +9 -2
package/AGENTS.md
CHANGED
|
@@ -73,7 +73,13 @@ semantics.
|
|
|
73
73
|
`docs/reference/api-limitations.md` — when you document a new Resolve API
|
|
74
74
|
limitation, add a `submit`-tagged entry to `src/utils/api_truth.py` and
|
|
75
75
|
regenerate with `scripts/gen_api_limitations.py` (a drift guard enforces it)
|
|
76
|
-
- Bundled Resolve API text: `docs/reference/resolve_scripting_api.txt`
|
|
76
|
+
- Bundled Resolve API text: `docs/reference/resolve_scripting_api.txt` — refresh
|
|
77
|
+
it by copying the shipped `Developer/Scripting/README.txt` over it wholesale,
|
|
78
|
+
never by hand-adding the lines you already know about. The file's only value
|
|
79
|
+
is that a diff against it is trustworthy; a hand-patch carries a newer
|
|
80
|
+
`Last Updated:` header while still hiding everything you did not know to look
|
|
81
|
+
for. That is exactly how it sat eight weeks stale at 26 May 2026 while ten
|
|
82
|
+
documented 21.0.4 methods went unwired (PR #139)
|
|
77
83
|
|
|
78
84
|
## Key Paths
|
|
79
85
|
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,71 @@
|
|
|
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.87.2
|
|
6
|
+
|
|
7
|
+
A refused `SetSetting` now says why, when the ledger already knows. Issue #141,
|
|
8
|
+
reported by @jus-kim.
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **`project_settings set_setting` returned a bare `{"success": false}` for a
|
|
13
|
+
key that can never be written.** `Project.SetSetting('timelinePlaybackFrameRate')`
|
|
14
|
+
refuses every value form, before and after a timeline exists — measured in
|
|
15
|
+
PR #99, written into `api_truth`, published in `api-limitations.md`, and
|
|
16
|
+
invisible at the one moment it mattered. A bare `false` reads as *your value
|
|
17
|
+
was wrong*, which sends a caller into retrying string, int, and float for a
|
|
18
|
+
key with no writable path at all. A refusal now carries the ledger entry for
|
|
19
|
+
that key: what is really happening, and the UI step that is the way through.
|
|
20
|
+
`timeline set_setting` gets the same treatment.
|
|
21
|
+
- The match is deliberately narrow. It requires the exact quoted key on the
|
|
22
|
+
right object — `Project.SetSetting('x')` will not be handed to a `Timeline`
|
|
23
|
+
refusal, and a substring like `timeline` will not collect the
|
|
24
|
+
`timelinePlaybackFrameRate` entry. An unmeasured refusal stays bare, because
|
|
25
|
+
inventing an explanation for a failure nobody measured is the thing this
|
|
26
|
+
ledger exists to prevent. The write is always attempted first, so a key that
|
|
27
|
+
starts working in a later build reports plain success.
|
|
28
|
+
|
|
29
|
+
### Documentation
|
|
30
|
+
|
|
31
|
+
- **The `timelinePlaybackFrameRate` ledger entry carries the second report.**
|
|
32
|
+
Issue #141 confirms it independently on **Resolve 20.2**, against a freshly
|
|
33
|
+
created project whose timeline rate already read 60 — so a matching
|
|
34
|
+
`timelineFrameRate` does not unlock the write, which the PR #99 measurement
|
|
35
|
+
alone left open. The reporter's workaround is now recorded too: for repeat
|
|
36
|
+
setups, duplicate a project that already carries the wanted playback rate
|
|
37
|
+
rather than creating one and trying to write it.
|
|
38
|
+
|
|
39
|
+
## What's New in v2.87.1
|
|
40
|
+
|
|
41
|
+
Follow-up evidence from @legionsound on PR #139, plus the process fix for the
|
|
42
|
+
tooling failure that PR exposed.
|
|
43
|
+
|
|
44
|
+
### Changed
|
|
45
|
+
|
|
46
|
+
- **`import_user_preferences_preset` now says what the preset ended up called.**
|
|
47
|
+
The no-name path was the one branch in v2.87.0 with nothing behind it — it
|
|
48
|
+
calls the single-argument binding, which would have been a `TypeError` rather
|
|
49
|
+
than a graceful failure if the binding wanted two positionals. Round-tripped
|
|
50
|
+
on Studio 21.0.4.5 (export → delete → import both ways): the single-arg form
|
|
51
|
+
returns `True`, and the imported preset is **named after the file**. The
|
|
52
|
+
answer now carries that, so a caller who passed no name knows to read the
|
|
53
|
+
name back with `list_user_preferences_presets` instead of guessing.
|
|
54
|
+
|
|
55
|
+
### Documentation
|
|
56
|
+
|
|
57
|
+
- **`api-coverage.md`: the import/export rows carry the round-trip**, not
|
|
58
|
+
`dir()` membership. Still 🔬 and still 23 untested of 361 — the counting
|
|
59
|
+
convention is unchanged, because none of this was executed here. What changed
|
|
60
|
+
is the strength of the contributor's evidence behind two of the rows.
|
|
61
|
+
- **`AGENTS.md` now states how the API snapshot must be refreshed:** copy the
|
|
62
|
+
shipped `Developer/Scripting/README.txt` over it wholesale, never hand-add the
|
|
63
|
+
lines you already know about. A hand-patch carries a newer `Last Updated:`
|
|
64
|
+
header while hiding everything you did not know to look for — which is
|
|
65
|
+
precisely how the file sat eight weeks stale at 26 May 2026 while ten
|
|
66
|
+
documented 21.0.4 methods went unwired. The v2.87.0 snapshot was verified as a
|
|
67
|
+
wholesale replace (md5 `d732b3f6c1da08dc516bc8f80c5acd92`, byte-identical to
|
|
68
|
+
the 21.0.4.5 shipped file per the contributor).
|
|
69
|
+
|
|
5
70
|
## What's New in v2.87.0
|
|
6
71
|
|
|
7
72
|
The ten Resolve 21.0.4 scripting surfaces that the 24 Jul 2026 README refresh
|
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
|
-
> 本翻译对应 v2.87.
|
|
15
|
+
> 本翻译对应 v2.87.2 版 README。如与英文原版有出入,以 [英文原版](README.md) 为准。
|
|
16
16
|
|
|
17
17
|
一个 Model Context Protocol (MCP) 服务器,让 AI 助手通过官方脚本 API 控制 DaVinci Resolve Studio(达芬奇)。它提供完整的 API 覆盖,外加带护栏的工作流助手,涵盖剪辑、媒体池整理、渲染设置、审阅标记、调色、Fusion、Fairlight、项目生命周期任务、扩展开发,以及不碰源媒体的媒体分析。
|
|
18
18
|
|
|
@@ -157,7 +157,7 @@ checkable rather than asserted.
|
|
|
157
157
|
| `Resolve.SaveUserPreferencesPreset` | Requires a Resolve 21.0.4 build; the validation machine runs Studio 19.1.3 and free 21.0.3 | Yes |
|
|
158
158
|
| `Resolve.LoadUserPreferencesPreset` | Deliberately not executed even by the reporter: swaps the user's global preferences session-wide | No |
|
|
159
159
|
| `Resolve.DeleteUserPreferencesPreset` | Requires a Resolve 21.0.4 build; the validation machine runs Studio 19.1.3 and free 21.0.3 | Yes |
|
|
160
|
-
| `Resolve.ImportUserPreferencesPreset` | Requires a Resolve 21.0.4 build
|
|
160
|
+
| `Resolve.ImportUserPreferencesPreset` | Requires a Resolve 21.0.4 build; round-tripped by the reporter on 21.0.4.5 in both call shapes (PR #139), not here | Yes |
|
|
161
161
|
| `Resolve.ExportUserPreferencesPreset` | Requires a Resolve 21.0.4 build; the validation machine runs Studio 19.1.3 and free 21.0.3 | Yes |
|
|
162
162
|
| `PM.GetProjectAttributesInCurrentFolder` | Requires a Resolve 21.0.4 build; the validation machine runs Studio 19.1.3 and free 21.0.3 | Yes |
|
|
163
163
|
|
|
@@ -218,8 +218,8 @@ Every method in the DaVinci Resolve Scripting API and its test status. Methods a
|
|
|
218
218
|
| 28 | `SaveUserPreferencesPreset(presetName)` | 🔬 | Resolve 21.0.4. Not executed here; part of the issue #138 round-trip on 21.0.4.5 |
|
|
219
219
|
| 29 | `LoadUserPreferencesPreset(presetName)` | 🔬 | Resolve 21.0.4. `dir()`-present on 21.0.4.5; **not executed** even by the reporter — swaps the user's global preferences session-wide (issue #138) |
|
|
220
220
|
| 30 | `DeleteUserPreferencesPreset(presetName)` | 🔬 | Resolve 21.0.4. Not executed here; part of the issue #138 round-trip on 21.0.4.5 |
|
|
221
|
-
| 31 | `ImportUserPreferencesPreset(presetFilePath, presetName)` | 🔬 | Resolve 21.0.4.
|
|
222
|
-
| 32 | `ExportUserPreferencesPreset(presetName, exportPath)` | 🔬 | Resolve 21.0.4.
|
|
221
|
+
| 31 | `ImportUserPreferencesPreset(presetFilePath, presetName)` | 🔬 | Resolve 21.0.4. Not executed here; reporter's export→delete→import round-trip on 21.0.4.5 (PR #139) covers **both** call shapes — single-arg returns `True` rather than a `TypeError`, and the imported preset is then named after the file; the two-arg form takes the given name. Per the README the imported preset is **not** auto-loaded, and the round-trip did not load either import |
|
|
222
|
+
| 32 | `ExportUserPreferencesPreset(presetName, exportPath)` | 🔬 | Resolve 21.0.4. Not executed here; reported writing the file at the given path with no extension appended, on 21.0.4.5 (PR #139) |
|
|
223
223
|
|
|
224
224
|
### ProjectManager
|
|
225
225
|
|
|
@@ -44,8 +44,8 @@ equivalent, blocking full automation.
|
|
|
44
44
|
### Project.SetSetting('timelinePlaybackFrameRate')
|
|
45
45
|
|
|
46
46
|
- **Object:** `Project`
|
|
47
|
-
- **Behavior:** Returns False for every value form tried (string, int, float), both before and after a timeline exists, so the playback frame rate cannot be set from the API at all. Reported by a community contributor against Resolve Studio while assembling a vertical timeline (PR #99).
|
|
48
|
-
- **Workaround / current handling:** Ask the user to set it in Project Settings > Master Settings > Playback frame rate as a SETUP step, before any timeline exists. Read it back to confirm; do not report it as set on the strength of the call alone.
|
|
47
|
+
- **Behavior:** Returns False for every value form tried (string, int, float), both before and after a timeline exists, so the playback frame rate cannot be set from the API at all. Reported by a community contributor against Resolve Studio while assembling a vertical timeline (PR #99), and independently on Resolve 20.2 against a freshly created project whose timeline rate already read 60 (issue #141) — so a matching timelineFrameRate does not unlock the write.
|
|
48
|
+
- **Workaround / current handling:** Ask the user to set it in Project Settings > Master Settings > Playback frame rate as a SETUP step, before any timeline exists. Read it back to confirm; do not report it as set on the strength of the call alone. The issue #141 reporter's workaround is worth passing on for repeat setups: duplicate a project that already carries the wanted playback rate rather than creating one and trying to write it.
|
|
49
49
|
- **Tags:** project-settings, silent-failure, timeline
|
|
50
50
|
|
|
51
51
|
### Timeline.GetCurrentClipThumbnailImage (Color page only)
|
package/install.py
CHANGED
|
@@ -36,7 +36,7 @@ from src.utils.update_check import (
|
|
|
36
36
|
|
|
37
37
|
# ─── Version ──────────────────────────────────────────────────────────────────
|
|
38
38
|
|
|
39
|
-
VERSION = "2.87.
|
|
39
|
+
VERSION = "2.87.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
package/src/granular/common.py
CHANGED
|
@@ -85,7 +85,7 @@ if not logging.getLogger().handlers:
|
|
|
85
85
|
handlers=[logging.StreamHandler()],
|
|
86
86
|
)
|
|
87
87
|
|
|
88
|
-
VERSION = "2.87.
|
|
88
|
+
VERSION = "2.87.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.87.
|
|
14
|
+
VERSION = "2.87.2"
|
|
15
15
|
|
|
16
16
|
import base64
|
|
17
17
|
import os
|
|
@@ -13589,7 +13589,10 @@ def resolve_control(action: str, params: Optional[Dict[str, Any]] = None) -> Dic
|
|
|
13589
13589
|
delete_user_preferences_preset(name) -> {success} — Resolve 21.0.4+
|
|
13590
13590
|
import_user_preferences_preset(path, name?) -> {success} — Resolve 21.0.4+.
|
|
13591
13591
|
The imported preset is NOT auto-loaded; follow with
|
|
13592
|
-
load_user_preferences_preset to activate it.
|
|
13592
|
+
load_user_preferences_preset to activate it. Omitting name is safe
|
|
13593
|
+
(reported on 21.0.4.5: single-arg returns True, not a TypeError) and
|
|
13594
|
+
the preset is then named after the file, so pass name when the preset
|
|
13595
|
+
should be called something the filename does not say.
|
|
13593
13596
|
export_user_preferences_preset(name, path) -> {success} — Resolve 21.0.4+
|
|
13594
13597
|
open_control_panel(port?, host?, open_browser?) -> {success, url, pid, port, status}
|
|
13595
13598
|
— Launches the analysis control panel (src/analysis_dashboard.py) as a background process.
|
|
@@ -13862,8 +13865,11 @@ def resolve_control(action: str, params: Optional[Dict[str, Any]] = None) -> Dic
|
|
|
13862
13865
|
ok = bool(r.ImportUserPreferencesPreset(p["path"], p["name"]))
|
|
13863
13866
|
else:
|
|
13864
13867
|
ok = bool(r.ImportUserPreferencesPreset(p["path"]))
|
|
13865
|
-
|
|
13866
|
-
|
|
13868
|
+
note = "The imported preset is not auto-loaded; use load_user_preferences_preset to activate it."
|
|
13869
|
+
if not p.get("name"):
|
|
13870
|
+
note += (" No name was given, so the preset takes its name from the file — "
|
|
13871
|
+
"list_user_preferences_presets to read it back.")
|
|
13872
|
+
return {"success": ok, "note": note}
|
|
13867
13873
|
elif action == "export_user_preferences_preset":
|
|
13868
13874
|
missing = _requires_method(r, "ExportUserPreferencesPreset", "21.0.4")
|
|
13869
13875
|
if missing:
|
|
@@ -16011,6 +16017,37 @@ def project_manager_database(action: str, params: Optional[Dict[str, Any]] = Non
|
|
|
16011
16017
|
# TOOL 8: project_settings
|
|
16012
16018
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
16013
16019
|
|
|
16020
|
+
def _setting_limitation(name: Any, obj: str = "Project") -> Optional[Dict[str, Any]]:
|
|
16021
|
+
"""The api_truth entry for a settings key on `obj`, when one exists.
|
|
16022
|
+
|
|
16023
|
+
`SetSetting` reports a refusal as a bare `False` with no reason, and for
|
|
16024
|
+
several keys this repo has already measured the reason and written it down —
|
|
16025
|
+
`timelinePlaybackFrameRate` returns False for every value form, before and
|
|
16026
|
+
after a timeline exists (issue #141, PR #99). A caller who gets
|
|
16027
|
+
`{"success": false}` has no way to tell "you passed a bad value" from "this
|
|
16028
|
+
key cannot be written from the API at all", and the second one is a
|
|
16029
|
+
different task: it has to go to the user as a UI step.
|
|
16030
|
+
|
|
16031
|
+
Matched narrowly on purpose. The entry must name this exact key *and* be
|
|
16032
|
+
`obj.SetSetting`, because attaching an unrelated explanation to a failure is
|
|
16033
|
+
worse than attaching none — it reads as a diagnosis. `Project` and
|
|
16034
|
+
`Timeline` both have a `SetSetting` and their keys overlap by name, so the
|
|
16035
|
+
object is part of the match rather than assumed.
|
|
16036
|
+
"""
|
|
16037
|
+
if not isinstance(name, str) or not name:
|
|
16038
|
+
return None
|
|
16039
|
+
prefix = f"{obj}.SetSetting"
|
|
16040
|
+
quoted = f"'{name}'"
|
|
16041
|
+
for entry in lookup_api_truth(name):
|
|
16042
|
+
symbol = entry.get("symbol", "")
|
|
16043
|
+
# The quoted form is what makes this an exact key match: `name in
|
|
16044
|
+
# symbol` would hand the timelinePlaybackFrameRate entry to anything
|
|
16045
|
+
# that is a substring of it, "timeline" included.
|
|
16046
|
+
if symbol.startswith(prefix) and quoted in symbol:
|
|
16047
|
+
return entry
|
|
16048
|
+
return None
|
|
16049
|
+
|
|
16050
|
+
|
|
16014
16051
|
@mcp.tool()
|
|
16015
16052
|
@_guard_missing_params
|
|
16016
16053
|
def project_settings(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
|
@@ -16020,7 +16057,9 @@ def project_settings(action: str, params: Optional[Dict[str, Any]] = None) -> Di
|
|
|
16020
16057
|
get_name() -> {name}
|
|
16021
16058
|
set_name(name) -> {success}
|
|
16022
16059
|
get_setting(name?) -> {settings} — omit name for all settings
|
|
16023
|
-
set_setting(name, value) -> {success}
|
|
16060
|
+
set_setting(name, value) -> {success, known_limitation?}
|
|
16061
|
+
A refusal carries the api_truth entry for that key when one exists —
|
|
16062
|
+
several settings cannot be written from the API at all.
|
|
16024
16063
|
get_unique_id() -> {id}
|
|
16025
16064
|
get_presets() -> {presets}
|
|
16026
16065
|
set_preset(name) -> {success}
|
|
@@ -16056,7 +16095,20 @@ def project_settings(action: str, params: Optional[Dict[str, Any]] = None) -> Di
|
|
|
16056
16095
|
return _err("set_setting requires name")
|
|
16057
16096
|
if "value" not in p:
|
|
16058
16097
|
return _err("set_setting requires value")
|
|
16059
|
-
|
|
16098
|
+
if bool(proj.SetSetting(p["name"], p["value"])):
|
|
16099
|
+
return {"success": True}
|
|
16100
|
+
known = _setting_limitation(p["name"])
|
|
16101
|
+
if not known:
|
|
16102
|
+
return {"success": False}
|
|
16103
|
+
return {
|
|
16104
|
+
"success": False,
|
|
16105
|
+
"known_limitation": {
|
|
16106
|
+
"symbol": known.get("symbol"),
|
|
16107
|
+
"reality": known.get("reality"),
|
|
16108
|
+
"recommended": known.get("recommended"),
|
|
16109
|
+
"ledger_verified_on": _API_TRUTH_VERIFIED_ON,
|
|
16110
|
+
},
|
|
16111
|
+
}
|
|
16060
16112
|
elif action == "get_unique_id":
|
|
16061
16113
|
return {"id": proj.GetUniqueId()}
|
|
16062
16114
|
elif action == "get_presets":
|
|
@@ -21396,7 +21448,8 @@ def timeline(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[str,
|
|
|
21396
21448
|
export(path, type, subtype?, background?) -> {success | job_id} — type: AAF, EDL, FCPXML, etc.
|
|
21397
21449
|
UNSAFE. No path sandboxing. Prefer export_timeline_checked.
|
|
21398
21450
|
get_setting(name?) -> {settings}
|
|
21399
|
-
set_setting(name, value) -> {success}
|
|
21451
|
+
set_setting(name, value) -> {success, known_limitation?}
|
|
21452
|
+
A refusal carries the api_truth entry for that key when one exists.
|
|
21400
21453
|
insert_generator(name) -> {success}
|
|
21401
21454
|
insert_fusion_generator(name) -> {success}
|
|
21402
21455
|
insert_fusion_composition() -> {success}
|
|
@@ -21756,7 +21809,20 @@ def timeline(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[str,
|
|
|
21756
21809
|
elif action == "get_setting":
|
|
21757
21810
|
return {"settings": _ser(tl.GetSetting(p.get("name", "")))}
|
|
21758
21811
|
elif action == "set_setting":
|
|
21759
|
-
|
|
21812
|
+
if bool(tl.SetSetting(p["name"], p["value"])):
|
|
21813
|
+
return {"success": True}
|
|
21814
|
+
known = _setting_limitation(p["name"], obj="Timeline")
|
|
21815
|
+
if not known:
|
|
21816
|
+
return {"success": False}
|
|
21817
|
+
return {
|
|
21818
|
+
"success": False,
|
|
21819
|
+
"known_limitation": {
|
|
21820
|
+
"symbol": known.get("symbol"),
|
|
21821
|
+
"reality": known.get("reality"),
|
|
21822
|
+
"recommended": known.get("recommended"),
|
|
21823
|
+
"ledger_verified_on": _API_TRUTH_VERIFIED_ON,
|
|
21824
|
+
},
|
|
21825
|
+
}
|
|
21760
21826
|
elif action == "insert_generator":
|
|
21761
21827
|
r = tl.InsertGeneratorIntoTimeline(p["name"])
|
|
21762
21828
|
return _ok() if r else _err("Failed to insert generator")
|
package/src/utils/api_truth.py
CHANGED
|
@@ -124,11 +124,18 @@ API_TRUTH: List[Dict[str, Any]] = [
|
|
|
124
124
|
"float), both before and after a timeline exists, so the "
|
|
125
125
|
"playback frame rate cannot be set from the API at all. "
|
|
126
126
|
"Reported by a community contributor against Resolve Studio "
|
|
127
|
-
"while assembling a vertical timeline (PR #99)
|
|
127
|
+
"while assembling a vertical timeline (PR #99), and "
|
|
128
|
+
"independently on Resolve 20.2 against a freshly created "
|
|
129
|
+
"project whose timeline rate already read 60 (issue #141) — "
|
|
130
|
+
"so a matching timelineFrameRate does not unlock the write.",
|
|
128
131
|
"recommended": "Ask the user to set it in Project Settings > Master "
|
|
129
132
|
"Settings > Playback frame rate as a SETUP step, before "
|
|
130
133
|
"any timeline exists. Read it back to confirm; do not "
|
|
131
|
-
"report it as set on the strength of the call alone."
|
|
134
|
+
"report it as set on the strength of the call alone. The "
|
|
135
|
+
"issue #141 reporter's workaround is worth passing on "
|
|
136
|
+
"for repeat setups: duplicate a project that already "
|
|
137
|
+
"carries the wanted playback rate rather than creating "
|
|
138
|
+
"one and trying to write it.",
|
|
132
139
|
"tags": ["project-settings", "silent-failure", "timeline"],
|
|
133
140
|
"submit": "missing",
|
|
134
141
|
},
|