影片剪輯 是做什麼的?
將一段未經編輯的長影片,轉換為精簡、可觀看的初剪——具決策力、內容感知,且經過自我驗證。由人類(你,模型)做出編輯決策:決定保留什麼;而附帶的腳本則負責執行機械性的精確任務(邊界對齊、無聲時間回收、記憶體安全渲染、自我檢查)。
使用時機
- 需要將原始/未編輯的影片(YouTube URL 或本地檔案)剪輯得更緊湊。
- 要求是「減半」、「剪掉無聊的部分」、「製作初剪/粗剪」。
- 不適用於:逐字填充詞的微手術、多機位 B-roll 組合、色彩/音訊修飾——這些屬於後續的 P1/P2 精修階段。此技能是 P0:驗證診斷結果並產出紮實的剪輯。
相依性
/video-understand 是必要的前置技能。請先執行它,並使用其驗證過的媒體、轉錄稿、分析和時間軸,而不是在此技能中重新建立。開始前,請確認它是否已安裝。如果未安裝,請警告使用者缺少此必要條件,並在處理媒體前停止。
啟動任何包含以下工具的環境(不要假設特定的 conda 環境名稱):
yt-dlp(下載),ffmpeg/ffprobe(剪輯/渲染/偵測)需在 PATH 中- Python 需安裝
faster-whisper(CPU 即可:device=cpu, compute_type=int8) 先檢查:yt-dlp --version,ffmpeg -version,python -c "import faster_whisper"。
專案協定工作流程
使用以下持久的專案檔案:
work/cut/edit-plan.json # 僅有人工編寫的保留/丟棄決策
work/timeline.json # 產生的精確範圍 + 來源/節目對應
review/01-cut/cut-summary.md
review/01-cut/timeline-map.png
review/01-cut/boundary-review.mp4
review/01-cut/full-proxy.mp4 # 可選:完整節目的節奏審查
每個標準決策都有一個穩定的 id、action(keep 或 drop)、start_s、end_s、reason 以及語義/轉錄稿的 evidence_refs。保留決策需按時間順序排列;V1 不支援重新排序或重複的來源片段。
重複使用共享的證據層,而不是重新轉錄或診斷:
python skills/video-understand/scripts/transcribe.py work/cache/audio16k.wav work/understand/transcript
python skills/video-understand/scripts/analyze.py work/understand/transcript.json work/understand/analysis.json
現有的精確腳本接受標準計畫。擴展單詞安全邊界,可選擇分配線性變速,然後產生共享時間軸:
python skills/video-cut/scripts/build_edit.py work/cut/edit-plan.json work/understand/transcript.json work/cache/edit-final.json
python skills/video-cut/scripts/assign_speed.py work/cache/edit-final.json work/understand/transcript.json
python skills/video-understand/scripts/build_timeline.py work/cache/edit-final.json work/timeline.json --fps-num SOURCE_FPS_NUM --fps-den SOURCE_FPS_DEN
使用 work/understand/media.json 中的確切有理數 FPS;切勿將 30000/1001 四捨五入為 30/1。審查 timeline-map.png 和簡短的 boundary-review.mp4。僅在需要審查整個節目節奏時才產生 full-proxy.mp4。將結果寫入 cut-summary.md,並將操作檢查報告指向該檔案。
記錄時間軸貢獻及其所需的來源輸入,更新操作整數 revision,並將 based_on 設定為所使用的確切理解修訂版:
"render": {
"kind": "timeline-transform",
"input": "../input/original-video.mp4",
"plan": "timeline.json"
}
共享的交付渲染器會在通過所有修訂檢查後對有效序列進行編碼。
獨立相容性工作流程
以下舊版檔案和指令在遷移期間仍受支援。build_edit.py 接受 edit_coarse.json 和標準的 decisions[];cut_render.py 接受 edit_final.json 和 timeline.json;此技能的 transcribe.py 是 ../video-understand/scripts/transcribe.py 的相容性包裝器。
將所有中間檔案保留在 work/ 下,交付到專案根目錄:
work/source.mp4 # 下載的原始檔案
work/audio16k.wav # 給 whisper 的 16k 單聲道音訊
work/transcript.json/.srt # 逐字時間戳
work/analysis.json # 診斷(無聲/填充詞/連續說話)
work/edit_coarse.json # *** 你的編輯決策(手寫)***
work/edit_final.json # 擴展後的緊湊渲染片段(自動產生;包含每段速度)
work/selfcheck/ # 剪輯重新轉錄 + 幀檢查
first_cut.mp4 # 交付成果
first_cut_report.md # 編輯 + 自我檢查報告
流程(6 個步驟)
1. 下載
最高可達品質 + 音訊。預設的播放器客戶端經常失敗(無法取得 / DRM / 需要登入)。可靠的備援方案是 android 客戶端:
yt-dlp --extractor-args "youtube:player_client=android" \
-o work/source.mp4 "<URL>"
如果無需登入只能取得混合的 360p(格式 18),請接受它並記錄此限制 — HD DASH 經常受 DRM/登入限制。然後提取音訊:
ffmpeg -y -i work/source.mp4 -ac 1 -ar 16000 work/audio16k.wav
2. 轉錄(逐字級別)
python scripts/transcribe.py work/audio16k.wav work/transcript [base.en]
產生 work/transcript.json(段落 + 每個單詞的開始/結束時間)和 .srt。使用 faster-whisper,CPU/int8,啟用 VAD,word_timestamps=True。
非英語: 傳遞多語言模型 + --lang,例如
python scripts/transcribe.py work/audio16k.wav work/transcript medium --lang zh
(.en 模型僅適用於英語)。先在一個簡短樣本上驗證語言。
注意: Whisper 會移除大部分發聲的「嗯/啊」——它們無法透過轉錄稿測量,因此填充詞檢測必須將下面的詞彙搜尋與無聲間隔分析相結合,不能僅依賴轉錄稿文字。
3. 診斷(「觀看」+「閱讀」)
python scripts/analyze.py work/transcript.json work/analysis.json
呈現:開頭/結尾的無聲時間、單詞間 ≥0.8 秒的停頓(附帶它們跟隨的單詞)、填充詞計數、WPM、說話比率、近似重複段落、>40 秒的獨白段落(可能是冗長的部分)。
要觀看畫面,每約 2 秒取樣一個幀製作成縮圖索引,並自行閱讀;確認場景結構 / 尋找黑畫面或垃圾幀:
ffmpeg -y -i work/source.mp4 -vf "fps=1/2,scale=240:-1,tile=10x10" -frames:v 1 work/contact.png
ffmpeg -i work/source.mp4 -vf "blackdetect=d=0.1:pic_th=0.98" -an -f null -
對於單一的靜態說話頭部鏡頭,不會有場景切換——請預期此情況,並注意後續的跳躍剪輯是正常的,並非故障。
4. 決定剪輯 — 手動撰寫 edit_coarse.json
這是編輯的核心。閱讀轉錄稿 + 分析,然後手寫一個粗略的計畫,包含保留區塊和丟棄區段,這些區塊和區段應無間隙無重疊地覆蓋整個時間軸,每個都有 in、out 和一行 reason。將緩慢的前言壓縮成幾個高價值的片段;保留實質內容為大區塊;丟棄明確的離題/冗長內容。保持時間順序;標準 V1 時間軸拒絕重新排序。目標是將執行時間大致減半。
- 格式規範 + 真實範例:
reference/edit_coarse.schema.md。 - 將每個區塊邊界對齊到句子/子句結尾——切勿在句子中間或口吃處開始/停止。使用:
它接受舊版的python scripts/inspect_bounds.py work/edit_coarse.json work/transcript.jsonkeep[]和標準的decisions[]計畫,並列印每個邊界附近的單詞,以便您可以將其調整到乾淨的邊緣。
5. 擴展 → 自動變速 → 渲染
將粗略區塊轉換為緊湊、無無聲時間的渲染片段,標準化說話速度,然後渲染:
python scripts/build_edit.py work/edit_coarse.json work/transcript.json work/edit_final.json 1.5
python scripts/assign_speed.py work/edit_final.json work/transcript.json # 添加每段速度
python scripts/cut_render.py work/edit_final.json work/source.mp4 first_cut.mp4
build_edit.py在內部停頓大於 SPLIT_GAP(第 4 個引數,例如 1.5 秒)處分割每個區塊,並將每個邊界修剪到無聲時間內(前 LEAD 0.10 秒 / 後 TRAIL 0.30 秒),這樣就不會裁剪到任何單詞,同時回收了無聲時間。較低的 SPLIT_GAP = 更緊湊但更斷續;對於講話節奏緩慢、深思熟慮的講者,建議使用 ~1.5,而不是 0.6(避免過多的微剪輯)。assign_speed.py(自動變速)— 按decision_ref分組渲染片段,測量每個手寫保留決策的說話速度,並將一個共享的speed因子以及每個片段產生的out_dur寫回edit_final.json。已經在舒適範圍內的速度保持為 1.0;超出範圍的速度僅移動到最近的死區邊界。相鄰的決策區塊速度差異不超過 0.08 倍,以避免「起伏感」。它僅改變速度——絕不改變保留的內容。在渲染前閱讀列印的表格:- 指標:words/min(英語)— 對於 CJK(zh/ja/ko)會自動切換為 chars/min。
- 預設值是首次運行的起點,並非經過驗證的常數:死區 145–185 WPM,速度限制在 1.0–1.3。這僅是加速:慢速的決策區塊可以被加快,而自然快速的講話則保持 1.0。短的決策區塊(<3.5 秒 / <6 個單詞)噪音太大無法測量,會繼承全域因子。
- 速率是在回收無聲時間後測量的,因此它比總體 WPM 更高,更接近發音速率。列印表格的審查是關鍵的:在渲染前仔細檢查並根據講者調整
deadband,不要盲目信任預設值。 - 參數(k=v):
mode=segment|global|off(預設segment;global=整個影片使用一個因子,off=停用),deadband=lo,hi,min_speed=/max_speed=,min_seg=/min_words=,lang=auto|en|cjk。要完全跳過此功能,只需不執行此步驟即可 —cut_render.py會將缺失的speed視為 1.0。
cut_render.py使用 N 個獨立的搜尋輸入 + concat 濾鏡,單次重新編碼。這是強制的:從一個解碼後的輸入修剪 N 次會使 ffmpeg 緩衝整個解碼後的串流(對於長影片來說是數十 GB)。請參閱reference/pitfalls.md。 當片段有speed時,它會重新設定每個輸入的時間(setpts影片 /atempo音訊)並在 concat 前標準化每個輸入的 fps/timebase/sample-rate,以保持影音同步;沒有速度時則回退到確切的原始圖形。- 最終比較:
video-edit-compare讀取標準時間軸加上實際的最終交付檔案。它將最終像素投射回來源時間,將變速片段拉伸到其來源持續時間,並以黑色顯示丟棄的範圍。
6. 自我檢查 — 在通過此檢查之前不要宣佈完成
# 協定精確度回歸檢查(從儲存庫根目錄執行)
python skills/video-cut/scripts/check_project_protocol.py
# 重新轉錄完成的剪輯
ffmpeg -y -i first_cut.mp4 -ac 1 -ar 16000 work/selfcheck/cut_audio16k.wav
python scripts/transcribe.py work/selfcheck/cut_audio16k.wav work/selfcheck/cut_transcript
# 內容 + 填充詞 + 邊界檢查
python scripts/verify_cut.py work/edit_final.json work/transcript.json work/selfcheck/cut_transcript.json
# 幀 / 黑畫面 / 接合點檢查(接合時間使用速度調整後的 out_dur,因此保持準確)
python scripts/selfcheck_frames.py work/edit_final.json first_cut.mp4 work/selfcheck
# A/V 同步 + 無殘留無聲時間
ffprobe -v error -select_streams v:0 -show_entries stream=duration -of csv=p=0 first_cut.mp4
ffprobe -v error -select_streams a:0 -show_entries stream=duration -of csv=p=0 first_cut.mp4
ffmpeg -i first_cut.mp4 -af silencedetect=noise=-32dB:d=1.5 -f null -
通過門檻:
| 檢查項目 | 通過條件 |
|---|---|
| 黑畫面 | 無 |
| 接合點幀 | 乾淨 — 僅有自然的頭部跳動,無黑/凍結/撕裂畫面 |
| A/V 同步 | 影片與音訊持續時間差異在約 1 幀(≈30ms)內;總和 ≈ 啟用變速時的 final_duration_s_after_speed |
| ≥1.5 秒的無聲時間 | 0(說話比率應明顯上升) |
| 遺漏/重複片段 | 無 — 所有關鍵內容都存在(difflib ~0.90 是可接受的;較低的比率通常是含糊說話導致二次轉錄的噪音,而非內容遺失 — 在歸咎於剪輯前,請透過關鍵字存在與否來驗證) |
| 被切斷的半句話 | 無 — 邊界落在無聲時間 / 句子結尾 |
誠實的填充詞說明: 段落級別的剪輯不會達到「幾乎沒有填充詞」。每分鐘的密度甚至可能上升,因為保留的內容是對話式的。請誠實報告此情況;逐字級別的填充詞移除屬於 P1/P2(並且會使單一鏡頭的說話頭部影片明顯斷續)。
最後
撰寫 first_cut_report.md:來源摘要、剪掉了什麼及原因、自我檢查表格,以及任何誠實的說明(360p 來源的品質上限、填充詞密度等)。如果應用了變速,請揭露:多少片段被重新計時以及速度範圍(來自 assign_speed.py 的表格 / speed_params),以便讀者知道剪輯並非 1:1 即時速度。另請注意,verify_cut.py 的每分鐘填充詞數在加速後會略高(相同的單詞,更短的分鐘)——這是一個報告假象,並非增加了填充詞。
關鍵原則(得來不易的原則)
- 你決定內容;腳本決定幀。 手寫
edit_coarse.json;讓build_edit.py處理精確度,assign_speed.py處理速度。 - 速度影響節奏,絕不影響內容。 自動變速為來自同一個
decision_ref的所有渲染片段設定相同的速度,僅將超出範圍的速度移動到最近的死區邊界,將相鄰決策的 delta 限制在 0.08 倍,並將輸出限制在 1.0–1.3。atempo保留音調。審查表格;使用mode=off或跳過此步驟以停用。 - 絕不裁剪任何單詞。 邊界始終落在無聲時間內。
- 僅使用記憶體安全渲染(N 個搜尋輸入 + concat)。切勿對一個輸入進行多次修剪。
- 果斷決策。 做出合理的判斷並繼續;不要過度詢問。
- 自我檢查不可妥協。 在宣佈完成前,務必重新轉錄 + 檢查幀。
- 請參閱
reference/pitfalls.md獲取完整的陷阱與解決方案清單。