Whiteboard Video Factory logo

Whiteboard Video Factory

Community
wwwzhouhui
whiteboard-video-factory

手绘白板风"边画边讲"讲解视频出片 skill(Excalidraw 逐笔动画 + 火山/小米/edge-tts 配音 + 烧录字幕并逐段跟读高亮 + 品牌水印与片尾卡 + 三张封面 4:3 / 3:4 / 9:16 + 各平台发布文案:视频号 / 小红书 / 抖音 / B 站 / 公众号),自带公共工序(事实核查与来源台账、发布前合规自查、成片机器与目视验收、交付边界)。当用户说"做一期白板视频 / 边画边讲 / 手绘讲解视频 / 用 excalidraw 做视频 / whiteboard video",或要在本仓库里新建一期、改场景、换贴纸或真实 Logo、重出片、改字幕(含字幕高亮 / 跟读高亮 / 卡拉OK式高亮)、出封面、写发布文案(含抖音)时使用。全链路本地:Playwright + ffmpeg + 火山/小米 TTS + 本地 codex CLI 生图,不需要剪辑软件。

Overview

Publisherwwwzhouhui
Repositoryskills_collection
Skill namewhiteboard-video-factory
Stars
285
Forks
57
Bundled files
42
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • 42 bundled files

    Scripts, templates, and references the model can read while it works. Files are read-only and never executed.

  • Open source

    Published by wwwzhouhui on GitHub. Read the source before you install it.

Installation

Install the Whiteboard Video Factory AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
    https://github.com/wwwzhouhui/skills_collection/tree/main/whiteboard-video-factory
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/wwwzhouhui/skills_collection.git /tmp/skills_collection
mkdir -p .claude/skills
cp -r /tmp/skills_collection/whiteboard-video-factory .claude/skills/whiteboard-video-factory
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Whiteboard Video Factory in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Whiteboard Video Factory on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Whiteboard Video Factory is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

whiteboard-video-factory:白板讲解视频出片

工具就是本仓库(下文 <仓库>),CLI 是 <仓库>/bin/wb,参数全在 <仓库>/config.json。每期内容在 config.json 的 dirs.projects(默认 <仓库>/episodes/<日期 标题>/),中间产物与成片在 dirs.build(默认 <仓库>/build/<日期 标题>/)。

本文件只讲流程与规矩。细节按需翻 references/,全部在本仓库内,不依赖外部目录:

文件讲什么
references/dsl.md场景与封面 API(一行代码一个元素)
references/scene-patterns.md版式坐标套路(可直接抄)
references/stickers.md贴纸、真实 Logo、真人漫画像
references/publish.md标题公式、各平台规格与机制、抖音怎么写
references/voices-volc.md火山 TTS:两个调用域、resourceId 与音色的配套关系、实测可用音色、报错速查
references/voices-mi.md小米 MiMo TTS:端点与 assistant 角色规矩、无语速参数、无时间戳怎么对齐、九个实测音色、三条通道怎么选
references/fact-check.md事实核查与来源台账
references/compliance.md发布前合规自查
references/delivery-qa.md成片机器与目视验收、封面验收、交付边界

