videolabs:建立影片(終端機課程風格)
所有內容都存放在同一個儲存庫 /opt/opus/videolabs(github.com/bigzaqui/videolabs,公開:絕不提交憑證;提交前請執行 scripts/check-secrets.sh)。此技能的原始碼位於該儲存庫的 skills/videolabs_create-video/ 中,並由 scripts/setup.sh 連結至 ~/.claude/skills。其設定值(ElevenLabs 和 Cloudflare 金鑰、渲染主機)位於 git 忽略的儲存庫根目錄 .env,請參閱 .env.example。
儲存庫包含:一個通用引擎(Remotion + ElevenLabs 工具,執行 npm ci)和 labs/ 下每個實驗的獨立資料夾。用於「kafka - 快速入門」(26 課 / 18 分鐘)、「ceph - 第一步」(14 課 / 11.6 分鐘),以及早期的「ceph 快速入門」(41 課 / 30 分鐘;其腳本和視覺效果保留在 reference/ 中,作為卡片、比較、量表、樹狀圖、事件、狀態和網站的範例)。這兩個現有實驗都是實作型:每個指令都先在真實實驗環境中執行過(請參閱「真實實驗」)。
/opt/opus/videolabs/
engine/ 通用工具:engine/cli.mjs <lab> <command>、src/(Remotion)、scripts/
labs/<slug>//
lab.config.json 目錄條目(標題、描述、標籤)+ 外觀(品牌、標誌、狀態列、工具名稱、範例片段)
script/script.md 旁白,每個 ### 標題為一個場景
visuals.ts 每個場景顯示的內容 + 提示覆寫
lab/ create-lab.sh、destroy-lab.sh、cap.sh、captures/、lab-meta.json
audio/raw/ ElevenLabs 輸出(付費;提交至 git)
out/ data/ public/voiceover/ 產生內容,git 忽略
site/ scripts/publish-all.sh state/ 網站及其發佈(技能 videolabs_publish)
script.md ──cli scenes──▶ data/scenes.json
└─ cli tts(ElevenLabs 文字計時) ─▶ audio/raw ──pace──▶ public/voiceover/* + data/{durations,captions}.json
visuals.ts + engine/src/cues.ts(提示 = 當該詞語被念出時) ─▶ Remotion ──cli render──▶ out/<slug>.mp4
cli player ─▶ out/player/{chapters.json,lab.json} ──videolabs_publish──▶ videolabs.pages.dev
所有指令:node engine/cli.mjs <lab> <scenes|tts|pace|cues|stills|typecheck|studio|sample|render|player>,以及 list 和 new <slug>(儲存庫的 README.md 中有表格)。實驗資料夾是各課程之間唯一不同的部分;請勿將引擎程式碼複製到實驗中。
工作流程(與使用者協商大綱和腳本;之後的所有步驟無需請求即可執行)
使用者的常駐授權:一旦腳本確定,即可執行付費 ElevenLabs tts、提示稽核、在渲染主機上進行渲染、播放器/海報步驟以及實驗環境拆除,無需再為每個步驟請求許可。之後報告結果和新的影片長度。僅在遇到此工作流程以外的事項時才停下來詢問(例如,對實驗未建立的資源進行破壞性操作,或在使用者未要求的情況下發佈到網站)。
- 是實作主題?先建立並執行真實實驗環境(請參閱下面的「真實實驗」)。切勿憑記憶撰寫指令輸出:ceph-first-steps 腳本的初稿在真實叢集上執行後,有十處錯誤。然後根據實際發生的情況編寫腳本。
- 先寫腳本,再製作影片。 提議一個大綱(模組 → 課程,並規劃每個課程的圖表),獲得同意後,再編寫
labs/<slug>/script/script.md。修改文字比重新產生音訊便宜得多。以速度 1.0 標記,停頓約等於 160 wpm:1,500 字約等於 9.5 分鐘(含模組卡片);速度 1.08 約等於 168 wpm。在tts之後立即檢查Paced … total X min行的實際長度,並在渲染前進行調整。對於版本特定的指令,請先檢查當前版本(文件和專案的下載/映像標籤)。格式(由engine/scripts/build-scenes.mjs解析):
在模組的最後一堂課結尾加上「Module takeaway: …」— 單詞 takeaway 會觸發 takeaways 視窗。## 模組 1 — 核心架構 ### S02 · RADOS **視覺效果:** 給設計師的備註 **指令:** `ceph -s` `ceph osd tree`(可選,僅顯示在螢幕上) **旁白:** 一段文字,專為口語朗讀而寫。以口語方式拼寫指令(「ceph dash s」)。 node engine/cli.mjs new <slug>會建立labs/<slug>/的結構(設定檔、一個三場景的腳本、一個 visuals.ts);填寫lab.config.json(目錄的標題、描述、標籤;以及外觀的brand、user、logo、statusbar、statusCmd、networkLabels、session、tools、sample)並執行node engine/cli.mjs <slug> scenes。- 視覺效果 在
labs/<slug>/visuals.ts中(類型在engine/src/types.ts中,colors來自@engine/theme),每個場景 ID 對應一個條目。圖表類型(engine/src/components/Diagrams.tsx):topology:主機(名稱、IP、守護程序晶片、磁碟/副本)位於一條或兩條網路上,可選的狀態列(statusCmd標籤,預設來自設定檔),以及flows(在主機之間移動的區塊,從cue開始,直到until)。任何主機/守護程序/磁碟/狀態都可以有cue(當被念出時出現)和changes: [{cue, state, label}](磁碟empty|osd|leader|idle|down|destroyed|new,主機ok|spare|down|maint)。沒有提示的元素從一開始就可見。使用輔助函數(host(n)、built(n))建立實驗狀態,以便圖表逐課建構。session:一個逼真的 Shell;每個cmd在被念出時會自行輸入,然後輸出out行;保留最新的行數。host設定整行的提示符號(預設來自設定檔中的session.prompt)。可選的緊湊型topology位於其上方。對於場景場景,底部的指令列被隱藏。 其他類型:title(fastfetch 卡片;user、額外的rows)、layers、cards、flow、tree、compare、gauge、terminal(帶有標註的指令輸出)、incident(症狀/檢查/操作)、checklist、states、sites;每個場景可選的takeaway和commandCues。對每個out和symptom使用lab/captures/中的真實輸出。
- 在付費取得音訊前審查視覺效果:
node engine/cli.mjs <slug> stills(每個場景一張畫面,比例 60%;S04,S11 --at=12.5指定時間點)並使用 ffmpegxstack拼貼成 2×2 的聯絡表以便檢視。檢查溢出、換行和空白版面。 - 語音:在儲存庫根目錄的
.env中設定ELEVENLABS_API_KEY/ELEVENLABS_VOICE_ID,然後執行node engine/cli.mjs <slug> tts(直接執行,無需批准;會略過已存在的檔案;使用--only=S03,S04 --force只重新生成旁白有變動的場景,也可節省點數)。它會自動執行pace。當腳本變長時,告知使用者新的長度。提交labs/<slug>/audio/(付費輸出)。 - 同步稽核:
node engine/cli.mjs <slug> cues(在變更任何指令後,也要變更其commandCues條目:為dmesg設計的晶片卻仍然提示在「smartctl」上,這是一個無聲的不匹配)— 列出揭示每個元素和每個指令行的詞語(MISS= 分散在相鄰元素之間)。使用提示覆寫(如下)修正錯誤的匹配。在旁白或語音變更後重新執行。 - 預覽:
node engine/cli.mjs <slug> sample(在渲染伺服器上約 1 分鐘),使用ffmpeg -ss <t> -frames:v 1和volumedetect檢查影格以確保音量(無削波:最大值 < -1 dB)。 - 渲染 完整課程(遠端,見下文),然後
node engine/cli.mjs <slug> player,製作海報labs/<slug>/out/spot.jpg,銷毀實驗 VM,並使用 videolabs_publish 技能發佈整個網站。
使用者已簽署的設計規則
- 外觀:Omarchy/Hyprland 桌面,Tokyo Night 調色板(
src/theme.ts;語意名稱如rados、mon、cephfs只是調色板插槽)。Waybar 狀態列,模組作為工作區 0–7,一個狀態(疑難排解模組上的HEALTH_WARN),課程計數器07/41。一個並排視窗,2px 方形邊框,無圓角/發光/漸層。每堂課以輸入型 robbyrussell 提示符號➜ module-1-core-architecture git:(s03) cat the-daemons.md開頭,然後是# 標題。備註顯示為// 註解,指令顯示為❯ cmd行,takeaways 顯示為浮動視窗cat sre-takeaway.md,模組分隔線顯示為cd module-N-…+ 大字型標題,開場為 fastfetch。全部使用 JetBrains Mono;Inter 僅用於字幕。它取代了使用者不喜歡的「通用」深色漸層外觀。 - 提示同步:絕不按時間交錯。每個元素在旁白首次說出其名稱時出現(
engine/src/cues.ts:最長的有意義標記、前綴詞幹、從上一個提示向前搜尋)。在實驗的visuals.ts中進行覆寫:cue: "=managers"確切的口語詞語(忽略連字號/標點符號:"=write-ahead"),cue: null= 無詞語,內插。pointCues(比較側)、noteCue(樹狀圖)、linkCue(網站)、cues(標題徽章)、commandCues(指令行,每個場景)。cueLabels()中的標籤順序必須與engine/src/components/Visuals.tsx中渲染器使用的索引順序相符(topology/session 使用topologyKeys/sessionKeys中的鍵值遍歷)。- 一個向前游標會遍歷場景的所有標籤,因此後面的標籤如果重複使用常見詞語(「three」、「recovery」、「add」),可能會落在後面的出現位置。偏好使用在該時刻附近只出現一次的詞語(「=repeat」、「=begins」),並且務必閱讀
cues稽核。
- 字幕:純文字短語,在句子/子句結尾處換行,每行 ≤ 64 個字元,無逐字卡拉 OK 高亮(「令人疲憊不堪」)。
- 呼吸:
engine/scripts/pace.mjs在原始 ElevenLabs 音訊中的. ? !之後插入 250 ms,在: ;之後插入 120 ms,並移動詞語計時。剪切點來自 ffmpegsilencedetect(嚴格 -38 dB/40 ms,然後 -30 dB/20 ms),否則選擇最安靜的 5 ms 視窗,並使用 8 ms 淡入淡出 — 絕不單獨使用 ElevenLabs 詞語邊界(這些邊界會落在詞語內部)。調整SENTENCE_PAUSE/CLAUSE_PAUSE,重新執行node engine/cli.mjs <slug> pace(免費;原始音訊保留在audio/raw/中)。速度調整取決於 ffmpeg 版本(7.1.5 和 8.0.1 對同一段 Kafka 音訊分別產生了 17.9 和 17.8 分鐘的速度):請在渲染的位置進行速度調整,或將data/和public/voiceover/複製到渲染主機。 - 轉場:透過
@remotion/transitionsTransitionSeries實現 15 影格的交叉淡入淡出;它們僅重疊靜音前導(0.4 秒)/ 尾聲(0.8 秒)。engine/src/timeline.ts減去重疊部分;engine/scripts/player.mjs和stills.mjs鏡像這些常數 — 請保持同步。 - 使用者選擇的語音:ElevenLabs「Mark」
v3p1kjzUvro6S76qmYmH(語音庫;雖然GET /v1/voices/{id}會回傳 404,但仍可用於 TTS)、eleven_multilingual_v2、速度 1.08、with-timestamps端點、previous_text/next_text以保持連續性。在完整執行前,務必先用一個句子的 TTS 呼叫驗證語音 ID(之前.env中的某個 ID 被截斷了)。
每個課程的設定(labs//lab.config.json)
Waybar 中的工作區以及 fastfetch 模組/課程計數會自動跟隨場景。其他課程特定的內容都是設定檔中的資料,而非程式碼:brand(Waybar 名稱)、user(標題卡上的提示符號)、logo(五行程式碼字母)、statusbar(ok/warn 文字、right 文字、warnModule = 顯示警告的模組)、statusCmd 和 networkLabels(topology 場景的預設值)、session.prompt/session.title(終端機工作階段)、tools(自動提示指令行時跳過的指令詞語)、sample(範例片段的場景 ID),以及目錄的 title、description、tags。具有不同外觀的新實驗無需修改引擎;新的圖表類型或狀態則需要(engine/src/types.ts、components、cues.ts),但之後所有實驗都能受益。
採用的 Remotion 實踐(來自 remotion-* 技能)
字型透過 @remotion/google-fonts(絕不依賴系統字型);<Audio> 來自 @remotion/media 並搭配 from;TransitionSeries;calculateMetadata 根據音訊持續時間調整組合大小;使用 interpolate/Easing.bezier 和 translate 進行動畫(無 CSS 轉場)。刻意未採用:Interactive.create/連線組合(僅在 Studio 中手動編輯時有用)、大型社交影片文字最小尺寸、meme 音效。
渲染
VIDEOLABS_RENDER_HOST(渲染和實驗伺服器的 SSH 目標,金鑰已設定)來自儲存庫根目錄的.env:在執行下列指令前,執行set -a; . /opt/opus/videolabs/.env; set +a,並在您提交的任何內容中寫入該變數,而非文字地址。同一台主機執行實驗 VM。TMPDIR=/var/tmp由 CLI 設定:/tmp在這些主機上是一個小的 RAM tmpfs,長時間的音訊混合會在最後一刻因「裝置上無剩餘空間」而失敗。- 本地機器(4 vCPU)約需 45 分鐘來處理 30 分鐘的影片。遠端渲染伺服器 $VIDEOLABS_RENDER_HOST(Ubuntu 26.04、24 執行緒、31 GB RAM、RTX 3090 Ti、KVM;Node 22、ffmpeg、rsync 以及來自 apt 的 Chromium 函式庫)以 12–16 的並行度在 3 分鐘內渲染 11.6 分鐘的 ceph 課程,並在 5 分鐘內渲染 18 分鐘的 kafka 課程。儲存庫位於兩台機器的
/opt/opus/videolabs。流程:(為了保持與您審查過的計時完全相同,請 rsyncrsync -az --delete --exclude node_modules --exclude out --exclude .env --exclude .git --exclude site --exclude state ./ $VIDEOLABS_RENDER_HOST:/opt/opus/videolabs/ # 從 /opt/opus/videolabs ssh $VIDEOLABS_RENDER_HOST 'cd /opt/opus/videolabs/engine && npm ci && cd .. && node engine/cli.mjs <slug> scenes && node engine/cli.mjs <slug> pace && node engine/cli.mjs <slug> render --concurrency=12 --log=error' rsync -a $VIDEOLABS_RENDER_HOST:/opt/opus/videolabs/labs/<slug>/out/<slug>.mp4 labs/<slug>/out/ && node engine/cli.mjs <slug> playerlabs/<slug>/data和public/voiceover,而不是在伺服器上執行scenes/pace。)切勿將.env複製到那裡。以分離模式執行長時間渲染(使用ssh -f -n啟動的nohup bash script &),並使用time的real行上的 Monitor 等待;不要 grep 日誌中的「error」(指令列已有--log=error)。如果 Waybar/圖表程式碼發生變更,請檢查兩個實驗(stills),因為它們共享該程式碼。 - 驗證引擎變更:使用新舊程式碼渲染範例片段,並比較
ffmpeg -i a.mp4 -i b.mp4 -lavfi ssim -f null -;重新生成的scenes/durations/captions.json和cues稽核必須…
[後續內容已截斷,等待翻譯]