Clipify
尋找影片中最有趣的時刻,將它們剪輯成獨立片段,可選擇將 16:9 轉換為 9:16(人臉平移或分割畫面),並加上 Opus 風格的逐字字幕。
輸入
- 影片檔案路徑(使用者將提供;否則詢問)
- 可選:要求的格式(9:16、16:9、1:1)— 若未提供,在選出候選片段後詢問
- 可選:字幕樣式偏好 — 若未提供,在加字幕前詢問
工具(僅使用最快路徑)
- Whisper:
whisper --model tiny.en --word_timestamps True --output_format json(比small.en快約 10 倍;英文品質足夠)。若非英文:--model base(省略--language)。 - ffmpeg: 硬體解碼為選擇性且依平台而定 — 使用
-hwaccel auto,或省略(macOS:videotoolbox;Linux:vaapi/cuda/無)。渲染時加上-preset ultrafast。最終主檔使用-c:v libx264 -crf 20。 - Numpy 用於音頻對齊(FFT 互相關)。不需要 scipy/cv2。
- 腳本:
<skill-dir>/scripts/(其中<skill-dir>為包含此 SKILL.md 的目錄,通常是~/.claude/skills/clipify/)analyze.py— 從兩個 ROI 動態檔案生成說話者時間線build_pan.py— 帶硬切換的 ffmpeg 裁切 x 表達式build_ass.py— 從 Whisper JSON 生成 Opus 風格 ASS 字幕audio_align.py— 在較長來源中尋找子片段的偏移量
工作目錄:/tmp/clipify/(開始時建立,保留除錯用產物)。
工作流程
步驟 1 — 尋找最有趣的部分
mkdir -p /tmp/clipify
ffmpeg -y -i "$VIDEO" -vn -ac 1 -ar 16000 /tmp/clipify/audio.wav
whisper /tmp/clipify/audio.wav --model tiny.en --word_timestamps True --output_format json --output_dir /tmp/clipify --language en
讀取產生的 JSON(或 .txt),選出 3–5 個候選片段。要掃描的有趣訊號:
- 笑點與反應: 如「什麼」、「等等」、「不會吧」、笑聲、「哈哈」、髒話
- 反轉時刻: 設定問題 → 意想不到的回答
- 尷尬停頓: Whisper 片段中有長間隔,或填充詞(「呃」、「嗯」)
- 自嘲 / 可引用的名言: 簡短陳述句,可獨立存在
- 音頻高峰: 透過
ffmpeg -af volumedetect偵測,或尋找快速來回對話(交替的短 Whisper 片段)
對每個候選片段,提議:[開始時間, 結束時間, 為何有趣, 建議標題]。目標為 10–25 秒的片段。顯示清單並讓使用者確認/選擇。
步驟 2 — 修剪每個選定的片段
ffmpeg -y -ss "$START" -t "$DURATION" -i "$VIDEO" -c copy /tmp/clipify/clip_$N.mp4
(使用 -c copy 進行即時修剪。僅在需要影格精確剪輯時才重新編碼。)
步驟 3 — 決定輸出格式
詢問使用者(若已指定則跳過):「9:16(TikTok / Reels)、16:9(YouTube)或 1:1(Insta 貼文)?」
步驟 4 — 若為 16:9 → 9:16:人臉間平移與分割畫面
使用 ffprobe 偵測來源寬高比。若來源為 16:9 且目標為 9:16,則詢問:
「兩個選項:(a) 硬切平移,跟隨說話者(一次單一畫面顯示人臉),或 (b) 分割畫面堆疊,同時顯示兩張臉。您想要哪個?」
若只有單一人臉(單一說話者片段),則跳過問題。對於單一說話者,只需中心裁切即可。
步驟 4a — 人臉間平移(推薦用於快速切換的對話)
-
定位兩張人臉的 ROI。 取樣一個影格:
ffmpeg -ss <中間時間> -i <片段> -frames:v 1 /tmp/clipify/probe.jpg。讀取它。目測每張臉的嘴巴+下巴區域,轉換為來源像素空間的x,y,w,h。(無需 cv2 — 片段內攝影機靜止;一個影格足夠。)繪製方塊驗證:ffmpeg -i probe.jpg -vf "drawbox=x=$LX:y=$LY:w=$LW:h=$LH:[email protected]:t=4,drawbox=x=$RX:y=$RY:w=$RW:h=$RH:[email protected]:t=4" verify.jpg最多迭代 兩次。方塊應覆蓋嘴巴+下巴,避開手部/麥克風。不要過度調校 — 影格差異法具有容錯性。
-
提取每個 ROI 的逐影格動能:
ffmpeg -y -i clip.mp4 -filter_complex " [0:v]split=2[a][b]; [a]crop=$LW:$LH:$LX:$LY,format=gray,tblend=all_mode=difference,signalstats,metadata=mode=print:key=lavfi.signalstats.YAVG:file=/tmp/clipify/L.txt[la]; [b]crop=$RW:$RH:$RX:$RY,format=gray,tblend=all_mode=difference,signalstats,metadata=mode=print:key=lavfi.signalstats.YAVG:file=/tmp/clipify/R.txt[ra] " -map "[la]" -f null - -map "[ra]" -f null - -
建立說話者時間線(最小停留時間 1.0 秒 — 簡短插話合併到前一位說話者):
python3 <skill-dir>/scripts/analyze.py /tmp/clipify/L.txt /tmp/clipify/R.txt 1.0 > /tmp/clipify/segments.json -
為來源的 9:16 垂直條帶選擇平移 x 座標。 假設來源寬度 W=1920 且目標寬度 W=1080,裁切條帶寬度 = 608。
- LEFT_X =
face_left_center_x - 304(限制 ≥ 0) - RIGHT_X =
face_right_center_x - 304(限制 ≤ source_W - 608)
- LEFT_X =
-
產生硬切 x 表達式並渲染:
EXPR=$(python3 <skill-dir>/scripts/build_pan.py /tmp/clipify/segments.json $LEFT_X $RIGHT_X) ffmpeg -y -i clip.mp4 -filter_complex \ "[0:v]crop=608:1080:x='$EXPR':y=0,scale=1080:1920:flags=lanczos[v]" \ -map "[v]" -map 0:a -c:v libx264 -preset fast -crf 20 -pix_fmt yuv420p \ -c:a aac -b:a 192k /tmp/clipify/clip_panned.mp4假設來源為 1920×1080;若為 4K 來源,則先降級至 1920×1080 或將所有座標加倍。
步驟 4b — 分割畫面(兩張臉始終可見)
兩個堆疊的圖塊,每個 1080×960。活躍說話者的圖塊在上方 — 疊加層會在說話者切換時翻轉。
[0:v]split=2[a0][a1];
[a0]crop=Wcrop:Hcrop:LX_tile:LY_tile,scale=1080:960,split=2[lt0][lt1];
[a1]crop=Wcrop:Hcrop:RX_tile:RY_tile,scale=1080:960,split=2[rt0][rt1];
[lt0][rt0]vstack[layoutL];
[rt1][lt1]vstack[layoutR];
[layoutL][layoutR]overlay=0:0:enable='<RIGHT_SPEAKER_ENABLE>'[v]
從 segments.json 建立 <RIGHT_SPEAKER_ENABLE>,表示為 between(t,a,b)+between(t,a,b)+...(涵蓋右側說話者片段)。圖塊裁切應以每張臉為中心,目標約 720×640(1.125:1 以匹配 1080×960)。
步驟 5 — 加入字幕
詢問一次(僅在使用者尚未指定樣式時):
「三種字幕樣式:opus(大膽粗體白色,黃色活躍單字高亮)、karaoke(4 字組,綠色高亮)、minimal(簡潔 Helvetica,無高亮)。或貼上您喜歡的範例。」
若他們貼上參考圖片/範例:盡可能匹配字體、大小、粗細、顏色、位置和動畫 — 手動撰寫自訂 ASS 或擴展 build_ass.py。
否則使用預設:
# 在修剪後的片段上重新執行 Whisper,以獲得相對於片段起始的精確時間戳
whisper /tmp/clipify/clip_panned.mp4 --model tiny.en --word_timestamps True --output_format json --output_dir /tmp/clipify --language en
python3 <skill-dir>/scripts/build_ass.py /tmp/clipify/clip_panned.json /tmp/clipify/captions.ass opus
燒錄字幕:
ffmpeg -y -i /tmp/clipify/clip_panned.mp4 -vf "subtitles=/tmp/clipify/captions.ass" \
-c:v libx264 -preset fast -crf 20 -c:a copy "$OUTPUT.mp4"
步驟 6 — 交付
- 將每個輸出儲存到
<來源目錄>/clipify_out/(若無則建立) - 每個片段輸出一行:名稱、時長、有趣之處、輸出路徑
- 輸出第一個輸出路徑(或開啟它 — Linux
xdg-open <路徑>,macOSopen <路徑>),以便使用者檢查 - 提供迭代選項(不同樣式、不同 ROI、切換至分割畫面、重新調整字幕時間)
常見陷阱(先前運行經驗 — 請勿重複)
- 不要過度調校 ROI。 最多兩次迭代。動態差異具有容錯性 — 覆蓋嘴巴+下巴的較寬 ROI 即使未完美對準嘴巴中心也能正常運作。
- 注意片段內的場景切換。 執行
ffmpeg -filter:v "select='gt(scene,0.3)',showinfo" -f null -計算切換次數。若 16:9→9:16 片段有多次切換,固定的人臉 ROI 僅適用於主場景;警告使用者,並提議選擇單一取鏡片段或接受切換期間的偏移構圖。 - 來源解析度很重要。 若來源為 4K,先降級至 1920×1080(更快,對 9:16 輸出而言品質足夠)或將所有 ROI/平移座標乘以 2。
- 來源中已嵌入字幕。 有些「原始」片段仍有字幕。若是如此,透過音頻互相關(
audio_align.py)找到無字幕主檔並從那裡修剪。 - 若短片段足夠,不要在完整的長片來源上執行 Whisper。 在步驟 2 之後才對修剪後的片段執行 Whisper;僅在步驟 1 中需要文字稿來尋找有趣時刻時,才對完整來源執行 Whisper。
- 用一句話說明計畫,然後行動。 不要敘述每次迭代。