硬规矩

  1. 全链路本地。 旁白走语音合成,config.json 的 tts.engine 三选一:volc(火山引擎,seed-tts-2.0,凭证在 <仓库>/.env,音色可以是官方音色或你自己的声音复刻;返回逐字时间戳,字幕最准,默认用它)/ mi(小米 MiMo mimo-v2.5-tts,凭证 MI_TTS_API_KEY,音色固定九个;接口没有语速参数、也不返回时间戳,语速靠本地 atempo、时间轴靠静音检测估算,见 references/voices-mi.md)/ edge(本机 edge-tts,免费、无凭证,tts.edge.python 指向装好 edge_tts 的解释器);语速默认 1.2 倍。贴纸走本地 codex CLI 生图(codex 分派到无生图能力的模型时,改用内置 ImageGen 工具出四宫格再 --rekey 切图);真实 Logo 走 Wikimedia Commons 官方 SVG(wb logo);配乐放 assets/bgm.mp3,没有就出无配乐成片。改配置不改代码。动了 TTS 配置先跑 wb voice:它回显引擎/端点/模型/音色并真合成一句,比出片到一半才报错省事。三个引擎各有自己的音色字段(火山 tts.voice / 小米 tts.mi.voice / edge tts.edge.voice),别互相顶替。
  2. 一处为主。 期目录就是工程目录,scenes.js 是唯一手写源(旁白、场景、封面都在里面);scenes/、旁白稿.md、字幕.srt、封面-*.png 是生成物,不手改、不外拷。README.md(资料来源)和 发布.md(文案)是人写的。视频不放进期目录:成片在 <dirs.build>/<期>/outputs/final.mp4,中间产物在同目录 work/,旁白稿.md 里自动带成片链接。
  3. 先旁白后画面。 旁白按 | 切 beat,每 beat 34 句配一组元素。时长是变量,不是固定 3 分钟:动手前先跟用户确认目标时长,再按「1 分钟 ≈ 8 个 beat ≈ 200 字(1.2 倍语速)≈ 1.52 个场景」折算。短片 23 分钟(68 个场景)、中片 510 分钟(1525 个场景)、长片 1540 分钟(4090 个场景)都行——每个场景都保持「34 beat / 80120 字 / 一屏 10~20 个元素」的舒适区,靠加场景变长,别把一个场景写得又长又满。长片另外要分幕与呼吸点,见「做长片」。
  4. 画布 1920×1080。y≥960 是字幕区,右上 320×130 是水印区,元素不进去;wb scenes 的 ⚠ 必须清零。
  5. 公司、产品、模型用真实 Logo;人物、器械、物件用贴纸;文字、箭头、框用 Excalidraw。 讲到具体公司时主角用官方 Logo(wb logo),不用生图拟人机器人代指,观众认不出是谁。讲到具体公众人物时用真人照片参考的漫画像(wb image ... --ref=照片 --likeness,见 references/stickers.md),不用通用小人代指。Excalidraw 画人很丑,人和物件一律出贴纸。
  6. 事实先查再写。 数字、日期、价格要有来源,写进期目录 README.md;估算值在旁白和文案里都标"据报道/估算"。查证动作见 references/fact-check.md。
  7. 品牌层自动带,不用每期写。 右上角手写水印(第一场景逐笔画入)、片尾品牌卡(5.5 秒,静音)、封面上的品牌标都由工具生成,名字、品牌色、slogan 在 config.json 的 brand;标题和点睛色优先用 C.brand。
  8. 交付四件套:成片 + 三张封面(4:3 横版 / 3:4 竖版 / 9:16 抖音竖屏)+ 发布.md,主用标题、视频号简介和抖音描述直接贴在回复里。

命令

bash
W=<仓库>/bin/wb
$W new "<标题>"                          # 建期目录:scenes.js 模板 + 发布.md 模板
$W logo "<标题>" logo-x="Commons 文件名.svg" ... [--vs=x,y]   # 真实 Logo → assets/<name>.png;--vs 拼 "A VS B" 封面图;--search="词" 先看候选
$W image "<标题>" name="英文描述" ...     # 贴纸,一次 ≤4 张(四宫格同风格)→ assets/<name>.png
$W stills "<标题>"                       # 每 beat 静帧 → <后台>/<期>/work/frames/,逐张看
$W cover "<标题>"                        # 封面-4x3.png / 封面-3x4.png / 封面-9x16.png → 期目录(画幅见 config cover.ratios)
$W captions "<标题>"                     # 核对字幕:每条的时间、文本,以及高亮分段([a·b·c] 里 · 的位置就是色块跳的节拍)
$W voice                                 # 配音自检:回显 engine / 端点 / 模型 / 音色,并真合成一句 → build/_voice-check.wav
$W build "<标题>"                        # scenes → tts → render → mix → cover → clean,出 outputs/final.mp4 / 字幕.srt / 旁白稿.md

分步:scenes / tts / voice / captions / render [scene|99-brand] / mix [gain] / clean / open / list。wb build 出片后自动 clean:删 work/frames 静帧和 work/out 里已不在场景表的旧分段,各场景分段与 master 保留(单场景重渲、重混配乐要用)。期参数用标题子串(wb build 薄肌)。wb render <期> 03-xxx 只重渲一段并自动重拼 master,接 wb mix 即新成片;TTS 有缓存,只有改过的旁白会重配。

一期的流程

1. 立题与查证

  • 明确选题、观众、口吻(默认冷静科普口吻,账号口吻写进 references/publish.md)。
  • 搜 2~3 轮;官方页优先且读到全文,二手站数字只做线索。
  • 数字、日期、来源列进 README.md「资料来源」,口径("240 倍 = 6000 万 / 25 万")单列一节。完整动作见 references/fact-check.md。

2. 写旁白(scenes.js)

  • 口语短句,每句一个信息点。数字用中文读法利于 TTS("三百美元");字幕用原文,所以阿拉伯数字也行,但 @、iOS 这类 TTS 会念歪的词要斟酌。
  • 结构:开场定义/反差 → 分解(三要素/两列对比/时间线)→ 怎么算/怎么做 → 数字与门槛 → 冷水/边界 → 一句话总结 + 评论区问题。
  • 每个 beat 都要有能画出来的东西,抽象句并入相邻 beat。按原速写即可,成片语速 1.2 倍。

