跳到正文
返回

黑板长卷短视频 · 制作流程手册(一镜到底 · 无限画布)

黑板长卷短视频 · 制作流程手册(一镜到底 · 无限画布)

读者:负责制作视频的 AI Agent。
目标:拿到一篇旁白文字后,按本手册产出一条与本账号既有风格完全一致的竖屏知识短视频。
验收基准:1080×1920 / 片长 = 配音时长 + 4.7s 引导层 / check 全绿(lint 0 error + 文字对比度 100% WCAG AA)/ 多模态读帧质检通过(内容不越安全区,相邻板边缘粉笔除外)/ 整体响度 -11.5 ~ -13 LUFS(2026-09-11 音量重平衡后)。作为短视频发布时,另交一张 3:4 封面(1080×1440),做法见 §7。

代码与资源:全部代码(引擎、渲染模板、工具脚本)、参考实例、图片/音频资源、封面壳打包在 blackboard-template.zip 中(文末附下载)。手册只写流程与规范,不复述代码。


1. 成品风格

2. 代码包与目录结构

本手册与代码包 blackboard-template.zip 一起参考(代码、参考实例、图片/音频资源、封面壳全在里面)。解压后得到:

blackboard-template/
├── README.md                    ← 包内速览(内容清单 + 依赖)
├── template/                    ← 引擎层(冻结层,除 bug 修复外不要动)
│   ├── composition/index.html   ← 渲染工程模板壳(CSS+DOM+@@ENGINE@@ 占位符)
│   ├── composition/{package.json, hyperframes.json}
│   ├── engine/core.js           ← 引擎本体:排布/镜头/黑板三态/连线跟随/打字机/CTA/动效工具
│   ├── assets/                  ← 图片音频资源(见下表)
│   └── tools/                   ← Python 工具脚本(见下表)
├── example/                     ← 参考实例 hidden-state 项目源文件
│   ├── script.txt               ← 旁白原文(空行分段)
│   ├── config.js                ← 项目参数示例
│   └── boards.js                ← 图示创作示例(14 个 case,覆盖全部常用模式)
└── cover/                       ← 封面壳(3:4 大字报,见 §7;每期只改槽位)
    ├── cover.html               ← 固定壳。默认填着示例期文案,出片前必须替换
    ├── cover-1920.html          ← 首帧版(1080×1920,同内容上下延展深绿,见 §7.5)
    └── README.md                ← 填槽说明 + 渲染命令

每个短视频 = 一个独立项目目录(new_video.py 创建,位置任意,与 blackboard-template/ 平级或另放):

<视频项目>/
├── script.txt               ← 旁白文字,空行分段(唯一文字输入)
├── config.js                ← 项目参数
├── boards.js                ← 图示创作(Agent 独立实现)
├── cover/                   ← 仅短视频发布时需要(Step 7:从包内 cover/ 复制后改槽位)
│   ├── cover.html
│   ├── cover-1080x1440.png  ← HTML 截图产物
│   └── cover-1080x1920.png  ← 首帧版截图(Step 8 插到成片第 0 帧)
└── build/
    ├── audio/               ← tts.py 输出(s{i}.wav/.srt、narration.m4a、narration_bgm.m4a)
    └── composition/         ← 自包含渲染工程
        ├── index.html       ← 模板壳 + 内联引擎
        ├── assets → template/assets
        ├── audio  → build/audio
        ├── config.js / boards.js → 项目源文件 (symlink)
        ├── scenes_data.js
        └── renders/         ← 渲染输出

资源清单(都在 template/assets/,解压即得,无需手动放置):

文件用途
board_wall.jpg烘焙好的整面黑板墙(板面+木框一体),camPan 背景
wood_frame_9.png实木框九宫格贴图(卡片 border-image)
logo.png账号 logo(结尾引导卡)
bgm/c260.mp3背景音乐(DJ L《C260》,129.3s)
wood_frame_src.jpg木纹原图(CC BY-SA 4.0,仅重制贴图时用)

工具脚本(都在 template/tools/,用系统 python3 跑,依赖仅 ffmpeg/ffprobe + edge-tts + Pillow/numpy):

脚本作用
new_video.py <项目路径>视频项目脚手架(创建独立项目目录 + 自包含渲染工程;已存在则刷新渲染工程,不覆盖源文件)
tts.py <视频项目>script.txt → 配音 build/audio/narration.m4a(-14 LUFS)+ 各段 srt/wav
build_scenes.py <视频项目>配音 srt → build/composition/scenes_data.js(打字机时间轴)
mix.py <视频项目> [--mux <video>]C260 固化配方混音(BGM 循环铺满片长);--mux 时合成 build/final.mp4
add_cover_frame.py <成片> <封面图> <输出> [--attach <3:4 封面>]在成片最前面插入一帧封面,原帧与音频整体后移一帧;可嵌入 attached_pic

3. 制作流程

Step 0 创建视频项目

首次使用先解压 blackboard-template.zip,下文以解压出的 blackboard-template/ 为根。

