Communitygithub.com

RolynTrotter/vibe-podcast

從源材料(YouTube 講座、演講、我們自己的過往集數)中挖掘可引用的語錄,並將其轉換為帶時間戳的剪輯和引用標題卡,用於播客製作。

vibe-podcast 是什麼?

這個技能專為播客製作人設計,自動化將原始素材(如 YouTube 影片)轉換為精準引用、時間戳剪輯和視覺標題卡(lower thirds)的流程。它整合了 yt-dlp 下載、mlx-whisper 語音轉文字、以及基於正則表達式的語錄搜尋與校對工具。使用者可以快速比對播客主持人提出的主張(claims),從講座或訪談中找到最佳佐證段落,並產出標準化的引用 JSON 檔。最終輸出包含可直接用於 Final Cut Pro 的 FCPXML 標題卡、以及供後期剪輯使用的音訊/影片剪輯預覽,大幅縮短從原始素材到成品播客的編輯時間。支援 macOS 環境,並透過 citkit.py 指令碼整合所有步驟,確保操作可重複且不覆寫原始檔案。

相容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/RolynTrotter/vibe-podcast/tree/HEAD/.claude/skills/citations

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

引用:來源 → 逐字稿 → 語錄 → 標題卡與剪輯

工具:agent-citations/citekit.py(stdlib Python 加上 mlx_whisper 和 ffmpeg)。它絕不會覆寫輸入檔,且在沒有 --force 參數時拒絕覆蓋自己的輸出。

批次目錄結構

每個主題一個資料夾,置於 active_edits/<subject>/ 下:

sources/video_sources.txt          使用者提供的 URL 列表
sources/audio/<id>.m4a .info.json  來自 yt-dlp(步驟 1)
sources/transcripts/<id>.json .md  步驟 2
podcast/<ep>.json .md              我們自己集數的字幕,匯入(步驟 2b)
sources/quotes.json                步驟 3,由你撰寫
sources/citations/                 步驟 4 輸出

1. 下載(由使用者執行)

YouTube 在這裡封鎖了匿名的 yt-dlp。請提供以下指令給使用者,而非由你直接執行。 它需要 Firefox cookies 和 tv/safari 客戶端,並保持音訊較小(Whisper 只需要 16 kHz 單聲道):

cd active_edits/<subject>/sources && for id in ID1 ID2; do yt-dlp --cookies-from-browser firefox --extractor-args "youtube:player_client=tv,web_safari,mweb" -f "wa/w" -x --audio-format m4a --postprocessor-args "ExtractAudio:-ac 1 -ar 16000" --write-info-json --write-auto-subs --sub-langs en --sub-format json3 --no-overwrites -o "audio/%(id)s.%(ext)s" "https://youtu.be/$id"; done

若要處理頻道的歷史影片,先列出清單,再挑選:yt-dlp --flat-playlist --print "%(id)s %(duration_string)s %(title)s" https://www.youtube.com/@CHANNEL/videos。 若清單很長,可使用 Jev (vibe-classification) 來分類標題。

2. 轉寫

  • citekit.py transcribe sources/audio/<id>.m4a:使用 mlx-whisper large-v3-turbo,含詞級時間戳。 初始提示(initial prompt)為影片標題與描述,可修正大部分名字拼寫。在此 Mac 上運行速度約為 85 倍即時(一段 15 分鐘的片段約需 35 秒;模型只會下載一次)。
  • citekit.py captions sources/audio/<id>.en.json3:YouTube 的自動字幕。瞬間完成,但無標點符號。 tv 客戶端通常不會儲存它們。
  • 2b. 我們自己的集數:citekit.py itt EP.itt --id podcastN --media EP.mp4 --out active_edits/<subject>/podcast --fix 'REGEX=>Right'。 FCP 的字幕常常搞錯名字。以第 1 集為例:--fix '\bver ?m+[iu]l+e?\b=>Vermeule' --fix '\bvermil\b=>Vermeule' --fix '\bAsian Vermeule=>Adrian Vermeule'。 第 1 集也有詞級修正記錄在 podcast/podcast1.fixes.json 中(Schmidt→Schmitt 等),重新匯入時會重播這些修正。

隨手修正錯譯(不必詢問)

當你發現一個確定的聽錯(名字、扭曲的引文、Whisper 重複的結巴),請修正它: citekit.py fix TRANSCRIPT '舊詞=>新詞' [...] [--near 秒數]。它會原地編輯 <id>.json/.md(新詞沿用舊區段的時間,匹配可跨越段落,結尾標點符號會保留),保留未修改的引擎輸出為 <id>.raw.json,並將每次修正記錄在 <id>.fixes.json 中。transcribe 和 itt 會重播這份記錄,因此修正可在重新執行後保留。不確定嗎?改為收聽該處,不要亂猜:ffmpeg -ss T -to T2 -i audio.m4a x.wav && mlx_whisper x.wav --output-format txt。先修正,再用修正後的詞彙進行引用:locate 會比對逐字稿。

