程序员爸爸的B站自制视频全流程:Playwright 实机录屏 + edge-tts 配音 + ffmpeg 一键合成
📺我的B站主页:只是甲
🎬本文配套视频(最近3期):
- 《100亿 Token 烧出来的血泪经验:两个月重度使用大模型后的实战总结》
- 《程序员爸爸给6岁儿子写了个学习游戏:边打怪边学语文数学英语》
- 《我一行代码没写,用 ZCode 给 6 岁儿子"聊"出了一个学习游戏》
后两期视频的完整工程代码和成片,都是按本文这条流水线做出来的。欢迎去B站一键三连支持一下!
前言
很多想做 B 站技术 / 产品介绍视频的朋友,卡在同一个地方:不会剪辑、不想出镜、配音还要求播音的朋友。
我的做法是:把整个视频生产做成一条纯代码流水线——
脚本(JSON) ──> 自动化实机录屏 ──┐ ├─> edge-tts 旁白 ──┼──> ffmpeg 合成 ──> 成片.mp4 + 字幕.srt + 封面.jpg ├─> numpy 合成BGM ──┤ └─> HTML卡片截图 ───┘全程零手动剪辑,改一行文案重跑脚本就出新片。我已经用这条流水线产出了两支播放视频(工程分别 4:54 和 4:40),本文把完整方法、代码骨架和踩过的坑全部写出来。
一、整体架构:5 个脚本各管一段
工程目录结构(以游戏介绍视频为例):
video-project/ ├── narration.json # 旁白文案(唯一需要人写的东西) ├── bot.js # ① Playwright 驱动 Edge 实机录屏 ├── tts.py # ② edge-tts 生成旁白 mp3 + 词级时间戳 ├── bgm.py # ③ numpy 合成无版权 BGM ├── cards.html/js # ④ 片头/章节卡:HTML 渲染 + Edge 截图 ├── compose.py # ⑤ ffmpeg 合成:画面拼接 + 混音 + 字幕 └── out/ # 成片.mp4 / 字幕.srt / 封面.jpg / 上传说明.md核心思想:旁白文案是唯一的"源代码",narration.json里每句旁白绑定一个画面段(segment),下游所有素材(配音时长、字幕时间轴、画面切换点)全部由它自动推导。
{"voice":"zh-CN-YunxiNeural","rate":"+6%","lines":[{"id":"L0","segment":"intro","text":"孩子爱玩游戏,却静不下心学习?我这位程序员爸爸,给出了自己的答案。"},{"id":"L5","segment":"B","text":"第一关是打怪兽:狂点怪兽攒能量,这个纯靠手速,小朋友最喜欢。"}]}二、实机录屏:Playwright 驱动本机 Edge,不要下载 Chromium
介绍软件 / 游戏类产品,最有说服力的画面是真实操作录屏。我用playwright-core直接驱动本机已装的 Edge(免下载浏览器):
const{chromium}=require('playwright-core');constEDGE='C:/Program Files (x86)/Microsoft/Edge/Application/msedge.exe';constbrowser=awaitchromium.launch({executablePath:EDGE,headless:true,recordVideo:{dir:'takes/',size:{width:1280,height:720}},});要点:
- 持久化 profile:用
launchPersistentContext('./profile'),多个 take 之间能续上游戏进度,避免每段都从新手引导重录。 - 分段录制:每个演示场景一个 take(建角 / 核心玩法 / 商店 / 设置),合成时再按需裁剪拼接。
- headless 关语音:录屏时把页面内 TTS 关掉(视频用外部旁白),否则无语音引擎环境下每句要等 15s 超时兜底。
录屏自动化踩坑清单(全是真金白银的时间)
- 顶层
const不在window上:游戏管理器若用顶层const GameManager = {...}声明,page.evaluate里必须用裸标识符GameManager.addGold(300)访问,window.GameManager是 undefined。 - 弹窗延迟出现拦截点击:签到宝箱、通用弹窗会在页面加载后延迟弹出并拦截全屏点击。写了个
clearOverlays()轮询:最多等min(2500, waitMs),出现弹窗就处理到底再走。 - 无限动画元素永远点不动:带无限浮动动画的按钮(宝箱),Playwright 的 actionability 稳定性检查永远不通过 → 直接
page.evaluate里 DOM click。 - 同
data-action有隐藏兄弟元素:按文本点击会命中隐藏的复制品 → 用getByRole按可访问名定位。 - 子界面没有底部导航:录"设置"前要先点返回按钮回主界面,否则视角不对。
三、旁白:edge-tts 免费拿到"电台级"中文配音
edge-tts调用微软 Edge 在线 TTS,完全免费,中文科普 / 新闻风推荐zh-CN-YunxiNeural(云希),语速+6%刚好。
它最值钱的功能是WordBoundary 词级时间戳——这是免费字幕时间轴的关键:
comm=edge_tts.Communicate(text,voice,rate=rate,boundary='WordBoundary')asyncforchunkincomm.stream():ifchunk['type']=='audio':f.write(chunk['data'])elifchunk['type']=='WordBoundary':# offset 单位是 100ns,除以 10000 换成毫秒bounds.append({'text':chunk['text'],'offset_ms':chunk['offset']/10000,'dur_ms':chunk['duration']/10000})两个必须处理的点:
- 间歇性超时:
speech.platform.bing.com的 WebSocket 经常抽风,必须带重试(我设 4 次、指数退避),并且校验输出文件大小(<2KB 视为失败)。 - 每句旁白单独出一个 mp3 + 词边界列表,落成
timeline.json(每句起止时长)和subs_raw.json(词级对齐),后面字幕和混音全靠它。
四、BGM:用 numpy 合成,彻底绕开版权
B 站对 BGM 版权越来越严,音乐库翻来翻去不如自己写一个。BGM 本质就是周期信号叠加——纯 numpy 200 行合成 128 BPM C 大调马林巴配乐:
# 马林巴 = 基频正弦 + 4倍频泛音 + 指数衰减包络defpluck(freq,dur,decay=0.18,harmonics=((1.0,1.0),(4.0,0.22)),vol=1.0):...# 和弦进行 C - G - Am - F,每小节一换# kick 底鼓 = 频率指数下滑的正弦,hat = 高通白噪声再叠加:三次均值卷积柔化音色、右声道 18ms 延迟做假立体声、首尾淡入淡出。生成的bgm.wav版权归自己,随便商用。
五、片头 / 章节卡:HTML 渲染 + 浏览器截图
为什么不用 PIL / Pillow 画标题卡?因为PIL 画不了彩色 emoji,排版也丑。直接写一个cards.html(分号排版 + 大字 + emoji),用 Edge headless 截图成 PNG——浏览器渲染天然支持彩色 emoji、Web 字体、CSS 动画。
六、合成:compose.py 一个脚本出成片
compose.py用imageio-ffmpeg自带的静态 ffmpeg,完成四件事:
- 时间线计算:卡片时长由该段旁白时长决定,实机片段按实际时长裁头(webm 开头有 ~0.3s 黑屏,必须裁)。
- 画面:统一转码 1280x720@30 yuv420p,卡片加 zoompan 缓推 + 淡入淡出,然后 concat。
- 音频:BGM 压到 0.16 音量垫底,每句旁白按时间线偏移
adelay定位,amix normalize=0混音 +alimiter限幅,最后loudnorm归一到-14 LUFS(B 站标准响度,传上去不会被二次压低)。 - 字幕 + 封面:SRT 由词边界直接生成(比 Whisper 事后识别对齐得准),封面图一并输出。
进阶:合成前先跑plan预检
第二支视频(vibe coding 主题)加了个plan模式:合成前逐段校验"旁白放不放在得下画面窗口",输出类似:
L3: 旁白结束@26.1s << 窗口21.0s 溢出!旁白比画面长是这类流水线唯一的结构性失败模式。解法优先级:加大画面窗口 > 放慢演示动画重录 > 删文案。第一版 V1 段 21s 画面装不下 26s 旁白,就是把演示动画打字速度调慢 + 尾部多 sleep 2 秒撑到 28s 解决的。
七、验收:模型看不了图怎么办
成片做 visual QA 时,如果当前 AI 模型不支持图片输入,用OCR 抽帧校验:每 30~50 秒抽一帧,对帧做 OCR(本地 RapidOCR / PaddleOCR),核对每帧文字与脚本段落对得上。9 帧全过 ≈ 整片无串段。
八、上传 B 站:这些细节决定推荐量
工程最后会生成一份B站上传说明.md,把发布要素一次配齐:
| 要素 | 建议 |
|---|---|
| 标题 | 悬念 + 结果式:“我一行代码没写,给6岁儿子’聊’出了一个学习游戏” |
| 简介 | 前 3 行放钩子,随后 bullet 列卖点,附时间轴(B 站会识别章节) |
| 标签 | 8-10 个,大词 + 长尾词混搭(#AIGC #vibecoding #程序员 #亲子) |
| 分区 | 科技 → 软件应用(AI 工具类在科技区流量更好) |
| 字幕 | 直接上传工程生成的.srt,B 站支持外挂字幕 |
| 响度 | 已 loudnorm -14 LUFS,B 站不会二次压音量 |
| 发布时间 | 工作日 18:00-21:00 或周末上午(受众活跃时段) |
| 发布后 | 1 小时内回复前排评论,利于推荐 |
两条合规提醒:
- AI 生成内容要如实标注。我的 coding 演示视频界面是"脚本化还原演示"(非原始录屏),这条声明直接写进了视频简介——诚实声明不减分,被观众扒出来"造假"才致命。
- 用了 AI 配音 / AI 画面的稿件,投稿页勾选"内容由 AI 生成"。
九、复用这套流水线
整套东西抽象出来就是:输入一份narration.json,输出一支成片。换主题只需要换三样:
- 旁白文案(唯一要人写的)
bot.js里的演示操作脚本(录什么画面)cards.html的卡片文案
配音、BGM、字幕、混音、响度全部不用动。第一支视频工程改造到第二支视频,只用了半天。
同类可参考的开源项目:MoneyPrinterTurbo(全自动文案→成片)、Manim(数学动画)。但我这条"自动化实机录屏 + TTS 词边界字幕 + 自合成 BGM"的路线,特别适合软件 / 游戏 / 工具类产品介绍——画面必须是真实操作,通用素材库流水线做不了。
最后
视频成品都在B站,感兴趣去围观、催更、提意见:
- 📺 B站主页:只是甲
- 🔥 最新:《100亿 Token 烧出来的血泪经验》
- 🎮 游戏介绍:《程序员爸爸给6岁儿子写了个学习游戏》
- 🤖 vibe coding 复盘:《我一行代码没写,用 ZCode "聊"出学习游戏》
觉得这条流水线有用,B站一键三连 + CSDN 点赞收藏,就是我更新的动力。有问题评论区见!