引用:來源 → 逐字稿 → 語錄 → 標題卡與剪輯
工具: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. 挖掘語錄(由你判斷)
- 閱讀我們的集數逐字稿(如果有),列出主持人在該主題上提出的主張,每個主張附上其在該集的時間點。這些就是剪輯可以佐證或反駁的時刻。
- 針對每個主張,使用
citekit.py search '正則表達式' sources/transcripts/尋找候選段落,然後閱讀匹配處附近的.md檔案。也請完整閱讀整場講座:最好的句子不一定總是主持人提到的那幾句。 - 好的引用段落應該簡短(口語 5–25 秒)、內容自足、明確是說話者本人的立場(而非他總結對手的觀點),並且能夠放在標題卡上。請保留逐字稿中的原文。不要清理它們,否則
locate會找不到。 - 撰寫
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 連接器僅能讀取「會議記錄器」的逐字稿。它無法轉寫檔案。