📚 离职知识库

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

这里面最值钱的三件事

  1. 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 直接照抄这套命名。
  2. 任务状态机WAITING/PROCESSING/FINISHED/FAILED/CANCEL/TIMEOUT + stage(planning → rendering → postprocessing → uploading) + progress(0.0–1.0) + 返回 videoUrl / srtUrl / assets / timings / title / scenes
  3. 老产品 /api/task(图→速绘视频)的完整参数面,暴露了它渲染引擎的能力边界: sketchDuration + colorFillDuration描线时长和上色时长是分开的两段)、needHand / handTitle / handType(left|right)needCanvas / canvasTitledrawDirection(上下/下上/左右/右左四种)、videoQuality(720p|1080p|2K|4K)fps(30|60)sizeMode(smart_cover|contain|cover|stretch)needFadeout / fadeOutAtEndwatermarkEnabled / 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.mdmcp.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} 返回:taskIdstatus(WAITING/PROCESSING/FINISHED/FAILED/CANCEL/TIMEOUT)、stage(planning → rendering → postprocessing → uploading)、progress(0.0–1.0)、videoUrlsrtUrlassetstimingstitlescenes

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 APIapp 参数默认 "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,服务端 serverInfoexplainer-video v0.5.0。完整 schema 存档在 ~/Desktop/explainer-video-逆向素材/01-接口契约/MCP-tools-list.json。三条新增关键情报:

  1. ⭐ 它把每张插画拆成 outline + color 两套资产 finalize_explainer_asset_upload 的描述原文:"Import an already uploaded illustration into the hand-drawable outline and color asset pipeline"。 这与老产品 /api/tasksketchDuration + colorFillDuration 两个独立时长参数完全对上 —— 它的渲染是两段式:先用笔描出黑色轮廓,再用马克笔铺色。 这一条直接改写我们的渲染器设计,见 §4.2。
  2. prepare_explainer_asset_upload 明确是 "short-lived R2 PUT URL",mimeType 只收 image/png|jpeg|webpassetId 正则 ^[A-Za-z0-9][A-Za-z0-9_-]{0,119}$
  3. 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"没扣积分

结论清单(全部是实测,不是推断)

  1. 每个视觉元素是三件套raster.png(生图原图 1536×1536)+ outline.svg(矢量化轮廓,只有 11–15 条 path,无 stroke 无 fill,是填充轮廓不是中心线)+ color.png(RGBA 色层,尺寸严格等于 SVG 的 viewBox,两层像素对齐)。两段式渲染实锤。我们的数据结构照抄这个。
  2. 渲染器就是 ffmpegencoder=Lavc59.37.100 libx264 / Lavf59.27.100(= FFmpeg 5.1.x),1920×1080@30fps,yuv420p,713kb/s;音频 aac 44.1k 单声道 116k。没有任何 Chromium/Remotion 痕迹 —— 印证我们排除浏览器截帧路线是对的。
  3. BGM 确认没有:silencedetect 抓到多段绝对静音(本底 -inf),片尾 3.6 秒全静音,且音轨单声道。
  4. 它有个我们能赢的缺陷:旁白 6.39 秒结束、视频硬撑到 10 秒,尾部空转 —— 它按 targetDuration 出片而不是按音频时长。我们「音频时长是唯一权威时钟」的设计更严谨,别跟着抄。
  5. 真实耗时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 核多进程的预算完全合理。
  6. 字幕是句级不是词级(SRT 只有 4 条、按逗号断),说明它的 TTS 只给到句级时间戳。我们用 edge-tts 的 WordBoundary 能做逐词卡拉OK,精细度超过它。
  7. 视觉设计照抄清单:画布是米白 ≈ #F7F5EF 不是纯白;左上角常驻标题(深灰细体、全片不动);字幕居中偏下、字号偏小、白底黑字无描边;一幕可含多个视觉元素,按确定性网格排布依次绘制;肉眼可见绘制顺序是先外框后细节(和我们"连通域按行分桶 + 桶内按面积从大到小"同思路)。
  8. 它用的手是浅肤色右手握铅笔、笔尖在左上 —— 与我们生成的 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 秒内。

#三、核心设计原则(违反了必出事)

  1. 音频时长是唯一权威时钟。分镜里的 durationSeconds 只是估算;真正的对齐顺序是:LLM 出稿 → TTS 出音频 → 量出每段真实时长 → 渲染器按真实时长生成对应长度画面。绝不允许反过来拉伸音频。
  2. 旁白必须可溯源。分镜 JSON 每幕强制带 source_span(对应原文片段),生成后用 difflib.SequenceMatcher 校验相似度 < 0.3 判为疑似编造,触发重试。这是最低成本的反幻觉手段。
  3. 风格统一靠 img2img 锚定,不靠堆提示词。先生成第 1 张定风格图,后续每张都把它作为 reference 传入;最后统一走 OpenCV 二值化收口。
  4. 中文字幕字体走 fontsdir 显式指定,把 NotoSansSC-Regular.otf 打进项目 assets/fonts/,不依赖系统 fontconfig。这个坑几乎必踩:不指定时 libass 会静默 fallback 成英文字体,中文全变豆腐块且不报错
  5. 所有分段视频编码参数写死统一(1280×720 / yuv420p / 25fps / h264),否则 concat demuxer 的 -c copy 会对不齐。
  6. 最终编码必须 -pix_fmt yuv420p -movflags +faststart,否则 Safari/移动端黑屏、网页不能边下边播。