3. 出 Logo 与贴纸

  • 先列本期出现的公司/产品/模型 → wb logo。 wb logo --search="<公司> logo" 看 Commons 候选,挑官方现行版(带年份的取最新),一期一条命令取齐:标志(logo-<名>,方形,放主视觉)+ 字标(logo-<名>-word,横长,当标签/表头);两家对比再加 --vs=a,b 出 logos-vs.png 当封面主图。来源自动记在 assets/logos.json,抄进 README「画面素材」。Commons 没有的,去官网 press kit / brand 页找 SVG,确认授权再下载。
  • 再列本期物件(人物、器械、设备、道具),wb image 一次 ≤4 张,英文描述写姿势/服装/颜色(Excalidraw 五色:pale yellow/blue/green/red/grey)。
  • 逐张看 assets/<name>.png:杂点、邻格残片、主体断块 → --single 单张重出;只是抠图问题 → --rekey。封面主角贴纸也在这一步出(公司题材封面用 Logo,不另出)。

4. 画场景

  • 抄 references/scene-patterns.md 的版式坐标再微调;贴纸 s.image(x, y, 'name', { h: 370, align: 'center' }),人形 360380 高;Logo 同样用 s.image:主视觉标志 h 260300,卡片/柱子里 h 100~130,字标当标签用 w。
  • 一屏 10~20 个元素;一个 beat 塞不下就删元素,别指望笔画得完(排期最多溢出到下一段前 35%,再多就整体压缩)。
  • wb stills 后逐张看静帧:重叠、越界、文字超宽、太挤。改到满意。

5. 封面

  • scenes.js 末尾 cover 函数:s.coverLayout({ ratio, title, sub, sticker }),build(__dirname, scenes, { cover });wb cover 按 config cover.ratios 出 4:3 / 3:4 / 9:16(9:16 给抖音,标题与贴纸压在中央安全区,因为抖音封面上下会被裁到约 1080×1464)。
  • 标题 ≤2 行、每行 ≤8 字:第一行说对象,第二行说钩子(自动品牌色 + 马克笔高亮);副标放数字;贴纸用主角(公司题材传 sticker: 'logos-vs' 或单个 logo-x,宽图在竖版会自动按宽度缩)。
  • 看三张 png:文字没撞贴纸、高亮压在钩子行、品牌标在角上。细则见 references/delivery-qa.md 的「封面验收」。

6. 出片与验收

  • wb build。抽 2~3 帧看(ffmpeg -ss <t> -i final.mp4 -frames:v 1 x.png):字幕在底、跟读高亮压在正念到的那几个字上(色块紧贴字、不吞标点、不切断 MiMo 这类英文词)、水印在右上、贴纸擦出正常;片尾看一眼 99-brand。抽帧时先看原图再下结论:把几十帧缩成一张长图看,容易把墨色字误判成"没高亮"。
  • wb captions <期>:不打图就能核对每条字幕的时间、文本,以及高亮分段([a·b·c] 里 · 的位置就是色块跳的节拍;每段大致 0.2~0.6 秒算正常)。
  • 看 字幕.srt 前几条:原文拼写、数字未拆。SRT 里没有高亮(纯文本格式装不下),高亮只烧进画面。
  • 机器检查与交付边界按 references/delivery-qa.md。
  • README.md 写好,时长以 ffprobe 为准。

7. 发布文案与标题

  • 按 references/publish.md:5 个标题候选(数字反差 / 事件主语 / 结论前置 / 生活单位换算 / 提问)选 1 主用;视频号简介、抖音描述(前 55 字钩子 + 话题 3~5 个排最后 + AIGC 标注)、小红书标题+正文+标签、B 站标题、公众号摘要、评论区置顶。
  • 数字与 README.md 一致;合规自查勾完(references/compliance.md)。
  • 填 发布.md,交付四件套。

做长片(>5 分钟)

代码层面没有时长上限。 渲染是按场景分段出 mp4、再用 ffmpeg concat 拼成 master(lib/render.js),场景数不受限;TTS 也是逐场景合成与缓存(lib/tts-volc.mjs)。所以「能拍多长」只取决于内容规划,不取决于工具。实测(2026-09-30,plan 域流式端点):单次提交 2200 字 / 6600 字节(≈6.5 分钟音频)也正常返回,所以即使某一场写得很长也不会卡在接口上。

目标时长场景数总字数分幕
2~3 分钟6~8500~700不用分幕
5~10 分钟15~251200~24002~3 幕
15~25 分钟40~603500~60003~5 幕
30~40 分钟75~907000~90005~8 幕