cd <blackboard-template>/template/tools
python3 new_video.py ../../my-video   # 项目名自取;一个文档 → 一个独立项目目录

产出项目目录骨架。★ 闸门 1:分段——把用户的旁白文字填入 script.txt(严格原文,不优化;空行分段,一段=一块画板),把分段结果给用户确认。

Step 1 配音

python3 tts.py ../../my-video

产出 build/audio/narration.m4a + 各段 s{i}.wav/.srt,并打印「回填 config.js」的 4 个时长参数。★ 闸门 2:试听 narration.m4a 给用户确认。

Step 2 时间轴

python3 build_scenes.py ../../my-video

产出 build/composition/scenes_data.js。核对输出段数/总时长与 Step 1 报告一致。

Step 3 回填 config.js + 图示创作 boards.js

见 §4(config.js 字段)与 §5(结构边界与图示创作)。这是每个视频的核心创作步骤。

Step 4 检查与渲染

cd ../../my-video/build/composition
npm run check     # 必须全绿:lint 0 error + 文字对比度全部 WCAG AA
npm run render    # 约 1-3 分钟,输出 renders/composition_*.mp4

Step 5 质检(多模态读帧)

抽 14-16 个关键帧(每块画板收尾时刻 + 开头 1s + 结尾 CTA,时刻取 scenes_data.js 各段 end 附近),用多模态大模型逐帧读图检查:

发现问题改 boards.js / config.js 后回到 Step 4。

★ 闸门 3:成片画面给用户审。

Step 6 混音出片

python3 ../../../template/tools/mix.py ../../my-video \
  --mux <composition 目录下 renders 里的 mp4 路径>

产出 build/final.mp4(视频流不重编码)。实测整体响度应在 -11.5 ~ -13 LUFS、真峰 < 0 dBTP。c260.mp3 只有 129.3s,片长更长时 mix.py 会循环 BGM(输入前 -stream_loop -1);自检时在中后段和旁白结束后的尾段各抽 1–2 秒测电平,确认全程有 BGM、没有中途断掉或尾段静音。★ 闸门 4:终审交付。

Step 7 封面(作为短视频发布时)

成片终审之后、发布之前做。标题已经锁定:封面服从标题,不要为了排版回头改标题。

  1. 复制封面壳:cp -R <blackboard-template>/cover <视频项目>/cover
  2. 只改 cover.html 里标明 SLOT 的几处(通常 2–4 个:标题断行、着色词、印章文案、副句和圆章留或不留)。规则见 §7。
  3. 按 cover/README.md,用 Chrome headless(或 Playwright)把 HTML 截成 cover-1080x1440.png。
  4. 先自己打开这张 PNG 读图,再给用户看。标题要完整、落在框内、没有挤成难看的三行;印章和圆章不压中部文案;对比度够;图形不超过 3 类。不过就改槽位重截。禁止改用 AI 生图。
  5. 给用户确认。

★ 闸门 5(轻):封面口味——用户点头,这张封面才可以发。

Step 8 封面作首帧(作为短视频发布时)

部分平台默认取视频第一帧做封面,所以封面验收后,要把它插到成片最前面:只插一帧,不加停留时长。

  1. 用 cover/cover-1920.html 截出 cover-1080x1920.png(原 3:4 内容不变,上下延展黑板深绿,见 §7.5)。
  2. 插帧:
python3 <blackboard-template>/template/tools/add_cover_frame.py \
  build/final.mp4 cover/cover-1080x1920.png build/final_cover.mp4 \
  --attach cover/cover-1080x1440.png

脚本把封面作为第 0 帧,原有帧整体后移一帧(帧率不变);音频开头补一帧时长的静音(采样数 = 采样率 / 帧率,48kHz·30fps 即 1600 采样),整体同步后移,音画不错位。--attach 同时把 3:4 封面嵌入容器作 attached_pic(可选)。

自检:第 0 帧读图确认是完整封面、无拉伸;总帧数 = 原片 + 1;新片第 1 帧 = 原片第 0 帧;音频起始偏移恰为一帧(与原片音轨做互相关);整体响度不变。通过后以 final_cover.mp4 作为交付成片。

4. config.js 字段说明

window.EP_CONFIG = {
  duration: 123.5,   // 片长 = narrDuration + 引导层停留(一般 +4.7s)
  narrDuration: 118.8,  // tts.py 报告的配音成片时长
  outStart: 116.8,   // 收尾拉远开始 ≈ 最后一段打字结束前 0.2s(留 0.1 给归档动画)
  outDur: 1.6, ctaIn: 118.5,  // ctaIn ≈ narrDuration - 0.3
  zoomOut: 0.137,    // 全景比例(整面黑板缩进视口),固定
  cols:  [0,0,1,1,0,...],  // 每段所在列:0=左列 1=右列,长度=段数。建议左右交错(0,0,1,1 循环)
  vh:    [430,430,...],    // 每段图示区高度(340-560,简单段小、复杂段大),长度=段数
  zoomPeeks: [[5,30.0,36.5],...],  // 关键节点整板展示 [[段号(1起), 拉远开始, 拉回结束], ...] 0-3 处
  keywords: [["hidden state","b"],...]   // 打字机关键词着色:b=蓝 g=黄 r=红
};

