html-explainer
OneMoh · OneMoh/html-explainer
Skill tác nhân này tạo video giải thích với lời thoại, phụ đề và ảnh bìa từ các cảnh HTML, hoàn toàn cục bộ và đồng bộ.
把一个主题做成有配音、字幕、封面的讲解分析类视频 —— 全本地、用 HTML 写画面,稳定音画同步。 面向 AI 编程智能体的确定性 HTML → MP4 渲染流水线。
Xem trước
Bạn có thể làm gì với nó
- Tự động tạo các video giải thích ngắn về các khái niệm kỹ thuật.
- Tạo nội dung video cho mục đích tiếp thị hoặc giáo dục mà không cần chỉnh sửa thủ công.
- Biến các báo cáo hoặc tài liệu phức tạp thành các bản tóm tắt video hấp dẫn.
Kiểm tra an toàn
Không thấy dấu hiệu rủi ro trong các file đã quét.
Quét tự động các file trong repo. Luôn xem lại code trước khi chạy.
Trích từ README
html-explainer
一个 Agent Skill:给任意主题,产出一条带配音、硬字幕、封面的讲解视频。
HTML 写画面 → 确定性逐帧渲染 → 真 MP4。全本地跑,核心链路零 API key、零按次计费。
画面用一套可 seek 的动效库写,风格由主题驱动编排,渲染支持 4K60、快门运动模糊与多进程并行。
两条用本技能生成的成片——画面、配音、字幕、封面全部由流水线产出,无手工后期。 封面等素材托管在演示仓库,本仓库零体积。
| 港股创新药 · 早盘 | 量化简史 |
|---|---|
| https://github.com/user-attachments/assets/8007843c-088d-457c-8550-81ec912e0add | https://github.com/user-attachments/assets/28949988-449b-4c4e-8d0f-6706af6f64ee |
它是什么
html-explainer 把一个主题做成带配音、带硬字幕、带进度条的讲解视频,画面用
HTML/CSS/GSAP 写。
它是一个 Agent Skill(SKILL.md + scripts/ + references/),遵循
Agent Skills 约定,Claude Code / OpenAI Codex /
WorkBuddy / Cursor / Gemini CLI 等都能直接加载。内部是普通的 Python 与 Node 脚本,
手工跑也完全没问题。
你只需要说一句:
把「为什么天空是蓝色的」做成一条 1 分钟的讲解视频。
技能会带着智能体走完:调研 → 解说词 → 配音 → 字幕与节拍 → 编排风格并写画面 → 渲染 → 体检 → 封面。
画面不必对着空白页从零写。技能内置一套可 seek 的动效库(40+ 动作词汇,见 动效库与模板编排),和一份模板编排器 —— 它读解说词本身,判断每场在片子里 扮演什么角色,再挑风格、混用、决定开场与转场;渲染层则支持 4K60、快门运动模糊与多进程并行。 这些开关都是可选的:不带任何参数跑,就是一条最朴素的 1080p30 片子。
安装
一句话安装(推荐)
把下面这句发给你的智能体:
给当前本地环境安装该 Skill:https://github.com/OneMoh/html-explainer.git
安装到你的技能目录,并检测安装必要的运行环境(Python 3.9+ / Node 18+ / Chrome 或 Edge / ffmpeg)
它会自己 clone 到对应目录、跑环境自检、把缺的东西装上。装完新开一个会话,让智能体重新 扫描技能目录。
手动安装
仓库根目录就是技能目录(SKILL.md 在根),直接 clone 进技能目录即可,不用再拷子目录:
git clone https://github.com/OneMoh/html-explainer.git ~/.workbuddy/skills/html-explainer
bash ~/.workbuddy/skills/html-explainer/setup_env.sh --install
Windows 上 ~ 就是 C:\Users\<你的用户名>。
各智能体的技能目录
| 智能体 | 个人级(全局) | 项目级(仓库内) |
|---|---|---|
| WorkBuddy | ~/.workbuddy/skills/ | <工作区>/.workbuddy/skills/ |
| Claude Code | ~/.claude/skills/ | .claude/skills/ |
| OpenAI Codex | ~/.codex/skills/ | .codex/skills/ 或 .agents/skills/ |
| Gemini CLI | ~/.gemini/skills/ | .gemini/skills/ 或 .agents/skills/ |
| Cursor | ~/.cursor/skills/ | .cursor/skills/ |
| GitHub Copilot / VS Code | ~/.copilot/skills/ | .github/skills/ |
| OpenCode | ~/.config/opencode/skills/ | .opencode/skills/ |
| Windsurf | ~/.windsurf/skills/ | .windsurf/skills/ |
| 其他(通用约定) | ~/.agents/skills/ | .agents/skills/ |
记不住放哪:优先 .agents/skills/,多数工具都认它(Claude Code 是例外,只认
.claude/skills/)。
环境要求
| 项 | 最低 | 说明 |
|---|---|---|
| Python | 3.9+ | edge-tts==7.2.8(刻意钉死 —— v7 改过边界 API)、numpy、pillow、imageio-ffmpeg。火山引擎引擎零额外依赖(只用标准库 urllib,不需要装 SDK) |
| Node.js | 18+ | 渲染器与封面器用 |
| 浏览器 | Chrome 或 Edge | 自动探测;都没有才下载 playwright chromium(约 115MB,只需一次) |
| ffmpeg | 任意版本 | 先在 PATH 找;没有则用 imageio-ffmpeg 自带的静态二进制 |
| 磁盘 | 每条成片约 2GB | 帧 PNG 体积大,合成后可删 |
bash setup_env.sh 只检查并报告缺什么,--install 才会装。全程不需要管理员权限。
动效库、编排器与全部渲染档位都只用上面已声明的库,没有引入任何新的运行时依赖。
配音:两个引擎
流水线开始前,智能体会先问你用哪个 TTS,再列候选音色让你挑(也可以直接给它音色 ID):
edge-tts | 火山引擎语音合成 2.0 | |
|---|---|---|
| 你要做什么 | 什么都不用做 | 填一次 API Key |
| 费用 | 免费 | 按字符计费 |
| 音色 | 内置中英文若干 | 豆包 2.0 音色库,含声音复刻 |
| 字级时间戳 | WordBoundary | sentence.words[] |
| 额外依赖 | edge-tts==7.2.8、imageio-ffmpeg | 零 —— 只用标准库 urllib |
| 什么时候选它 | 默认。开箱可用、够用 | 想要更自然的语气与更好音质 |
两者产出的 audio-manifest.json 结构完全一致,所以时间轴、字幕、渲染全都不用改 ——
换引擎只是换一个字段的事。
选火山时,你唯一要动手的地方
技能第一次跑火山时会生成一个 tts.env 并停下来,然后告诉你把 API Key 填进去
(火山控制台 → 语音技术 → API Key 管理)。你填好回来说一声,智能体就去测连接、确认音色,
然后接着往下跑。
这一步刻意不经过对话窗口 —— 密钥不发给智能体,智能体也不需要知道它。
内置常用音色(完整列表见官方音色文档):
| 男声 | 女声 |
|---|---|
| 云舟 2.0(默认)· 温暖阿虎 2.0 · 解说小明 2.0 · 磁性解说男声 2.0悬疑解说 2.0 · 广告解说 2.0 · 儒雅青年 2.0 · 少年梓辛 2.0 · 深夜播客 2.0 | 小何 2.0 · Vivi 2.0 · 知性灿灿 2.0甜美桃子 2.0 · 邻家女孩 2.0 · 温柔淑女 2.0 |
内置列表不够用时,也可以直接用声音复刻出来的音色 ID。
密钥纪律
API Key 只存在 tts.env 里,而且只有技能里的一个接口包读它。
智能体调的是那个接口包,拿不到、也不需要读密钥的值:
你(手工填一次)→ tts.env(已被 .gitignore 忽略)
↓ 只有接口包读
合成调用 ← 智能体只调这个,永远拿不到密钥值
↓
火山引擎
- 不进对话:问智能体密钥状态,它给你的是脱敏摘要(
ef90******86c8)。 - 不进仓库:
.gitignore覆盖tts.env/*.env/*.key/*.pem/secrets/; 仓库自检里有专门的密钥防线,并且做过反向自证 —— 故意植入一个假密钥会被当场抓到。 - 不进日志:出错信息过脱敏,只抹「这次实际用到的密钥值」与
AKLT…形态的 token —— 刻意不用「长字符串就算密钥」这种宽规则,否则连排查要用的请求 ID 一起抹掉。 - 不进分发:技能打包时排除密钥文件。
- 兜底层(真正的安全边界):万一还是泄了,损失要可控 —— 建议在火山控制台用 子账号只授语音合成权限、设用量告警与限额,密钥可随时轮换。
说句实话:合成代码是在你本机执行的,运行时进程里密钥对那一段脚本可见, 「智能体绝对读不到」做不到。上面保证的是不进对话、不进仓库、不进日志、不进分发, 把泄露半径收敛成一次可撤销的事故。
另外别用环境变量存密钥 —— 很多宿主会把进程环境原样打进会话记录。
别把 tts.env.example 改名成 tts.env 去填:那是要留在仓库里的模板,
改名会让后来的人没模板可抄(仓库自检会报错并提示)。复制一份再填。
视频项目结构
说一句需求,智能体会在一个视频项目目录里走完整个流水线;产物落在 out/:
slug.mp4、cover_169.png、cover_34.png、slug.srt、slug.vtt,外加 qc_report.md
与 qc_sheet.jpg;做竖版投放时再加一张 cover_916.png。
项目目录长这样:
my-video/
├── project.json # slug、fps、尺寸、音色、语速、场景顺序、章节
├── narration.json # [{ id, text }] —— 用 "|" 切字幕块
├── theme.css # 所有颜色,以 CSS 变量形式
├── style-plan.json # 每场的主/次风格、角色、转场、动效强度(编排器产出)
├── consent.json # 六个确认点(含渲染通道)由用户拍板才放行
├── frames/ # 一个场景一个 HTML;<id>.beats.js 自动生成,绝不手改
├── audio/ render/ research/ script/
└── out/ # MP4、封面、SRT/VTT、QC 报告
为什么不用录屏
多数「HTML 转视频」工具是启动浏览器、播放动画、录屏。这条路会生出一整类间歇性 bug —— 动画在录制开始前就播了、网络字体加载晚了导致半段视频用了错误字体、前几帧拍到的是动画前的状态。
html-explainer 不录制,它 seek:把 GSAP 时间轴定位到那一瞬间
(tl.pause(t, false)),同步所有 CSS 动画(document.getAnimations().currentTime),
等两个动画帧后截图。于是第 1204 帧与下一次渲染的第 1204 帧逐像素一致 —— QC 可以定点抽查、
单个场景可以独立重渲,一整类时序 bug 从根本上无法发生。
代价是每个动画都必须可 seek:墙钟动画(setInterval、requestAnimationFrame 计数、
CSS transition 入场)不可能工作,会被 lint_frames.py 直接拒绝。这也正是动效库被设计成
「t → 一组数字」的纯函数的原因:它天生可 seek。
特性
| 节拍锚定解说词 | 画面按匹配字幕文本定位动画(B('块文本')),绝不硬编码帧号。改解说词,全片自动重排时间,画面代码零改动。 |
| 词边界字幕 | 时序来自 TTS 的字级时间戳,不按字数插值 —— 中文里两个同字数的短语时长可以差 3 倍。edge-tts 取 WordBoundary,火山引擎取 sentence.words[]。 |