长片比短片多做的四件事:

  1. 分幕。 每幕开头加一张「章节卡」场景(普通 new Scene('02-act2', ...) 就行:画幕标题 + 一句导览),观众才知道自己在哪;发布文案的时间戳也按幕切。片尾卡由工具自动追加,章节卡要手写。
  2. 每 2~3 分钟一个呼吸点。 白板讲解连续 20 分钟会累:呼吸点可以是一句小结、一个踩坑现场、一张对比表。别一路平铺。
  3. 配乐改用播放列表。 一首 2~3 分钟的曲子配 20 分钟的片子要循环十几次,一听就出戏——config.json 的 bgm.playlist 填几首([{ "file": "assets/bgm-1.mp3" }, { "file": "assets/bgm-2.mp3", "trim": [0, 90] }]),它们会按顺序拼成一整段再整体循环,重复感立刻降下来;playlist 为空时仍走 bgm.file 单曲循环。
  4. 封面标签别提时长。 cover.seriesTag 默认「白板讲清楚」;要强调时长就在 scenes.js 的 cover 里按本期实际时长传 tag,别在 config 里写死「3 分钟」。

耗时与体积(本机 4 路并行实测 + 外推;1080p30):

时长出帧wb build 全程成片 + 中间产物
2.7 分钟(《DeepSeek Harness 桌面端》实测 8 场景)~1.8 分钟~3 分钟12 MB + 24 MB
10 分钟~7 分钟~10 分钟~46 MB + ~90 MB
30 分钟~21 分钟~30 分钟~140 MB + ~270 MB

出帧速度按本机实测 ≈44 帧/秒(4 路并行)折算;体积按实测 ≈4.5 MB/分钟。白板片画面白、运动少,比实拍素材小得多。机器吃紧就把 render.workers 降到 2。

长片一定要用断点续跑:TTS 与渲染都按场景缓存,改一句旁白或一个场景不必整片重跑——

bash
wb tts   <期> <场景名>     # 只重配改过的那一幕(缓存命中时几乎瞬时)
wb render <期> <场景名>    # 只重渲这一段,并自动重拼 master
wb mix   <期>              # 出片

⚠️ wb build 会整期重渲(即使 TTS 全命中缓存),长片别拿它做小改动。另外 wb captions <期> 在长片上尤其有用:不打图就能逐条核对 90 条字幕的时间与高亮分段。

修改类请求怎么接

