davinci-resolve-mcp 2.134.0 → 2.135.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.
- package/docs/guides/native-drt-authoring.md +1 -1
- package/install.py +1 -1
- package/package.json +1 -1
- package/resolve-advanced/server/tools/audio.mjs +14 -4
- package/resolve-advanced/server/tools/drt.mjs +1 -1
- package/resolve-advanced/vendor/drp-format/assemble-timeline.js +2 -2
- package/src/granular/common.py +1 -1
- package/src/server.py +1 -1
|
@@ -55,7 +55,7 @@ window. `render.verify_output` covers the container-level checks.
|
|
|
55
55
|
| Subtitles | `spec.subtitles` / `spec.subtitlesSrt` (raw SRT) | v2.128 |
|
|
56
56
|
| Compound clips | survive `extract_from_drp` → `.drt` (inner containers kept, recursive) | v2.130 |
|
|
57
57
|
| Compound authoring | `spec.compounds` (multiple compose; nested edits render) | v2.131–2.132 |
|
|
58
|
-
| Nested compounds | `compounds[].compounds` (depth-2 playback render-verified) | v2.134 |
|
|
58
|
+
| Nested compounds | `compounds[].compounds` (depth-2 and depth-3 playback render-verified) | v2.134 |
|
|
59
59
|
| Fusion titles | `elements: [{type:'title', text}]` — **21-gen hosts only** | v2.108 |
|
|
60
60
|
|
|
61
61
|
`assemble_from_interchange` drives the same engine from an EDL / OTIO /
|
package/install.py
CHANGED
|
@@ -37,7 +37,7 @@ from src.utils.update_check import (
|
|
|
37
37
|
|
|
38
38
|
# ─── Version ──────────────────────────────────────────────────────────────────
|
|
39
39
|
|
|
40
|
-
VERSION = "2.
|
|
40
|
+
VERSION = "2.135.0"
|
|
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
|
@@ -22,9 +22,19 @@ function load(mod) {
|
|
|
22
22
|
return require(`../../vendor/audio/${mod}.js`);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
// .strict(): a mistyped window key (start/duration/…) must refuse, not be
|
|
26
|
+
// silently stripped — a stripped window turned `trim` into a whole-file copy
|
|
27
|
+
// reported as success (measured through the MCP layer, E59 sweep). Extra
|
|
28
|
+
// ffmpeg knobs belong in `opts`, which stays passthrough.
|
|
29
|
+
const splitSchema = z.object({ input: z.string(), output: z.string().optional(), mode: z.string().optional(), opts: z.object({}).passthrough().optional() }).strict();
|
|
30
|
+
// durationFrames is REQUIRED: trim without a window is a no-op copy wearing
|
|
31
|
+
// a trim's name. Use `convert` for windowless transcodes. fps converts it to
|
|
32
|
+
// the vendored module's seconds vocabulary (default 24) — the module knows
|
|
33
|
+
// {startTime, endTime, duration} in SECONDS only, so an untranslated
|
|
34
|
+
// durationFrames was silently discarded and trim NEVER trimmed (measured
|
|
35
|
+
// through the MCP layer, E59: durationFrames 24 returned the whole file).
|
|
36
|
+
const trimSchema = z.object({ input: z.string(), output: z.string(), durationFrames: z.number().positive(), fps: z.number().positive().optional(), opts: z.object({}).passthrough().optional() }).strict();
|
|
37
|
+
const convertSchema = z.object({ input: z.string(), output: z.string(), opts: z.object({}).passthrough().optional() }).strict();
|
|
28
38
|
|
|
29
39
|
export const audioTool = {
|
|
30
40
|
name: 'audio',
|
|
@@ -41,7 +51,7 @@ export const audioTool = {
|
|
|
41
51
|
const p = trimSchema.parse(args);
|
|
42
52
|
const m = load('trim');
|
|
43
53
|
const fn = m.trimAudio || m.trim || m.default;
|
|
44
|
-
return fn(p.input, p.output, {
|
|
54
|
+
return fn(p.input, p.output, { duration: p.durationFrames / (p.fps ?? 24), ...(p.opts || {}) });
|
|
45
55
|
}
|
|
46
56
|
if (action === 'convert') {
|
|
47
57
|
const p = convertSchema.parse(args);
|
|
@@ -51,7 +51,7 @@ const assembleSchema = z.object({
|
|
|
51
51
|
spec: z
|
|
52
52
|
.object({})
|
|
53
53
|
.passthrough()
|
|
54
|
-
.describe("assembleTimeline spec: { timelineName?, startFrame? (timeline start frame @24, default 86400=01:00:00:00 — sets the start TIMECODE, render-verified on 19), media?: {mediaFilePath, spec:{width,height,frameCount,fps}, cuts:[{startFrame,durationFrames,srcIn?,track? (1-based video track; >1 = video-only, render-verified stacking),speed?/reverse? (constant retime, e.g. 0.5, forward or backwards; video-only; readback+render-verified on 19),freeze? (true = hold source frame srcIn for the whole cut; video-only; render-proven frozen on 19 via freezedetect),audioOnly?+track? (explicit AUDIO placement on audio track 1-8; presence suppresses the A1 mirror; render-verified on 19)}]} | [same, ...] (multi-source needs media_pool.capture_media_template run once per file), transitions?: [{track, atFrame, durationFrames?, trackType? ('video' dissolve | 'audio' cross-fade, both render-verified on 19)}], markers?: [{frame (timeline-absolute), color? (16 names), name?, note?, duration?, customData?}] (readback-verified on 19), compounds?: [{name, startFrame (parent, absolute), durationFrames, track?, cuts:[{mediaFilePath, startFrame (INNER, 0-based), durationFrames, srcIn?}], compounds?: [same, nested — frames inner-relative]}] (multiple PARALLEL compounds compose AND compounds NEST — depth-2 playback render-verified on 19 (the old depth-2 black was a missing SequenceSetup key, fixed);
|
|
54
|
+
.describe("assembleTimeline spec: { timelineName?, startFrame? (timeline start frame @24, default 86400=01:00:00:00 — sets the start TIMECODE, render-verified on 19), media?: {mediaFilePath, spec:{width,height,frameCount,fps}, cuts:[{startFrame,durationFrames,srcIn?,track? (1-based video track; >1 = video-only, render-verified stacking),speed?/reverse? (constant retime, e.g. 0.5, forward or backwards; video-only; readback+render-verified on 19),freeze? (true = hold source frame srcIn for the whole cut; video-only; render-proven frozen on 19 via freezedetect),audioOnly?+track? (explicit AUDIO placement on audio track 1-8; presence suppresses the A1 mirror; render-verified on 19)}]} | [same, ...] (multi-source needs media_pool.capture_media_template run once per file), transitions?: [{track, atFrame, durationFrames?, trackType? ('video' dissolve | 'audio' cross-fade, both render-verified on 19)}], markers?: [{frame (timeline-absolute), color? (16 names), name?, note?, duration?, customData?}] (readback-verified on 19), compounds?: [{name, startFrame (parent, absolute), durationFrames, track?, cuts:[{mediaFilePath, startFrame (INNER, 0-based), durationFrames, srcIn?}], compounds?: [same, nested — frames inner-relative]}] (multiple PARALLEL compounds compose AND compounds NEST recursively — depth-2 AND depth-3 playback render-verified on 19 (the old depth-2 black was a missing SequenceSetup key, fixed); inner cuts need captured templates w/ native clips), subtitles?: [{startFrame (timeline-absolute), durationFrames, text}] + subtitlesSrt? (raw SRT, cues anchor at the origin; readback-verified on 19; angle-bracket runs read as SRT markup), elements?: [{type:'title'|'generator', track, startFrame, durationFrames?, text?, generatorName? ('Solid Color'|'SMPTE Color Bar'|'Grey Scale' render-verified on 19), ...}] }. startFrame is timeline-absolute (origin 86400)."),
|
|
55
55
|
outputPath: z.string().describe('Absolute path where the importable .drt will be written'),
|
|
56
56
|
targetAppVersion: z
|
|
57
57
|
.union([z.string(), z.number()])
|
|
@@ -248,8 +248,8 @@ async function assembleTimeline(spec = {}) {
|
|
|
248
248
|
// Recursive: compounds nest (comp.compounds, frames inner-relative like
|
|
249
249
|
// cuts). Depth-2 playback is RENDER-VERIFIED on 19.1.3.7 (E56, white 234
|
|
250
250
|
// through two levels) — the historical depth-2 black was one missing key,
|
|
251
|
-
// SequenceSetup, in the donor template's embedded-sequence blob.
|
|
252
|
-
//
|
|
251
|
+
// SequenceSetup, in the donor template's embedded-sequence blob. Depth-3 is
|
|
252
|
+
// render-verified too (E58, white 234 through three levels).
|
|
253
253
|
const placeTree = async (comp, parentUuid, label) => {
|
|
254
254
|
if (!comp || typeof comp !== 'object') throw new TypeError(`assembleTimeline: ${label} must be an object`);
|
|
255
255
|
const res = await placeCompound(buffer, {
|
package/src/granular/common.py
CHANGED
|
@@ -87,7 +87,7 @@ if not logging.getLogger().handlers:
|
|
|
87
87
|
handlers=[logging.StreamHandler()],
|
|
88
88
|
)
|
|
89
89
|
|
|
90
|
-
VERSION = "2.
|
|
90
|
+
VERSION = "2.135.0"
|
|
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()}")
|