#四、手绘渲染器(本项目唯一的技术核心)

#4.0 ⭐ 笔画排序已定案 —— 见独立规格书

~/Desktop/白板视频-笔画排序算法规格书.md(v1.0,含完整算法四层、参数表、代码骨架、降级预案、可自动化的验收指标)。M1 阶段直接照那份写代码,不要重新选型。

两条最该记住的:

  1. 对手的算法已被逆向出来 —— 就是「按 path 起点 y 从上到下排序」(三个样本的 y 递增比例 89%/100%/80%,x 只有 47–56% ≈ 随机)。它没有骨架、没有连通域分组、没有语义、没有 TSP。而 VideoScribe/Doodly 连这个都没有,纯靠人工排图层。门槛比想象中低得多。
  2. 我们的定案主路线:连通域切分 → 线/块分类 → 贪心就近排序(不是静态 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 天)

  1. 本地建 ~/personal/scribe-studio,起步就建 GitHub 私有仓库(按用户惯例:拿不准偏私有),分步 commit + push。
  2. 目录结构:
    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 名存在)
  3. VPS 侧:apt install -y redis-server fontconfig fonts-noto-cjk potracepip install fastapi uvicorn rq redis edge-tts opencv-python-headless scikit-image numpy pysubs2 httpx pillowfc-cache -f && fc-list :lang=zh 验证中文字体。
  4. Caddy 反代 scribe-api.saveme505.help127.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() 收集 WordBoundaryoffset/duration 单位是 100ns,除以 10000 得毫秒)。
  • 先做一次语速标定:固定音色 zh-CN-YunjianNeuralrate=+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-taskGET /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.tsproxy.tslogin/page.tsxapi/login/route.ts,env 与其它站完全一致(值见 ~/Desktop/配置信息/站点访问密码门.md绝不写进代码或提交进仓库)。
  • 部署严格走:改本地 → git commit(LeoLee0812 / Leo,中文注释)→ push main → Vercel 自动部署。禁止 vercel --prod
  • 绑定 scribe.saveme505.helpvercel domains add)。
  • ~/personal/projects-hubPROJECT_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 sheeta 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 秒的旁白量:中文 2836/ 英文 1822 词。宁可把旁白改短,也绝不允许
   一句话跨越两幕。
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 年后停更,别选。

#十、给执行者的开场动作

  1. ssh <VPS> 确认环境(8 核 / 62G / ffmpeg 6.1.1 已验证)
  2. 先读 daslearning-org/image-to-animation-offline 的核心渲染循环,再动手写 R1
  3. 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-hubPROJECT_META

#⚠️ 本计划书中被实测推翻的地方(照着旧文改会返工)

完整版见仓库 docs/实测偏差记录.md,这里列最关键的:

  1. §4.2 / 规格书 §3.1 骨架 DFS:「允许重走边」实测像素覆盖率只有 6% (骨架图常有多个不连通分量,重走永远走不出当前分量)。已改为显式跳转,涨到 98%。
  2. 规格书 §3.2 色块同心圈涂抹:实测只覆盖 42%。已改为之字形横向扫涂
  3. 规格书 §4 参数表W_ANCHOR_DIST 等三项权重压不下反跳率(最好也只到 32%)。 已新增第四项 w_momentum = 0.30,降到 13%。MIN_UNIT_AREA 必须从 60 降到 12。
  4. §M3 edge-tts:7.x 默认 boundary='SentenceBoundary',不显式指定拿不到逐词时间戳, 字幕会静默退化成按字均分 —— 不报错、肉眼也看不出来。必须传 boundary="WordBoundary"
  5. §1.4 语速「10 秒 28–36 字」是理论值:实测 5.1 字/秒,要 44–50 字。 按旧值下单,60 秒的片只出得到 44.6 秒。
  6. §2.1 Caddy 用不了:这台 VPS 的 80/443 早被 nginx 占着(在跑 line.saveme505.help)。 已改用 nginx + certbot
  7. §6.1 json_schema 不可信:开了 strict: true 仍会吐裸数组、把字符串字段吐成数组。 解析层必须按「拿到的是任意 JSON」来写。
  8. §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)