用户说做法
改某句旁白 / 加一段改 scenes.js → wb build(只重配改过的场景)
用了假机器人 / 要真 Logowb logo 取官方 SVG → scenes.js 把 s.image 名字换成 logo-* → wb stills → wb render && wb mix && wb cover
换贴纸 / 人物太丑wb image 重出 → wb stills → wb render && wb mix(封面用到的话再 wb cover)
画得太快/太慢、文字蹦出来config render.pen:speed 描边 px/s、charSeconds 每字秒数区间、minSeconds 单元素下限、gapSeconds 抬笔间隙 → wb render && wb mix
画面跟不上旁白(声音在讲、画还没画完,越到后面越明显)先量:node lib/diag-schedule.mjs <期目录绝对路径> 逐拍打印「起笔偏移」,> 0.35s 就是落后,且会累积到后面每一拍。修法(只动 config):render.pen.overflow 设 0(禁掉"一拍画不完就溢到下一拍")、speed 提到 1400~1600、charSeconds 上限压到 ~0.14、fillMaxSeconds ~0.8、gapSeconds 0.09;再跑一次 diag 复核到 ≤0.1s,最后 wb render && wb mix。元素 >20 个的拍会被压缩得明显变快,想更从容就删几个元素(比让整片晚 23 秒划算)
字幕与语音对不齐(小米通道)node lib/diag-captions.mjs <期目录绝对路径> 看「锚定率」与起点偏移。锚定率不是 100% 就是有字幕时间在靠字数插值,来源几乎总是长小句被 captions.maxChars 硬拆:把 maxChars 放大到装得下最长小句(本期 25 字 → 26),或给那句旁白补个逗号让它自然分成两个小句(补标点不改语义,但要重跑 wb tts)。细则见 references/voices-mi.md
渲染太慢 / 机器吃紧config render.workers(默认 4 路,每路一个 Chromium;1 = 串行),出帧与路数无关、逐帧一致
语速快/慢config tts.speed(默认 1.2)→ wb build,全部场景自动重配、字幕同步。⚠️火山是原生变速,小米是本地 atempo 后处理(接口无语速参数)
换配音引擎(火山 / 小米 / edge)config tts.engine 改 "volc" / "mi" / "edge" → 必须重跑 wb tts:三家音频、语速、停顿都不一样,字幕是跟着音频走的,缓存也按引擎分开。换完跑 wb voice 试听
要更长 / 更短的片子不改代码、不改配置,只改 scenes.js 的场景数(按「1 分钟 ≈ 1.52 个场景 ≈ 200 字」折算,见「做长片」)→ wb build。⚠️这是重写内容的活,不是调参:加长就补场景(每场景仍是 34 beat),别把现有场景往里灌更多旁白。>5 分钟记得分幕 + 加呼吸点 + 换 bgm.playlist
配乐大/小config bgm.gain → wb mix;临时试听 wb mix <期> 0.4
字幕字号/位置config captions.fontSize / baselineY → wb render && wb mix
字幕跟读高亮(开关 / 颜色 / 粒度 / 半透明)config captions.highlight → wb render && wb mix。enabled 关掉就是原来的静态字幕;color 留空用品牌色;opacity + keepHalo:true 出半透明马克笔效果;minUnitChars/maxUnitChars 调粒度(默认 2/4,目标 3 个字)。改完先 wb captions <期> 看分段:[用的是·小米 ·MiMo ·的配音] 里的 · 就是色块依次跳的位置|字幕文本与 字幕.srt 都不受影响
水印位置/关掉/换 logoconfig brand.watermark.position/enabled、brand.logo(透明底 png)→ wb render && wb mix
片尾卡 slogan / CTA / 时长 / 不要config brand.slogan、brand.endCard.* → wb render <期> 99-brand && wb mix
封面文案 / 贴纸 / 画幅scenes.js 末尾 cover 函数 → wb cover;画幅 config cover.ratios(含抖音用的 9:16),标签 cover.seriesTag(tag: '' 去掉)
标题 / 发布文案 / 换平台改 发布.md,不用重出片
在 Obsidian 里改了图wb render && wb mix 直接读 .excalidraw.md;增删元素会改逐笔顺序,结构性改动回 scenes.js
换声线 / 换曲先 wb voice 确认档位,再改各自引擎的音色字段:火山 tts.voice(2.0 音色配 resourceId: seed-tts-2.0,声音复刻配 volc.megatts.default)/ 小米 tts.mi.voice(只有九个,见 references/voices-mi.md)/ edge tts.edge.voice;或 .env 的 VOLC_TTS_VOICE、MI_TTS_VOICE / 配乐改 bgm.playlist(多首按序拼接,长片用)或 assets/bgm.mp3(单曲循环,换曲先 volumedetect 量电平再定 gain);临时换曲 BGM=路径、临时换列表 BGM_PLAYLIST='[{...}]'
配乐固定几首轮播config bgm.playlist 填 [{ "file": "...", "trim": [起, 止] }];trim 省略即整首,gain 可逐首配平响度 → wb mix。列表非空时 bgm.file / bgm.trim 被忽略
TTS 报 Invalid X-Api-Key / not granted先怀疑调用域不是额度:ark- 开头 Key 只走 plan,改 config tts.endpoint,见 references/voices-volc.md 的报错速查
小米报 Unknown voice / assistant role / 404三类坑:音色不在九个之内、正文没放在 assistant 角色、路径不是 {baseUrl}/chat/completions,见 references/voices-mi.md 的报错速查
换账号品牌config brand.name/accent/slogan,其余不动

跨平台说明(Windows / macOS / Linux)

