davinci-resolve-mcp 2.33.0 → 2.33.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 +29 -0
- package/README.md +1 -1
- package/docs/SKILL.md +8 -2
- package/docs/authoring/setting-files/README.md +152 -0
- package/docs/authoring/setting-files/references/category-patterns.md +780 -0
- package/docs/authoring/setting-files/references/controls.md +259 -0
- package/docs/authoring/setting-files/references/format.md +568 -0
- package/docs/authoring/setting-files/references/gotchas.md +168 -0
- package/docs/authoring/setting-files/references/thumbnails.md +106 -0
- package/install.py +1 -1
- package/package.json +1 -1
- package/src/granular/common.py +1 -1
- package/src/server.py +67 -3
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# Gotchas
|
|
2
|
+
|
|
3
|
+
Hard-won rules that are not obvious from the stock files. Check against this list whenever an effect "loads but doesn't work."
|
|
4
|
+
|
|
5
|
+
## 1. `ordered()` vs `{}` — Order Actually Matters
|
|
6
|
+
|
|
7
|
+
`Inputs = ordered() { ... }` preserves the order you wrote. `Inputs = { ... }` does **not** — Fusion may re-order, which scrambles your inspector layout, breaks `ControlGroup` pairings (consecutive grouping stops working), and shuffles your Page tabs. Always use `ordered()` for:
|
|
8
|
+
|
|
9
|
+
- `GroupOperator.Inputs`
|
|
10
|
+
- `GroupOperator.Tools` (order determines evaluation order for some purposes)
|
|
11
|
+
- `UserControls`
|
|
12
|
+
|
|
13
|
+
The internal `Inputs = { ... }` of individual nodes can be unordered — node params are keyed by name.
|
|
14
|
+
|
|
15
|
+
## 2. `ActiveTool` Must Match a Top-Level Key
|
|
16
|
+
|
|
17
|
+
`ActiveTool = "MyEffect"` has to name an entry in the outermost `Tools` list. If you rename the GroupOperator without updating `ActiveTool`, the file loads but Resolve doesn't know which node is the "primary" — some categories (titles especially) misbehave.
|
|
18
|
+
|
|
19
|
+
If there's exactly one top-level node, `ActiveTool` is effectively optional — but set it anyway for clarity.
|
|
20
|
+
|
|
21
|
+
## 3. `SourceOp` References Must Resolve
|
|
22
|
+
|
|
23
|
+
Every `SourceOp = "Name"` has to name a node in the *same* `Tools` list. A reference from an `InstanceInput` points into the GroupOperator's internal `Tools`. A reference from inside one internal node's `Input` points to a sibling in that same internal `Tools`. You cannot reach across groups.
|
|
24
|
+
|
|
25
|
+
Typos here cause silent failures: the effect loads, the control exists, but it doesn't affect anything. When debugging, grep for all `SourceOp = "..."` values and verify each one is defined.
|
|
26
|
+
|
|
27
|
+
## 4. `Source` Names Are Case-Sensitive and Exact
|
|
28
|
+
|
|
29
|
+
`XBlurSize` is not the same as `xBlurSize` or `XBlursize`. When you're not sure of the exact parameter name, build the effect in Fusion, save it, and open the `.setting` file — it will contain the authoritative names.
|
|
30
|
+
|
|
31
|
+
## 5. Transition Progress is Automatic — Don't Look for a "Progress" Parameter
|
|
32
|
+
|
|
33
|
+
There is no `Progress` or `Time` parameter on the transition GroupOperator. Fusion evaluates internal `LUTLookup` nodes with `Source = FuID { "Duration" }` across the clip duration and produces a 0→1 ramp for free. Use that ramp to drive any animated parameter.
|
|
34
|
+
|
|
35
|
+
If your transition "plays" in one frame or doesn't animate at all, you probably fed a static value into `Dissolve.Mix` instead of a `LUTLookup`.
|
|
36
|
+
|
|
37
|
+
## 6. Titles Need `KeyStretcher` or They Don't Stretch
|
|
38
|
+
|
|
39
|
+
A TextPlus with baked-in `BezierSpline` animation has a fixed length in frames (e.g., the animation runs from frame 1 to frame 120). If you expose its `Output` directly as `MainOutput1`, the title shows its animation in the first 120 frames regardless of clip length — a 5-second title clip will play the animation for the first 4 seconds and freeze for the last 1 second.
|
|
40
|
+
|
|
41
|
+
Fix: wrap the output in a `KeyStretcher` and output `Source = "Result"`. Set `SourceEnd` to the animation's native length and `StretchStart` / `StretchEnd` to the range you want stretched.
|
|
42
|
+
|
|
43
|
+
## 7. `MainOutput1.Source` for Titles is `"Result"`, Not `"Output"`
|
|
44
|
+
|
|
45
|
+
Easy to miss. If your title renders but the animation doesn't stretch properly, check whether you pulled `Output` (unstretched) or `Result` (stretched) from the `KeyStretcher`.
|
|
46
|
+
|
|
47
|
+
## 8. `Width`, `Height`, `UseFrameFormatSettings` on Generator Nodes
|
|
48
|
+
|
|
49
|
+
Nodes that generate pixels (`Background`, `FastNoise`, `TextPlus`, `RectangleMask`, `EllipseMask`) have a `Width` / `Height` pair that defines the canvas size. For Edit-page use, always set `UseFrameFormatSettings = Input { Value = 1 }` so the node inherits the timeline's resolution instead of being locked to 1920×1080. Stock effects all do this; skipping it makes your effect break on 4K timelines.
|
|
50
|
+
|
|
51
|
+
## 9. Namespaced Parameter Keys Need Quoting
|
|
52
|
+
|
|
53
|
+
Any parameter with a `.` in its name must be wrapped in brackets and quoted:
|
|
54
|
+
|
|
55
|
+
```lua
|
|
56
|
+
-- wrong
|
|
57
|
+
Gamut.SLogVersion = Input { Value = FuID { "SLog2" } },
|
|
58
|
+
|
|
59
|
+
-- right
|
|
60
|
+
["Gamut.SLogVersion"] = Input { Value = FuID { "SLog2" } },
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
This bites hardest on 3D nodes (`Transform3DOp.Translate.X`, `Diffuse.Color.Red`, `SurfaceCylinderInputs.Radius`) and ofx nodes.
|
|
64
|
+
|
|
65
|
+
## 10. `FuID` Enum Values Must Be Exact
|
|
66
|
+
|
|
67
|
+
`FuID { "Fast Gaussian" }` is one of several possible enum values for `Blur.Filter`. If you guess the wrong name, the file loads but the parameter falls back to the default and silently ignores you. Don't guess — inspect a working composition or grep the stock files for the node type you're using to find valid values.
|
|
68
|
+
|
|
69
|
+
## 11. Don't Reuse Internal Node Names Across Groups
|
|
70
|
+
|
|
71
|
+
Each `GroupOperator` has its own namespace for internal `Tools`. Within a single group, names must be unique. If you want two `Blur` nodes, call them `Blur1` and `Blur2`. Fusion appends `_1`, `_2` when you create duplicates in the UI; in hand-authored files, just number them yourself.
|
|
72
|
+
|
|
73
|
+
Name collisions produce "last one wins" behavior: only the last definition is honored, and any `SourceOp` references resolve to that last one.
|
|
74
|
+
|
|
75
|
+
## 12. `Instance_NodeName` is a Magic Pattern
|
|
76
|
+
|
|
77
|
+
When stock effects need the same internal node available at two points in the graph with slightly different routing, they use the `Instance_` prefix:
|
|
78
|
+
|
|
79
|
+
```lua
|
|
80
|
+
Rectangle1 = RectangleMask { ... actual params ... },
|
|
81
|
+
|
|
82
|
+
Instance_Rectangle1 = RectangleMask {
|
|
83
|
+
SourceOp = "Rectangle1", -- creates an instance that shares Rectangle1's params
|
|
84
|
+
Inputs = { ... different routing ... },
|
|
85
|
+
},
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
An `Instance_X` with `SourceOp = "X"` at the node level (not inside an `Input`) creates a live clone. Edits to `Rectangle1` propagate to `Instance_Rectangle1`. Use this when you need the same mask / text / curve to appear in two places in the graph.
|
|
89
|
+
|
|
90
|
+
## 13. `CustomData` is Optional — Strip It
|
|
91
|
+
|
|
92
|
+
Stock effects carry large `CustomData.Settings` blocks because Fusion saves the full UI state of preset variations into them. You can delete the entire `CustomData = {...}` block from any node you're hand-editing; the effect will still load and work. The blocks just bloat the file and obscure the real logic.
|
|
93
|
+
|
|
94
|
+
## 14. Restart Resolve Picks Up New Templates
|
|
95
|
+
|
|
96
|
+
Resolve indexes the Templates folder at launch. Dropping a new `.setting` file in while Resolve is running will NOT make it appear in the Effects Library until you quit and relaunch. (Refreshing via right-click in the library works sometimes, not reliably.)
|
|
97
|
+
|
|
98
|
+
## 15. A Broken `.setting` Crashes the Category, Not Resolve
|
|
99
|
+
|
|
100
|
+
If you ship malformed Lua, Resolve skips the file and logs a warning. But a subtler problem — a file that parses but has a dangling `SourceOp` reference or a wrong `Source` name — can make an entire subfolder look empty in the Effects Library. When nothing shows up, move your new file out of the folder temporarily and check whether the rest of the category reappears.
|
|
101
|
+
|
|
102
|
+
## 16. `Dissolve.Map` is the Wipe Shape
|
|
103
|
+
|
|
104
|
+
For luma-ramp transitions (`Operation = FuID { "DFTLumaRamp" }`), `Dissolve.Map` takes an image input that defines the shape of the wipe. Bright areas reveal the foreground first, dark areas last. Generate the map with any node: `Background` with a gradient for a linear wipe, `FastNoise` for a dissolve, `RectangleMask` + `Transform` for a box wipe, etc.
|
|
105
|
+
|
|
106
|
+
## 17. `Fuse.Wireless` is a Carrier, Not a Real Node
|
|
107
|
+
|
|
108
|
+
`Fuse.Wireless` exists to host `UserControls` entries (buttons, labels, etc.) that don't belong to any real node. It has no pixel output. Don't try to use it in the render graph. Think of it as a "control panel node."
|
|
109
|
+
|
|
110
|
+
## 18. Files Loose in `Templates/Edit/` Silently Fall Through to the Fusion Library
|
|
111
|
+
|
|
112
|
+
The Edit-page Effects Library only indexes `.setting` files that live inside a **category subfolder**: `Templates/Edit/Effects/`, `Templates/Edit/Transitions/`, `Templates/Edit/Titles/`, `Templates/Edit/Generators/`. A file dumped directly into `Templates/Edit/` (no category subfolder) is invisible to the Edit page — but the Fusion-page library is laxer and picks it up anyway, so the effect *appears to install successfully*, just in the wrong UI.
|
|
113
|
+
|
|
114
|
+
Symptom: "I wrote an Edit effect, restarted Resolve, and it's showing up under the Fusion page's Effects Library instead of on the Edit page." Cause: you skipped the `<category>` folder. Always write to the full path including the subfolder:
|
|
115
|
+
|
|
116
|
+
```
|
|
117
|
+
%APPDATA%\Blackmagic Design\DaVinci Resolve\Support\Fusion\Templates\Edit\Effects\MyBlur.setting
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
not
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
%APPDATA%\Blackmagic Design\DaVinci Resolve\Support\Fusion\Templates\Edit\MyBlur.setting
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Same rule for Fusion macros: they need a category subfolder too (`Fusion/Tools/`, `Fusion/Backgrounds/`, etc.), not loose in `Fusion/`.
|
|
127
|
+
|
|
128
|
+
## 19. `.alut3` Files Are LUTs, Not Compositions
|
|
129
|
+
|
|
130
|
+
`Fusion/Looks/*.alut3` live in the same `Core Davinci Effects` folder structure but they're **plain-text 3D LUTs**, not `.setting` files. Header: `F5LT3\nSize: 33\nType: float32\n\n` followed by R G B triples. They install to the LUT folder and apply from the Color page. Do not try to edit them as Fusion comps.
|
|
131
|
+
|
|
132
|
+
## 20. `ControlGroup = N` Must Equal the Anchor Input's Number
|
|
133
|
+
|
|
134
|
+
Fusion collapses controls into one inspector row when they share a `ControlGroup` integer. That integer must equal the `InputN` number of the preceding label/anchor Input in the list — not a free-choice group ID. In `CCTV.setting`, `Font` (Input2) and `Style` (Input3) both carry `ControlGroup = 2` because they share a row anchored to `Input2`. In `Background Reveal Lower Third.setting`, `VerticalJustificationTop`, `VerticalJustificationCenter`, and `VerticalJustificationBottom` (Input7/8/9) all carry `ControlGroup = 6`, grouping them under the `Input6` row anchor. If you pick sequential integers starting from 1, grouping silently breaks: controls render on separate rows and multi-button radios disappear.
|
|
135
|
+
|
|
136
|
+
Rule: count from the top of your `Inputs = ordered() { ... }` block and use the anchor row's `InputN` number as the group ID for all siblings that share that row.
|
|
137
|
+
|
|
138
|
+
## 21. `Width` on `InstanceInput` Is Inspector Column Width, Not Mask Size
|
|
139
|
+
|
|
140
|
+
`Width` means two completely different things depending on context. On mask nodes (`EllipseMask`, `RectangleMask`) it is the physical mask size as a fraction of image width — e.g., `Ellipse1.Width = 0.5069` in `Binoculars.setting` is the mask diameter, not a UI hint. On an `InstanceInput`, `Width` is a fractional inspector column width (0.0–1.0): `DSLR.setting` exposes five timecode fields each with `Width = 0.5`, packing them two-per-row in the inspector. `Width = 1` (the default when omitted) gives the control its own full row.
|
|
141
|
+
|
|
142
|
+
A related field, `ICD_Width`, serves the same purpose inside `UserControls` button definitions. Authors packing button grids who accidentally copy a mask-scale value like `0.5069` produce weirdly-wide or full-row controls.
|
|
143
|
+
|
|
144
|
+
## 22. `GlobalOut` on Internal Generator Nodes — Set It, Keep It Consistent
|
|
145
|
+
|
|
146
|
+
`GlobalOut` on a generator or text node caps its internal frame timeline. Every `TextPlus`, `Background`, and `FastNoise` inside an effect with baked animation must carry `GlobalOut = Input { Value = N }`, and all nodes in the same group must share the same `N`. In `CCTV.setting`, every generator node carries `GlobalOut = 716`. If you omit `GlobalOut`, the node's internal timeline is unbounded and Resolve may render garbage or refuse to stretch the effect to clip length. If siblings in the same group have different `GlobalOut` values, animation phases drift out of sync.
|
|
147
|
+
|
|
148
|
+
## 23. Fusion Bare-Node Comps Don't Work as Edit Effects — Edit Requires `GroupOperator`
|
|
149
|
+
|
|
150
|
+
All `Edit/Effects/`, `Edit/Transitions/`, `Edit/Titles/`, and `Edit/Generators/` stock files wrap their graph in a `GroupOperator` with `InstanceInput`s. Several Fusion categories — especially `Fusion/Styled Text/` — ship raw top-level node trees with no `GroupOperator` and no exposed `InstanceInput`s at all. In those cases, users customize by opening the node graph on the Fusion page. On the Edit page this model fails silently: the effect loads, appears in the Effects Library, but has a blank inspector.
|
|
151
|
+
|
|
152
|
+
If you copy `Fusion/Styled Text/Alien.setting` (bare nodes, no GroupOperator) as a starting point for a new Edit effect, you get an unusable empty-inspector result. Always wrap Edit-page effects in a `GroupOperator`. `Edit/Effects/Binoculars.setting` is a clean example of the required structure.
|
|
153
|
+
|
|
154
|
+
## 24. Transition Easing Uses `LUTLookup` + `LUTBezier`, Not `BezierSpline`
|
|
155
|
+
|
|
156
|
+
For parameters that need to follow transition progress (0→1 over clip duration), the stock pattern is a `LUTLookup` node with its `Lookup` input connected to a sibling `LUTBezier`. `LUTBezier` uses `KeyColorSplines` with an outer `[0]` (single channel index) and inner float keys 0.0–1.0 (normalized position). This is completely different from `BezierSpline`, which uses `KeyFrames` with absolute frame numbers. Hand-authored effects that drive transition progress via a `BezierSpline` with frame keys play at wrong timing or freeze.
|
|
157
|
+
|
|
158
|
+
A bare `LUTLookup {}` with no `Lookup` input (as in `Cross Dissolve.setting`) automatically consumes the built-in `Duration` source for a default linear 0→1 ramp — leaving `Lookup` disconnected is valid shorthand for "linear progress." `Box Wipe.setting` shows the full explicit form with a custom `LUTBezier` easing curve.
|
|
159
|
+
|
|
160
|
+
## 25. `BTNCS_Execute` Lua References `InputN` Keys, Not Internal Node Parameter Names
|
|
161
|
+
|
|
162
|
+
`Fuse.Wireless` buttons use `BTNCS_Execute` to call `tool:SetInput(...)`. The first argument must be the exposed `InstanceInput` key name (e.g., `'Input9'`), not the underlying node's parameter name (e.g., `'Start'`). In `Box Wipe.setting`, direction-preset buttons call `tool:SetInput('Input9', {x,y})` to write the wipe start position — `Input9` is the `InstanceInput` key that wraps `Background2.Start`. Writing `tool:SetInput('Start', ...)` silently does nothing because `Start` isn't a top-level `GroupOperator` input. Authors naturally reach for the internal param name since that's what they see in the node graph.
|
|
163
|
+
|
|
164
|
+
## 26. OFX Nodes Require a Mandatory Boilerplate Input Block
|
|
165
|
+
|
|
166
|
+
Every `ofx.com.blackmagicdesign.resolvefx.*` node in the stock files carries seven required OFX overlay parameters: `blendGroup`, `blendIn`, `blend`, `ignoreContentShape`, `legacyIsProcessRGBOnly`, `refreshTrigger`, and `resolvefxVersion`. Both `Binoculars.setting` and `Digital Glitch.setting` include this block on every OFX node. Omitting any one of these causes the node to fall back to defaults that may differ from your intent, or the effect fails to load entirely. `resolvefxVersion` should match the version the effect was authored against (e.g., `"2.2"`); mismatches can cause parameter layout changes in newer Resolve versions.
|
|
167
|
+
|
|
168
|
+
Authors hand-writing OFX invocations rather than copying from a stock file invariably omit this block. Copy the seven-field block verbatim from any working stock OFX node.
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# Thumbnail Files
|
|
2
|
+
|
|
3
|
+
Each `.setting` (or `.alut3`) ships with a bundle of PNGs named after the effect file. They are not embedded — they live as sibling files in the same folder. Resolve indexes them at launch and uses them for the tiles in the Effects Library. If your effect is `My Cool Thing.setting`, every thumbnail starts with `My Cool Thing.` followed by a suffix that identifies the UI slot. Spaces in the base name are fine — Resolve matches on exact prefix.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Suffix Catalog
|
|
8
|
+
|
|
9
|
+
Every distinct suffix found in the stock library, with confirmed dimensions and purpose.
|
|
10
|
+
|
|
11
|
+
| Suffix | Dimensions (1×) | Dimensions (`@2x`) | UI slot |
|
|
12
|
+
|--------|-----------------|---------------------|---------|
|
|
13
|
+
| `.large.png` | 128 × 128 | 256 × 256 | Square tile in grid/icon view |
|
|
14
|
+
| `.large@2x.png` | — | 256 × 256 | HiDPI version of `.large.png` |
|
|
15
|
+
| `.small.png` | 30 × 30 | 60 × 60 | Small icon in list/compact view |
|
|
16
|
+
| `.small@2x.png` | — | 60 × 60 | HiDPI version of `.small.png` |
|
|
17
|
+
| `.small.active.png` | 30 × 30 | 60 × 60 | Button pressed/active state |
|
|
18
|
+
| `.small.active@2x.png` | — | 60 × 60 | HiDPI active state |
|
|
19
|
+
| `.small.hover.png` | 30 × 30 | 60 × 60 | Mouse-hover state |
|
|
20
|
+
| `.small.hover@2x.png` | — | 60 × 60 | HiDPI hover state |
|
|
21
|
+
| `.small.push.png` | 30 × 30 | 60 × 60 | Mouse-down state |
|
|
22
|
+
| `.small.push@2x.png` | — | 60 × 60 | HiDPI push state |
|
|
23
|
+
| `.wide.png` | 52 × 29 | 104 × 58 | Wide tile (transitions, titles) |
|
|
24
|
+
| `.wide@2x.png` | — | 104 × 58 | HiDPI wide tile |
|
|
25
|
+
| `.wide.active.png` | 52 × 29 | 52 × 29 | Active state for wide tile |
|
|
26
|
+
| `.wide.active@2x.png` | — | 104 × 58 | HiDPI wide active state |
|
|
27
|
+
| `.wide.hover.png` | 52 × 29 | 52 × 29 | Hover state for wide tile |
|
|
28
|
+
| `.wide.hover@2x.png` | — | 104 × 58 | HiDPI wide hover state |
|
|
29
|
+
| `.wide.push.png` | 52 × 29 | 52 × 29 | Push state for wide tile |
|
|
30
|
+
| `.wide.push@2x.png` | — | 104 × 58 | HiDPI wide push state |
|
|
31
|
+
|
|
32
|
+
The full 12-suffix set (`.large`, `.large@2x`, `.small`, `.small@2x`, `.small.active`, `.small.active@2x`, `.small.hover`, `.small.hover@2x`, `.small.push`, `.small.push@2x`, `.wide`, `.wide@2x`) is the standard complement for most categories. Categories that omit `.large.*` use only the wide/small subsets.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Per-Category Reference
|
|
37
|
+
|
|
38
|
+
All 14 stock categories verified against the `Core Davinci Effects/` library (2,897 PNG files total).
|
|
39
|
+
|
|
40
|
+
**Suffix set key:**
|
|
41
|
+
- **Full-12** = `.large`, `.large@2x`, `.small`, `.small@2x`, `.small.active`, `.small.active@2x`, `.small.hover`, `.small.hover@2x`, `.small.push`, `.small.push@2x`, `.wide`, `.wide@2x`
|
|
42
|
+
- **Wide-only** = `.wide`, `.wide@2x` (no large or small variants)
|
|
43
|
+
- **Partial** = some effects have the full set, some have a smaller subset — see notes
|
|
44
|
+
|
|
45
|
+
| # | Category | Baseline thumbnail set | Notes |
|
|
46
|
+
|---|----------|----------------------|-------|
|
|
47
|
+
| 1 | **Edit / Effects** | Full-12 | 22 of 23 effects ship the full-12. Both `_default.png` and `_default.wide.png` fallbacks present. |
|
|
48
|
+
| 2 | **Edit / Transitions** | Partial | 30 of 53 effects ship full-12 including `.large.*`. All 53 have `.wide` + `.wide@2x`. Most have `.small.*` button states. `.wide.active@2x`, `.wide.hover@2x`, `.wide.push@2x` exist but only on a single effect. |
|
|
49
|
+
| 3 | **Edit / Titles** | Partial | Baseline is wide-only (`.wide`, `.wide@2x`) across 90 of 97 titles. 18 titles additionally ship the full-12 (`.large.*` + `.small.*` button states). Do not assume titles are always wide-only. |
|
|
50
|
+
| 4 | **Edit / Generators** | Partial | Baseline is full-12. 14 of 16 effects have `.large.*`. All have `.wide.*`. Not all have `.small.*` button states. |
|
|
51
|
+
| 5 | **Fusion / Tools** | Full-12 | All Tools effects ship the complete 12-suffix set. |
|
|
52
|
+
| 6 | **Fusion / Backgrounds** | Full-12 | All Backgrounds effects ship the complete 12-suffix set. |
|
|
53
|
+
| 7 | **Fusion / Generators** | Full-12 | All Fusion Generators ship the complete 12-suffix set. |
|
|
54
|
+
| 8 | **Fusion / Particles** | Partial | Some effects ship full-12; some ship only `.large.*` + `.wide.*` without `.small.*` button states. |
|
|
55
|
+
| 9 | **Fusion / Shaders** | Full-12 | All Shaders ship the complete 12-suffix set. |
|
|
56
|
+
| 10 | **Fusion / Motion Graphics** | Full-12 | All Motion Graphics ship the complete 12-suffix set. |
|
|
57
|
+
| 11 | **Fusion / Lens Flares** | Full-12 | All Lens Flares ship the complete 12-suffix set. |
|
|
58
|
+
| 12 | **Fusion / Looks** | Mixed | Only `.setting`-based effects get thumbnails; `.alut3` files do not get per-file thumbnails. The one `.setting` effect (`Posterize`) ships the full-12. The 10 `.alut3` files (e.g. `Abstract.alut3`, `Film chrome.alut3`) carry no per-effect thumbnail — only `_default.png` and `_default@2x.png` serve as fallbacks for the whole category. |
|
|
59
|
+
| 13 | **Fusion / Styled Text** | Wide-only | All Styled Text effects use only `.wide` + `.wide@2x`. No `.large.*` or `.small.*` variants. |
|
|
60
|
+
| 14 | **Fusion / How To** | None (defaults only) | Zero per-effect thumbnails in this category. All 12 How To `.setting` files share only the two `_default` fallbacks (`_default.png`, `_default@2x.png`). Do not try to add `.large.png` thumbnails here — the UI does not render them for How To entries. |
|
|
61
|
+
|
|
62
|
+
**Minimum viable ship:**
|
|
63
|
+
- For any category that uses `.large.*`: provide at least `.large.png` (128×128). Resolve handles the rest with fallbacks.
|
|
64
|
+
- For titles and transitions: provide at least `.wide.png` (52×29).
|
|
65
|
+
- For Styled Text: provide `.wide.png` + `.wide@2x.png`.
|
|
66
|
+
- For Looks `.alut3` files: no per-effect thumbnail is possible; the category relies entirely on `_default.*`.
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## `_default.*` Fallbacks
|
|
71
|
+
|
|
72
|
+
Every stock Edit and Fusion folder ships a small set of fallback files that Resolve displays when no per-effect thumbnail exists.
|
|
73
|
+
|
|
74
|
+
| File | Dimensions | Role |
|
|
75
|
+
|------|-----------|------|
|
|
76
|
+
| `_default.png` | 52 × 29 | Fallback for the wide slot. Despite the name suggesting a "default large", this file is actually wide-sized — do not use it as a large-thumbnail fallback. |
|
|
77
|
+
| `_default@2x.png` | 256 × 256 | HiDPI fallback; note the dimension mismatch — `_default.png` is wide-sized (52×29) while `_default@2x.png` is large-sized (256×256). The two files are not matched scales. |
|
|
78
|
+
| `_default.wide.png` | 52 × 29 | Explicit wide-slot fallback, present in Edit category folders alongside `_default.png`. |
|
|
79
|
+
| `_default.wide@2x.png` | 104 × 58 | HiDPI version of the wide-slot fallback. |
|
|
80
|
+
|
|
81
|
+
The `_default.wide.*` pair is distinct from `_default.*` — both exist side-by-side in Edit folders. When building new categories, supply your own `_default.png` and `_default@2x.png` rather than relying on Resolve's built-in fallback graphic.
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Creating Thumbnails
|
|
86
|
+
|
|
87
|
+
1. Apply the effect to a clip in Resolve.
|
|
88
|
+
2. Grab a frame (File → Export → Still, or viewer right-click → Grab Still).
|
|
89
|
+
3. Crop to the target dimensions with any image tool. Alpha PNGs are supported.
|
|
90
|
+
4. Name the crops per the suffix rules above and drop next to the `.setting` file.
|
|
91
|
+
|
|
92
|
+
For button states (`.active`, `.hover`, `.push`), start from the base `.small.png` and brighten / darken / tint by ~10% — this matches the stock library's approach.
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Quirks — Stock Anomalies (Do Not Replicate)
|
|
97
|
+
|
|
98
|
+
These are bugs or accidents in the stock library. They are documented here so authors recognize them as anomalies, not as valid naming patterns.
|
|
99
|
+
|
|
100
|
+
- **Double-dot typo:** `Stretch Region.small.@2x.png` and `Watermark.small.@2x.png` in `Edit/Effects/` have an extra dot (`.small.@2x.png` instead of `.small@2x.png`). Resolve ignores these files; the correct form is `.small@2x.png` with no dot before `@2x`.
|
|
101
|
+
|
|
102
|
+
- **Bare-suffix transition:** `Rotate 90.png` and `Rotate 90@2x.png` in `Edit/Transitions/` have no slot suffix at all — just the base name with `.png`. This appears to be a naming error; use an explicit slot suffix on every thumbnail.
|
|
103
|
+
|
|
104
|
+
- **Compound Slide name:** The transition `Slide.push` has its base name parsed as `Slide.push`, producing filenames like `Slide.push.small.push.png` and `Slide.push.small.push@2x.png`. This is a side effect of the base name containing a dot. Avoid dots in effect base names.
|
|
105
|
+
|
|
106
|
+
None of these patterns are understood by Resolve as intentional slots — authors should not replicate them.
|
package/install.py
CHANGED
|
@@ -35,7 +35,7 @@ from src.utils.update_check import (
|
|
|
35
35
|
|
|
36
36
|
# ─── Version ──────────────────────────────────────────────────────────────────
|
|
37
37
|
|
|
38
|
-
VERSION = "2.33.
|
|
38
|
+
VERSION = "2.33.2"
|
|
39
39
|
# Only hard floor: mcp[cli] requires Python 3.10+. There is no upper bound —
|
|
40
40
|
# Resolve's scripting bridge loads into newer interpreters on recent builds
|
|
41
41
|
# (Python 3.14 verified against Resolve Studio 20.3.2). Older Resolve builds
|
package/package.json
CHANGED
package/src/granular/common.py
CHANGED
|
@@ -80,7 +80,7 @@ if not logging.getLogger().handlers:
|
|
|
80
80
|
handlers=[logging.StreamHandler()],
|
|
81
81
|
)
|
|
82
82
|
|
|
83
|
-
VERSION = "2.33.
|
|
83
|
+
VERSION = "2.33.2"
|
|
84
84
|
logger = logging.getLogger("davinci-resolve-mcp")
|
|
85
85
|
logger.info(f"Starting DaVinci Resolve MCP Server v{VERSION}")
|
|
86
86
|
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.33.
|
|
14
|
+
VERSION = "2.33.2"
|
|
15
15
|
|
|
16
16
|
import base64
|
|
17
17
|
import os
|
|
@@ -1173,6 +1173,18 @@ def _requires_method(obj, method_name, min_version):
|
|
|
1173
1173
|
return None
|
|
1174
1174
|
return _err(f"{method_name} requires DaVinci Resolve {min_version}+")
|
|
1175
1175
|
|
|
1176
|
+
def _is_truncated(text):
|
|
1177
|
+
"""True if a transcription preview was cut off.
|
|
1178
|
+
|
|
1179
|
+
Resolve's `Transcription` clip property is a preview that ends with an
|
|
1180
|
+
ellipsis (… or ...) when the full transcript is longer than the property
|
|
1181
|
+
exposes, so the caller knows the returned text is partial.
|
|
1182
|
+
"""
|
|
1183
|
+
if not isinstance(text, str):
|
|
1184
|
+
return False
|
|
1185
|
+
t = text.rstrip()
|
|
1186
|
+
return t.endswith("…") or t.endswith("...")
|
|
1187
|
+
|
|
1176
1188
|
_MARKER_COLORS = [
|
|
1177
1189
|
"Blue", "Cyan", "Green", "Yellow", "Red", "Pink", "Purple", "Fuchsia",
|
|
1178
1190
|
"Rose", "Lavender", "Sky", "Mint", "Lemon", "Sand", "Cocoa", "Cream",
|
|
@@ -5408,6 +5420,22 @@ def _audio_mapping_report(mp, tl, p: Dict[str, Any]):
|
|
|
5408
5420
|
return {"timeline_items": timeline_items, "media_pool_items": clip_rows}
|
|
5409
5421
|
|
|
5410
5422
|
|
|
5423
|
+
def _clip_name(clip):
|
|
5424
|
+
try:
|
|
5425
|
+
return clip.GetName()
|
|
5426
|
+
except Exception:
|
|
5427
|
+
return None
|
|
5428
|
+
|
|
5429
|
+
|
|
5430
|
+
def _synced_audio(clip):
|
|
5431
|
+
"""Best-effort read of whether a clip currently has synced audio linked."""
|
|
5432
|
+
try:
|
|
5433
|
+
v = clip.GetClipProperty("Synced Audio")
|
|
5434
|
+
except Exception:
|
|
5435
|
+
return False
|
|
5436
|
+
return bool(v) and str(v).strip() not in ("", "None", "0", "Off", "No")
|
|
5437
|
+
|
|
5438
|
+
|
|
5411
5439
|
def _safe_auto_sync_audio(mp, p: Dict[str, Any]):
|
|
5412
5440
|
root = mp.GetRootFolder()
|
|
5413
5441
|
resolved, err = _clips_from_params(root, mp, p)
|
|
@@ -5421,7 +5449,26 @@ def _safe_auto_sync_audio(mp, p: Dict[str, Any]):
|
|
|
5421
5449
|
settings = _normalize_auto_sync_settings(dict(p.get("settings") or {}), get_resolve())
|
|
5422
5450
|
if p.get("dry_run", True):
|
|
5423
5451
|
return _ok(would_auto_sync=True, clips=_clip_summaries(clips), missing=missing, settings=settings)
|
|
5424
|
-
|
|
5452
|
+
# Read-back verification: AutoSyncAudio's boolean return is unreliable, so
|
|
5453
|
+
# capture each clip's "Synced Audio" linkage before and after and report the
|
|
5454
|
+
# delta. Trust `linked`/`newly_linked`, not `success`.
|
|
5455
|
+
before = [_synced_audio(c) for c in clips]
|
|
5456
|
+
ok = bool(mp.AutoSyncAudio(clips, settings))
|
|
5457
|
+
linked, newly_linked, already_linked = [], [], []
|
|
5458
|
+
for c, was in zip(clips, before):
|
|
5459
|
+
name = _clip_name(c)
|
|
5460
|
+
if _synced_audio(c):
|
|
5461
|
+
linked.append(name)
|
|
5462
|
+
(already_linked if was else newly_linked).append(name)
|
|
5463
|
+
return {
|
|
5464
|
+
"success": ok,
|
|
5465
|
+
"linked": linked,
|
|
5466
|
+
"newly_linked": newly_linked,
|
|
5467
|
+
"already_linked": already_linked,
|
|
5468
|
+
"count": len(clips),
|
|
5469
|
+
"missing": missing,
|
|
5470
|
+
"settings": settings,
|
|
5471
|
+
}
|
|
5425
5472
|
|
|
5426
5473
|
|
|
5427
5474
|
def _resolve_audio_constant(resolve_obj, name: str, fallback):
|
|
@@ -13626,6 +13673,9 @@ def media_pool_item(action: str, params: Optional[Dict[str, Any]] = None) -> Dic
|
|
|
13626
13673
|
get_unique_id(clip_id) -> {id}
|
|
13627
13674
|
transcribe_audio(clip_id, use_speaker_detection?) -> {success} — use_speaker_detection is Resolve 21+
|
|
13628
13675
|
clear_transcription(clip_id) -> {success}
|
|
13676
|
+
get_transcription(clip_id) -> {text, truncated, status, has_transcription}
|
|
13677
|
+
Read a clip's transcription. `truncated` flags when Resolve's preview
|
|
13678
|
+
property cut the text off (the full transcript is longer).
|
|
13629
13679
|
perform_audio_classification(clip_id) -> {success} — Resolve 21+
|
|
13630
13680
|
clear_audio_classification(clip_id) -> {success} — Resolve 21+
|
|
13631
13681
|
analyze_for_intellisearch(clip_id, identify_faces?, is_better_mode?) -> {success} — Resolve 21+, AI IntelliSearch Extra
|
|
@@ -13827,6 +13877,20 @@ def media_pool_item(action: str, params: Optional[Dict[str, Any]] = None) -> Dic
|
|
|
13827
13877
|
return {"success": bool(clip.TranscribeAudio(bool(usd)))}
|
|
13828
13878
|
elif action == "clear_transcription":
|
|
13829
13879
|
return {"success": bool(clip.ClearTranscription())}
|
|
13880
|
+
elif action == "get_transcription":
|
|
13881
|
+
raw = clip.GetClipProperty("Transcription")
|
|
13882
|
+
text = raw if isinstance(raw, str) else ("" if raw is None else str(raw))
|
|
13883
|
+
try:
|
|
13884
|
+
status = clip.GetClipProperty("Transcription Status")
|
|
13885
|
+
except Exception:
|
|
13886
|
+
status = None
|
|
13887
|
+
return {
|
|
13888
|
+
"clip_id": p.get("clip_id"),
|
|
13889
|
+
"text": text,
|
|
13890
|
+
"truncated": _is_truncated(text),
|
|
13891
|
+
"status": status or None,
|
|
13892
|
+
"has_transcription": bool(text.strip()),
|
|
13893
|
+
}
|
|
13830
13894
|
elif action == "perform_audio_classification":
|
|
13831
13895
|
missing = _requires_method(clip, "PerformAudioClassification", "21.0")
|
|
13832
13896
|
if missing:
|
|
@@ -13905,7 +13969,7 @@ def media_pool_item(action: str, params: Optional[Dict[str, Any]] = None) -> Dic
|
|
|
13905
13969
|
return {"success": bool(clip.SetMarkInOut(p["mark_in"], p["mark_out"], p.get("type", "all")))}
|
|
13906
13970
|
elif action == "clear_mark_in_out":
|
|
13907
13971
|
return {"success": bool(clip.ClearMarkInOut(p.get("type", "all")))}
|
|
13908
|
-
return _unknown(action, ["get_name","get_metadata","set_metadata","get_third_party_metadata","set_third_party_metadata","get_media_id","get_clip_property","set_clip_property","get_clip_color","set_clip_color","clear_clip_color","link_proxy","unlink_proxy","replace_clip","set_name","link_full_resolution_media","monitor_growing_file","replace_clip_preserve_sub_clip","get_unique_id","transcribe_audio","clear_transcription","perform_audio_classification","clear_audio_classification","analyze_for_intellisearch","analyze_for_slate","remove_motion_blur","get_audio_mapping","get_mark_in_out","set_mark_in_out","clear_mark_in_out","open_in_viewer"])
|
|
13972
|
+
return _unknown(action, ["get_name","get_metadata","set_metadata","get_third_party_metadata","set_third_party_metadata","get_media_id","get_clip_property","set_clip_property","get_clip_color","set_clip_color","clear_clip_color","link_proxy","unlink_proxy","replace_clip","set_name","link_full_resolution_media","monitor_growing_file","replace_clip_preserve_sub_clip","get_unique_id","transcribe_audio","clear_transcription","get_transcription","perform_audio_classification","clear_audio_classification","analyze_for_intellisearch","analyze_for_slate","remove_motion_blur","get_audio_mapping","get_mark_in_out","set_mark_in_out","clear_mark_in_out","open_in_viewer"])
|
|
13909
13973
|
|
|
13910
13974
|
|
|
13911
13975
|
# ═══════════════════════════════════════════════════════════════════════════════
|