极光描物 · 技术文档
极光描物把教学动画拆成三层:白盒分镜(storyboard)→ 场景模块(scene)→ 确定性渲染(frame(t))。 本页说明如何本地跑起来、如何改分镜、如何对接支付,以及交付包里到底有什么。
快速开始
环境要求
| 依赖 | 说明 |
|---|---|
| Node.js ≥ 20 | 本项目在 Node 22 上开发;使用 ESM 与 --env-file-if-exists |
| ffmpeg | 帧序列编码、混音、响度统一(libx264 + aac) |
| Chromium | 逐帧截图。运行 bash scripts/setup-chrome.sh 自动下载 |
| Python 3.10+ / MeloTTS ONNX | 本地中英混读配音(同一发音人,离线)。运行 bash scripts/setup-melo-tts.sh 下载约 785MB 模型 |
# 1. 安装依赖
npm install
# 2. 准备渲染内核与中文语音(各约 1 分钟)
bash scripts/setup-chrome.sh
bash scripts/setup-melo-tts.sh
# 3. 启动
cp .env.example .env
npm start
# → http://127.0.0.1:8787 落地页
# → http://127.0.0.1:8787/studio 工作台
# → http://127.0.0.1:8787/docs.html 本文档
不保存、不匹配模板:每次生成都由 AI 现场重写分镜;AI 调用失败只重试一次,仍失败会进入生成失败页,不再生成通用兜底内容。配音可选本地 CPU(MeloTTS,男声为同一女声低音变频)或在线 edge-tts;支付可用模拟支付或易支付。
关键目录
engine/ 渲染引擎(浏览器与无头 Chromium 共用)
framekit.js 确定性动画工具箱(时间 t 的纯函数)
composition.js 合成器:时间轴、字幕、水印、布局守卫
player.js 交互播放器(工作台与交互讲义共用)
scenes/ 场景模块(一个文件一幕)
content/fixtures/topics.qa.json 视觉回归夹具(QA 用,不参与生成)
server/ Express API + 渲染队列 + TTS + 支付
web/ 落地页 / 工作台 / 文档 / 收银台
scripts/ 安装、冒烟测试、本地渲染
产品流程与计费
- 输入知识点 → 命中主题库(或配置 LLM 后由模型生成)→ 得到 3–5 幕白盒分镜。
- 编辑分镜 → 改旁白、时间点、参数(如 a=3,b=4)→ 自动保存并重算时间轴。
- 免费预览 → 浏览器内逐帧播放;需要听配音时渲染 540p 带水印预览。
- 付费下载 → 支付宝(或模拟支付)成功后,后台渲染 1080p/4K 无水印成片并打包交付。
计价按成片分钟数向上取整(不足 1 分钟按 1 分钟):1080p ¥29/分钟起,4K 商用 ¥59/分钟起。金额一律由服务端按 SKU 计算,前端只做展示。
分镜格式
分镜是一份 JSON,服务端与播放器、渲染器共用。核心约定:旁白按句存储,每句带相对本幕起始的秒数 at,TTS 之后用真实音频时长重排时间轴。
{
"meta": {
"title": "勾股定理:面积重排的力量",
"subject": "math",
"subjectLabel": "初中数学 · 几何",
"audience": "初二",
"theme": "ink", // ink | paper | chalk
"aspect": "16:9", // 16:9 | 9:16
"fps": 30,
"checks": ["3^2+4^2=5^2", "(a+b)^2=a^2+2ab+b^2"]
},
"scenes": [{
"id": "s2",
"type": "math.pythagoras",
"duration": 17.4, // 设计时长;若旁白更长会自动延长
"props": { "a": 3, "b": 4 },
"lines": [
{ "at": 0.3, "text": "先看这个大正方形,边长 a 加 b。" },
{ "at": 4.0, "text": "把它切成四个相同的直角三角形,中间是 c 平方。" }
]
}]
}
| 字段 | 说明 |
|---|---|
meta.checks | 显式公式校验项。支持数值等式与恒等式,写入后会出现在「校验」面板。 |
duration | 设计时长。最终时长 = max(设计时长, 最后一句旁白结束 + 0.6s)。 |
lines[].at | 该句旁白相对本幕开始的秒数。动画节奏按此对齐。 |
props | 场景参数,会被服务端按白名单收敛(数字限幅、字符串截断、数组限长)。 |
meta.cues | 只读。TTS 完成后由服务端写入的绝对字幕区间,播放器与 SRT 都以此为准。 |
场景目录
完整清单(含默认参数)也可以从 GET /api/engine/catalog 获取。新增场景只需在 engine/scenes/ 放一个文件并在 index.js 注册。
| type | 画面 | 主要 props |
|---|---|---|
cover | 章节封面:学科标签 + 大标题 + 朱印 | title, subtitle, eyebrow, formula |
math.pythagoras | 勾股定理面积重排:四个三角形真实位移 | a, b |
math.parabola | 二次函数:抛物线、顶点、对称轴、判别式 | a, b, c |
math.derivative | 导数:割线 shrinking 到切线 | x0 |
physics.projectile | 抛体运动:速度分解、轨迹、最高点 | v0, angle, g, h0 |
algo.bubblesort | 冒泡排序:柱状图 + 伪代码同步 | array |
algo.binarysearch | 二分查找:lo/mid/hi 指针与区间收缩 | array, target |
algo.dijkstra | Dijkstra:选点、松弛、距离表 | source |
concept | 通用讲解:三条要点 + 公式卡 | title, points[], formula, note |
recap | 复盘:三张要点卡 | title, items[] |
渲染引擎与校验
确定性渲染
每一幕是一个函数 build(ctx),通过 ctx.at(t0, dur, fn) 注册动画;seek(t) 会把所有动画以 p = clamp((t - t0)/dur) 重算一遍。因此:
- 同一分镜 + 同一时间 = 同一帧,可任意跳转、可断点续渲、可并行分段;
- 预览与成片使用同一份代码,所见即所得;
- 禁止
Math.random()/ CSS 动画 /Date.now(),否则逐帧截图会抖。
公式校验
生成分镜后,服务端会对 meta.checks 与旁白中抽取的数值等式做机器验算:数值等式直接求值,恒等式在定义域内取 6 组确定性样本比较两边。结果分四档:ok / mismatch / warn / skip,mismatch 会阻断「一次通过」并在工作台标红。
布局守卫
在每幕 25% / 50% / 75% 三个时刻,用 getBoundingClientRect 检查所有 data-fk-block 元素:越界、两两重叠(交集 > 16% 较小面积)、字号 < 22px、字幕遮挡。返回问题列表供工作台展示。这是把「看起来不对」变成可修问题的最小实现。
REST API
所有接口基于 Cookie 会话(访问即自动创建匿名会话,登录后可合并项目)。错误统一返回 { error } 与 4xx/5xx。
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/health | 队列、Chromium、TTS 状态 |
| GET | /api/config | 支付渠道、套餐、引擎目录、限额 |
| POST | /api/projects/plan | 生成分镜:{topic, audience, aspect, theme} |
| GET | /api/projects/:id | 项目 + 分镜 + 校验 + 预览/订单/下载 |
| PATCH | /api/projects/:id | 保存分镜(服务端会校验并重算时间轴) |
| POST | /api/projects/:id/inspect | 跑一次布局守卫 |
| POST | /api/projects/:id/render | 渲染:{kind: "preview"|"final"} |
| GET | /api/renders/:id | 渲染进度(轮询) |
| POST | /api/orders | 下单:{projectId, sku: "hd"|"pro"} |
| GET | /api/orders/:id | 订单状态(含自动支付检测) |
| POST | /api/projects/:id/finalize | 支付后生成成片并打包交付 |
| GET | /api/downloads/:token | 下载交付物(7 天有效) |
| GET | /api/projects/:id/handout | 在线交互讲义(HTML) |
# 一行命令生成分镜并查看校验结果
curl -s -c jar -X POST localhost:8787/api/projects/plan \
-H 'content-type: application/json' \
-d '{"topic":"勾股定理","aspect":"16:9","theme":"ink"}' | jq '.project.validation'
# 端到端冒烟:规划 → 支付 → 出片 → 下载
node scripts/smoke.mjs http://127.0.0.1:8787 勾股定理
支付对接
模拟支付(默认)
下单后生成二维码(内容指向本机 /pay.html?t=token),扫码即可在手机上完成「演示支付」;桌面端也提供「模拟支付成功」按钮。服务端默认 6 秒后自动置为已支付,便于演示与自动化测试(MOCK_PAY_AUTO=0 可关闭)。
接入支付宝电脑网站支付
PAYMENT_PROVIDER=alipay
ALIPAY_APP_ID=2021000000000000
ALIPAY_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----"
ALIPAY_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----"
ALIPAY_NOTIFY_URL=https://your-domain.com/api/pay/alipay/notify
ALIPAY_RETURN_URL=https://your-domain.com/api/pay/alipay/return
- 下单:
alipay.trade.page.pay,RSA2 签名,金额只由服务端 SKU 计算; - 异步通知:
POST /api/pay/alipay/notify,验签 → 校验金额 → 校验trade_status→ 幂等置为已支付; - 同步跳转:
GET /api/pay/alipay/return,验签后回到工作台并触发出片; - 通知丢失时可用
queryTrade()主动查单补偿(server/lib/alipay.js)。
交付物与授权
| 文件 | 说明 |
|---|---|
*.mp4 | 无水印成片(1080p / 4K),已写入 AIGC 隐式标识元数据 |
*.srt | 字幕,与画面字幕逐句一致 |
handout.html | 交互讲义:章节跳转、逐句点读,双击即可播放 |
storyboard.json | 分镜工程文件,可继续编辑 |
source/engine/ | 场景源码,可改动画与样式 |
render-local.mjs | 本地渲染脚本(Node + Chromium) |
LICENSE.txt | 个人非商用 / 商用授权条款 |
「4K 商用交付」含可去除片尾标识的商用授权;「1080p 标准交付」限本人非盈利教学使用。授权文本随订单生成,模板见 server/lib/bundle.js。
合规说明
- 按《人工智能生成合成内容标识办法》要求,成片 MP4 写入隐式标识(metadata 中的生成说明与编码器信息),并在产品页面标注 AI 辅助生成;
- 「无水印」指无品牌水印,不等于无 AI 标识;如后续接入显式标识,将作为播放器叠层保留;
- 学科表述由使用者终审;系统只保证公式与数值层面的机器校验;
- 教材插图、字体、题目原文的版权由上传者负责,平台不提供未经授权的教材扫描件。
限制与已知问题
- 渲染是 CPU 密集型:1080p 约 2–4 分钟 / 30 秒成片(单机 4 核);预览 540p 约 40–60 秒。生产环境应改为多 worker 队列;
- 场景动画的拍点按旁白节奏设计,大幅改写旁白长度时建议同步调整
lines[].at; - 4K 渲染目前为同步任务,耗时较长,建议只在付费后触发;
- 本地女声由 MeloTTS 中英混读模型提供;本地男声为同一女声低音变频,中英文衔接不换人;后台可切换本地或在线 edge-tts。
- 竖版(9:16)目前是「舞台重排版」:把 16:9 舞台缩放进竖屏卡片并放大字幕,尚未为每一幕单独设计竖版构图。