3. 挖掘語錄(由你判斷)

  1. 閱讀我們的集數逐字稿(如果有),列出主持人在該主題上提出的主張,每個主張附上其在該集的時間點。這些就是剪輯可以佐證或反駁的時刻。
  2. 針對每個主張,使用 citekit.py search '正則表達式' sources/transcripts/ 尋找候選段落,然後閱讀匹配處附近的 .md 檔案。也請完整閱讀整場講座:最好的句子不一定總是主持人提到的那幾句。
  3. 好的引用段落應該簡短(口語 5–25 秒)、內容自足、明確是說話者本人的立場(而非他總結對手的觀點),並且能夠放在標題卡上。請保留逐字稿中的原文。不要清理它們,否則 locate 會找不到。
  4. 撰寫 sources/quotes.json:
{"project": "Ep1 Vermeule citations", "fps": 29.97, "episode": "../podcast/podcast1.json",
 "quotes": [{"id": "publius-weak-exec", "source": "Bw9M01S8MFI", "approx": 1234,
   "text": "逐字稿中的確切文字",
   "episode_at": 2575, "episode_context": "主持人討論漢彌爾頓與羅馬獨裁官",
   "why": "他本人對該論點的陳述", "tags": ["行政權", "聯邦論"],
   "citation": {"author": "~Adrian Vermeule", "title": "帕布里烏斯悖論", "date": "2018年6月5日",
                "publication": "喬利講座,倫敦政經學院"}}]}

episode(相對於 quotes.json 的路徑)是我們集數的逐字稿。有了它,每條語錄的 episode_at(大致是主持人提出該觀點的時間)就變成插入點:即該時間點主持人那句結束的地方。引用完整的想法。如果下一句話能完整呈現圖像或論證,就包含進來(目標是 5–25 秒的剪輯,而非硬性上限)。

citation 預設使用影片標題、上傳日期和頻道。請將真實的場合與日期(來自描述)以每個講座為單位寫入頂層的 "sources": {"<id>": {"citation": {...}}} 中。單條語錄自身的 citation 會覆寫該預設值。作者需加上節目的 ~ 前綴,而 \n 可將長標題分成兩行。

檢查他所引用的任何內容(漢彌爾頓、卡夫卡、《學說彙編》)是否與原文一致。Whisper 會聽到看似合理但錯誤的詞彙:在帕布里烏斯講座中,它將卡夫卡的「祭品壺」(sacrificial pitchers)誤聽成「圖片」(pictures)。

4. 標題卡與剪輯

citekit.py cues sources/quotes.json 將每條語錄對齊到詞級時間戳(模糊比對;若匹配分數低於 0.8 則發出警告),並寫入 sources/citations/:

檔案用途
citations.md以集數順序排列的審閱表:插入時間(錄製時間及扣除更早插入後的時間)、主持人在剪切入點前的話語、語錄內容、來源/剪輯時間、YouTube ?t= 連結
clips.json供代理剪輯使用:來源入/出點、含緩衝的入/出點(預設兩側各 5 秒,可透過 --pad-before/--pad-after 設定,單條語錄可指定 pad_before/pad_after)、insert_at、insert_at_after_earlier_inserts、insert_after
citation_cues.json供 agent-motion/titles_fcpxml.py 使用:適用於已插入剪輯的集數的引用標題卡(Citation v2),時間設定在說話者開始說話時出現。未放置的語錄會頭尾相接排列
download_clips.sh由使用者執行:僅下載這些含緩衝範圍的全高畫質影片(使用 --download-sections)

接著執行 citekit.py audio-clips sources/citations/clips.json 從本機音訊裁剪出含緩衝的音訊預覽,以便用耳朵檢查。若要將標題卡放入 FCP,請遵循 motion-graphics 技能:python3 agent-motion/titles_fcpxml.py sources/citations/citation_cues.json -o citations.fcpxml。

注意事項

  • transcribe 會自行解碼為 16 kHz WAV,並拒絕解碼後長度遠短於影片的音訊。使用不同 -f 參數續傳的 yt-dlp 下載會將兩種編碼器拼接在同一個 .m4a 中(2026-10-18 出現過:AAC-LC 後接 HE-AAC),而 ffmpeg 會默默丟棄切換點之後的所有資料。請使用 --no-continue 重新下載。絕不要在變更格式後續傳。

  • 用耳朵驗證剪輯,或以低成本方式驗證:執行 mlx_whisper citations/audio_clips/<id>.m4a --output-format txt 應可顯示語錄內容,外加幾秒的前導與結尾。

  • Whisper 時間戳在詞級上誤差約 ±0.3 秒。這就是剪輯需要緩衝區的原因。FCP iTT 字幕只對整行進行時間標記,因此 itt 會將每個單詞平均分散到每一行中。

  • Wispr Flow 連接器僅能讀取「會議記錄器」的逐字稿。它無法轉寫檔案。

相關技能