resolve-cut 是做什么的?
你通过保存的带参数脚本在 DaVinci Resolve Studio 中执行编排、渲染和检查视频。你不会在每次会话中重写编排逻辑:而是调用脚本,读取其返回值,并通过数字和帧数验证结果。审美判断仍由人类保留:由人来决定保留哪些内容,你来执行和测量。
$SKILL 表示该技能文件夹(本文件所在的位置)。在 scripts/resolve/ 中的脚本通过 run.py 在 Resolve 内部运行;scripts/media/ 中的脚本是使用 ffmpeg 的普通 Python 脚本。每个脚本都有 --help。
开始前准备
- Resolve 响应测试:
python3 "$SKILL/scripts/resolve/run.py" - '{}' <<< 'result = resolve.GetVersionString()'。 退出代码 2 表示 Resolve 已关闭、使用的是免费版、或者“偏好设置 > 系统 > 常规 > 外部脚本使用”未设置为“本地”。 ffmpeg -version、python3 -c "import numpy, PIL"以及ELEVENLABS_API_KEY(环境变量或./.env文件)。- 如果项目有备注文件(
VIDEO_NOTES.md或类似文件),请阅读:了解之前用户批准和拒绝的内容。不要重复询问相同的修改。 - 工作文件夹:你创建的所有内容(转录、档案、帧、渲染输出)都要放入工作文件夹,切勿放入原始素材文件夹。
工作流程
1. 转写。 逐词级转录,每个剪辑一个 JSON 文件:
python3 "$SKILL/scripts/media/transcribe.py" <clips 文件夹> --out work/transcripts [--language por]。
转录是一切的基础:用于选择镜头、在帧边界上剪辑以及检查最终混音。
2. 制作档案。 将所有素材紧凑地整合到一个文本文件中,只阅读一次:
python3 "$SKILL/scripts/media/dossier.py" <clips 文件夹> --transcripts work/transcripts --out work/dossier.md [--sheets]。
它会列出每个句子及其时间码,并在某个句子开头与之前的句子相似时标记 «repeats C0076 20.2»,表示这是同一句台词的又一次尝试。有脚本的情况下,保留每个段落的最后一个完整镜头;删除犹豫、假开头和重复尝试。只有在需要做出决定时才打开剪辑:
python3 "$SKILL/scripts/media/inspect_range.py" <clip> <t0> <t1> --words work/transcripts/<clip>.json(在一张图中显示帧、波形和词汇)。
3. 剪辑列表。 编写 work/plan.json,按顺序列出选中的镜头
({"Lesson 01 v1": [["01a", "C0081", 1.9, 12.6], ["01b", "C0083", 5.4, 23.8], ...]}:镜头、剪辑、起始时间、结束时间(相对于剪辑的秒数)),然后:
python3 "$SKILL/scripts/media/cut_list.py" work/plan.json work/transcripts <clips 文件夹> work/cuts.json。
剪辑点会落在词汇附近最安静的帧边界上,超过 1 秒的停顿会被缩短,并且片段会交替使用缩放 1 / 1.2,使得镜头间的切换不突兀。对于没有间隙的假开头(例如 "co- contar"),可以通过 {"in_frame": N} 或 {"out_frame": N} 强制指定帧。编辑器备注:add_markers.py。
4. 在 Resolve 中装配。 每次调用执行一个重量级操作,并在下一次调用前读取每个 result:
R="$SKILL/scripts/resolve/run.py"
python3 "$R" "$SKILL/scripts/resolve/project.py" '{"name": "Lesson 01", "width": 1920, "height": 1080, "fps": "25"}'
python3 "$R" "$SKILL/scripts/resolve/assemble.py" '{"phase": "import", "bin": "Raw", "media": "/abs/clips", "files": ["C0081.MP4"]}'
python3 "$R" "$SKILL/scripts/resolve/assemble.py" '{"phase": "timeline", "bin": "Raw", "cuts": "/abs/work/cuts.json", "timeline": "Lesson 01 v1", "zoom": {"center": [0.45, 0.37]}, "voice_isolation": 50}'
zoom.center 是人脸在画面中的位置(以帧的分数表示),因此放大镜头可以保持人脸位置不变。检查 frames == expected 且 failures 为空。
5. 渲染前查看。 frames.py 可以在一秒内直接从时间线导出帧
({"timeline": "Lesson 01 v1", "frames": [100, 500], "folder": "/abs/work/frames", "max": 960})。查看一个普通帧和一个放大帧。
6. 渲染。
python3 "$R" "$SKILL/scripts/resolve/render.py" '{"phase": "start", "timeline": "Lesson 01 v1", "folder": "/abs/out", "name": "lesson-01", "preset": "reel", "width": 1920, "height": 1080, "mark": [0, <frames - 1>]}'
然后每 10 秒轮询 {"phase": "status", "job": "<id>"} 直到 JobStatus 变为 Complete。在渲染完成之前不要回答:如果无头运行在“渲染开始”时结束轮次,将无人检查渲染状态。
7. 验证。 python3 "$SKILL/scripts/media/qc.py" out/lesson-01.mp4 --expected <frames> --transcribe
(添加 --ref 比对上一个批准的版本,--frames 生成表格,--simulate 模拟 Instagram)。阅读最终混音的转录,在每个剪辑点检查:重复词汇("decide, decide")、被剪掉的词汇以及位置错误的词汇是剪辑列表最容易产生的缺陷。在移动剪辑点前,在波形上确认可疑的剪辑(inspect_range.py),修复剪辑列表,装配新版本(v2)并再次渲染。最多进行 3 轮,每个缺陷需注明其精确时间。永远不要因为“看起来正确”就认为完成:必须引用数字。
8. 交付。 当混音偏离目标时使用 loudness.py(线性增益加限制器,绝不使用 loudnorm),然后使用 deliver.py export.mp4 clean.mp4 [--no-editlist] [--copy <folder>]:无时间码轨道、无元数据、moov 优先,逐字节比较复制结果。
9. 复查,待用户批准后。 在备注文件中记录用户批准和拒绝的内容,使用用户的原始措辞,每行一条;记录每个新陷阱及其日期;以及任何你手动执行超过一次的操作,都成为旁边的带参数脚本。
在 Resolve 中运行脚本
run.py连接到打开的 Resolve 实例,并运行定义了resolve、project、P(JSON)和__file__的脚本,然后将result打印为 JSON。这比通过 MCP 编写相同脚本消耗的 token 少得多。- 如果使用 MCP 服务器(官方
run_script_unsafe):P = {...}; __file__ = "<脚本的绝对路径>"; exec(open(__file__, encoding="utf-8").read())。沙盒在未指定encoding时以 ASCII 读取文件,且变量名在调用间持久存在:脚本需要为其变量添加前缀。 - 在写入前后都要保存 (
GetProjectManager().SaveProject());没有撤销功能。每次创建新版本时,都要在时间线的副本上进行操作 (DuplicateTimeline,此操作也会复制 Fusion 合成)。 - 查找 API 而非猜测:使用官方 MCP 的
search_scripting_api、get_scripting_docs和get_whats_new(每个会话一次),或者查阅 Resolve 的Developer/Scripting文件夹中的 Blackmagic README(帮助 > 文档 > 开发者)。 - 有需要重复的新操作?一次性编写为带有
P字典和result的脚本,放在这些脚本旁边。
交付目标
| 目标平台 | 画面 | 音频 |
|---|---|---|
| Reels, Stories, Shorts, TikTok | 1440x2560,拍摄帧率,H.264 High 约 20 Mbps 实际码率,Rec.709 标记 (1-1-1),faststart | -14 LUFS,真峰值低于 -1.5 dBTP |
| 课程和教程 | 原始分辨率(1920x1080 或 3840x2160) | -16 LUFS,真峰值最高 -1 dBTP |
| YouTube | 4K 母版(1080p 上传会损失画质),MP4 不含编辑列表 | 母版的混音 |
| 1920x1080 和 1080x1920 16 Mbps,带 SRT | 母版的混音 |
已验证的陷阱
在 macOS 上使用 Resolve Studio 21.1.1 和 21.1.2 测量,2026 年 10 月和 11 月。
机器方面
- 每次只运行一个 ffmpeg,使用
nice -n 15和-threads 2:96 个并行 ffmpeg 进程会使 Mac 死机并强制重启。 - 在 zsh 中,
$var不会分割成单词 (ffmpeg $args会出错):在 Python 中构建参数列表。 - 将输出写入工作文件夹:如果辅助脚本将图像保存到剪辑旁边,则会写入原始素材驱动器。
Resolve 设置与 API
- Resolve 21.1 将 Python 脚本功能转移到了 Studio:
run.py和两个 MCP 服务器都需要 DaVinci Resolve Studio。 - 必须将“外部脚本使用”设置为“本地”(偏好设置 > 系统 > 常规),否则
scriptapp("Resolve")返回 None。 StartRendering是脚本的最后一次调用:在此之后所有内容都返回 None,直到渲染结束。- 项目帧率在创建第一个时间线时锁定:之前设置好 (
project.py)。 - 分辨率设置:
SetSetting一次设置一个键,先设置useCustomSettings。 - MCP 超时(默认 10 秒,最多 60 秒)会返回错误,但脚本会在 Resolve 内部继续运行:再次运行前需检查状态。
- 没有修剪、移动或滑动操作:删除该项目并使用
startFrame、endFrame(不包含)和recordFrame重新添加。 startFrame从文件的第一个帧开始计数,即使有摄影机时间码。GetSourceStartFrame有时会读取早一帧(866 读取为 865),但放置位置是正确的:使用GetLeftOffset读取切入点。AddMarker每个帧只保留一个标记。ReplaceClip会清除剪辑的元数据。SetTrackEnable在某些时间线上返回 True 但实际上不生效:使用SetClipEnabled禁用剪辑,并在导出的帧上确认。- 在非当前时间线上调用
SetClipEnabled返回 False:先调用SetCurrentTimeline,然后使用GetClipEnabled确认。 DeleteClips(items, True)会影响所有轨道:先删除跨越该剪辑点的内容(对白、字幕)而不进行波纹编辑。- 语音隔离可通过脚本实现:
TimelineItem.SetVoiceIsolationState({"isEnabled": True, "amount": 100})可将房间混响降低 3 到 4 dB;Timeline.SetVoiceIsolationState(track, {...})设置整个轨道的一个控制。 - API 无法设置 ResolveFX 参数(面部美化、去瑕疵):由人工设置,或通过无障碍功能驱动 UI。
- 当屏幕锁定或 Resolve 最小化时,窗口会从无障碍功能中消失,但 API 仍可工作:通过导出的帧验证。
- Super Scale 是媒体池属性:
SetClipProperty("Super Scale", 2);替换文件需要重新设置。 Graph.SetLUT的 LUT 放在 Resolve 的系统 LUT 文件夹的子文件夹中,在RefreshLUTList()后通过相对路径传递。- 在 Resolve 播放时切换页面会导致挂起;
item.GetFusionCompByIndex(1).Execute(lua)可以在不打开 Fusion 页面的情况下编辑合成。
导入与媒体
- 每次导入只调用一个 ProRes 文件:在一次调用中导入多个 ProRes 4444(以及一次两个新的 ProRes 422)导致 Resolve 21.1 崩溃(
UiMpController,std::out_of_range)。相机 MP4 文件,一次 22 个,没有问题。 - 崩溃后,会有一个残留的
ResolvePython进程使得 MCP 的launch_resolve报告 Resolve 正在运行:杀死该进程并重新打开 Resolve。 ImportMedia使用路径列表时,在 21.1 版本上使用字典形式对 MP4 返回 None。- Resolve 根据路径缓存解码:重新渲染的文件需要新名称。
- MOV 中的 PCM 24 位小端格式被读取为白噪声:请使用
pcm_s24be。 - iPhone HEVC 导致 macOS 解码器崩溃:从 ProRes 进行编辑。
- iPhone HDR (HLG, Dolby Vision):我们自己的色调映射看起来褪色,被拒绝了两次。一开始就要求 SDR 导出。
- 1 帧的
.mov被导入为 150 帧的静止图像:至少提供 3 帧,然后在时间线上修剪。 - 直通 Alpha:Resolve 以未预乘的方式读取 Alpha,因此在透明画布上使用 ffmpeg
overlay创建的叠加层会变暗;使用pad=W:H:x:y:[email protected]定位。
渲染
- 使用
SelectAllFrames时,StartRendering有时返回 False:设置MarkIn/MarkOut(render.py 的mark),这也能剪掉延长时间线的音频尾音。 - 在仅音频任务之后立即创建的 H.264 任务输出不带视频:在
SetRenderSettings之前加载预设,并用 ffprobe 检查视频流。 - H.264 的
VideoQuality是上限而非平均值:20000 在 1440x2560 下给出 14.7 Mbps,27300 给出 20.7 Mbps。 - "Force sizing to highest quality" 不是
SetRenderSettings的键,但它包含在渲染预设中 (ForceHighestQualitySizing)。 ExportRenderPreset(name, path)将path视为文件夹;ImportRenderPreset以文件名命名预设。- 预设存储帧率:从时间线设置
FrameRate,否则 29.97 的时间线会被转换(render.py 会处理)。 ExportFrame(21.1.2) 在 0.75 秒内给出 1440x2560 的 4 帧,与渲染的差异在 41 到 46 dB 之间:无需渲染即可检查帧。
音频与转录
- 两个镜头之间的剪辑可能连接一个重复的词汇("decide, decide"):转录最终混音并查找重复内容。修复而不改变长度:在词汇之前结束第一段,在优秀镜头的停顿中更早开始第二段。
- 转录听不到低语和呼吸声:剪辑点来自相机自带音频 (
cut_list.py)。 - 有一次 Scribe 将一个词提前了 3 秒放置(在一声拍手和低语之后):在移动剪辑点前确认波形。
- MP3 音效会延迟约 60 毫秒(编码器延迟未补偿):对于需要精确到帧的音频使用 WAV。
- loudnorm 在单遍模式下是动态的,会改变已批准的混音;在两遍模式下会静默回退到动态模式:使用
loudness.py。
素材与平台
- Instagram 上的 25 fps:以 25 fps 服务(2026 年 11 月对服务文件的 ffprobe 测试)。卡顿来自 ProMotion iPhones,这些手机没有 25 或 50 Hz 模式。保持 25 fps 素材为 25 fps(转换为 30 会每五帧重复一帧);拍摄新素材时使用 1/50 快门的 29.97 fps。
- 50 Hz 灯光下的闪烁取决于快门而非帧率:1/50 和 1/100 不闪烁;30 fps 下的 1/60 会闪烁。
- Instagram 在手机上和服务端都会重新编码:基础交付约为 720x1280 在 1.4 Mbps,音频归一化到约 -14 LUFS,因此高于 -14 的母版没有增益。
qc.py --simulate显示哪些会留存。 - ffmpeg 的 CRF 会使暗色背景码率不足:一个 Reel 的码率只有 6.8 Mbps。在交付前检查实际码率。
- 无头运行的
claude -p在渲染开始时结束轮次,除非提示要求等待渲染完成。
脚本
| 脚本 | 功能 |
|---|---|
scripts/media/transcribe.py | 每个剪辑的 Scribe 逐词 JSON(密钥通过 stdin 传入,绝不在进程列表中显示) |
scripts/media/dossier.py | 每个剪辑的紧凑文本:带时间的句子、停顿、重复尝试,可选的帧表格 |
scripts/media/inspect_range.py | 范围的一张图像:帧、波形、带时间的词汇 |
scripts/media/cut_list.py | 镜头选择与剪辑 |
scripts/media/add_markers.py | 为编辑器添加标记 |
scripts/resolve/run.py | 在 Resolve 中执行 Python 脚本的入口 |
scripts/resolve/project.py | 创建或打开项目 |
scripts/resolve/assemble.py | 导入媒体并装配时间线 |
scripts/resolve/render.py | 渲染时间线 |
scripts/resolve/frames.py | 导出时间线中的特定帧 |
scripts/media/qc.py | 质量控制:检查帧数、转录、响度 |
scripts/media/loudness.py | 调整音频响度(线性增益和限制器) |
scripts/media/deliver.py | 最终交付版本清理 |
提示:所有脚本的详细用法请使用
--help参数查看。