name: davinci-resolve-scripting description: >- Build and revise DaVinci Resolve projects and timelines through raw Python scripting. Use when an edit needs direct Scripting API access beyond the installed MCP tools. For general Resolve operation start with davinci-resolve; for creative direction use music-edit. Covers connection, source/timeline frame placement, owned rebuilds, and playback diagnostics.
davinci-resolve-scripting
Assemble and revise an authored edit on a real Resolve timeline. Hard-won facts below — they cost real round-trips; don't relearn them.
Connect through the available surface
Probe the running edition and connection (resolve_control / get_version
through the installed MCP surface, or the product string from a connected
script). Studio 21.0.3.7 supported external system-Python scripting in the
2026-08 local verification. Prefer the installed MCP tools when they cover the
operation; use raw Python when it adds a needed capability.
If external acquisition returns None, inspect the current edition, scripting
preferences, and bridge status before concluding the operation needs a user
click. The server's in-app resolve_bridge is another connection path. For a
menu-launched script, Workspace > Scripts > Utility supplies the injected
resolve global; preserve it. Use the manual menu route only when the available
agent-controlled routes do not connect, and read a file log after execution.
Setup
- Resolve at
/Applications/DaVinci Resolve/DaVinci Resolve.app(download from blackmagicdesign.com or Mac App Store; verify the installed path). - Put agent scripts here so they appear in the menu:
~/Library/Application Support/Blackmagic Design/DaVinci Resolve/Fusion/Scripts/Utility/<name>.pyThey show under Workspace > Scripts > Utility. Resolve scans on launch and re-scans when the menu opens; restart Resolve only if a new script doesn't appear. - Studio + external use only — export:
RESOLVE_SCRIPT_API="/Library/Application Support/Blackmagic Design/DaVinci Resolve/Developer/Scripting" RESOLVE_SCRIPT_LIB="/Applications/DaVinci Resolve/DaVinci Resolve.app/Contents/Libraries/Fusion/fusionscript.so" PYTHONPATH="$PYTHONPATH:$RESOLVE_SCRIPT_API/Modules"
Non-obvious gotchas (each cost a round-trip)
- Output doesn't surface.
print()from a menu/Console run is not reliably shown. Always log to a file (/tmp/resolve_build.log) and read it with Bash — that's the agent's only reliable feedback channel. - ASCII only. The loader reads files as ASCII; em-dashes/curly quotes/
...->UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2. Keep scripts pure ASCII (hyphens, straight quotes) oropen(path, encoding='utf-8'). ASCII is safest. - Don't clobber the injected global. In the Console,
resolve = scriptapp(...)overwrites the good injected object with None. Acquire into a local (R), never reassignresolve. - Robust acquisition (works menu / Console / external):
R = None try: R = bmd.scriptapp("Resolve") # noqa: F821 except Exception: pass if R is None: R = globals().get("resolve") # <- the free MENU-launch winner if R is None: import DaVinciResolveScript as dvr R = dvr.scriptapp("Resolve") # external (Studio) - Idempotency. Re-running naively duplicates media + timelines. Dedup media against
root.GetClipList()beforeImportMedia;mp.DeleteTimelines([...])the timelines the script OWNS (by name) before recreating — never delete the user's hand-named cuts.
Core API recipe
pm = R.GetProjectManager()
proj = pm.CreateProject("name") or pm.LoadProject("name") or pm.GetCurrentProject()
for k, v in {"timelineResolutionWidth":"1080","timelineResolutionHeight":"1920",
"timelineFrameRate":"30"}.items():
proj.SetSetting(k, v) # frame rate locks after first timeline/media — set early
mp = proj.GetMediaPool()
clips = mp.ImportMedia(["/abs/a.mov", "/abs/b.mov"]) # -> MediaPoolItems
tl = mp.CreateEmptyTimeline("My Cut")
proj.SetCurrentTimeline(tl)
mp.AppendToTimeline([ # one dict per clip/beat
{"mediaPoolItem": clips[0], "startFrame": 99, "endFrame": 387}, # SOURCE frames, OUT inclusive
{"mediaPoolItem": clips[1], "startFrame": 1504, "endFrame": 1754},
])
pm.SaveProject()
- Frames are SOURCE frames at the clip's exact rate. Probe rate and cadence;
use the corpus export contract when
footage-indexsupplied the selects. - Precise placement works (verified 21.0.3.7 external): dicts also take
"trackIndex","recordFrame"(timeline frames), and"mediaType"(1 = video only, 2 = audio only). Music bed = onemediaType: 2entry atrecordFrame: tl.GetStartFrame(); beat chunks =mediaType: 1with explicit recordFrames — no append-order drift, and game/source audio stays off the timeline. recordFrameis ABSOLUTE timeline frames including the start timecode. A fresh timeline starts at 01:00:00:00 =GetStartFrame()(432000 @120 fps); passingrecordFrame: 0places clips an HOUR BEFORE the visible timeline — tracks report items, the GUI looks empty, andGetEndFrame()-GetStartFrame()reads 0. Always addbase = int(tl.GetStartFrame())to every recordFrame. That zero-duration-but-items-exist signature IS this bug.- Lay each authored source range as its own timeline item for trim handles and take-swapping. A range may span several musical beats; split where the edit calls for a new shot or independent treatment.
Playback frame-rate mismatch
If timeline playback is slow with popping audio while the source viewer plays fine, compare playback and timeline rates before changing media. In the Studio 21.0.3.7 local verification:
pm.CreateProject()makes the project at Resolve's default fps (often 24).proj.SetSetting("timelineFrameRate","30")works, BUTproj.SetSetting("timelinePlaybackFrameRate","30")returned False, leaving the Playback frame rate at the project's birth default.- Result: a 30 fps timeline played at 24 fps = 30/24 = 1.25x slow motion + stretched audio.
Diagnose: proj.GetSetting("timelineFrameRate") vs proj.GetSetting("timelinePlaybackFrameRate")
(the template logs both and WARNs on mismatch; compare as floats -- one reads "120", the other "120.0").
Check the current setting surface and read back any attempted correction. If
the setter still fails as it did on 21.0.3.7, two tested alternatives are:
- GUI: Project Settings (gear, bottom-right) > Master Settings > "Playback frame rate" -> match the timeline -> Save (saved with the project; idempotent rebuilds reuse it).
- Scripted (no GUI): clone a project whose playback rate is already right --
pm.ExportProject(shell, "/tmp/shell.drp");pm.ImportProject("/tmp/shell.drp", new_name); load it,mp.DeleteTimelines(...)+mp.DeleteClips(root.GetClipList())to gut the copy (the original is untouched); thenSetSetting("timelineFrameRate", ...)DOES work again on the emptied project, playback stays as inherited. Build into that. Keep one known-good shell per target rate; find candidates by loading projects and reading their playback setting.
Playback performance (iPhone HEVC stutters/pops on the timeline)
Separate issue: long-GOP 10-bit HEVC beats each start mid-GOP, so the TIMELINE viewer drops frames (stutter) and glitches audio at cuts even when the SOURCE viewer plays one clip fine. That's decode cost. Fixes (non-destructive, don't affect export):
- Playback > Render Cache > Smart, set Project Settings > Master Settings > "Render cache format" = ProRes 422. Wait for the bar above the ruler to turn blue.
- Playback > Timeline Proxy Resolution > Half for an instant preview.
- Generate Optimized Media (right-click clips) for permanent smoothness. If cache/proxy changes do not help, check rate settings, CPU contention, and rendered output separately instead of assuming the source is corrupt. HDR note: Resolve handles iPhone 10-bit HLG/BT.2020 natively; no flags needed to lay clips.
Template
resolve_template.py here is a ready-to-adapt builder: robust acquisition, file logging,
media dedup, idempotent owned-timeline rebuild, beat catalog + timeline definitions.
# Studio (this machine): adapt a copy, export the Setup env vars, run it from Bash
python3 my_build.py && cat /tmp/resolve_build.log
# Menu fallback when agent-controlled connections are unavailable: install under
# Fusion/Scripts/Utility/<name>.py, run it from Workspace > Scripts > Utility,
# then read the same log.
Verify the assembly
Log the acquired connection, imported sources, timeline item bounds, and any exceptions. Read back source ranges, track placement, and output duration after mutation. Render a short passage with the music to check timing, source audio, and framing; successful API calls or populated tracks alone do not verify an edit.
