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__的環境中執行腳本,然後以 JSON 格式輸出result。這比透過 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(每個對話階段一次),或者 Blackmagic 在 Resolve 的Developer/Scripting資料夾中的 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 上傳會損失畫質),不含 edit list 的 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-bit little-endian 會被讀取為白噪聲:請使用
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 fps 的影格率會被轉換(render.py 會處理)。 ExportFrame(21.1.2)在 0.75 秒內輸出 4 個 1440x2560 的影格,品質在渲染的 41 到 46 dB 之間:可以在不渲染的情況下檢查影格。
聲音與逐字稿
- 兩個鏡頭之間的剪輯可能會連接重複的字詞("decide, decide"):請對最終混音進行逐字稿,並尋找重複的內容。在不改變長度的情況下修復:在第一個片段中該字詞之前結束,在第二個片段中從良好鏡頭的停頓處更早開始。
- 逐字稿不會聽到耳語和呼吸聲:剪輯點應來自攝影機自己的聲音(
cut_list.py)。 - Scribe 有一次在拍掌和耳語之後,將一個字詞提前了 3 秒放置:在移動剪輯點之前,請在波形上確認。
- MP3 音效大約延遲 60 毫秒(編碼器延遲未補償):對於任何必須精確對齊影格的內容,請使用 WAV。
- 單次 loudnorm 會動態調整並改變已批准的混音;兩次 loudnorm 會靜默地回退到動態模式:請使用
loudness.py。
素材與平台
- 25 fps 在 Instagram 上:以 25 fps 發布(2026 年 11 月對發布檔案的 ffprobe 結果)。卡頓來自 ProMotion iPhone,它們沒有 25 或 50 Hz 模式。將 25 fps 素材保持為 25 fps(轉換為 30 fps 會每五幀重複一幀);以 29.97 fps 和 1/50 快門拍攝新素材。
- 在 50 Hz 燈光下閃爍取決於快門而非影格率:1/50 和 1/100 不會閃爍;1/60 在 30 fps 下會閃爍。
- 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 | 取鏡頭,放置於最安靜的影格邊界,縮短停頓,產生交錯縮放... |
...(此為翻譯後的局部內容,完整文件應涵蓋所有後續腳本)