平台差异全部收在工具层,写新脚本时照抄下面几条:

  • 路径展开:lib/paths.cjs、lib/scene-dsl.js、lib/render.js 里的 ~ 统一走 require('os').homedir()。别用 process.env.HOME——Git Bash 下它是 MSYS 写法 /c/Users/x,Windows 版 Node 会解析成 C:\c\Users\x 而 ENOENT。
  • file:// URL:Chromium 载入 render.html 和本地图片一律用 require('url').pathToFileURL(p).href,不要手拼 file:// + path.join(Windows 反斜杠会被当转义)。
  • ffmpeg concat 清单:写进 list.txt 的路径要 p.replace(/\\/g, '/')。
  • Chromium:lib/chromium.cjs 的 resolveChromium() 在 ms-playwright 里自动取本地最高版本,绕开"Playwright 要的版本本机没有、npx playwright install 也装不上"的死路。
  • npm install 可能以 exit 1 收尾,但依赖其实装好了:postinstall 跑 playwright install chromium,网络下不来 Chromium 时报 Download failure, code=1;此时 node_modules/ 里 playwright / roughjs / lz-string 都已就位,成片照跑。别为了这个 exit 1 反复重装;嫌吵就用 npm install --ignore-scripts,浏览器反正走 resolveChromium()。
  • codex 在 Windows 上只有 codex.cmd,spawn('codex') 会 ENOENT;lib/gen-image.mjs 的 resolveCodex() 会去找 node_modules/@openai/codex/bin/codex.js 并用 process.execPath 跑。
  • Node 联网:Commons API 在部分网络下必须经代理,lib/proxy.mjs 的 installProxyFetch() 给 Node 的 fetch 装一层 HTTPS CONNECT 隧道(读 HTTPS_PROXY / HTTP_PROXY 环境变量,写进 .env 也行;没有配就是空操作)。lib/fetch-logo.mjs、lib/gen-image.mjs 入口调用它。
  • 本地 SVG 转 PNG:非 Commons 来源的 Logo(如官网 SVG)用 lib/svg2png.mjs 过 Playwright 转透明 PNG。
  • bin/wb:ROOT 传给 Node 前先 cygpath -m 转成 C:/...(Git Bash 的 /c/... Node 读不了;没有 cygpath 就原样传);open 在 Windows 用 explorer.exe,Linux 用 xdg-open。
  • 期目录移出仓库时:config.json 的 dirs.projects / dirs.build 支持绝对路径(如 D:/临时/2026/9月/2026年9月29日),临时改动完记得改回 ./episodes / ./build,别把本机绝对路径提交进仓库。另外 wb new 生成的 scenes.js 会把 __WB_ROOT__ 替换成仓库绝对路径,但手拷来的 scenes.js 第 4 行的 require('../../lib/scene-dsl') 是相对期目录写的,换位置后会 ENOENT——要么重写成仓库绝对路径(正斜杠),要么用 wb new 重建。
  • Windows 上用 Git Bash 跑命令;路径含中文或空格记得加引号。