cols/vh 的确定方法:读完文案后按每段信息量预估(数字统计类段 vh 420-520、单纯叙述段 340-430),首渲后按画面微调。完整示例见 example/config.js。

5. boards.js:结构边界与图示创作

画布与画板的结构、样式全部由引擎负责;boards.js 只负责画板下半图示区的内容创作,其余一律不要碰。

5.1 固定结构与样式(引擎强制,勿改)

5.2 接口与硬约束

function buildBoards(ctx) {
  const { b, i, z, vh, S, E, T, tl, ... } = ctx;   // 上下文与可用原语的完整签名见 template/engine/core.js 注释
  switch (b.sc.id) {   // 从 1 起,对应 script.txt 第几段
    case 1: { ... break; }
  }
}

5.3 图示创作(自由发挥)

6. 质量清单(交付前逐项打勾)


7. 封面制作规范(大字报克制版)

短视频发布时和成片一起交。封面不是片里某一帧,也不是另一套生成流程:壳固定,每期只改槽位。已验收的参考实现在包内 cover/cover.html(示例期标题「执行和验收拆开:/goal的实现原理」)。每期必须把槽位换成当期文案,不要把示例词原样发出去。

7.1 固定规则

角色色值用途
board#16241c / #1e3026板面(暗绿)
chalk#F3F0E4主标题、粉笔字
muted#9aa897眉题等次要信息
accent#7EC8F8关键词、/goal 一类术语
warm#E8C85A验收、强调

.big 的两行是 white-space: nowrap。字太长不会折成第三行,会直接冲出外框。冲出去就改断行,或把字号降到仍然占主导、且不超过 112px。不要加第三行来硬塞。

7.2 每期可改的槽位

印章只有三个颜色角色,对应壳里已有的 class,不要再加第四枚:.exec 是 accent 描边,.split 是粉笔底反白,.accept 是 warm 描边。换字,不换壳。

7.3 风格来源

手法从 EwingYangs/social-media-cover 的 bigtype-cover 收过来:外框、大字、印章、硬阴影,再落到上面的黑板色板。只借手法,不要求克隆那个仓库。像素基准以包内 cover/cover.html 为准。

7.4 渲染与自检

在已经改好槽位的 cover.html 所在目录:

CHROME="$(command -v google-chrome || command -v google-chrome-stable || command -v chromium || command -v chromium-browser)"
"$CHROME" --headless=new --disable-gpu --hide-scrollbars \
  --force-device-scale-factor=1 \
  --window-size=1080,1440 \
  --screenshot=cover-1080x1440.png \
  "file://$(pwd)/cover.html"

--force-device-scale-factor=1 要留着,否则高分屏会截出 2 倍尺寸。等价做法:Playwright / Puppeteer,viewport 1080×1440、deviceScaleFactor: 1,对 cover.html 截图。机器上要有 Noto Serif CJK SC(或宋体)和 Noto Sans SC;缺字体会换掉大字气质,先装字体再截。

截完用读图核对尺寸(模板依赖里已有 Pillow):

python3 -c "from PIL import Image; im=Image.open('cover-1080x1440.png'); assert im.size==(1080,1440), im.size"

然后打开 PNG 自己读一遍,通过后再给用户:

不通过只改槽位或断行,重跑同一条截图命令。

7.5 首帧版(1080×1920)

封面验收后,成片最前面要插一帧封面(Step 8),因为部分平台默认取首帧做封面。视频是 1080×1920,所以用 cover/cover-1920.html:内容与 cover.html 完全相同,画布加高到 1920,3:4 内容区垂直居中,上下延展同一块黑板深绿。不要把 3:4 图拉伸或裁切去凑竖屏。

把 cover.html 改好的槽位原样同步到 cover-1920.html,把截图命令换成 --window-size=1080,1920、--screenshot=cover-1080x1920.png 截图,读图确认内容与 3:4 版一致、居中、无裁切。



模板工程包下载

⬇︎ blackboard-template.zip(5.8MB,31 个文件)——内含渲染工程模板壳、引擎 core.js(画布/画板三态/打字机/镜头/CTA + 结构原语)、5 个工具脚本(new_video / tts / build_scenes / mix / add_cover_frame)、参考实例 hidden-state 三源文件、全部图片音频资源与素材署名记录,以及 cover/(3:4 封面 HTML 壳、1080×1920 首帧版 + 渲染说明)。解压即得完整工程,配合本手册 §3 使用(成片见 Step 0–6,封面见 Step 7–8 与 §7)。


上一篇
edge-tts 配音流水线:音色、语速、字幕与响度标准化
下一篇
快讯风竖屏短视频(NewsFlash)制作与排版布局规范