SpeedPainter explainer-video 逆向素材包
2026-07-21 抓取。全部来自公开端点或用你自己的账号授权后的只读调用,没有任何绕过鉴权的操作。
#01-接口契约/ —— 它的 API 全貌
| 文件 | 是什么 | 怎么拿到的 |
|---|---|---|
REST-openapi.json |
它后端 REST 服务的完整 OpenAPI 文档,标题 A1d Image Upscale Worker API |
完全公开、无需鉴权:https://api.speedpainter.org/openapi.json(根路径 / 直接就是一个 SwaggerUI 页面) |
MCP-tools-list.json |
7 个 MCP 工具的完整 JSON Schema(含所有字段约束、正则、枚举、annotations) | 用你授权后的 token 调 tools/list |
templates-素材清单.json |
它的手部/画布素材清单 + 图片直链 | 公开端点 /api/templates |
oauth-*.json |
OAuth 发现文档(PKCE S256、动态注册、scope video:render) |
公开的 .well-known |
这里面最值钱的三件事:
POST /api/whiteboard-task的全部字段和约束:text(≤20000) /sourceUrl/durationSeconds(5–300) /aspectRatio(16:9 9:16 1:1 4:5) /language(≤10) /voiceId(≤80) /subtitles(none|soft|burn)。我们自建 API 直接照抄这套命名。- 任务状态机:
WAITING/PROCESSING/FINISHED/FAILED/CANCEL/TIMEOUT+stage(planning → rendering → postprocessing → uploading) +progress(0.0–1.0) + 返回videoUrl / srtUrl / assets / timings / title / scenes。 - 老产品
/api/task(图→速绘视频)的完整参数面,暴露了它渲染引擎的能力边界:sketchDuration+colorFillDuration(描线时长和上色时长是分开的两段)、needHand/handTitle/handType(left|right)、needCanvas/canvasTitle、drawDirection(上下/下上/左右/右左四种)、videoQuality(720p|1080p|2K|4K)、fps(30|60)、sizeMode(smart_cover|contain|cover|stretch)、needFadeout/fadeOutAtEnd、watermarkEnabled/endcardEnabled。
MCP-tools-list.json里新挖到的关键一句(比之前的文档更进一步):finalize_explainer_asset_upload的描述是 "Import an already uploaded illustration into the hand-drawable outline and color asset pipeline"。 也就是说它把每张插画拆成 outline(可描边轮廓)+ color(填色)两套资产,先描线后填色 —— 这正好对上老产品的sketchDuration/colorFillDuration两段式。这是它渲染引擎的核心设计,我们应该直接抄。另外
cancel_explainer_task写着 "Any reserved credits are refunded through the existing billing ledger" —— 证实是积分制,创建任务时预扣积分。validate_explainer_manifest写着校验 "deterministic layout" —— 排版是确定性算法,不是模型。
#02-官方提示词/ —— 它的产品逻辑
| 文件 | 价值 |
|---|---|
SKILL.md |
默认行为规则:默认 16:9 / 60 秒 / 烧录字幕 / 无 BGM;<30 秒要先警告;轮询要守 pollAfterSeconds;失败后必须换新 UUID |
advanced-review.md |
含金量最高。分镜规则:场景数 = ⌈目标秒数 ÷ 10⌉(1–30 幕);每 10 秒场景的旁白量 = 英文 18–22 词 / 中文 28–36 字;生图并发每波最多 6 张;>60 秒的视频先做「场景 1–3 风格校准组」;完整的 manifest JSON 结构;以及那段生图"视觉锁"提示词全文(纯白/透明底、手绘黑线+浅交叉线影、珊瑚色/深蓝/青绿/绿/琥珀色马克笔填色、无水印无 logo 无长文字) |
public-copy.test.mjs(泄漏供应商名单的测试) |
见下 |
#那个"防泄漏"测试反而泄漏了供应商名单
文件里有这么一行,作用是在 CI 里禁止公开文档出现这些词:
const forbiddenSourceNames = /\b(?:minimax|evolink|tuzi|pollinations|z-image(?:-turbo)?|gpt(?:-[\w.]+)?)\b/i;要防止泄漏的名字本身就是名单:MiniMax、EvoLink.AI、兔子API(tuzi)、Pollinations.ai、Z-Image(-Turbo)、某个 GPT 系列。
结论:SpeedPainter 没有自研任何模型,它和我们一样是拿中转 key 调第三方 API,用的中转商(tuzi)跟云雾还是同类角色。它月费 $16–50 卖的是编排 + 渲染引擎 + 打包。这就是复刻可行的根本原因。
同一个测试还禁止出现 music 字样 ⇒ BGM 功能代码里有,产品上藏着没开放。
#03-他们的素材/ —— 仅供分析构图,不得商用
从 storage.speedpainter.org 公开直链下的 5 只手 + 3 块画布(后缀写 .png 其实是 WebP,200×200 RGBA)。
用途仅限:看它的手部构图怎么设计(笔尖落在哪、手从哪个角切出、留多少透明边距)。生产环境一律用 04-我们生成的手/ 里我们自己出的素材。
#04-我们生成的手/ —— 我们自己的,可商用
用你 image2.0 那把云雾 key 调 gpt-image-2(1024×1024,quality=medium,一张几分钱)生成,一次成功,构图和 VideoScribe 官方素材几乎一致。
已做后处理:白底转 RGBA 透明(按到纯白的距离做柔和 alpha,无锯齿)→ 只保留最大连通域去噪 → 自动定位笔尖像素 → 输出渲染用的 360px 版。
*_rgba.png:1024 原尺寸透明版*_360.png:渲染用尺寸tips.json:每只手的笔尖锚点坐标,渲染器贴手时直接读这个gen_hand.py/gen_hand2.py:生成脚本(提示词在里面,改一个词就能出新肤色/新笔)mkhand.py:批量抠图 + 笔尖定位脚本
#已验证好使的提示词模板
A realistic {肤色} {right|LEFT} hand holding a {thin black marker pen|yellow wooden pencil|
thick chisel-tip marker}, seen from above at a 45 degree angle as if drawing on paper.
The pen tip touches the TOP-{LEFT|RIGHT} corner of the frame. Only hand, wrist and a bit of
sleeve are visible, cropped at the bottom-{right|left}. Pure flat white background, no shadow,
no desk, no paper, no text. Clean product-style lighting, sharp edges, isolated subject.要点:"笔尖落在画面某个角" + "手从对角切出" + "纯白底无阴影" 这三句是构图的关键,缺一张图就没法当素材用。左手版把角和切出方向都镜像。
#05-原始仓库/ —— GitHub 上那个壳
install.mjs(安装器,只做两件事:拷 skill 到 ~/.claude/skills、跑 claude mcp add)、README.md、mcp.json。整个仓库没有一行生成逻辑。
#还没拿到的(真正的护城河)
渲染引擎本身——笔画排序算法、手部跟随、outline/color 两段式时间线合成。这部分只在服务端,任何逆向都只能拿到输出物。
想再往前一步,只能用账号真跑一条任务,然后从产物反推:
ffprobe看 MP4 metadata(encoder字段常泄漏渲染器和 ffmpeg 版本)- 听音轨判断有没有 BGM(这是唯一能确认 BGM 的办法)
- 看
assets返回的插画风格 → 反推生图模型 - 看
srtUrl的切分粒度 → 反推 TTS 是否自带逐词时间戳 - 看
timings→ 拿到它各阶段的真实耗时,直接对标我们的性能预算
LLM 供应商基本无解:从分镜文本反推不出模型,除非 scenes 元数据里带模型名。
另外试过:validate_explainer_manifest 这个只读接口本来想用来枚举 manifest 的合法枚举值(它不消耗额度),但它只回一句笼统的 "The video manifest could not be validated.",只有 id 非法时才给具体报错 —— 错误信息刻意做了脱敏,这条路走不通。
#白板解说视频站-复刻计划
#白板手绘解说视频站 —— 复刻 SpeedPainter explainer-video 的完整实施计划
交给 Fable 在
/goal模式下自主执行。本文档已包含全部调研结论、技术选型、踩坑预警和分阶段验收标准,执行时不需要再联网做技术选型调研,直接按阶段推进即可。生成时间:2026-07-21 调研来源:逆向 SpeedPainter 线上服务 + 8 个并行调研 agent(GitHub / 学术 / 中英社区 / 商业产品文档)
配套素材包:
~/Desktop/explainer-video-逆向素材/—— 里面有它的完整 OpenAPI、MCP 工具 schema、官方提示词原文,以及已经做好、可直接使用的 7 套手部素材(含笔尖锚点坐标)。执行前先看那个目录的00-这些是什么.md。
#〇、一句话目标
在东京 VPS 上自建一个「粘一段文字/一个链接 → 几分钟后拿到一条带旁白和字幕的手绘白板解说 MP4」的网页站,前端挂 scribe.saveme505.help,效果对标 speedpainter.org 的 explainer-video,但后端完全自有、无第三方账号绑定。
#一、被复刻对象的逆向结论(已完成,无需重做)
#1.1 它的真实形态
GitHub 上的 SpeedPainterOrg/explainer-video 只有一份 Agent Skill 提示词 + 一个 MCP 配置,没有任何生成逻辑。真正的能力全在远程服务 https://api.speedpainter.org/mcp(OAuth + Google 登录,scope video:render)。
#1.2 关键发现:它的 OpenAPI 文档完全公开
https://api.speedpainter.org/openapi.json 无需鉴权即可读取(本地已存档:/private/tmp/.../scratchpad/sp_openapi.json)。由此拿到它的真实接口契约:
POST /api/whiteboard-task(对应 MCP 工具 create_explainer_video):
| 字段 | 类型/约束 |
|---|---|
userId |
uuid,必填 |
text |
string,maxLength 20000 |
sourceUrl |
uri(可选,服务端抓取) |
durationSeconds |
int,5–300 |
aspectRatio |
enum 16:9 / 9:16 / 1:1 / 4:5 |
language |
string,maxLength 10 |
voiceId |
string,maxLength 80 |
subtitles |
enum none / soft / burn |
GET /api/whiteboard-task/{id} 返回:taskId、status(WAITING/PROCESSING/FINISHED/FAILED/CANCEL/TIMEOUT)、stage(planning → rendering → postprocessing → uploading)、progress(0.0–1.0)、videoUrl、srtUrl、assets、timings、title、scenes。
DELETE /api/whiteboard-task/{id}:best-effort 取消,只在阶段边界生效。
GET /api/templates:公开返回它的手部/画布素材清单(5 种手 + 3 种画布,PNG 实为 WebP,200×200 RGBA,可下载参考构图,但不得直接商用,我们自己画或用开源素材)。
它的老产品 /api/task(图→速绘视频)参数面更全,值得抄的枚举:sketchDuration / colorFillDuration / needHand / handType(left|right) / drawDirection(top_to_bottom|bottom_to_top|left_to_right|right_to_left) / videoQuality(720p|1080p|2K|4K) / fps(30|60) / sizeMode(smart_cover|contain|cover|stretch) / renderMode(final|preview)。我们的 API 直接沿用这套字段命名,将来若想再包一层 MCP 给 Claude Code 用,契约天然对齐。
#1.3 它的技术栈(证据级)
- 边缘/网关:Cloudflare(大概率 Workers),存储 Cloudflare R2(openapi 原文 "Upload file to R2")
- 官网
speedpainter.org:Vercel - 母公司:A1D.AI(openapi 标题
A1d Image Upscale Worker API,app参数默认"sp") - 支付:Stripe;定价 $16.58/$33.25/$49.92 月(积分制)
- 供应商池(强证据):仓库里
test/public-copy.test.mjs有一条"禁止公开文案泄漏供应商名"的正则,反向坐实它内部用了minimax | evolink | tuzi | pollinations | z-image(-turbo) | gpt-*。即它自己也是拼装第三方 API,不是自研模型 —— 这是我们能低成本复刻的根本原因。 - 同一测试禁止出现
music字样 ⇒ 服务端已有 BGM 能力但对外隐藏。
#1.3·补 授权后从 tools/list 拿到的新情报(2026-07-21 更新)
用自建 OAuth(动态注册 + PKCE,回调 localhost:8765)拿到 token 后调 tools/list,服务端 serverInfo 报 explainer-video v0.5.0。完整 schema 存档在 ~/Desktop/explainer-video-逆向素材/01-接口契约/MCP-tools-list.json。三条新增关键情报:
- ⭐ 它把每张插画拆成 outline + color 两套资产
finalize_explainer_asset_upload的描述原文:"Import an already uploaded illustration into the hand-drawable outline and color asset pipeline"。 这与老产品/api/task的sketchDuration+colorFillDuration两个独立时长参数完全对上 —— 它的渲染是两段式:先用笔描出黑色轮廓,再用马克笔铺色。 这一条直接改写我们的渲染器设计,见 §4.2。 prepare_explainer_asset_upload明确是 "short-lived R2 PUT URL",mimeType只收image/png|jpeg|webp,assetId正则^[A-Za-z0-9][A-Za-z0-9_-]{0,119}$。cancel_explainer_task:"Any reserved credits are refunded through the existing billing ledger" ⇒ 积分制,创建任务时预扣。validate_explainer_manifest校验的是 "deterministic layout" ⇒ 排版是确定性算法不是模型。
已试过但走不通的路:想用只读、不耗额度的 validate_explainer_manifest 反向枚举 manifest 的合法枚举值(style / layoutTemplate / role / importance / preferredSide),但它只回一句笼统的 The video manifest could not be validated.,只有 id 非法时才给具体错误 —— 错误信息刻意脱敏了。别再浪费时间试。
#1.3·补2 ⭐ 真跑了一条任务,护城河已拆穿(2026-07-21)
完整拆解见 ~/Desktop/explainer-video-逆向素材/06-真实任务产物/00-拆解报告.md,产物(MP4/SRT/SVG/PNG/逐秒关键帧/全程返回 JSON)都在同目录。这条任务 billing 返回 creditsCost: 0, status: "waived",没扣积分。
结论清单(全部是实测,不是推断):
- 每个视觉元素是三件套:
raster.png(生图原图 1536×1536)+outline.svg(矢量化轮廓,只有 11–15 条 path,无 stroke 无 fill,是填充轮廓不是中心线)+color.png(RGBA 色层,尺寸严格等于 SVG 的 viewBox,两层像素对齐)。两段式渲染实锤。我们的数据结构照抄这个。 - 渲染器就是 ffmpeg:
encoder=Lavc59.37.100 libx264/Lavf59.27.100(= FFmpeg 5.1.x),1920×1080@30fps,yuv420p,713kb/s;音频 aac 44.1k 单声道 116k。没有任何 Chromium/Remotion 痕迹 —— 印证我们排除浏览器截帧路线是对的。 - BGM 确认没有:silencedetect 抓到多段绝对静音(本底 -inf),片尾 3.6 秒全静音,且音轨单声道。
- 它有个我们能赢的缺陷:旁白 6.39 秒结束、视频硬撑到 10 秒,尾部空转 —— 它按
targetDuration出片而不是按音频时长。我们「音频时长是唯一权威时钟」的设计更严谨,别跟着抄。 - 真实耗时(
timings):LLM 分镜 72.19s(瓶颈,占一半以上)、视觉规划 24.22s、生图 23.66s、TTS 10.05s、矢量化 2.56s、传 R2 3.84s、渲染 4.65s、后处理 1.07s、上传 2.08s,端到端约 145 秒出 10 秒片。 ⇒ **瓶颈是 LLM 不是渲染。**我们用云雾快模型把这段压到 10 秒内,总时长直接赢它。渲染 4.65s/10s 成片 ≈ 0.5× 实时,我们 8 核多进程的预算完全合理。 - 字幕是句级不是词级(SRT 只有 4 条、按逗号断),说明它的 TTS 只给到句级时间戳。我们用 edge-tts 的 WordBoundary 能做逐词卡拉OK,精细度超过它。
- 视觉设计照抄清单:画布是米白 ≈
#F7F5EF不是纯白;左上角常驻标题(深灰细体、全片不动);字幕居中偏下、字号偏小、白底黑字无描边;一幕可含多个视觉元素,按确定性网格排布依次绘制;肉眼可见绘制顺序是先外框后细节(和我们"连通域按行分桶 + 桶内按面积从大到小"同思路)。 - 它用的手是浅肤色右手握铅笔、笔尖在左上 —— 与我们生成的
hand_pencil/hand_light构图完全一致,素材方向验证正确。
#1.4 它的分镜规则(advanced-review.md 原文,直接抄)
- 场景数 =
ceil(目标秒数 / 10),1–30 个场景 - 每 10 秒场景的旁白量:英文 18–22 词 / 中文 28–36 字
- manifest 结构:
schemaVersion / id / title / language / aspectRatio / style("explainer-video-v1") / targetDurationSeconds / subtitles / scenes[{id,title,narration,durationSeconds,layoutTemplate("single_focus"),visuals[{id,assetId,role,importance,preferredSide,narrationAnchor}],caption}] - 生图统一"视觉锁"提示词:纯白/透明背景、手绘黑线 + 浅交叉线影、珊瑚色/深蓝/青绿/绿/琥珀色马克笔填色、无水印无 logo 无长文字
#二、目标产物与技术选型(已拍板,不要再纠结)
#2.1 部署形态
浏览器 ──► scribe.saveme505.help (Vercel / Next 16 前端 + 统一密码门)
│ 同源 /api/* 走 Next Route Handler 代理,注入 X-Internal-Token
▼
scribe-api.saveme505.help (VPS 66.42.32.208 / Caddy 自动 HTTPS)
│
FastAPI + RQ(Redis) + 多进程渲染 worker + ffmpeg
│
产物 MP4/ASS 落 /var/lib/scribe/out,Caddy 直出,7 天后清理理由:渲染重(吃 CPU、要 ffmpeg、跑几分钟),Vercel 函数扛不住;但用户既有的部署流水线和密码门全在 Vercel 侧,所以前端留在 Vercel,只把重活推到 VPS。前端和后端各自独立部署,互不阻塞。
#2.2 VPS 实测配置(已确认)
ssh <VPS> → 8 核 / 62 GB RAM / 351 GB 可用 / ffmpeg 6.1.1 / Python 3.12.3 / Node v22.23.1,负载常年 0.00。资源完全够,渲染并发按 6 个进程算。
#2.3 技术选型表
| 环节 | 选型 | 备选/兜底 |
|---|---|---|
| 分镜脚本 | 云雾中转 LLM(json_schema 强约束) | 换模型即可,见 ~/Desktop/配置信息/云雾API-中转/ |
| 生图 | 云雾 gpt-image-2,见 ~/Desktop/配置信息/生图API-gpt-image-2.md |
失败降级:简化 prompt 重试 → 占位图 |
| 线稿清洗 | OpenCV 自适应二值化 + 中值滤波 + 闭运算 + 裁白边 + 统一画布 | — |
| 手绘渲染 | R1:连通域 + 骨架 + 遮罩推进(Python/OpenCV 多进程) R2:vtracer 矢量化 + 路径描边 |
见 §4,R1 先上线,R2 作为渲染器热插拔升级 |
| TTS | edge-tts(免费、中文音色好、自带 WordBoundary 逐词时间戳) | Piper(本地 CPU,2–3 秒/分钟音频);失败自动切换 |
| 字幕 | pysubs2 生成 ASS,\k 卡拉OK逐词高亮 |
无时间戳时用 faster-whisper 对齐 |
| 合成 | ffmpeg:concat demuxer 拼接 + aac + subtitles 烧录 |
需要转场时才上 xfade |
| 队列 | Redis + RQ | 不要用 Celery(过度设计),不要自写 SQLite 轮询 |
| 进度 | SSE(EventSource,浏览器原生自动重连) |
轮询兜底 |
明确排除(调研已证否,别浪费时间试):
- ❌ Remotion / Puppeteer 截帧渲染:单帧 200–500ms,比纯栅格化慢一个数量级;且 Remotion 有商用授权限制
- ❌ CosyVoice / GPT-SoVITS / IndexTTS / F5-TTS 在 CPU-only 上跑生产:RTF > 3,不现实
- ❌ WhisperX 做中文强制对齐:中文 wav2vec2 对齐模型不成熟,且需 GPU
- ❌ manim 做手绘:不是为此设计,改造成本高于自写渲染器
#二·补 它的 LLM 干了什么(以及它翻的车)
timings 里有两个独立计时的 LLM 阶段,说明是两次调用:
| 阶段 | 耗时 | 干什么(从返回结构反推) |
|---|---|---|
narrativeSeconds |
72.19s | 读源文本 → 出 title + 每幕 narration(中文,受"10 秒 28–36 字"约束) |
visualSeconds |
24.22s | 每幕规划 N 个视觉意图 → intentId + role(object/symbol 枚举) + concept(英文短语,直接当生图 prompt) |
注意 concept 是英文而 narration 是中文 —— 视觉规划这步做了跨语言转换,中文旁白 → 英文生图提示词。我们照做(英文 prompt 生图效果稳定得多)。
LLM 不参与:排版布局(deterministic layout)、笔画顺序、渲染、字幕切分。
⚠️ 它当场翻了个车,正好证明我们的反幻觉设计是必要的:
- 我喂的原文是「白板手绘解说视频的原理其实很简单:先把文字拆成分镜,再为每一幕画一张简笔画,然后让程序沿着线条一笔一笔把它描出来……」
- 它产出的旁白是「假设要教「泡茶」,把步骤拆成四张草图,程序一笔笔描,加旁白,视频完成。」
「泡茶」这个例子原文里根本没有,是它自己编的,而且整张主插画(四格泡茶漫画)都是围绕这个虚构例子画的。10 秒的短片还好说,做长视频这就是事故。
⇒ 计划里 §6.1 强制每幕带 source_span + 相似度校验那条必须实现,这是我们对它的又一个质量优势。
优化空间:它两次 LLM 调用花了 96 秒。我们用云雾快模型 + 一次 json_schema 调用把 narration 和 visual_prompt 一起产出,这段能压到 10 秒内。
#三、核心设计原则(违反了必出事)
- 音频时长是唯一权威时钟。分镜里的
durationSeconds只是估算;真正的对齐顺序是:LLM 出稿 → TTS 出音频 → 量出每段真实时长 → 渲染器按真实时长生成对应长度画面。绝不允许反过来拉伸音频。 - 旁白必须可溯源。分镜 JSON 每幕强制带
source_span(对应原文片段),生成后用difflib.SequenceMatcher校验相似度 < 0.3 判为疑似编造,触发重试。这是最低成本的反幻觉手段。 - 风格统一靠 img2img 锚定,不靠堆提示词。先生成第 1 张定风格图,后续每张都把它作为 reference 传入;最后统一走 OpenCV 二值化收口。
- 中文字幕字体走
fontsdir显式指定,把NotoSansSC-Regular.otf打进项目assets/fonts/,不依赖系统 fontconfig。这个坑几乎必踩:不指定时 libass 会静默 fallback 成英文字体,中文全变豆腐块且不报错。 - 所有分段视频编码参数写死统一(1280×720 / yuv420p / 25fps / h264),否则 concat demuxer 的
-c copy会对不齐。 - 最终编码必须
-pix_fmt yuv420p -movflags +faststart,否则 Safari/移动端黑屏、网页不能边下边播。
#四、手绘渲染器(本项目唯一的技术核心)
#4.0 ⭐ 笔画排序已定案 —— 见独立规格书
~/Desktop/白板视频-笔画排序算法规格书.md(v1.0,含完整算法四层、参数表、代码骨架、降级预案、可自动化的验收指标)。M1 阶段直接照那份写代码,不要重新选型。
两条最该记住的:
- 对手的算法已被逆向出来 —— 就是「按 path 起点 y 从上到下排序」(三个样本的 y 递增比例 89%/100%/80%,x 只有 47–56% ≈ 随机)。它没有骨架、没有连通域分组、没有语义、没有 TSP。而 VideoScribe/Doodly 连这个都没有,纯靠人工排图层。门槛比想象中低得多。
- 我们的定案主路线:连通域切分 → 线/块分类 → 贪心就近排序(不是静态 sort,这是防瞬移的关键)→ 线走骨架 DFS(岔路口按方向连续性 cos 打分)、块走同心圈涂抹 → 按点数分配时长。比它多做三件事:就近连续、线-块配对强制先勾线后上色、岔路方向启发式。
#4.1 认知前提
VideoScribe 之所以效果好,是因为它要求用户喂人工排好图层顺序的 SVG,从不自己猜笔顺(官方文档明说"从图层面板最底层画到最顶层")。我们的输入是 AI 生成的位图线稿,所以必须自己解决笔画排序——这是全项目最难、也最决定观感的一步。
另一个关键坑:potrace/vtracer 描的是轮廓不是中心线。一条粗线会被描成"跑道形"闭合轮廓,直接 dashoffset 描边会画出两条平行边,一眼假。所以要么先骨架化,要么保证输入线稿本身是细线。
#4.2 R1 渲染器(先做,当天可出片)
⚠️ 重要修正(2026-07-21,来自 tools/list 的新发现):SpeedPainter 是两段式渲染 —— 先描黑色轮廓(sketch),再铺马克笔颜色(colorFill),两段时长独立可配。我们必须照抄这个设计,因为:
- 生图出来的插画是"黑线 + 局部彩色马克笔填色",如果一次性把彩色像素也按笔画放出来,观感是"彩色渐显"而不是"先画后涂",一眼假;
- 分成两段后,第一段只处理黑色通道(笔画排序、骨架走笔都只在黑线上做,算法简单可靠),第二段的填色可以用粗马克笔素材(
hand_marker_360.png)做块状扫涂,根本不需要精细笔顺。具体做法:清洗后的插画先分离成两层 ——
outline(黑/深色像素二值图)和color(去掉黑线后的彩色区域)。默认时长分配sketch : colorFill = 7 : 3。第二段按连通色块逐块横向扫涂,手换成粗马克笔那只。✅ 已被真实产物证实(见 §1.3·补2):它每个元素就是存
raster.png+outline.svg+color.png三件套,且 color.png 的尺寸严格等于 outline.svg 的 viewBox(裁到内容 bbox 后两层像素对齐)。我们的中间产物直接按这个三件套来存,好处是 R1(位图遮罩)和 R2(SVG 描边)可以共用同一套资产,切换渲染器不用重新处理素材。 另外实测它的 outline.svg 只有 11–15 条 path(1536² 的插画矢量化后),说明矢量化前做了足够的简化 —— 别指望上百条 path,路数少反而排序容易。
线稿 PNG(outline 层)
→ cv2.connectedComponentsWithStats 切连通域(每个团块 = 一"笔")
→ 连通域排序:先按行分桶(桶高 = 图高/10),桶内按面积从大到小
(依据:AE 动画师的行业经验法则「从左上画到右下」+「先框架后细节」)
→ 每个连通域 skimage.morphology.skeletonize → 骨架像素
→ 骨架构图(8邻接),从度=1 的端点 DFS,岔路选转角最小的分支 → 有序笔尖轨迹
→ 按弧长比例分配时长(单笔最短 0.15s,笔间留 0.05–0.1s 抬笔停顿)
→ 逐帧:把"已完成部分"的原始像素从蒙版里放出来 + 在当前笔尖贴手 PNG
手的旋转角 = atan2 到前瞻 15px 处的切线,做 3–5 帧滑动平均防抖
→ multiprocessing.Pool(6) 并行出帧 → 管道喂 ffmpeg为什么是"放出原始像素"而不是"画黑线":这样彩色马克笔填色部分也能自然显现,且不损失线稿的笔触细节。
性能预期:纯 numpy/OpenCV 栅格化约 15–40ms/帧,1280×720、25fps、60 秒 = 1500 帧,6 进程并行 约 10–30 秒。(对比 Puppeteer 方案要 1–3 分钟。)
#4.3 R2 渲染器(上线后升级,接口保持一致)
vtracer --preset bw(★6.4k,MIT,Rust,O(n),比 potrace 快且不卡大图)矢量化 → svgsort(★262)或自写排序重排 path → svgpathtools 按弧长参数化采样 → 同样的手部跟随逻辑逐帧栅格化。
升级路径不返工:R1/R2 都实现同一个接口 render_scene(image_path, duration_sec, out_path, hand_asset) -> None,配置项 RENDERER=r1|r2 切换。
#4.4 手部素材(✅ 已完成,直接用)
不需要再做这一步了,素材已生成并处理好,在 ~/Desktop/explainer-video-逆向素材/04-我们生成的手/:
| 文件 | 用途 | 笔尖锚点(360px 版) |
|---|---|---|
hand_light_360.png |
默认,浅肤色右手 + 细黑马克笔 | (16, 18) |
hand_asian_360.png |
中等肤色右手 | (13, 31) |
hand_deep_360.png |
深肤色右手 | (36, 25) |
hand_child_360.png |
儿童手 | (1, 42) |
hand_left_360.png |
左手(构图镜像,笔尖在右上角) | (324, 26) |
hand_pencil_360.png |
黄杆铅笔版 | (18, 18) |
hand_marker_360.png |
粗头马克笔,给 colorFill 段用 | (15, 36) |
全部 1024×1024 生成 → 白底转 RGBA 透明 → 只留最大连通域去噪 → 缩到 360px。笔尖坐标都在同目录 tips.json 里,渲染器直接读取,不要写死。 贴手时把 tip 那个像素对齐到当前笔尖轨迹点,旋转角 = 到前瞻 15px 处的切线角。
要加新肤色/新笔,改 gen_hand2.py 里的一个词再跑 mkhand.py 即可(提示词模板已验证一次成功,成本几分钱一张)。
⚠️ 03-他们的素材/ 里 SpeedPainter 的手和画布 PNG 只作构图参考,绝不进生产。
#4.5 值得抄的开源项目(已逐个验证存在)
| 项目 | 星/许可 | 抄什么 |
|---|---|---|
| daslearning-org/image-to-animation-offline | ★28 MIT,2026-07 活跃 | 和 R1 思路最接近的现成实现,OpenCV 做白板手绘,先读它的核心循环 |
| yogendra-yatnalkar/storyboard-ai | ★145 GPL-3.0,2026-07 | 端到端管线参考;它用 SAM 分割做"物体级排序",是 R3 的方向 |
| leeyeel/Sketch2Motion | ★204 | 图→SVG→动画管线 |
| subroy13/handanim | ★46 MIT | 手绘风格矢量渲染 + Cairo 出 MP4 |
| visioncortex/vtracer | ★6.4k MIT | R2 的矢量化器 |
| inconvergent/svgsort | ★262 | 笔绘顺序贪心优化 |
| harry0703/MoneyPrinterTurbo | ★98k MIT | edge-tts + 字幕 + ffmpeg 那一段可直接参考 |
| tkarabela/pysubs2 | ★434 MIT | ASS 生成 |
注意:facebookresearch/AnimatedDrawings(★12.8k)已于 2025-09 归档,只当论文读,别依赖。
#五、分阶段实施计划(Fable 按此顺序推进)
每阶段结束必须跑通「验收命令」并把产物路径打印出来,再进下一阶段。
#M0 — 骨架与环境(约 0.5 天)
- 本地建
~/personal/scribe-studio,起步就建 GitHub 私有仓库(按用户惯例:拿不准偏私有),分步 commit + push。 - 目录结构:
scribe-studio/ web/ # Next 16 前端(部署到 Vercel) server/ # FastAPI + RQ(部署到 VPS) app/api.py app/tasks.py app/pipeline/{script,image,tts,subtitle,render,mux}.py assets/fonts/NotoSansSC-Regular.otf assets/hands/*.png docs/ # 中文文档 README.md # 中文 + shields.io flat-square 徽章(推送前 curl 验证 logo 名存在) - VPS 侧:
apt install -y redis-server fontconfig fonts-noto-cjk potrace、pip install fastapi uvicorn rq redis edge-tts opencv-python-headless scikit-image numpy pysubs2 httpx pillow,fc-cache -f && fc-list :lang=zh验证中文字体。 - Caddy 反代
scribe-api.saveme505.help→127.0.0.1:8080,在 Vercel DNS 加 A 记录指向66.42.32.208。
验收:curl https://scribe-api.saveme505.help/healthz 返回 200。
#M1 — 渲染器 R1 单点打通(约 1 天,最高优先级)
先不接 LLM/TTS,手工准备 1 张"黑线 + 局部马克笔填色"的插画 PNG,直接跑通「图 → 10 秒手绘 MP4」。手部素材直接从 ~/Desktop/explainer-video-逆向素材/04-我们生成的手/ 拷进 server/assets/hands/(连 tips.json 一起)。
必须实现两段式:sketch(描黑线,用 hand_light_360.png)→ colorFill(铺色块,换成 hand_marker_360.png),默认 7:3 分配时长。
验收:python -m app.pipeline.render sample.png 10 out.mp4 产出可播放 MP4,肉眼确认:黑线按"从上到下、先大后小"顺序被画出 → 然后才是彩色块被涂上;手跟着笔尖走且不抖;笔画间有抬笔停顿;两段切换时手要换成马克笔那只。渲染耗时打印出来(目标 < 30 秒)。
⚠️ 这一步不通过,后面全部无意义。如果 5 次迭代后观感仍然像"雨刷擦玻璃",立即降级:改为按连通域整块淡入 + 手在块中心停留,先保证能出片,把自然度问题记进 TODO,不要卡死在这里。
#M2 — 文本 → 分镜 → 生图(约 1 天)
- LLM 用 json_schema 强约束输出(schema 见 §6.1),场景数 =
ceil(秒数/10),中文每幕 28–36 字/10 秒。 - 长文(>2万字)走 map-reduce:1500–2000 字滑窗(重叠 10%)并发出要点 → 汇总后再分镜。
- 生图:固定"视觉锁"前后缀 + 第一张作 reference 锚风格 + 3 次指数退避重试(2/4/8s)+ 占位图兜底。
- 清洗:
cv2.adaptiveThreshold(blockSize=15, C=8)→medianBlur(3)→MORPH_CLOSE(2×2)→ 裁白边留 6% → 居中贴 1280×720 白底。
验收:给定一段 2000 字文章,产出 6–8 张风格统一的白底黑线 PNG + 一份带 source_span 的分镜 JSON,溯源校验全部通过。
#M3 — TTS + 字幕(约 0.5 天)
- edge-tts
Communicate.stream()收集WordBoundary(offset/duration单位是 100ns,除以 10000 得毫秒)。 - 先做一次语速标定:固定音色
zh-CN-YunjianNeural、rate=+0%,跑 500 字量出真实"字/秒",写进 config,别用理论值。 - 调用串行、间隔 200–500ms 抖动、3 次重试,失败切 Piper。
- pysubs2 出 ASS,每行 ≤16 字,行内
{\k厘秒}逐词高亮,Fontname必须写fc-list里的精确族名(Noto Sans CJK SC)。
验收:一段中文文本产出 mp3 + ass,ffplay 检查字幕逐词高亮与人声同步误差 < 100ms。
#M4 — 合成与串联(约 0.5 天)
顺序严格按:TTS 先出 → 量真实时长 → 渲染器按该时长出画 → 每幕音画合并 → concat 拼接 → 混 BGM(volume=0.15)→ 烧字幕 + 最终编码。
最终编码固定:
-c:v libx264 -profile:v high -level 4.0 -pix_fmt yuv420p -preset medium -crf 20 \
-c:a aac -b:a 128k -movflags +faststart \
-vf "subtitles=full.ass:fontsdir=./assets/fonts"验收:POST /api/whiteboard-task 传一段文本,几分钟后拿到能在 Safari 和微信里正常播放的 MP4。端到端耗时打印分阶段 timings。
#M5 — 服务化(约 1 天)
- FastAPI 接口字段名照抄 §1.2 的契约:
POST /api/whiteboard-task、GET /api/whiteboard-task/{id}、DELETE /api/whiteboard-task/{id};状态机WAITING/PROCESSING/FINISHED/FAILED/CANCEL/TIMEOUT+stage(planning/rendering/postprocessing/uploading) +progress(0–1)。 - RQ:
job_timeout="20m",Retry(max=2, interval=[10,60]),worker 数 = 4。 - SSE 进度:worker 往
task:{id}:progress发 Redis pub/sub,FastAPI 转text/event-stream。 - 清理:合成成功后立即删中间产物;最终 MP4 保留 7 天,每日 cron 扫;磁盘 >80% 停止收新任务。
- 限流:同 IP 每小时最多 3 个任务。
验收:并发提交 3 个任务不互相拖垮,SSE 前端能看到阶段流转,取消能在阶段边界生效。
#M6 — 前端 + 上线(约 1 天)
- Next 16 + shadcn,一个页面:文本框/URL 输入 + 时长滑杆(5–300) + 比例(16:9/9:16/1:1/4:5) + 音色 + 字幕开关 → 提交 → SSE 进度条(显示中文
statusMessage)→ 播放器 + 下载。 - 密码门 4 文件必须一起提交:
lib/hub-auth.ts、proxy.ts、login/page.tsx、api/login/route.ts,env 与其它站完全一致(值见~/Desktop/配置信息/站点访问密码门.md,绝不写进代码或提交进仓库)。 - 部署严格走:改本地 → git commit(LeoLee0812 / Leo,中文注释)→ push main → Vercel 自动部署。禁止
vercel --prod。 - 绑定
scribe.saveme505.help(vercel domains add)。 - 去
~/personal/projects-hub补PROJECT_META(图标 + 中文简介 + backend 分组)并 push。 - 完成后调 email skill 发邮件:结论先行 + 改动摘要 +
scribe.saveme505.help链接 + 待拍板事项。
验收:浏览器里输密码进站,粘一段文章,几分钟后拿到成片。
#六、关键代码契约
#6.1 分镜 JSON Schema(LLM 强约束)
{
"type":"object",
"required":["title","scenes"],
"properties":{
"title":{"type":"string"},
"scenes":{"type":"array","minItems":1,"maxItems":30,"items":{
"type":"object",
"required":["scene_id","narration","visual_prompt","duration_sec","source_span"],
"properties":{
"scene_id":{"type":"integer"},
"narration":{"type":"string","description":"旁白,只能改写原文,禁止新增原文没有的数字/日期/人名/因果结论"},
"visual_prompt":{"type":"string","description":"英文,白底黑线简笔画画面描述"},
"duration_sec":{"type":"number"},
"source_span":{"type":"string","description":"该幕对应的原文片段,用于溯源校验"}
}}}
}
}#6.2 生图提示词 —— ⭐ 直接用它的「视觉锁」原文
供应商是谁无所谓,提示词才是资产。 而它的生图提示词已经完整公开在 advanced-review.md 第 42–47 行(因为"高级审阅模式"下这张图是由客户端本地生成的,它不得不把提示词写出来给 agent 执行)。原文逐字照抄:
Coherent full-scene editorial whiteboard cartoon on a pure white or transparent background; varied hand-drawn black ink, light crosshatching, marker fills in coral, deep blue, teal, green, and amber; expressive characters; clear arrows and motion marks; generous whitespace; one dominant concept; no watermark, logo, brand name, border, photorealism, gradient, or long baked-in headline.
拆开看它每个词在干什么,一个都别改掉:
| 片段 | 作用 |
|---|---|
Coherent full-scene editorial whiteboard cartoon |
定死画风:整场景编辑插画,不是图标 |
pure white or transparent background |
保证后面能抠 outline/color 两层 |
varied hand-drawn black ink, light crosshatching |
"varied"= 线条粗细有变化(更像手绘);只允许浅交叉线影,不要块状阴影 |
marker fills in coral, deep blue, teal, green, and amber |
锁死五色调色板 —— 这是多张图风格统一的真正手段,比"style reference"还硬 |
expressive characters; clear arrows and motion marks |
解说视频要的表现力元素 |
generous whitespace; one dominant concept |
留白 + 单一主体,保证矢量化后 path 数量少(实测它 1536² 的图只出 11–15 条 path) |
no watermark, logo, brand name, border, photorealism, gradient, or long baked-in headline |
负向:特别注意 no gradient(渐变会毁掉二值化)和 no border(边框会变成矢量化里最大的一条 path,抢走第一笔) |
实际调用时拼成:{视觉锁原文} + " " + {LLM 产出的英文 concept}。
concept 的写法也照抄它的风格 —— 名词短语、一句话、说清主体和动作,实测样例:
a four-panel tea-making storyboard sheet、a pen tracing a play-button outline。
另外两条实测经验:
- 敏感话题在分镜阶段就让 LLM 做意象抽象化("冲突"→"两个箭头对撞"),别等生图被审核拒了再重试。
- 生成后仍要走 §M2 的 OpenCV 清洗(它的
raster.png也是有颗粒噪点的,不做二值化清洗直接矢量化会炸出一堆碎 path)。
#6.3 分镜规划提示词 —— 拿它的规则拼一份更好的
服务端那次 LLM 调用的提示词没公开,但 advanced-review.md 本身就是它的镜像:高级模式下 agent 在本地做的,正是默认模式下服务端做的那件事。所以它的规划规则等于全泄漏了。把它的规则 + 我们的改进合成一份,一次调用同时产出旁白和画面描述(省掉它那 96 秒的两次调用):
你是解说视频的分镜编剧。把用户给的原文改写成可直接配音的分镜脚本。
【硬规则,来自对标产品实测】
1. 场景数 = ceil(目标秒数 / 10),最少 1 幕,最多 30 幕。
2. 时长在各幕间尽量均分,余数放到最后一幕,所有 duration_sec 之和必须严格等于目标秒数。
3. 每 10 秒的旁白量:中文 28–36 字 / 英文 18–22 词。宁可把旁白改短,也绝不允许
一句话跨越两幕。
4. 每幕只有一个主视觉概念(one dominant concept),不要在一幕里塞多个并列主体。
5. narration 用原文的语言;visual_concept 一律用英文名词短语(一句话,说清主体和
动作),例如 "a four-panel tea-making storyboard sheet"。
【反幻觉,这条最重要】
6. 你只能对原文做摘要、改写、合并。严禁添加原文没有的具体数字、日期、人名、机构名、
因果结论,尤其严禁自己编造举例场景。
7. 每一幕必须填 source_span:该幕旁白所依据的原文片段(原文里的连续文字,不要改写)。
如果某句旁白在原文里找不到依据,说明你在编,重写它。
8. 需要过渡句时只能用不含具体事实的通用措辞("与此同时"、"那么问题来了")。
【输出】严格按给定 JSON Schema 输出,不要输出任何解释文字。第 6/7 条不是我加戏:实测对标产品就在这里翻了车 —— 见 §2·补,它把一段讲"白板视频原理"的原文,凭空编成了"假设要教泡茶",还照着这个虚构例子画了整张主插画。
配套 JSON Schema 在 §6.1,注意 visual_prompt 字段就是上面说的 visual_concept(英文),生图时前面拼视觉锁。
#6.3 渲染器接口
def render_scene(image_path: str, duration_sec: float, out_path: str,
hand_asset: str | None = None, fps: int = 25,
size: tuple[int,int] = (1280, 720)) -> None:
"""把一张线稿渲染成 duration_sec 秒的手绘过程视频(无音轨)。
R1/R2 实现同一签名,由 config.RENDERER 决定用哪个。"""#七、成本与耗时预算
| 项 | 值 |
|---|---|
| LLM 分镜 | ¥0.1–0.3 / 条(云雾中转,2万字走 map-reduce) |
| 生图 8 张 | ¥0.3–1 / 条(按 生图API-gpt-image-2.md 实际单价核准) |
| TTS | ¥0(edge-tts 免费) |
| 渲染/编码 | 仅 VPS 算力,不额外花钱 |
| 合计 | 约 ¥0.4–1.5 / 条 60 秒视频 |
端到端耗时(60 秒视频、8 幕):分镜 5–15s + 生图 40–120s(并发 2–3)+ 清洗 <5s + TTS 15–40s + 渲染 60–180s + 合成编码 15–45s ≈ 3–6 分钟。生图和 TTS 可 asyncio.gather 并发,能省掉两者中较小的那一段。
#八、风险与预案
| 风险 | 预案 |
|---|---|
| R1 观感不自然(最大风险) | 5 次迭代内不达标就降级为连通域淡入,先上线再优化;R2 作为后续升级 |
| edge-tts 被微软限流/协议变更 | 它是"白嫖"非 SLA 服务。3 次重试 + 自动切 Piper + 告警;单 worker 内串行调用 |
| 生图风格漂移 | img2img 锚定 + OpenCV 二值化统一收口;实在不行降到"每条视频只出 3 张图循环用不同构图" |
| 中文字幕变豆腐块 | 一律 fontsdir 指定项目内字体,不依赖系统 fontconfig |
| concat 拼接对不齐 | 所有分段编码参数写死统一;音画差值 >0.1s 直接报错而不是靠 -shortest 掩盖 |
| 磁盘写满 | 中间产物合成后立即删;日清 cron;水位 80% 停收任务 |
| 版权 | 不得使用 speedpainter 的手/画布 PNG 素材(已下载的仅供分析构图),生产素材自己画或用 MIT/CC0 开源素材 |
#八·补 — 对标基准(拿它的实测数据当标尺)
我们做出来的东西,要在这几项上明确超过或至少持平:
| 指标 | SpeedPainter 实测 | 我们的目标 |
|---|---|---|
| 端到端耗时(10 秒片) | ~145 秒 | < 60 秒(LLM 换云雾快模型,它那 72 秒是白等的) |
| 渲染耗时 | 4.65 秒 / 10 秒成片 | 持平即可(8 核多进程) |
| 分辨率 / 帧率 | 1920×1080 @ 30fps | 先 1280×720 @ 25fps,跑顺了再上 1080p |
| 音画对齐 | ❌ 旁白 6.4s、视频硬撑 10s,尾部空转 | ✅ 画面严格贴合音频真实时长 |
| 字幕粒度 | 句级(4 条) | ✅ 逐词(edge-tts WordBoundary + ASS \k) |
| BGM | ❌ 没有 | 可选开关(有就是加分项) |
| 音轨 | aac 单声道 116k | 持平 |
| 画布 | 米白 #F7F5EF + 左上角常驻标题 |
照抄 |
#九、明确的非目标(本期不做)
- 不做「高级审阅模式」(本地生图 → 上传 → manifest → 渲染)那一整套,先做服务端全包的单一路径
- 不做 MCP server 化(做完网页站后如果想要,再包一层薄 MCP,接口契约已经对齐)
- 不做多用户账号体系、计费、BGM 库
- 不追求 4K / 60fps,固定 720p / 25fps
#九·补 — 后续如果要做 MCP 化(本期不做,先记着)
把自建服务包成远程 MCP 给 Claude Code / Codex 用,现成路线(已验证):
- jlowin/fastmcp(★26.7k,Apache-2.0,2026-07 活跃):Python 函数直接反射成 MCP 工具,不用手写 JSON Schema。我们的 FastAPI 已有的 pipeline 函数可以直接暴露。
- peterlarnholt/fastmcp-oauth(MIT):给 FastMCP 加 OAuth 2.1 + PKCE,支持 Google/GitHub,正好对应 SpeedPainter 那套流程。
- iceener/streamable-mcp-server-template(★133,MIT):生产级 TS 模板,5 种认证策略 + 多租户会话,Streamable HTTP 传输(2025 新标准,别再用旧 SSE 方案)。
- 更简单的私用做法:跳过 OAuth,用固定 Bearer Token,只自己用,
claude mcp add --transport http --header "Authorization: Bearer xxx"即可。
另外记两条排除结论,别踩:
- Remotion(★53.8k)虽好但商用授权按团队规模收费(≤3 人免费),且底层仍是 headless Chrome 截帧;同类里 Revideo(★3.9k,MIT)和 Motion Canvas(★18.8k,MIT)无授权限制,如果将来想做"可编程动画模板"再考虑。
- FFCreator(★3.2k)2023 年后停更,别选。
#十、给执行者的开场动作
ssh <VPS>确认环境(8 核 / 62G / ffmpeg 6.1.1 已验证)- 先读
daslearning-org/image-to-animation-offline的核心渲染循环,再动手写 R1 - 从 M1 开始,不要先搭前端 —— 渲染器不通过,别的都是空转
#✅ 执行结果(2026-07-22 完成,本计划书到此归档)
M0–M6 全部跑通并线上验证。 本文件已随代码拷入仓库 ~/personal/scribe-studio/docs/,
后续改动以仓库内那一份 + docs/实测偏差记录.md 为准,本桌面副本仅作历史留存。
#落地状态
| 阶段 | 状态 | 说明 |
|---|---|---|
| M0 骨架与环境 | ✅ | curl https://scribe-api.saveme505.help/healthz 返回 200 |
| M1 渲染器 R1 | ✅ | 五项自动指标全达标,均优于「纯 sort by y」基线 |
| M2 分镜 + 生图 | ✅ | 溯源校验实测 4/4 幕相似度 1.0 |
| M3 TTS + 字幕 | ✅ | 逐词卡拉OK(42 字旁白 23 个时间戳) |
| M4 合成串联 | ✅ | 音画严格贴合,Safari/微信可播 |
| M5 服务化 | ✅ | FastAPI + 4 worker + SSE + 限流 + 日清 cron |
| M6 前端上线 | ✅ | scribe.saveme505.help,密码门四文件就位 |
- 代码:GitHub 私有仓库
LeoLee0812/scribe-studio(monorepo:web/+server/) - 已补进导航站
projects-hub的PROJECT_META
#⚠️ 本计划书中被实测推翻的地方(照着旧文改会返工)
完整版见仓库 docs/实测偏差记录.md,这里列最关键的:
- §4.2 / 规格书 §3.1 骨架 DFS:「允许重走边」实测像素覆盖率只有 6% (骨架图常有多个不连通分量,重走永远走不出当前分量)。已改为显式跳转,涨到 98%。
- 规格书 §3.2 色块同心圈涂抹:实测只覆盖 42%。已改为之字形横向扫涂。
- 规格书 §4 参数表:
W_ANCHOR_DIST等三项权重压不下反跳率(最好也只到 32%)。 已新增第四项w_momentum = 0.30,降到 13%。MIN_UNIT_AREA必须从 60 降到 12。 - §M3 edge-tts:7.x 默认
boundary='SentenceBoundary',不显式指定拿不到逐词时间戳, 字幕会静默退化成按字均分 —— 不报错、肉眼也看不出来。必须传boundary="WordBoundary"。 - §1.4 语速「10 秒 28–36 字」是理论值:实测 5.1 字/秒,要 44–50 字。 按旧值下单,60 秒的片只出得到 44.6 秒。
- §2.1 Caddy 用不了:这台 VPS 的 80/443 早被 nginx 占着(在跑
line.saveme505.help)。 已改用 nginx + certbot。 - §6.1 json_schema 不可信:开了
strict: true仍会吐裸数组、把字符串字段吐成数组。 解析层必须按「拿到的是任意 JSON」来写。 - §6.2 视觉锁单独用不够:只给视觉锁 + 一句 concept 时,生图会发挥成多格漫画或
跟旁白无关的道具(真出现过番茄钟)。已把 concept 提到最前面并追加
FRAME_LOCK。
#对标结果(§八·补 的标尺)
| 指标 | SpeedPainter | 本项目 | 结论 |
|---|---|---|---|
| LLM 分镜 | 96 秒(两次调用) | 11–14 秒(一次) | ✅ 大幅超越 |
| 时长命中 | 硬出片,尾部空转 3.6 秒 | 41.4 秒 / 目标 40 秒 | ✅ 超越 |
| 字幕粒度 | 句级 | 逐词 | ✅ 超越 |
| 旁白可溯源 | 编造原文没有的例子 | 4/4 幕相似度 1.0 | ✅ 超越 |
| 渲染耗时 | 4.65 秒 / 10 秒片 | 2.0 秒(本机)/ 13 秒(VPS) | ✅ 持平(VPS 单核弱,已多进程) |
| 画布 / 标题 | 米白 + 左上常驻 | 照抄 | ✅ 持平 |
| 分辨率 | 1080p@30 | 720p@25 | ⏳ 按计划先 720p |
| BGM | 无 | 接口预留未开 | ⏳ |
#未做的(原计划就是非目标,或待拍板)
- R2 矢量化渲染器:R1 观感已达标,接口已预留
RENDERER=r1|r2 - 高级审阅模式、MCP server 化、多用户计费、BGM 库(§九 明确的非目标)
- 真正的瓶颈是生图(每张 30–60 秒,占端到端七成),不是渲染也不是 LLM
来源:沉淀/explainer-video-逆向素材/00-这些是什么.md;沉淀/explainer-video-逆向素材/白板解说视频站-复刻计划.md(整理于 2026-08-18)