已知坑

  • Logo:很多官网有验证页拦截,Logo 一律走 Commons API(wb logo),别去官网抓图。OpenAI 2025 字标最后的 "I" 就是一根竖条,不是被裁掉;Claude 星芒 SVG 边缘略锯齿,放大到 300 高以内看不出来。
  • 火山 TTS 偶尔只返回半段:tts-volc.mjs 按逐字数校验自动重试;全量重配 FORCE_TTS=1。缓存键含文本+音色+语速+音量+风格+调用域,改任一项都会自动重配。
  • 火山 TTS 的两个调用域互不相通(2026-09-29 实测):控制台发的 ark- 开头 API Key 只能走 plan 域(/api/v3/plan/tts/unidirectional),直购/旧版凭证走 standard 域(/api/v3/tts/unidirectional)。打错域报 45000010 Invalid X-Api-Key,看着像 Key 错或没额度,其实只是域不对——先查 tts.endpoint 再怀疑凭证。ark.cn-beijing.volces.com/api/plan/v3 是方舟另一套(非 TTS)入口,别当 baseURL 拼。音色与 resourceId 也必须配套(2.0 音色↔seed-tts-2.0),配错报 55000000 mismatched with speaker。lib/tts/tts.mjs 的 explainFailure() 会把这些错翻成可执行的下一步;wb voice 可提前自检。
  • 画面落后旁白是这套排期的头号同步问题(2026-09-29 实测,小米通道):schedule()(lib/render.html)给每拍的绘制上限是 limit = 本拍结束 + overflow × 下一拍时长,而下一拍的起笔又取 max(本拍起点, 上一笔结束)——所以某一拍画不完就会把后面每一拍一起推后,误差是累积的。实测默认参数(speed:1000 / overflow:0.35)下最差一拍落后 3.62 秒(旁白已经在讲第三拍,画面还在画第二拍)。修法是只动 config.json 的 render.pen:overflow: 0 + speed: 1450 + charSeconds:[0.05,0.14] + fillMaxSeconds: 0.8 + gapSeconds: 0.09,实测把最大起笔偏移压到 +0.07s。改完必须量,不能凭感觉:node lib/diag-schedule.mjs <期目录绝对路径> 逐拍打印「起笔偏移/收笔偏移/压缩倍率」,目标每拍起笔偏移 ≤0.35s(diag-schedule.mjs 支持 PEN_OVERRIDE='{...}' 环境变量传入旧参数做 A/B,不用改 config)。
  • 小米 MiMo TTS(tts.engine: "mi")与火山处处不同(2026-09-29 实测):① 走 POST {baseUrl}/chat/completions,正文必须放在 assistant 角色消息里(只给 user 报 messages must contain an assistant role);/audio/speech、/tts 全是 404。② 接口没有语速参数——顶层 speed/audio.speed/audio.speech_rate 全被忽略(audio.speed=0.5 与不传返回字节完全相同),tts.speed 由本地 atempo 后处理实现。③ 不返回逐字时间戳(audio.transcript 恒 null,AI 语音识别模型也只回文本),句子时间靠「标点小句 + silencedetect 停顿 + 单调 DP 吸附」估算,实测边界误差约 0.05s、各句语速一致性 CV 0.144;work/audio/<scene>.json 的 align 字段会标 silence-estimate(火山是 native)。小句边界不含顿号(ALIGN_VERSION = 3):顿号处停顿弱且不稳,实测 28 处里 10 处根本没停顿、DP 只能按字数插值(把小句起点推早 1.16s);改这个集合要同时改 lib/tts/mi.mjs 的 clausesOf 与 lib/captions.cjs 的 PUNCT/splitText(两边必须逐字一致,否则配对份数不等会整片退化成等比)并 ALIGN_VERSION +1。④ 音色固定九个(mimo_default / 冰糖 / 茉莉 / 苏打 / 白桦 / Mia / Chloe / Milo / Dean),写错报 Unknown voice 并在报错里列出全部可选值。⑤ 静音阈值 -35dB/0.10s 是扫出来的最优,别凭直觉调灵敏(越灵敏语速一致性越差,已复现)。细节见 references/voices-mi.md。
  • 改旁白后先 wb scenes 再 wb tts:scenes.js 是手写源,TTS 读的是 scenes/script.json(由 wb scenes 生成)。只改 scenes.js 就直接跑 wb tts,读到的还是旧 script.json,会误以为"改了没生效"(2026-09-29 踩过)。wb build 一条龙里有 scenes 那一步,所以走 build 不受影响。
  • 验收字幕/画面同步用两个诊断脚本(都在 lib/,不改产物、可反复跑):
    • node lib/diag-captions.mjs <期目录绝对路径>:字幕条数、有声覆盖率、起点偏移中位数、锚定率(起点是否正好落在语音小句边界上,100% 为佳)。
    • node lib/diag-schedule.mjs <期目录绝对路径>:每拍的旁白窗口 vs 实际绘制窗口、起笔/收笔偏移、压缩倍率。 两个脚本都直接吃期目录的绝对路径(不走 dirs.projects),但后台目录仍按 config.json 的 dirs.build 解析——跨目录出片时别忘了 dirs.build 要和成片实际所在的后台目录一致,否则量到的是上一次构建的音频(我第一轮诊断就栽在这,白改一轮参数)。
  • 字幕的逐字时间戳靠 enable_subtitle: true(火山 2.0/ICL 2.0 音色生效;1.0 音色靠 enable_timestamp)。plan 域复测:关掉 enable_subtitle 直接 words=0,字幕会退化成按字数估时间——别为了"省点数据"关它。(小米通道无此开关,它本来就没有时间戳。)
  • 字幕文本必须用原文,TTS 词会把 @grok 写成 atgrok;字幕时间来自 TTS 逐字时间戳,改旁白必须重跑 tts。
  • 字幕跟读高亮(config captions.highlight,默认开)改 render.html 时注意三件事(2026-09-29 实测): ① 别用 Range.getBoundingClientRect() 量 SVG 文本的局部字符区间——Chromium 只量得出第一个字("MiMo" 量成 "M"),色块比文字窄一半,高亮段剩下的字被涂成纸色、看起来像丢了字。用 tspan.getExtentOfChar(i)(返回用户坐标,本页 1:1 就是画布坐标),下标用码点数 [...str],别用 UTF-16 下标。 ② 火山的 wordList 会吞掉空格(用的是小米 MiMo 的配音 返回成 ...小米MiMo的配音),拿 w.word 拼回去 ≠ 原文;若按逐字节校验就会判"对不上"、整条退化成等比估算,英文字母还会被字数上限切成 [米 MiM]·[o 的配音]。captions.cjs 的 wordsToUnits() 按非空白字符对齐、文本取原文切片,就是为了这个。 ③ 粒度只按字数调,别按"时长太短就并"——按时长会把原生词边界冲掉([小米][MiMo] 并成 [米 Mi][Mo 的])。火山的词表是逐字切的("字""幕""也""能"…),靠"目标 3 个字"的贪心分组才得到 [用的是]·[小米]·[MiMo]·[的配音]。
  • SVG dash 在每个子路径 M 处重起,rough.js 又双描边:整条 path 一起 dashoffset 会"所有边同时长、每边描两遍"。渲染器已按子路径拆节点、双描边拆 A/B 层,别回退。
  • rough.js toPaths 不带 dasharray,虚线在 render.html 手动设。
  • 文本宽度是估算值,居中用 align:'center' 才准;左对齐长文本别超 1920。
  • Playwright 截图偶发 30s 超时,渲染器带 3 次重试;再挂重跑 wb render。
  • 不要并发跑 wb image:codex 没按指定路径落盘时会兜底抓"最近生成的图",并发会互相抓错。
  • 并行出帧靠两点,别动:render.html 的 seek() 每帧把底色矩形原地重插,强制整屏重画(否则 Chromium 只重画变化区域,帧会跟出帧顺序有关);每路单开一个 Chromium。改渲染器后用 render.workers=1 和 4 各出一遍,ffmpeg -f framemd5 对比必须 0 帧不同。
  • 用 require() 跑 templates/scenes.js 会在 templates/ 下生成产物,别这么测;冒烟测试用 wb new。
  • 渲染耗时看机器:参考值 2.5 分钟片 4 路并发出帧约 35 秒;本机实测 2 分钟片全量出帧约 82 秒、wb build 全程约 2.5 分钟(即使 TTS 全命中缓存也会整期重渲再 clean)。改一个场景想省时间用 wb render <期> <场景> + wb mix。后台长任务不要用 &,用运行环境自带的后台运行。
  • 3 分钟不是上限,是旧规范(2026-09-30 核实并改掉):lib/render.js 按场景分段出 mp4 再用 ffmpeg concat 拼 master、lib/tts-volc.mjs 逐场景合成+缓存,两者都不受场景数约束;「68 个场景 / 22.5 分钟」原本只是内容建议。实测火山 plan 域流式端点单次吃下 2200 字 / 6600 字节(≈6.5 分钟音频)无报错——所以长片不用改代码,加场景即可。真正的代价是出帧时间(≈44 帧/秒)与磁盘(≈4.5 MB/分钟),以及 wb build 整期重渲 —— 长片一律用 wb tts/render <期> <场景> 断点续跑。⚠️别在封面标签里写死时长:默认 cover.seriesTag 原来就是「白板 3 分钟讲清楚」,做长片会自相矛盾,已改成「白板讲清楚」;另外 bgm 单曲循环配 20 分钟片子会一听就出戏,长片把 bgm.playlist 填上(多首按序拼接后整体循环)。
  • wb build 的清理步可能被安全钩子拦:成片已落盘、字幕封面都出完之后,如果清理 work/frames 时一次删超过数十个文件,某些 Agent 沙箱会抛 SAFE_DELETE_BULK_CONFIRM_REQUIRED。不影响交付,手动清一次或用 wb clean 即可。
  • edge-tts 引擎(tts.engine: "edge"):lib/tts-edge.py 读 work/audio/ 落 <scene>.mp3 + <scene>.json,json 里用 WordBoundary 事件拼出 wordList(和火山的逐字时间戳同格式),字幕链路不用改。缓存键含文本+音色+语速,对不上会自动重配(强制重配 FORCE_TTS=1)。解释器由 config.json 的 tts.edge.python 指定(指向装好 edge_tts 的那个),留空则用 PATH 里的 python3 / python。edge 读不出中文多音字,数字/英文模型名建议在旁白里写成中文读法。

