課程影片製作器
將一個課程的投影片組轉換為完整的教學影片:合成語音朗讀旁白,每張卡片、步驟或列在語音到達時精準出現,轉場方式變化多端,每次揭示伴隨柔和音效,鋼琴背景音樂在語音時降低音量,學習者暫停處會出現倒數計時,關鍵項目加上手繪圓圈,字幕以獨立檔案提供,實作課程中則嵌入真實的螢幕錄影。一次只處理一個課程;執行、檢查,然後交付。
輸入資料來自 lesson-slide-designer 技能:slides/<課程>/<課程單元>/slides.json、<檔案名稱>.pptx 及 _preview/slide-NN.png。如果這些檔案不存在,請先建置投影片組。越南文版本(kieu、loi_doc、_kiem/)的投影片組也能讀取。
輸出在 videos/<課程>/<課程單元>/(路徑與投影片組對應,否則為 <投影片資料夾>/video/):
| 檔案 | 說明 |
|---|---|
<檔案名稱>.mp4 | 影片:1920x1080,30 fps,H.264,AAC 48 kHz,-16 LUFS |
<檔案名稱>.<語言>.vtt / .srt | 獨立字幕檔案,絕不內嵌(否則會遮蓋投影片文字);上傳 .vtt 做為隱藏字幕 |
<檔案名稱>.narration.txt | 純淨旁白,每行一個口語節拍;` |
<檔案名稱>.storyboard.json / .md | 事件時間線:從 pptx 測量出的區域、每個區域出現的行數、特效、轉場、圓圈、縮放、倒數計時;.md 供人工檢查 |
<檔案名稱>.voice.mp3 + .timeline.json | 旁白以及每一行的精確開始與結束時間(行內容快取於 .voice.mp3.cache/ 中) |
<檔案名稱>.events.json、.audio.mp3 | 聲音事件與三層混音 |
video.json(選用,由你撰寫) | 每張投影片的覆寫設定:揭示行數、轉場、停留、圓圈、縮放、螢幕錄影 |
_check/ | contact-NN.png(完成影片的幀影像)、<檔案名稱>.report.md(品質測量)、voice-review.txt(Whisper 語音檢查)、voice-exceptions.txt |
_segments/ | 每張投影片渲染一個片段,當投影片未變更時重複使用 |
工具:Python 3.10+(需安裝 python-pptx、numpy、Pillow;opencv-python 用於錄影平滑處理;faster-whisper 用於語音檢查);Node 18+(需在 assets/engine 執行 npm install 安裝 puppeteer-core)以及 Chrome、Chromium 或 Edge;ffmpeg/ffprobe。語音引擎:Kokoro(pip install kokoro soundfile)或 VieNeu-TTS,兩者皆為 Apache 2.0 授權且在本機執行。如果語音引擎位於自己的虛擬環境中,請將 KOKORO_PYTHON 或 VIENEU_PYTHON 設為該 Python 直譯器路徑。GPU 有助於加速,但非必要。
工作流程
第 0 步:讀取投影片組並確認需求
- 讀取
slides.json(旁白、製作備註、投影片類型),並瀏覽_preview/contact-sheet.png。如果課程原始資料存在,一併讀取以了解學習者練習位置及提問處。 - 讀取
references/video-standards.md(標準:單一主題、腳本、開場-主體-結尾、轉場、互動)與references/motion-and-sound.md(每種投影片類型對應的行為,以及影片必須動態但不雜亂的原則)。 - 確認:語言與語音(slides.json 中的
language;英文預設為 Kokoroam_michael,女性聲音用af_heart,其他 Kokoro 語言依語音前綴,越南文用 VieNeu);課程是否需要真實螢幕錄影(任何教授點擊、打字或軟體操作的課程:需要,請見第 2 步);哪些問題投影片需要暫停及其暫停秒數;需要標記圓圈或縮放的項目。旁白已在投影片步驟中為文字轉語音編寫完成。請勿在此重寫教學內容;僅在語音讀錯時修正單字發音。
第 1 步:建置故事板並檢查
python scripts/storyboard.py slides/<課程>/<課程單元>/slides.json
閱讀 <檔案名稱>.storyboard.md:每張投影片的區域、每個區域出現的行數、轉場、停留、圓圈。對照 motion-and-sound.md 第 4 節進行檢查:第三張卡片應在語音讀到第三個想法時出現;相鄰卡片不可使用相同轉場;問題投影片需停留;類似「圈選答案 B」的備註應產生圓圈。如有問題,使用 video.json(範本見 templates/video.example.json)修正後重新執行。「未找到區域」表示該投影片以完整畫面呈現:對於 image 類型沒問題,但請檢查 pptx 中其他類型是否正確。
第 2 步:真實螢幕錄影(實作、手動操作與軟體課程)
當課程教授操作步驟時,請展示真實操作。絕對不要用截圖或模擬頁面取代。規則請參考 references/screen-recording.md。簡要說明:
- 準備視窗:使用私密瀏覽器視窗或乾淨帳號,無通知,側邊欄無真實姓名。在 Windows 上,
python scripts/record_screen.py --list可列出視窗標題。 - 錄製:
python scripts/record_screen.py videos/<課程>/<課程單元>/clips/S07-名稱.mp4 --window "<標題>" --seconds 40 --size 1280x720 [--crop-top 90] [--scroll "6:200:4"](Windows;其他系統請使用 OBS 或 ffmpeg)。透過瀏覽器工具自行操作點擊與打字,或讓使用者操作。僅使用--scroll或流暢的頁面內捲動(每 0.6 到 0.8 秒執行一次window.scrollBy({top: 200, behavior: 'smooth'})),切勿逐格跳動。每次結果出現後停留 3 到 5 秒;長內容以緩慢速度捲動(每秒 150 到 250 像素)讓學習者能夠閱讀。 check_motion.py會在每次錄製後執行。若有殘留跳動:執行python scripts/smooth_scroll.py <輸入> <輸出>,然後再次測量直到為零。檢查片段頂部區域是否有帳號名稱或雜項分頁。- 在
video.json中宣告:"S07": {"clip": {"file": "clips/S07-名稱.mp4", "mode": "frame", "speed": "auto"}}。該投影片應為image或two-column類型且包含大型畫面;將片段的第一幀放到投影片上(composite_clips.py --first-frame clips/S07-名稱.mp4 slides/<課程>/<課程單元>/S07-第一幀.png)並重新建置投影片組,使片段淡入時不會產生跳動。 - 執行
make_video.py ... --extend:當錄影長度超過旁白時,會自動加入靜音。切勿將錄影速度調至 1.25 倍以上。
第 3 步:用單一指令渲染
python scripts/make_video.py slides/<課程>/<課程單元>/slides.json [--extend]
包含九個步驟:故事板、旁白(透過 gpu_lock.py)、Whisper 檢查、渲染計畫、三層混音、影像(無頭 Chrome,逐張投影片)、螢幕錄影、字幕、最終檢查。對於超過 3 分鐘的課程,請在背景執行(首次渲染約每分鐘影片需一至二分鐘,投影片層級修正則只需數秒)。當程式停止時:
- 語音檢查(
_check/voice-review.txt):Whisper 先完整聽取一次,僅針對可疑行重新生成並再次檢查,最多三輪。它會過濾 Whisper 對短詞的誤判、在音樂背景下虛構的片語,以及第二次提示聆聽後正確的內容;僅在長詞、專業術語、名稱或連續數詞時停止。使用ffmpeg -ss 1:23 -t 4 -i <voice.mp3> clip.wav聆聽特定位置。如果語音錯誤,請在make_voice.py的PRONUNCIATION中加入重拼(字幕保留原始拼法)或修改slides.json中的單字;如果 Whisper 誤聽,請將該詞加入_check/voice-exceptions.txt。然後使用--from-step 2重新執行(快取行不會重新生成)。 - 最終檢查失敗(
_check/<檔案名稱>.report.md):修正原因(使用--music-db調整音量、透過平滑錄影處理跳動、重新渲染修正長度)並從該步驟重新執行。
第 4 步:交付前先檢查
- 檢視
_check/contact-NN.png(每張投影片三幀:到達時、穩定時、即將離開時),對照references/quality-checks.md的檢查清單:文字上是否有封面殘留、文字是否因縮放而被裁切、轉場期間是否同時顯示兩張投影片、倒數計時是否蓋住文字、錄影是否超出畫面、第一幀是否為空白。若要仔細檢查特定時刻:node assets/engine/render.mjs sheet <storyboard.json> <timeline.json> _check/zoom --from 40 --to 46 --every 0.25。 - 聆聽兩處:前 20 秒與一個轉場處(
ffmpeg -ss 0 -t 20 -i <mp4> -vn test.mp3)。音效應清晰可聞但明顯低於語音,音樂音量低。 - 透過
video.json或相關腳本修正,重新渲染(僅變動的投影片會重新渲染),再次檢查,直到完整檢核通過。在檢視最新渲染的連續幀圖之前,絕對不要回報影片已完成。
第 5 步:回報
提供使用者:mp4 與 vtt 路徑;實際長度;語音與速度(每秒字數);投影片數、特效數、暫停數;檢查報告(判定結果、LUFS、跳動次數);合成的錄影片段;加入發音表或 Whisper 例外清單的單字;未解決的警告及其原因。顯示連續幀圖。
連續多個課程單元
在整個課程中保持語音、音樂音量和音效一致;不同課程單元的差異不應超過 1.5 LU(比較每個報告的 LUFS 數值)。發音表和 Whisper 例外清單會隨時間累積,請勿重設。錄影保留在 videos/<課程>/<課程單元>/clips/ 中,每次錄製使用新的檔案名稱。如果投影片組被拆分為多個主題影片(V<編號>-<名稱>/slides.json),則逐個子投影片組進行渲染。
原則
- 標準是固定的(
video-standards.md):每片一個主題、腳本、開場-主體-結尾、各部分之間的口語轉場、互動。節奏、畫面與聲音是你發揮創意的地方。 - 畫面與語音相輔相成,語音絕不朗讀投影片文字。 區域在語音到達時出現;正在被朗讀的文字不應移動。
- 暫停與畫面內容一致:旁白說「暫停影片」時,應停留 3 秒且無倒數計時(學習者自行按暫停;在「60 秒」標籤旁邊顯示執行計時器會造成矛盾)。沒有該片語的問題則根據指定時間停留,最多 60 秒,並顯示倒數計時。
- 注意力圓圈:答案與類似「圈選 B」的備註會產生明顯的圓圈;帶有強調語氣的想法會自動產生淡出圓圈;卡片使用橢圓形,列使用圓角矩形;每張投影片最多三個。
- 遊標預設關閉。 對大多數觀看者來說,繪製的滑鼠指標看起來像亂入的遊標。在
video.json中設定{"_all": {"cursor": true}}可啟用(適用於示範型影片);遊標會沿曲線移動,離開文字區域時靜止,且絕不顯示在標題、章節、重點訊息或結尾投影片上。 - 生動但不雜亂:每 5 到 10 秒有變化,任何靜止畫面不超過 25 秒;但每幀只有一個焦點,無旋轉或翻轉效果,每分鐘最多 16 個音效,轉場音效僅在投影片切換時出現,音樂音量比語音低 17 到 23 dB。
- 語音是人們最先感受到的品質。 不要對聽起來會失真(VieNeu)的模型進行時間拉伸;改以較長的暫停來放慢速度。Whisper 檢查不可省略:發音錯誤的專業術語是教學失誤。
- 字幕是獨立檔案,絕不內嵌。
- 影片步驟不會改變內容:不加入新想法,不發明數字。旁白問題應回歸到課程或投影片步驟處理。
- 執行、測量、檢視後再交付:音量、跳動、長度、連續幀圖。
- 錄影就是錄影:不要用截圖取代操作,捲動必須流暢。
- 若要新增轉場、特效或音效:擴展
assets/engine/video.js、scripts/storyboard.py與references/motion-and-sound.md中的表格,渲染測試、檢查、然後使用。
參考資料
references/video-standards.md:音訊與影片學習材料的教學標準,對應投影片類型,以及平台規範(Udemy 等)。references/motion-and-sound.md:區域與動作的推斷方式、投影片類型表格、動態數值、音軌、video.json語法、如何保持學習者的注意力。references/narration-voice.md:Kokoro 與 VieNeu、速度、發音修正、Whisper 檢查。references/screen-recording.md:錄製視窗、流暢捲動、測量與平滑跳動、合成、隱私。references/quality-checks.md:最終檢查的門檻值與連續幀圖檢查清單。templates/video.example.json:每張投影片的覆寫設定。