Bundled files

The model reads these on demand while the skill is loaded. They are exposed as readable files and are never executed.

Frequently asked questions

What does the Whiteboard Video Factory AI skill do?

手绘白板风"边画边讲"讲解视频出片 skill(Excalidraw 逐笔动画 + 火山/小米/edge-tts 配音 + 烧录字幕并逐段跟读高亮 + 品牌水印与片尾卡 + 三张封面 4:3 / 3:4 / 9:16 + 各平台发布文案:视频号 / 小红书 / 抖音 / B 站 / 公众号),自带公共工序(事实核查与来源台账、发布前合规自查、成片机器与目视验收、交付边界)。当用户说"做一期白板视频 / 边画边讲 / 手绘讲解视频 / 用 excalidraw 做视频 / whiteboard video",或要在本仓库里新建一期、改场景、换贴纸或真实 Logo、重出片、改字幕(含字幕高亮 / 跟读高亮 / 卡拉OK式高亮)、出封面、写发布文案(含抖音)时使用。全链路本地:Playwright + ffmpeg + 火山/小米 TTS + 本地 codex CLI 生图,不需要剪辑软件。

Why use Whiteboard Video Factory on TypingMind?

Because you install it once and use it with any model. Whiteboard Video Factory is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Whiteboard Video Factory in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/wwwzhouhui/skills_collection/tree/main/whiteboard-video-factory. TypingMind reads its SKILL.md and bundles its files and installs it as a skill you can enable per chat.

Which AI models can use Whiteboard Video Factory?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Whiteboard Video Factory?

As many as you like. As long as a model supports skills, you can use Whiteboard Video Factory with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Whiteboard Video Factory AI skill free?

It is published on GitHub by wwwzhouhui. Check the repository for licensing terms. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