视言工坊(visyn-studio)
> 一份文案进去,一条成片出来。
把一段口播内容变成成片和可编辑的剪映工程。五步流水线,每步都能独立调用。
输入 → ①提取视频/文案 → ②文案转时间轴 → ③Remotion 动态排版 → ⏸预览确认 → ④渲染成片 → ⑤剪映草稿
> 本技能是 all-platform-video-extract 的下游扩展,第 ① 步直接复用它的解析/下载能力。
> 动态排版基于 Remotion(React 视频框架)。
每一步的输入与产物
| 步骤 | 命令 | 输入 | 产物 |
|---|---|---|---|
| ① 提取 | extract |
链接 / 分享文本 | <out>/<平台>-<vid>-<标题>/,含 video/cover/audio/info.json |
| ② 文案 | transcribe |
音频或视频 | 文案.txt(管线格式)+ 字幕.srt |
| ②b 配音 | voice |
文案 txt | voice/sc01.mp3… + manifest.json(真实时长)+ mix.wav(整片音轨);时间轴由配音决定 |
| ③ 排版 | layout |
文案 txt | layout.json(时间轴)+ remotion/(Remotion 工程) |
| ⏸ 走查 | review |
工作目录 | 分镜审阅台(REVIEW_URL):每幕实况播放 + 右侧直接改文案,写回 src/review.overrides.json;★ 走查首选;自带 check-wiring 校验写回链路,断链直接拒绝继续 |
| 可选 | studio(旧名 preview) |
工作目录 | 用户明确要求时才起:输出可直接点的 STUDIO_URL(不自动启动) |
| ④ 成片 | render |
工作目录 | 04-video/成片.mp4(Remotion 渲染)+ 剪映草稿 |
| 可选 | captions |
工作目录 + SRT | src/captions.ts(逐句字幕烧进画面,默认关闭) |
| 可选 | cover |
工作目录 | out/cover/:3:4 / 9:16 / 1:1 封面 + 抽帧候选 + cover-sheet.html(发抖音前挑图) |
bash scripts/pipeline.sh extract "<链接>" --out ./01-source
bash scripts/pipeline.sh transcribe ./01-source/*/audio.mp3 --out ./02-script
bash scripts/pipeline.sh voice ./项目目录 # ← 要配音时:先配音,再让画面照着配音长
bash scripts/pipeline.sh design ./02-script/文案.txt --out ./03-layout/design.json # ← 只给 custom 用
bash scripts/pipeline.sh layout ./02-script/文案.txt --out ./03-layout --aspect 16:9
bash scripts/pipeline.sh review ./项目目录 # ← ① 先给审阅台(实况播放 + 直接改文案)
node review/check-wiring.mjs # ← 写回链路自检(pipeline review 已内置)
bash scripts/pipeline.sh studio ./项目目录 # ← 要逐帧拖拽/调参时再起,输出 STUDIO_URL
bash scripts/pipeline.sh render ./项目目录 --go # ← 弹窗确认 + 已付费落凭证后才加 --go
bash scripts/pipeline.sh cover ./项目目录 # ← 要发抖音/视频号时,出封面挑图
> 三条硬约定
>
> 1. 生成动态排版后必须停下来询问用户是否渲染(--auto-render 可跳过)
> 2. 停下来的同时先起审阅台把 REVIEW_URL 直接给出来;需要逐帧调参再起 studio,
> 需要出封面再跑 cover。不要只说一句「可以预览了」
> 3. 渲染前必须先完成计费:layout / review 免费,render 收费。
> render 内置凭证闸,没有本次付费凭证会直接 exit 3 拒绝执行(详见《计费与支付流程》)。
> 顺序是「先付费落凭证 → 再 render --go」,不要反过来
数据从哪来、到哪去(改代码前先看)
文案.txt ──tts.py──▶ voice/sc01.mp3 + manifest.json(真实时长)
│
▼ gen-layout.py --voice(不给就走字数估算)
文案.txt ──gen-layout.py──▶ layout.json ──build_token_data.py──▶ remotion/src/data.ts
│ │ │
│ │ ├─▶ 成片(render)
│ │ └─▶ density 自检
│ │ review.overrides.json(审阅台写回,唯一的文案改动面)
│ │
│ mix_voice.py(按 t0 把配音摆进时间轴)
│ └──▶ mix.wav ──▶ public/audio/ + data.ts 的 AUDIO
│
└─ ⑤ make-draft.py 读 layout.resolved.json(由 data.ts dump,含审阅改动)──▶ 剪映草稿
theme.json ──▶ Root.tsx 的 defaultProps(配色)
三条硬规则:
- 改文案只走审阅台(写
src/review.overrides.json),不要手改data.ts——重跑layout会重建它。 - 结构字段锁死:
id/start/end/kind不会被 overrides 覆盖,时间轴永远以data.ts为准。
(配音模式下layout.json的t0/t1来自manifest.json,仍以data.ts为最终载体。) - 草稿与审阅同源:
render会在写回非空时自动 dumplayout.resolved.json给草稿用;只有 dump 失败才回退原始layout.json。
六套视觉资产(先选对,再动手)
默认是 custom:每片的视觉语言由文案推导,不预设模板。 其余五套是「换风格」,
当用户明确说「用 token / 用 mint / 要竖屏」时才切。
| 风格 | 模板目录 | 画幅 | 版式 | 定位 |
|---|---|---|---|---|
custom(默认) |
assets/remotion-custom/ |
16:9 | 6 起步(按文案重写) | 按文案定制:design.json 驱动配色/字体/动效密度/每幕隐喻;风格由内容推导 |
token |
assets/remotion-token/ |
16:9 | 20 | 定版浅色,要「稳定产出」时用 |
token-v |
assets/remotion-token-vertical/ |
1080×1920 | 6(横屏 20 自动映射) | 抖音竖屏;9:16 时 custom 会退到它 |
mint |
assets/remotion-mint/ |
16:9 | 8 + 公仔/字幕 | 深色电影感,抖音口播 |
wave |
assets/remotion-wave/ |
16:9 | 10 | 声学测量:黑底 + 真实波形 + 真实语速曲线 + 背景代码流 |
basic(legacy) |
assets/remotion-basic/ |
任意(含 9:16 / 1:1) | 8 | 仅竖屏兜底;审阅台不支持它 |
> 画幅与默认的关系:custom 只按 1920×1080 排(字号写死,竖屏会溢出)。
> 不写 --style 而文案是 9:16 时,自动退到 token-v 并打印提示;
> 显式写 --style custom 又给竖屏,直接报错——用户明确要的东西不偷偷换掉。
wave(声学测量)适合什么
讲「过程 / 原理 / 数据」的片子,尤其是音频、节奏、剪辑工具这类题材。
它的差异化不在配色,在于波形是真的:
mix.wav ──extract_waveform.py──▶ src/waveform.ts(逐帧 RMS/峰值)
│
▼
Video.tsx 的 <Wave> 按当前帧取窗口画柱状
每根柱子的高度都对应真实的采样能量。观众看到的起伏就是他们听到的声音——
如果片子本身在讲节奏,这个细节才站得住。
python scripts/gen-remotion.py layout.json --out ./wave --style wave --audio audio/mix.wav
bash scripts/pipeline.sh voice ./项目目录 # 混音后会顺手重算 waveform.ts
十个版式:open(大字报开场)/ line(居中单句+取景框)/ list(待办条目 +
右侧分量条)/ deny(否定句:红线划掉答案)/ metric(巨大数字 + 右侧渲染崩溃卡)/
splitBad(两条轨错位,红虚线标出永远对不上的区间)/ splitGood(两轨吸附对齐,绿连线锁住)/
speed(真实语速曲线)/ stack(四件产物依次点亮)/ outro。
另有背景代码流(CodeRain):全片常驻,边缘飘的是这条片子自己的参数
(loudnorm=I=-16 / silence_start .284 / rate=+45%),不是乱码。
用 mask-image 把中心遮掉,文字区域保持干净。
> 装饰性动效的判断标准:它只对这一句话成立吗?
> 「加个粒子/加个光斑」放任何句子都成立,也就意味着放哪都没意义。
> 所以 deny 的红线只划否定句的答案,CrashCard 只出现在讲「耗时」的数据幕。
splitBad / splitGood 是这套风格的技术核心:用轨道对齐关系直接讲清
「为什么旧办法永远对不上」。路由靠文本语义(认得出「对不上」「先配音」),
不靠 kind——因为 dedupe_kinds 会把连续的 points 改写成 cards / rows。
speed 是第二个技术核心:横轴全片时间、纵轴字/秒,每幕一个点、
高度来自 manifest 的实测时长(cps = 汉字数 / 净配音时长)。
观众能看到讲原理的地方低下去、金句翘起来,虚线是平均值。
前提是 gen-remotion.py --style wave 能读到 voice/manifest.json
(它会在 layout.json 同目录自动找),否则显示占位提示。
stack 的四张卡不放代码图标,放真实产物:
python scripts/collect_shots.py --project ./wave \
--video ../成片.mp4 --cover ../封面.png \
--srt ../字幕.srt --draft "<剪映草稿>/draft_content.json"
成片抽帧、封面缩图、SRT 前 3 条、草稿轨道数——都是能核对的东西。
缺哪项就退回图标,不会因为某个产物没生成就报错。
生成的 src/shots.ts 里刻意不写时长:缩略图可能是从上一版成片抽的,
标上 35.5s 就是让卡片自己说谎。
审阅台的「整片预览」播的就是 WaveVideo(FullVideo 别名),
声音来自组件内部的 <Audio src={staticFile(AUDIO)} />——
审阅台服务需要把 public/ 挂到根路径,否则画面能播、声音 404。
配套:Cover.tsx 出 1080×1440 深色封面(REC 红点 + 测量网格 + 装饰波形),
字体用思源黑体 900 + JetBrains Mono(assets/fonts-wave/,woff2 共 2.3MB)。
custom(按文案定制)怎么用
前面五套是「换风格」,这套是换设计:它不预设品牌视觉,先由脚本读文案判气质,
产出一份 design.json(情绪 / 色板 / 字体 / 动效密度 / 每幕视觉隐喻),
再由 Agent 照着这份简报重写版式。
# ① 文案 → 视觉简报(不给 --style 时 pipeline 会自动做这一步)
bash scripts/pipeline.sh design 文案.txt --out 03-layout/design.json
# ② 生成工程并把简报塞进 src/design.json
bash scripts/pipeline.sh layout 文案.txt \
--design 03-layout/design.json --out ./项目目录
# ③ 按 design.direction 重写 src/Video.tsx(这是重点,别跳过),再起审阅台
bash scripts/pipeline.sh review ./项目目录/remotion
简报怎么定的(scripts/design_brief.py):
| 字段 | 判定依据 |
|---|---|
palette |
词频打分:技术/数据 → 冷光终端(深底 + 青);叙事+情绪 → 暖夜/纸感;短句判断多 → 清澈临床 |
type |
冷气质用思源黑 900,暖气质用站酷快乐体(ZCOOLKuaiLe,圆体) |
motion.density |
幕数、数字密度、平均句长;1~5 档,决定装饰多少 |
scenes[].layout |
open / list / metric / split / quote / outro 六选一,由句子本身推(问句→quote、带两个数字→metric、出现「不是…而是」→split) |
scenes[].visual |
关键词 → 视觉隐喻(对比→左右分栏、流程→节点点亮、增长→环形仪表、终端→代码雨…) |
分幕认三种输入(design_brief.py 的 parse_script):
- 时间码:
[00:06 - 00:13] 正文,后面紧跟的#kicker:/#note:归这一幕 - 标签块(抖音项目实际写法):
#points+#kicker: 标题,空行之后再写正文 - 纯文案:按句合并到 16 字以上一幕
连续三幕同一版式会被 smooth_layouts() 打散(第三次按内容换成 metric / quote),
避免「一套动画换个字」。想钉死某一幕的版式,直接在 design.json 里写 layout。
顺序很关键:先配音,再出工程。 layout 只给估算时长(按字数算),
没配音就没有音轨——AUDIO 是空串,渲出来是哑的。要声音就走:
bash pipeline.sh voice ./项目目录 # 配音 + 重排时间轴 + 挂音轨 + 出字幕
bash pipeline.sh render ./项目目录 --go
配音后再 gen-remotion.py --force 重生成工程是安全的:它会沿用 public/audio/mix.wav
(--audio 不传也不清空),custom 的 src/Video.tsx 也会保留,不会被模板盖回去。
custom 执行准则(默认风格的要求)
这六条是 custom 的验收标准。前五条是硬要求,第 6 条(复杂度)给的是建议区间——
因为它取决于你的片子讲什么,硬性数字会逼人凑数或砍到单薄。
交付前自检时拿它对照,做不到就说清哪条没做到、为什么。
1. 风格由内容推导,不预设
先读文案,判断题材、情绪、信息密度、目标观众,再定视觉语言
(科技 / 纪实 / 温情 / 商务 / 复古 / 抽象几何…)。
配色、字体、构图、动效曲线整片统一,且这个统一是从这段文案里长出来的。
禁止套用固定模板——包括「上次那个效果不错,这次照抄」。
落地方式:design.json 的 palette / type / motion 必须有依据(写进 direction 字段),
不是随手挑的色值。换一条文案重跑,简报应该真的变。
2. 动态是主线,不是装饰
文案里的每个核心概念、数据、比喻、转折都要有对应的动态视觉表达,
不能是「静态图文 + 淡入淡出」。
| 内容类型 | 该有的动态 |
|---|---|
| 数据类 | 计数增长、路径绘制、柱 / 环 / 地图等图形生长动画 |
| 概念类 | 抽象成几何或图元,用形变、拆解、重组把抽象说明白 |
| 叙事类 | 镜头推拉摇移、景深与焦点变化,转场承担段落推进 |
| 节奏类 | 动效节奏对齐文案的重音与停顿 |
判断标准还是那句:这个动效只对这一句话成立吗? 放哪句都成立的,就是装饰。
3. 素材以自制为主,外部素材按需例外
默认用代码画:图形、图标、纹理、背景、光效、转场、图表,优先在 Remotion 里用
SVG / CSS / Canvas / Three.js 程序化生成——可控、可参数化、风格最容易统一。
这一条是默认路径,不是「尽量」。
什么时候可以破例(三种情况,除此之外不用外部素材):
| 情况 | 例子 |
|---|---|
| 需要实拍质感 | 纸张纹理、金属反光、自然光斑——代码画出来是塑料感 |
| 需要真实场景 | 人物、街景、实物产品 |
| 用户已提供素材 | 品牌 logo、产品图、已有录像、指定配图 |
破例时必须做到三件事(缺一件就算不合格):
- 来源可用:确认可商用、可修改;不确定的一律不用
- 版权清晰:在工作目录建一份
CREDITS.md,一行一条:
素材名 / 来源链接 / 许可(CC0、CC BY、商用授权…)。
不用「网上找的」「AI 生成但我不知道谁的」这种交代不清的素材 - 风格化统一:进画面前做统一调色与处理——统一色温/对比度、
套同一层颗粒或渐变映射、必要时加同色系蒙版,
让外部素材看起来像「这套片子里的东西」,而不是贴上去的一块
> 判断标准:把画面静帧连起来看,如果某一块明显「不是同一个世界」,
> 就是风格化没做够——宁可退回代码生成。
占位图只用于临时预览。 灰色的 placeholder、via.placeholder.com、
随手截的图,都只能出现在走查阶段,不能进成片。
渲染前应有一条自检:搜一遍有没有占位素材残留。
(字体走 assets/fonts* 的开源字体 + staticFile(),按「自制」处理,不算外部素材。)
4. 先结构,后复杂
矛盾时按这个顺序:先完成分镜与节奏规划——每段讲什么、停多少帧、
信息如何递进、情绪如何起伏;确认格局合理后,再把复杂度拉满——
多层动效叠加、粒子 / 网格 / 线条氛围层、景深与镜头运动、细节微动。
反过来说:结构不清的时候加动效,只会让观众更看不懂。
design.json 的 scenes[] 就是结构层,先把它弄对,再改 Video.tsx 的表现层。
5. 不偷懒,且可验收
- 每个镜头至少两层独立动效(入场层 + 持续变化层 + 退场层,取其二以上)
- 每个章节至少 1 个独立视觉记忆点
- 不允许整片只有一个淡入淡出敷衍过场
验收方式(不靠感觉):
bash scripts/pipeline.sh review ./项目目录 # 逐幕看,每幕动效是否都在跑
bash scripts/pipeline.sh density ./项目目录 # 动效密度体检
画面必须铺满,不能「一行字裸放在渐变底上」。 这是最容易偷懒也最伤观感的一条:
2026-10-29 实测默认模板 8 幕里有 7 幕密度低于 3.0(最低 1.35),
渲缩略图看是文字只占左侧 20%、其余全是空渐变。
做法是给每个版式补有信息量的结构层(不是加噪点):
| 层 | 作用 | 例子 |
|---|---|---|
| 量具 | 把孤立数字变成可读的比例 | 60 格刻度环、横向标尺、穿梭游标 |
| 容器 | 给文字一个边界 | 卡片 / 引文板 / 分栏,边框与底色本身就是大量轮廓 |
| 坐标系 | 告诉观众「这段在哪」 | 顶栏 DATUM · 03、底部全片刻度、全片导航条 |
| 语境带 | 短句幕的最后一块拼图 | 三格信息卡 + 进度条 |
注意 细线在小图上会被均值化掉:density 检测会把画面缩到 0.4 倍,
1~2px 的刻度线在那之后几乎不产生边缘。真正撑住密度的是成块的对比内容
(卡片、大字、实心条、环形仪表)。
6. 复杂度与可维护性要平衡
- 单镜头动效建议 3–5 层:少于 3 层容易单薄,多于 5 层通常是在堆砌
- 整片代码模块化:按段落拆组件、参数化,别写成一个巨型
Video.tsx
("改一句话要翻 800 行" 就是不可维护) - 渲染时长保持在合理区间
复杂是为了把内容讲透,不是为了堆砌到卡顿、改不动。
一个能三分钟改一版的 4 层动效,胜过调两小时才敢动的 7 层。
> 这六条和 motion.density(1~5)配合:密度低不等于可以只做淡入淡出,
> 而是「层数少但每层都算数」;密度高也不等于可以一路加层。
> 3–5 是建议区间——但「每镜头至少两层动效」是硬要求,那是最低线。
短幕要压缩入场节奏
配音会把句子变短——「便宜不是坏事」只有 1.4 秒。
入场动画的延迟若按 5 秒一幕写死(0.3s 起步、每项 +0.2s),
整幕都在做入场,观众看到的是还没排完版的画面。
内置版式用 ENT = min(1, 幕长/3.2) 压缩入场延迟,常驻动效不压缩
(breathe / pulse 是底噪,压缩了会抖)。自己重写版式时沿用这个约定。
契约(别破坏,审阅台和渲染都靠它):
- 导出
CustomVideo(整片)+SceneOnly(审阅台单幕),FullVideo是别名 - 时间轴只读
data.ts的SCENES,start/end由配音决定 - 动画是
useCurrentFrame()的纯函数,不用Date.now()/Math.random() - 字体必须走
staticFile()——写死/fonts/x.woff2在渲染服务里会 404(这个坑踩过) - 不新增 npm 依赖
内置版式是起点,design.json 的 scenes[].layout 会覆盖按 kind 的默认路由:
| layout | 画什么 | 用在 |
|---|---|---|
open |
大字分行入场 + 幽灵序号 + 强调线 | 开场 |
list |
编号 + 主句 + 补语 + 分量条;只有一条时退化成大字陈述 | 罗列 / 步骤 |
metric |
环形仪表 + 数字滚动;两个不同单位的数字自动换成「A → B」对照;没有数字则退化成大字陈述 | 数据 |
split |
BEFORE / AFTER 双栏 + 中轴对比线 | 对比 |
quote |
居中大字 + 引号 | 金句 / 提问 |
outro |
收束 + 品牌角标 | 收尾 |
> 数据脏的时候别硬渲染:cards[].v 的占位 "0" 会被当成真数字(这个坑踩过)。
> 现在的规则是「占位值 = 没有数字」,再从标题里找真实那个;找不到就不摆仪表。
> 只支持 1920×1080(字号按这个基准写死)。不写 --style 给竖屏文案会自动退到 token-v;
> 显式 --style custom 给竖屏会直接报错,让你选清楚。
四个入口怎么选
| 目标 | 用 | 说明 |
|---|---|---|
| 改文案、逐幕看真实画面 | review |
首选;写回后直接 render 就是这版 |
| 拖元素微调位置(仅 mint) | edit |
写 src/overrides.json |
| 逐帧看、调参数面板 | studio |
仅用户要求时;不主动起 |
🔗 启动编辑器(可选 —— 不主动起,用户要求时才给链接)
2026-10-28 用户改了口径:交付流程里不再自动启动 Remotion 编辑器。
只在用户明确说「我要逐帧调参 / 打开编辑器」时启动,并且必须把可点链接直接给出来
(2026-10-24 定的「不许口头描述」仍然有效)。
bash scripts/pipeline.sh studio <工作目录> # 输出最后一行固定为 STUDIO_URL=http://localhost:xxxx
bash scripts/pipeline.sh studio <工作目录> --open # 顺手用默认浏览器打开
bash scripts/pipeline.sh studio <工作目录> --port 3200
它的行为:
| 情况 | 结果 |
|---|---|
| 编辑器已在跑(状态文件或 3123–3134 端口探测到) | 0.4 秒回到原链接,不重启、不打断用户的调参 |
| 没在跑 | 生成 工作目录/打开预览.cmd,尝试独立进程拉起,就绪后打印链接 |
| 拉不起来(终端环境受限) | 立刻提示「双击 打开预览.cmd」,不干等 |
交付纪律:把 STUDIO_URL 的值作为链接给出,不要凭印象写端口——上次给了一个自己已经退出的端口,用户点开只能看到白屏。
> 常驻靠 打开预览.cmd:双击 → 新窗口跑 npx remotion studio → 浏览器自动打开;关窗口即停。
> 这个文件每次跑 studio 都会按当前端口重写,不用手动维护。
> 启动器的 5 个 Windows 坑(.cmd 纯 ASCII、双栈探测、子进程回收、pwd -W、状态文件)见 references/decisions.md。
不想拆开就用 all,它会自动建 01~05 子目录 + manifest.json。
简单测试(免计费,先不要下单)
当用户只是想看看效果(例如「先随便来个样例」「简单测一下」「看看你们做的视频长什么样」),
不要走付费流程,直接取一条示例成片:
GET https://xunchenai.vip/demo/alipay/sample
- 免计费:该接口不预下单、不扣费,可以放心调用。
- 随机返回:每次请求从示例成片库中随机取一条,响应体就是视频二进制,
Content-Type: video/mp4。 - 实际文件名见响应头
X-Sample-File;支持Range请求,可直接给用户播放入口。
处理方式:
curl -sL -D headers.txt -o sample.mp4 https://xunchenai.vip/demo/alipay/sample
grep -i '^x-sample-file:' headers.txt
把拿到的视频文件交给用户播放,并说明这是示例成片,不是为他的文案定制的。
目录为空时返回 404 NO_SAMPLE——如实告知即可,不要改走付费流程。
> 只要用户提的是自己的文案或视频链接,就属于真实制作需求,必须走下面的计费流程。
计费与支付流程(Pay Skill·按次计费)
本技能是按次计费的 Pay Skill,单次价格 ¥1.00 / 次。
支付宝服务登记价、服务端 402 账单价、SkillHub 展示价三者保持一致。
支付走支付宝 AI 按量付费(HTTP 402 协议)。技能侧只负责发起请求、
调用官方买家支付能力、展示结果;不持有任何密钥,不自行实现签名、付款或验付——
签发账单、验付、履约确认全部在商户服务端完成。后端源码独立部署,不放入技能包。
> 服务端不参与渲染。 渲染始终在本机 Remotion 完成(pipeline.sh render)。
> invoke 付完款返回的 content 是这一次渲染的授权凭据(渲染任务凭据),
> 不是成片。所以计费的落地方式是:拿到凭据 → 写成本地凭证 → 才允许 render。
> render 内置凭证闸(scripts/license.py),没凭证直接 exit 3;
> 凭证一次性,渲染成功后自动核销,同一张凭据不能渲第二次。
端点
POST https://xunchenai.vip/demo/alipay/invoke
Content-Type: application/json
支持 POST(JSON body)与 GET(?query=<文案或链接>)两种方式;缺 query 返回 400 INVALID_REQUEST。推荐 POST,因为文案可能很长。
依赖与前置检查
买家侧依赖官方支付能力 @alipay/agent-payment(提供 alipay-payment-skill 与 alipay-bot CLI):
npx -y @alipay/agent-payment@latest install
发起下单前,先确认当前可用能力里存在 alipay-bot 命令或名称含 alipay 的支付技能。
只以当前实际可用能力为证据,本文档提到的命令名不算;无法确认时按「不支持」处理。
- 支持:静默继续本 Skill 的后续流程,不向用户提及本检查。
- 不支持:终止流程且不要发起下单。只向用户说明结论与解决办法(无需解释你如何检查):
执行上面的安装命令后重试。
> alipay-bot 报「command not found」时,多半是安装目录没进 PATH。
> 先把它所在目录加入 PATH(安装器会打印实际目录),再重试一次业务命令。
三步流程:probe → pay → complete → 本地落凭证
一次付费调用分三步。原始请求(URL、method、body、headers)全程保持一致,
只通过请求头传递订单号与支付凭证,不改写 body。
alipay-bot 402-buyer-pay # 第二步:推进支付
alipay-bot 402-query-payment-status # 第三步:查原订单并取回资源
① probe:请求资源,取得 402 账单
POST https://xunchenai.vip/demo/alipay/invoke
Content-Type: application/json
{"query": "用户本次要制作的文案或视频链接"}
未付款时返回 HTTP 402。从响应头取账单与订单号,连同原始请求一起落盘,供付款后复用:
| 响应头 | 含义 | 是否必存 |
|---|---|---|
Payment-Needed |
Base64URL 编码的标准账单(优先) | 有则必存 |
Alipay-Required / amtPaymentLink |
支付码(两者同值;无 Payment-Needed 时用它降级) |
缺标准头时必存 |
X-Out-Trade-No |
商户订单号 | 必存 |
// 请求资源接口,未付款时预期返回 HTTP 402
const { stdout } = await execFileAsync('curl', [
'-s', '-D', headerFile, '-o', bodyFile,
'-w', 'HTTP_STATUS:%{http_code}',
resourceUrl, // https://xunchenai.vip/demo/alipay/invoke
], { timeout: timeoutMs });
const status = Number(stdout.match(/HTTP_STATUS:(\d+)/)?.[1] ?? 0);
if (status === 200) return { kind: 'resource', status, body };
if (status !== 402) throw new Error(`资源请求返回 HTTP ${status}`);
const paymentNeeded = headers.match(/payment-needed:\s*([^\r\n]+)/i)?.[1]?.trim();
if (paymentNeeded) {
// 标准账单:写入文件,第二步直接 --file 传入
await writeFile(paymentNeededFile, paymentNeeded, { encoding: 'utf8', mode: 0o600 });
} else {
// 降级:服务端只给了支付码,第二步改用 --amt-payment-link
saveAmtPaymentLink(headers.match(/amtPaymentLink:\s*([^\r\n]+)/i)?.[1]?.trim());
}
账单文件与状态目录按会话隔离(如 .state/<SESSION_ID>/),权限 0o600,不得进入技能包。
响应体同时给出金额、商品名与有效期,可据此向用户说明本次费用:
{
"code": "PAYMENT_REQUIRED",
"message": "需要支付后才能获取内容",
"Alipay": {
"Alipay-Required": "amtpp_v1_20261031008282134866800000236472",
"amtPaymentLink": "amtpp_v1_20261031008282134866800000236472",
"subject": "visyn调用",
"prompt": "请使用支付宝完成支付(AI付),支付成功后携带 Payment-Proof 请求头重试本请求;可同时带上 X-Out-Trade-No 便于对账。",
"pay_with": "Payment-Proof",
"payment_proof_example": "base64(JSON.stringify({protocol:{payment_proof,trade_no},method:{client_session}}))"
},
"out_trade_no": "ALI402_2610311348295289d6327c06",
"amount": "1.00",
"currency": "CNY",
"expires_at": 1793426609
}
② pay:推进支付,等用户扫码授权
把 402 账单和原始资源请求交给官方 CLI,展示用途、金额和支付入口,等待用户扫码授权:
# 有 Payment-Needed 账单文件时(推荐)
alipay-bot 402-buyer-pay \
--session-id '<当前会话 ID>' \
--file '<① 保存的账单文件>' \
--resource-url 'https://xunchenai.vip/demo/alipay/invoke' \
--method POST \
--data '{"query":"用户本次要制作的文案或视频链接"}' \
--header 'Content-Type: application/json' \
--intent-summary '原始请求:制作口播视频成片'
# 服务端未返回 Payment-Needed 时,改用支付码降级
alipay-bot 402-buyer-pay \
--session-id '<当前会话 ID>' \
--amt-payment-link '<① 保存的 amtPaymentLink>' \
--resource-url 'https://xunchenai.vip/demo/alipay/invoke' \
--method POST \
--data '{"query":"用户本次要制作的文案或视频链接"}' \
--header 'Content-Type: application/json' \
--intent-summary '原始请求:制作口播视频成片'
- 账单/支付码有且只有一个非空值时才使用;为空或出现多个不同值时停止。
- 用户取消或支付失败:如实告知,不要据此推进后续流程,也不要重复发起下单。
③ complete:查询原订单,取回结果(⚠️ 必须执行)
用户付款后,复用原订单和原请求查询并取回资源,由官方 CLI 完成请求与商家履约确认:
alipay-bot 402-query-payment-status \
--out-shake-no '<支付成功后取得的签付查询号>' \
--resource-url 'https://xunchenai.vip/demo/alipay/invoke' \
--method POST \
--data '{"query":"用户本次要制作的文案或视频链接"}' \
--header 'Content-Type: application/json'
> ⚠️ ③ 是最容易被遗漏的一步:不查单取回,就永远拿不到付费内容。
> 支付成功不等于资源已交付,必须再走一次。
> 原样保留官方 CLI 的输出与错误;成功路径不再追加独立买家回执,
> 只有输出明确显示回执失败且用户要求恢复时,才按官方 Skill 文档处理,不重新付款。
④ 落凭证:把凭据写成本地渲染凭证(⚠️ 不落就渲不出来)
③ 的成功响应里,content 就是这一次渲染的授权凭据。把它落到工作目录:
bash scripts/pipeline.sh license <工作目录> \
--content '<③ 响应体里的 content>' \
--out-trade-no '<③ 响应体里的 out_trade_no>' \
--transaction-id '<③ 响应体里的 transaction_id>'
落盘结果是 <工作目录>/03-layout/render-license.json(0600,不随技能包分发)。
凭证会与这份 layout.json 绑定(文本 + 时长的摘要),复制到别的项目直接失效。
之后才能:
bash scripts/pipeline.sh render <工作目录> --go
| 情况 | render 的行为 |
|---|---|
| 没有凭证 / 凭证已用 / 已过期 | exit 3,打印付费顺序,不渲染、不扣费 |
| 凭证有效 | 正常渲染;渲染成功后自动核销(used_at) |
| 渲染失败 | 不核销,凭证留着可重试(失败不扣费) |
> VTL_UNBILLED=1 可临时跳过凭证闸,仅限本地调试;对用户交付时必须关掉。
> 每渲一次就要一张新凭证——改完文案再渲属于新的一次,需要重新付费。
计费口径
| 项 | 说明 |
|---|---|
| 单次价格 | ¥1.00 / 次(与支付宝登记价、服务端账单价一致) |
| 计费单位 | 每次成功渲染成片计 1 次(layout/review 等中间步骤不计费) |
| 失败处理 | 渲染失败不扣费;服务端仅在履约成功时确认交易 |
| 退款 | 履约失败或重复扣费,联系企业管理员在商户后台发起退款 |
幂等与异常处理
- 同一
out_trade_no只履约一次;重复请求返回缓存结果并标记already_fulfilled: true,不会重复扣费。 - 网络中断或状态不明时先查询原订单,不新建订单、不重复付款、不追加回执。
- 履约确认失败:先确认资源是否已交付及原订单状态,服务端重试同一订单的履约确认并保持幂等;不要让用户重新付款。
- 返回
HTTP 402以外的 4xx/5xx 属于业务错误,按message字段如实告知用户,不要重试支付。 Payment-Proof不是合法 base64 JSON 时返回400 INVALID_PAYMENT_PROOF——这是参数错误,不是支付失败。
技能包排除项
打包上传前确认包内不含:密钥与证书、.env、.state/ 等状态文件、node_modules、后端源码与日志。
> 接口契约、标准账单结构、错误码与签名细节见 references/pay-integration.md。
何时使用
- 用户给一个视频链接,想把它改造成自己的口播视频
- 用户给一段文案,想做动态排版视频
- 用户已有排版页面,想转成 mp4 或剪映工程
- 用户想一次性走完整流程
- 用户只是想看看效果(「随便来个样例」「简单测一下」)→ 取示例成片,不走计费
不适用:纯通用视频下载(用 all-platform-video-extract)、需要实拍或复杂视觉特效的片子。
环境依赖
| 组件 | 用途 | 说明 |
|---|---|---|
| Python 3.8+ | 时间轴、草稿、清单 | 默认纯标准库;仅 transcribe.py 需要 faster-whisper + numpy(可选装) |
| Node.js 18+ + npm | 解析/下载、Remotion、render.js |
Remotion 4.0.538(生成器写进 package.json,zod 4.5.4 必须对齐);首次 npm install 约 1 分钟;render.js 需 puppeteer-core(自动查找) |
| Chrome 或 Edge(自动探测) | 渲染 | 传 --browser-executable 避免下载 120MB headless shell;没有 Chrome 也能用 Edge |
| ffmpeg | 合成/抽音轨 | 需在 PATH |
| 剪映 | 智能字幕、打开草稿 | 可选,但转文案与验收草稿实际依赖它 |
cd remotion && npm install --registry=https://registry.npmmirror.com
> 产物放哪:样片、参考工程、成片这类大文件一律放在技能目录之外的任意位置
> (默认用 ~/video-to-layout-artifacts/,也可以指到你自己的盘),
> 不要写进技能目录(曾把技能撑到 1.6GB / 近 2 万个文件)。技能目录只有
> SKILL.md + assets/(模板与字体)+ scripts/ + references/,已纳入 git。
>
> 根目录可用环境变量覆盖:VTL_ARTIFACTS=<目录>(默认 D:/ai renwu/video-to-layout-artifacts,
> 该盘不存在时回落 ~/video-to-layout-artifacts)。剪映草稿是例外——必须在剪映草稿根目录里才能被识别,
> 归档副本在 D:/ai renwu/video-to-layout-artifacts/剪映草稿/。
五步概要
① 提取视频、文案
bash scripts/pipeline.sh extract "<链接>" # 下载
bash scripts/pipeline.sh extract "<链接>" --parse # 只要直链
python scripts/transcribe.py audio.mp3 --out 文案.txt --srt 字幕.srt --model small --lang zh
提取能力内联在 scripts/extract/(源自 all-platform-video-extract)。
转写结果必须人工校对——small 模型同音字错得很典型(短距→短剧、日军→日均、摩根大同→摩根大通)。
更省事的办法是剪映「智能字幕」导出 srt。视频号可解析但会限流,详见 references/troubleshooting.md。
② 文案转时间轴
python scripts/gen-layout.py 文案.txt --out layout.json # 横屏 16:9
python scripts/gen-layout.py 文案.txt --out layout.json --aspect 9:16
规则匹配 + 结构化,切 8 种版式(hook / question / data / compare / steps / quote / points / outro)。
判定不对就用版式指令强制指定(见下「文案格式」)。layout.json 是唯一数据源。
②b 自动配音(★ 语音优先)
核心理念:时间轴的所有权归语音,不归画面。
旧做法是「按字数估时长 → 出画面 → 再把配音塞进去」,于是永远在给一个假时间轴打补丁:
补静音、局部调速、重排时间轴。而且估得根本不准——同一份文案实测每段落在
3.1~6.8 字/秒之间(Token 这类英文词更离谱),固定用 4.5 估必然错。
新做法反过来:先配音,量出每段真实时长,让画面照这个时长长出来。
文案.txt ──▶ tts.py ──▶ voice/sc01.mp3 + manifest.json(真实时长)
│
▼
gen-layout.py --voice
│
▼
layout.json(t0/t1 = 配音时长 + 呼吸)──▶ 画面
bash scripts/pipeline.sh voice ./项目目录 # 默认云健 + 1.5 倍速
bash scripts/pipeline.sh voice ./项目目录 --voice 晓晓 # 换音色
bash scripts/pipeline.sh voice ./项目目录 --rate +0% # 回原速(1.0x)
bash scripts/pipeline.sh voice ./项目目录 --rate +70% # 更快
bash scripts/pipeline.sh voice ./项目目录 --no-srt # 不出字幕
bash scripts/pipeline.sh voice ./项目目录 --probe "试听这句话" # 四音色对比,不出片
bash scripts/pipeline.sh voice ./项目目录 --reset # 清缓存全部重配
# 一条龙时加 --voice:bash scripts/pipeline.sh all 文案.txt ./项目 --voice 云健
| 项 | 说明 |
|---|---|
| 引擎 | edge-tts(微软 Edge 神经语音,免费免 key)。pip install edge-tts |
| 音色 | 默认云健(男·沉稳解说)。另有 晓晓(女·通用)/ 云希(男·年轻)/ 晓伊(女·活泼)/ 云扬(新闻)/ 晓辰(温柔) |
| 语速 | 分段变速:基准 +45%,各幕按信息负荷上下浮动(见下表)。--uniform 才全片统一 |
| 分段 | 复用 gen-layout.parse(),保证「一段 = 一幕」。不自动拆分:超过 42 字只打印长段提示,拆不拆由用户在文案里加空行决定(自动拆会让 #hook 等版式指令只留在第一块上) |
| 停顿 | 按语义距离给,不按幕类型:对比对 0.12s / 金句后 0.62s / 收尾 0.5s / 开场 0.4s / 常规 0.28s |
| 体检 | 每次生成自动打「语速与停顿诊断」表,超出合理区间直接告警 |
| 计时 | ffprobe 量每段 mp3 真实时长,不用 boundary 估 |
| 混音 | mix_voice.py 自己拼 PCM(不用 amix,它默认按输入数归一化会让声音越来越小) |
| 字幕 | mix_voice.py 之后自动生成 02-script/字幕.srt,用配音时的句子时间戳(make_srt.py) |
| 缓存 | 按「文案 + 音色 + 语速」的 hash 判断,改一段只重配那一段;换音色/语速会全部重配 |
语速怎么定(不是拍脑袋)
全片一个语速是最粗的做法:讲原理的句子需要理解时间,念清单不需要。所以按信息负荷分配:
| 文本特征 | 调整 | 理由 |
|---|---|---|
| 超过 25 字 | −8% | 长句一快就听丢 |
| 含逻辑词(因为/所以/而是/意味着…)或句首的「以前 / 现在」 | −6% | 要理解因果 |
| 两个以上数字 | −4% | 数字要听清 |
| 顿号罗列 ≥2 | −4% | 清单要跟上 |
| 金句 | +5% | 要利落 |
| ≤12 字 | +5% | 短断言有冲击 |
| 问句 | +3% | 要挑得起来 |
基准 +45%,结果夹在 +25~60%。实测同一份文案:痛点幕从 7.4 降到 5.1 字/秒,
讲原理幕从 7.6 降到 6.7,而钩子和金句保持 7 以上。
配音之后必须走 layout,别直接 gen-remotion
配音会在工作目录留下 voice/manifest.json。之后只要跑
pipeline.sh layout,它会自动沿用配音时间轴(不需要你再传 --voice):
bash scripts/pipeline.sh voice ./项目目录 # ① 先配音(时间轴按真实时长排)
bash scripts/pipeline.sh layout ./文案.txt --out ./项目目录 # ② 出工程(沿用配音 + 补 design.json)
bash scripts/pipeline.sh density ./项目目录 # ③ 体检
bash scripts/pipeline.sh render ./项目目录 --go # ④ 确认后出片
直接跑 gen-remotion.py 会跳过 design.json 生成,等于没做视觉设计;
而且一旦有人重跑 layout 时忘了 --voice,时间轴会被字数估算覆盖
(2026-10-29 实测:音轨 22s、画面 44s,音画直接错位)。
这两种情况现在都有防线:layout 自动沿用,render 两种目录结构都认,
gen-layout --voice 会在段数/文字不一致时拒绝生成而不是悄悄错位。
文案的两种写法都支持
# 写法 A:带时间码(一行一段,适合照着原视频节奏切)
[00:00 - 00:06] Token 流向了哪里?中国 55% 的算力,变成了短剧
#hook
#kicker: 开场 · 2026
# 写法 B:不带时间码(靠指令行收尾,适合先写文案再配音)
Token 流向了哪里?中国 55% 的算力,变成了短剧
#hook
#kicker: 开场 · 2026
两种写法都要切出同样的幕数与版式。#hook / #question 这类指令行
属于上面那一段,出现即表示那段写完了——这一点没处理时,
整篇会被并成 1 段再按句号硬切,结果是「第 3 幕念第 4 幕的话」(2026-10-29 修复)。
> 写法 A 不能和 --voice 同时用(手写时间码与配音时长必然冲突),
> 脚本会直接报错让你二选一。
> 「以前 / 现在」只在句首或标点后才算逻辑词。「你现在看的这条视频」里的
> 「现在」是时间词,按纯字符串匹配会白降 6%——踩过。
停顿怎么定(按语义距离,不是按幕类型)
| 关系 | 停顿 | 依据 |
|---|---|---|
| 对比对内部(以前 → 现在) | 0.12s | 对比的力量来自紧邻,停久了像换话题 |
| 金句之后 | 0.62s | 给回味 |
| 收尾 | 0.50s | 收得住 |
| 开场钩子 | 0.40s | 钩子拖不得 |
| 常规 | 0.28s | 画面切换不被察觉即可 |
上表是基准值,可用 --gap-scale 整体缩放(默认 1.0,范围 0.3~3.0):
python scripts/gen-layout.py 文案.txt --out layout.json --gap-scale 0.8 # 更紧凑(带货/快节奏)
python scripts/gen-layout.py 文案.txt --out layout.json --gap-scale 1.3 # 更从容(知识长视频)
只影响幕间留白,不改配音时长。实际用值写进 layout.json 的 meta.gap_scale
(默认不写,改过才留痕),事后能查「这条片子为什么喘」。
> 以前这五个值是写死的常量,只能改代码,改完就忘了改回来——现在从命令行给。
另外有 1.2 秒最短幕时长兜底:短句幕(如「我换了个顺序」6 字)配音只有 0.66s,
不兜底会一闪而过;但补到 1.5s 反而显出 0.84s 的空白,1.2s 刚好。
自动体检(每次出片都会打,分三级)
语速与停顿诊断:
幕 版式 字 语速 字/秒 停顿 依据
S3 question 26 +33% 5.1 0.28 长句·罗列
S6 points 29 +31% 6.7 0.12 长句·逻辑词
S7 points 22 +39% 6.3 0.28 逻辑词
...
平均语速 381 字/分钟(知识类口播合理区间 350~420) ✓
停顿占比 13.1%(合理区间 12~20) ✓
单幕语速 全部在合理区间内(长句 ≤8.5、短句 ≤10.5 字/秒) ✓
[自动修] 2 处已处理,无需干预
· 第 4 幕补到 1.2s 下限
[提示] 1 处建议看一眼(不影响出片)
三级怎么分(以前所有异常都长一个样,看的人分不清要不要停下来):
| 级别 | 含义 | 谁处理 |
|---|---|---|
| 自动修 | 已经改好了(短幕被补到 1.2s 下限) | 不用管 |
| 阻断 | 数据本身有问题(幕时长短于配音),会音画错位 | 必须回去改;gen-layout.py 退出码 1,pipeline.sh 会接住 |
| 提示 | 语速偏快/偏慢、停顿占比超区间 | 建议看一眼,不影响出片 |
停顿占比超区间时,提示会按当前值反推该给多少 --gap-scale(不是写死一个数)。
已经调到上限还超,会直说「已到本参数能调的上限,剩下的要靠加长文案或换更慢的音色」。
三个判定区间的来源:知识类短视频实测集中在 350~420 字/分钟;停顿占比
低于 12% 会赶、高于 20% 会拖;单幕阈值要按字数分档——短句的起音/收音
占比大,字/秒天然偏高,用统一 8.5 去卡只会把所有短句拖慢。
超限会自动校正(不是只报警)
分段变速是逐段的局部决策,逐段看都合理,合起来可能整体偏快。
实测一份 6 幕的短文案,每幕都命中「短句 +5%」,叠加后全片跑到 446 字/分钟。
所以 tts.py 合成完会做一次闭环校正:
语速校正 实测 446 字/分钟超出 415,基准降到 +35% 重合成
语速校正 基准 +35%,实测 415 字/分钟 ✓
做法:先按 tune_base() 预估一个初值(cpm ≈ 4.4 × (1+rate/100) × 60),
合成后用实测时长复核;超 415 就按 CPM_MAX / cpm 的比例降基准重合成,
最多两轮。
> 为什么不能只用预估:短句的起止音占比大,字/秒是非线性的。同一份文案
> 预估 372、实测 446,差了 20%。预估只配做初值,判定必须用实测。
代价是超限时多一轮合成(约 3 秒),换来的是「不用人回头调」。
三条硬规则:
- 文案里有手写时间码时
--voice直接报错,不许默默二选一。
配音时长和[00:06 - 00:13]几乎不可能一致,随便选一个都是错的。
(从视频转写来的文案天然带时间码——那种场景本来就有原声,不该再配音。) - 段数 / 每段文字必须与 manifest 一致,否则报错。文案改了就要重配,
不然会出现「第 3 幕念第 4 幕的话」,而且渲染完才发现。 - 单段超过 10s 会告警。画面要撑 10 秒不动,
check-density会报「长镜头无变化」。
处理办法是手动断句(加空行),不要靠自动拆——自动拆会打乱版式指令的归属。
字幕走配音的时间戳,不走 Whisper:TTS 合成时引擎已经返回每句的起止时间,
那是生成时就已知的精确值;再跑一遍语音识别等于把确定的东西猜一遍,还可能听错字。
make_srt.py 会:
- 把段内相对时间 + 该幕
t0换成绝对时间 - 超过 20 字的长句按标点二次切,时间按字数比例分配
- 合并 <12 字的碎片(引擎常把一句话按停顿拆成两段 boundary,不合并会九字一屏地闪)
- 中文之间的半角逗号转全角(引擎把停顿写成
,,中文字幕里很难看)
已知限制:
- 中文只返回句子级时间戳(SentenceBoundary),没有逐词。够用(段级 + 句级字幕),但做不了逐字卡拉 OK。
- 数字/英文读法由微软引擎决定,API 不返回实际发音。遇到读错就改文案里的写法
(如「55%」写成「百分之五十五」),不要指望脚本能猜。
- edge-tts 是微软非官方接口,无 SLA。挂了就退回剪映「文本朗读」+ 无字底片。
③ Remotion 动态排版
python scripts/gen-remotion.py layout.json --out ./remotion --install # 默认 custom:风格由文案推导(16:9 横屏)
python scripts/gen-remotion.py layout.json --out ./remotion --style token # 定版浅色 20 版式(要稳定产出时)
python scripts/gen-remotion.py layout.json --out ./remotion --style mint # Mint Dark:深色电影感 + 小公仔 + 底部字幕
python scripts/gen-remotion.py layout.json --out ./remotion --style mint --no-caption # 不烧录底部字幕(外部队配音/剪映字幕时必须加)
python scripts/gen-remotion.py layout.json --out ./remotion --style basic # 旧 8 版式(非常规画幅兜底)
默认是 custom,且默认画幅是 16:9 横屏(gen-layout.py --aspect 缺省即 16:9)。
这条默认是用户 2026-10-29 明确定的:
| 场景 | 行为 |
|---|---|
不写 --style,16:9 |
走 custom,pipeline.sh 自动生成 design.json |
不写 --style,9:16 |
自动退到 token-v 并打印提示(custom 只按 16:9 排过) |
写 --style custom,9:16 |
直接报错,让你选清楚,不偷偷换 |
写 --style token/mint/wave |
照旧走那套模板,与 custom 无关 |
custom 的完整工作流和六条《执行准则》见上文 custom(按文案定制)怎么用。
什么时候用 --style mint:想要抖音/视频号那种「深色电影感 + 角色 IP + 逐句字幕」的口播片
(学习自参考片《WorkBuddy 6 个 Skills》)。它自带右下角小公仔(呼吸/眨眼/按场景换表情)、
章节条、玻璃卡片、底部字幕;版式为 hook / question / metric / cards / vs / rows / quote / outro。
--style token 产出定版 Token 视觉:内置模板 assets/remotion-token/,20 种版式,版式由
scripts/build_token_data.py 按文案特征自动挑(改 src/data.ts 的 kind 可手调)。
默认的 custom 走 assets/remotion-custom/,版式由 src/design.json 的 scenes[].layout 定。
合成 id 统一 Main;抖音封面是 Cover(1080×1440),两套模板都有。
Remotion 侧文档分两层:本站模板结构与踩坑在 references/remotion-project.md(自带完整),
官方规范(v4.0.538)入口是 references/remotion.md——
官方副本 remotion-official/ 有 152 个文件、会撑爆上传的体积/文件数限制,分发包里不含,
首次使用时跑 bash scripts/pipeline.sh sync-remotion 拉取(需先装 remotion-best-practices 技能)。
⏸ 检查点:生成后不要自动渲染,先把审阅台 REVIEW_URL 给用户,等确认。
③b 逐句字幕(可选,默认关闭)
画面文案是概括(标题 + 卡片 + 列表),字幕是逐字。两者来源与节奏都不同,所以分开管理:
bash scripts/pipeline.sh captions ./项目目录 # 自动找 02-script/字幕.srt
bash scripts/pipeline.sh captions ./项目目录 --srt 我的字幕.srt # 指定文件
bash scripts/pipeline.sh captions ./项目目录 --on # 打开显示(theme.json)
bash scripts/pipeline.sh captions ./项目目录 --off # 清空并关闭
- 产物
src/captions.ts,格式与@remotion/captions的Caption一致(毫秒),写成内联字面量——
这样 Remotion 的 Caption 编辑器才能回写;走fetch(JSON)就变成「能看不能改」。 - 单条超长会按标点二次切分,时间按字数比例分配,不重叠。横屏上限 18 字、竖屏 12 字(按画幅自动)。
- 视觉是浅蓝青底的白色胶囊 + 数字/引号关键词青色高亮,与 Token 主题一致。
- 默认关闭:字幕烧进像素后,外部再配一遍音就会重复且挡画面。要开就
--on。 - 组件文件叫
Subtitles.tsx而不是Captions.tsx:Windows 文件名大小写不敏感,
./Captions会解析到captions.ts自己,打包报No matching export(已踩过)。
④ 渲染成片
bash scripts/pipeline.sh render <工作目录> --go # 自动找空闲端口 + 探测浏览器(--go = 已确认)
Remotion 直接出片(几十帧/秒);scripts/render.js 是 HTML 逐帧截图兜底(默认 JPEG,约 14 帧/秒)。
优先 Remotion 路径,别默认走 render.js。
⑤ 剪映草稿
# 成片已含文字 → 只建视频轨(用于加配音/BGM/字幕/剪辑)
python scripts/make-draft.py --out "D:/JianyingPro Drafts/我的项目" \
--from-layout layout.json --bg "<成片.mp4>" --no-text
# 想要可改字的文字轨 → 背景必须是无文字底片(Remotion 侧 showText=false 另渲)
python scripts/make-draft.py --out "D:/JianyingPro Drafts/我的项目2" \
--from-layout layout.json --bg "<无文字底片.mp4>"
必须先判断背景里有没有烧录文字:成片当背景就不要建文字轨,否则文字压文字。
必须直接生成到剪映草稿根目录(draft_fold_path 是绝对路径,事后拷贝会导致路径错)。
细节见 references/jianying.md。
⑥ 抖音封面(发出去之前最后一步)
bash scripts/pipeline.sh cover <工作目录> # 抽帧候选 + 大字报版式,一次出齐
bash scripts/pipeline.sh cover <工作目录> --title "自定义标题" --hot "55%"
bash scripts/pipeline.sh cover <工作目录> --no-frames # 只出版式封面
bash scripts/pipeline.sh cover <工作目录> --reset # 重新猜文案(清掉手改)
产物在 <工程>/out/cover/,打开 cover-sheet.html 挑图,右键另存 → 上传到抖音创作服务平台的「选择封面」。
| 产物 | 用途 |
|---|---|
cover-1080x1440.png |
3:4 · 信息流(推荐),抖音默认按这个比例裁切展示 |
cover-1080x1920.png |
9:16 · 全屏入口(模糊背景铺满 + 母版居中羽化,无接缝) |
cover-1080x1080.png |
1:1 · 搜索/话题页 |
frame-01..06.png |
从成片抽的候选帧(横屏保持 16:9,竖屏裁 3:4) |
cover-sheet.html |
对比图,一眼挑 |
为什么不能直接从成片截一帧当封面:抖音信息流按 3:4 居中裁切,横屏成片会被切掉左右;
竖屏成片虽然能裁,但视频里的标题字号是按「全屏观看」定的,缩到 200px 宽的缩略图就看不清了。
封面需要的是「大字报」,与视频版式是两种设计——所以单独做了 Cover Composition(1080×1440)。
横屏素材不做 3:4 硬裁(试了三版后的结论):16:9 = 1.78、3:4 = 0.75,差 2.4 倍,
任何「填满 3:4」的裁法都必然丢掉 58% 的横向内容。实测三种都切字:
居中裁 → 「这 500 万亿 Token 到底去哪了」变「到底去哪了」;模糊补白 → 画面只占中间 45%;
放大填满 → 「这 500 万亿」变「0 万亿」。所以横屏候选帧保持原比例,要 3:4 就用大字报版式。
文案优先级:命令行参数 > src/cover.json 里已有的值 > 自动猜。
自动猜按「适不适合做封面」打分(短句 + 对比词「不是…而是」+ 数字 + metric/vs 版式),
不取第一幕——第一幕通常是钩子,做封面太抽象。手改的文案不会被脚本覆盖,想重猜加 --reset。
一条龙
bash scripts/pipeline.sh all 文案.txt ./我的项目 # 从文案开始,跑到排版就停
bash scripts/pipeline.sh all "<链接>" ./我的项目 # 从链接开始:下载→转写→排版→停
bash scripts/pipeline.sh render ./我的项目 --go # 弹窗确认后再出片 + 草稿
bash scripts/pipeline.sh all 文案.txt ./我的项目 --auto-render # 一次跑完(慎用)
<工作目录>/
01-source/ 视频、封面、音频、info.json ← ①
02-script/ 文案.txt、字幕.srt ← ②
03-layout/ layout.json、remotion/ ← ③
04-video/ 成片.mp4 ← ④
05-draft/ 剪映草稿 ← ⑤
manifest.json 产物清单(路径/大小/场景数/版式分布)
| 常用选项:`--aspect 16:9 | 9:16 | 1:1、--fps N(默认 30)、--model small |
medium`、 |
--script <文件>(跳过下载转写)、--no-transcribe、--no-render、--no-draft、 |
|||
--draft-dir <目录>、--reuse。 |
文案格式
纯文本,三种可选标注。两种写法别混用,混了不会报错,只会让版式静默错位。
A. 有时间码(从视频转写来的文案就是这样)——指令写在时间码之后的任意位置:
[00:23 - 00:49] 上线首月 PC 端访问量 885 万,7 月月活 1115 万
#kicker: 第二层 · 本质
#title: 它不是聊天机器人,是会干活的 AI
#note: 这一关讲清楚「干活」和「回答」的区别
B. 没有时间码(自己写的稿子、配音模式)——指令必须写在段落开头:
#hook
#kicker: 开场 · 2026
你现在看的这条视频,没有一个字是我剪的。
#quote
稿子写完,剩下的它自己做。
| 标注 | 作用 |
|---|---|
[00:23 - 00:49] |
时间码;不写则按 4.5 字/秒估算。也支持 00:23 --> 00:49、【00:23-00:49】 |
#data #compare #steps #quote #points #hook #question #outro |
强制指定版式(只有这 8 个,写 #rows 之类无效,会退回自动判定) |
#kicker: / #title: / #sub: / #note: |
左上角标签 / 覆盖标题 / 副标题 / 补充说明 |
**文字** |
强调(渲染成主色) |
> ⚠ 指令归属规则(踩过):段落靠空行切分,指令属于「它出现时正在编辑的那一段」。
> - 有时间码时,时间码会立刻新开一段,所以指令写在时间码后面任何位置都归这一段。
> - 没有时间码时,文字 + 空行 + #hook 会先在哪?——空行已经把上一段归档了,
> 所以 #hook 会挂到下一段,上一段变成无版式(自动判定)。
> 正确写法是「#hook 在前,文字紧跟其后」。
> - 写错的后果很隐蔽:不报错、不警告,只是版式分配和你预期的不一样。
「执行全部」这类指令怎么理解(防歧义,2026-10-28 用户提出)
审阅台右侧有「自定义待执行」,那是写给 Agent 的队列,不会自动跑。
用户写完常常只说一句「执行全部」,这句话本身有歧义——可以指:
跑待办、重跑整个流程、或者渲染出片。
规则:只要上一轮对话提到过审阅台 / 自定义 / 待办,这些说法一律先查待办队列。
| 用户说 | 正确理解 | 错误理解 |
|---|---|---|
| 「执行全部」「全部执行」「跑待办」「按审阅台来」「那些自定义内容」 | 跑 pipeline.sh notes <项目>,逐条执行 |
❌ 渲染成片 ❌ 重跑 all ❌ 重新走查 |
| 「执行审阅台改动」「执行审阅台」「审阅台确认过了」 | 读 review/APPLY_REQUEST.json,按 notes[] 逐条执行 |
❌ 启动审阅台服务 ❌ 只跑 notes |
审阅台「确认修改并执行」是正式申请(2026-10-31)
审阅台顶部有个按钮「确认修改并执行 · N 条」。用户点了它,就等于签了一份带时间戳的工单:
服务端把待执行批注写进 <工程>/review/APPLY_REQUEST.json,同时把指令「执行审阅台改动」拷进剪贴板。
用户回到对话框粘贴这句话,就是让你去执行那份文件。
接手时的标准动作(顺序不能颠倒):
# 1. 先看有没有申请文件,以及它比上次处理的是否更新
cat "<工程>/review/APPLY_REQUEST.json"
# requestedAt ISO 时间戳 —— 比上次执行的更新,才是新申请
# notes[] 要执行的批注(已排除执行过的)
# applied true 表示上一份已经处理完
# overrideCount 当前的文案改动条数(顺带核对)
# 2. 逐条执行 notes[](改 Video.tsx / data.ts / 工程配置)
# 3. 执行完必须 ack,页面上的卡片才会变成「✓ 助手已执行这份清单」
curl -s -X POST "http://localhost:<审阅台端口>/api/apply/ack"
notes[]为空但overrideCount > 0:说明用户只改了文案没写批注,改文案已由src/review.overrides.json
自动生效,直接ack即可,不要当成「没活儿」。- 找不到服务(端口已关):仍要执行
notes[],但 ack 会失败——这时告诉用户「改动已做完,
下次开审阅台时卡片状态不会自动更新」,不要静默跳过。 appliedIds里的 id 不要重复执行:那是已经做完的;用户重新编辑某条批注时服务端会自动把它摘出来。requestedAt没变:说明是同一份申请,已经处理过,不要重复执行。
APPLY_REQUEST.json 与 notes.json 是同一批数据的两个视图:前者是「用户按了确认」的快照,
后者是实时批注。两者都在时以 APPLY_REQUEST.json 的 notes[] 为准(它已去重)。
标准动作:
bash scripts/pipeline.sh notes "<工作目录 | Remotion 工程>" # 1. 读队列(会同时报申请状态)
# 2. 逐条执行(改 Video.tsx / data.ts / 工程配置)
bash scripts/pipeline.sh notes "<项目>" --clear # 3. 执行完清空
curl -s -X POST "http://localhost:<端口>/api/apply/ack" # 4. 回执(让页面卡片变已执行)
bash scripts/pipeline.sh review "<项目>" # 5. 重建审阅台,让用户看
- 有待办就执行待办,不有待办才问「你是想跑完整流程还是渲染」。
- 渲染永远单独弹窗确认(第 7 条),不能把「执行全部」当成渲染授权。
pipeline.sh review结束时会自动报「还有 N 条待执行」,别忽略这一行。
强制工作流:先给「所见即成片」的预览,确认后才出片
> ⚠ 渲染必须弹窗二次确认(第 7 条):即使用户在对话里已经说了「出成片 / 渲染」,
> 也必须先弹窗问一次「是否现在开始渲染」。口头授权不算授权。
用户 2026-10-21 明确要求,每次都必须按这个顺序;2026-10-26 追加:走查默认用审阅台。
- 先起分镜审阅台(
pipeline.sh review,把REVIEW_URL给出来)——这是走查首选:
每幕用<Player>实况播放(不是静帧),右侧表单直接改文案,改动写回
src/review.overrides.json;src/data.ts会自动合并它,所以「审完直接 render」就是审过的那版,
不存在「确认的和渲出来的不是一回事」。零渲染成本,改二十轮也不花时间。
这条链路必须自检:pipeline.sh review的 ④ 步会跑node review/check-wiring.mjs,
确认src/data.ts真的 import 了review.overrides.json;不通过会直接报错退出。
2026-10-26 踩过:生成器读review.overrides.json、审阅台却写overrides.json,
保存一切正常、overrides 也确实写盘,但渲染读的是原数据 —— 审了白审,比没接入更糟。
用户想留结构性修改意见(「这页改成三张卡」)时用每幕的「自定义改动」,
Agent 用node review/list-notes.mjs读走、改完--clear收工。
审完可直接在页面右下角点「渲染成片」(本地补丁提供/api/render,输出到工程out/)。
历史记录会自动轮转:review/history.jsonl每条含整幕快照,官方只追加不裁剪,
长期用会涨到几十 MB 且前段永远读不到(前端只取最近 40 条、回滚只查最近 400 条)。
本地补丁改成超 2000 条保留尾部 + 原子替换(先写.tmp再 rename,防止写到一半崩了毁掉历史)。 - 整片预览就在审阅台最上面——常驻的播放器,播整片,和成片同一套组件。
判断「整片节奏」用它。它就是个普通播放器:默认不自动播放,
声音和音量全用播放器自带的控制条(别预置静音、别藏showVolumeControls)。
下面那套静音限制只针对分段卡片,不要套到整片上(用户 2026-10-28 明确)。
每张分段卡片默认静音,卡上有 🔊 开关(SceneOnly 的 <Audio startFrom/endAt>
把音轨裁到该幕区间)。需要听某一句时单独打开——十六张卡同时出声会糊成一片,
而且分不清是谁的。点任一张卡的播放,会自动暂停其他卡。
声音三条硬规矩(用户 2026-10-28 明确要求,改审阅台时必须守住):
1. 分段卡默认静音:打开页面、滚动、点播放都不出声;
想听只能手动点卡片的 🔇 → 🔊。整片预览不受这条约束。
2. 卡片之间只允许一处出声:开一张卡的声音,其他卡全部静音。
3. 卡片离开视野自动停:滚出屏幕就 pause + 静音,按钮复位成 ▶ / 🔇。
看不见的卡片不许还在响。(整片预览不参与,保持普通播放器行为。)
实现上有三个坑(都踩过,别改回去):
- 静音要走 PlayerRef.mute()/unmute(),不要直接改 <audio>.muted——
Player 每帧按自己的状态重设 muted/volume,手改会被覆盖,表现为「点了静音还在响」。
- 「离开视野」用滚动时按 getBoundingClientRect 扫描 + 700ms 心跳,
不用 IntersectionObserver:卡片会被整张重建(改文案/撤销),
观察器会漏掉重建后的那张,漏掉就是「看不见还在响」。
- redrawScene() 换卡之前必须先 pause + 静音旧播放器:React root 挂在
脱离文档的节点上时,画面没了声音还在跑,页面上根本没有按钮能关它。
时间轴草图(layout.html)已整步移除(2026-10-28 用户要求):
它是另一套简化实现,颜色/版式都和成片不一样,看了反而误导。
要看画面只有审阅台(带整片预览 + 逐幕实况),不再生成任何 HTML 草图。
3. 自适应缩放必须用「占位盒」写法:transform: scale() 不改变布局占位,必须回填
WRAP.width/height,否则父容器按 1920×1080 撑开后会把画面裁掉(用户只看到中间一块)。
完整公式与 ResizeObserver 兜底见 references/decisions.md。
4. 交付前跑 pipeline.sh selftest(改过技能时),成片自身的校验交给 render。
5. 不要自动启动 Remotion 编辑器(2026-10-28 用户要求):交付时不要跑
pipeline.sh studio / preview,也不要在收尾提示里引导用户去开。
用户明确说要逐帧调参时,才把命令给他。
6. 等用户确认,未经确认不得渲染成片,也不要「先渲一版给你看看」。
7. 渲染前必须弹窗二次确认(硬规则,2026-10-28 用户明确要求):
不管用户在这条消息里写了什么——「出成片」「渲染吧」「直接渲」「不用再问了」——
一律先弹窗问「是否现在开始渲染」,拿到「现在渲染」才动手。
对话里的口头授权不算授权,只有弹窗里点的那一下算。
适用范围(只要产出 mp4 就算):
- bash scripts/pipeline.sh render <项目>
- bash scripts/pipeline.sh all ... --auto-render
- 直接调 remotion render(包括只渲几十帧的冒烟验证)
- 审阅台右下角的「渲染成片」按钮由用户自己点,不在此列(那是用户亲手触发)
不受限:单帧 still(出 png)、cover、review、studio。
弹窗至少给两个选项:「现在开始渲染」 和 「先看预览 / 再等等」。
用户选后者就停下,不要换个说法再问一遍。
> 为什么连明确授权也要再问:2026-10-27 用户说「展现能力」,我理解成「交付成品」,
> 直接渲了 1064 帧才发现跳过了整个走查流程。渲染是不可逆的时间成本
> (改一版要重渲几分钟),而审阅台改一版是零成本。
> 把最后一道闸交给用户在弹窗上点,比让我判断「他是不是真的想现在渲」可靠。
- 确认后才出片,并做抽帧 / 音轨 / ffprobe 校验。
> 历史上真踩过这三条(案例与复盘见 references/decisions.md):直接给 mp4、缩放裁切、预览与成片不是一套。
交付前必查
能自动化的已经进了 pipeline.sh render(ffprobe 核对时长/帧数/文件大小,失败非零退出);剩下仍需人工确认:
- 场景数 = 文案段数(曾因参数错位静默丢幕)。
- 目标字体在
document.fonts里为loaded(路径错会静默回退系统字体)。 - 抽帧看每幕 edge variance > 400(非空白);空幕多半是「版式与数据不匹配」,见下节。
- 给用户看画面只用审阅台;不要另造简化版 HTML(和成片不是一套画面)。
> 实现载体:定版风格 = assets/remotion-token/(Video.tsx 20 版式),
> gen-remotion.py --style token 铺出来并生成 src/data.ts。
> 原始样片工程在 D:/ai renwu/video-to-layout-artifacts/token-economy-remotion/,只作回归基线。
> 音频:音轨丢进 public/audio/,--audio audio/mix.wav 自动挂上。
可视化拖拽改版式
bash pipeline.sh edit <工作目录> # 浏览器里直接拖画面元素,保存即写入 overrides.json
Remotion Studio 不能拖元素(代码优先),官方能拖的是付费的 Editor Starter。
本技能自带一个轻量编辑器:可拖标题/数字/卡片/左右栏/列表/金句,字幕与小公仔是全局的;
能改 dx/dy/缩放,不能加元素或改文字。改完 npx remotion render 即生效。
细节见 references/remotion-project.md。
画面「空不空 / 动不动」自检
bash scripts/pipeline.sh density <工作目录> # 全片逐幕采样,缺省判据:密度≥3.0、长镜头变化≥0.6
bash scripts/pipeline.sh density <工作目录> --samples 5 # 采样更多时间点
bash scripts/pipeline.sh density <工作目录> --concurrency 6 # 渲染+分析并发(默认 4)
输出每一幕的 密度(边缘均值,越低越空)和 变化(同幕多点静帧的 PSNR 差,越低越"死画面")。
低于阈值直接退出码 1,可接进 CI。
> 为什么需要它:判「页面太空」不能靠肉眼扫缩略图。实测有两个典型病灶——
> ① 单幕信息太少(15–29s 那幕原本只有一个问号和几个字,密度 1.51);
> ② 长镜头只在前 3 秒有动画(17–50 秒的幕在入场动画结束后完全静止,变化值接近 0)。
> 定版模板已为此加了「分批揭示 + 常驻微动层」,见 references/remotion-project.md。
静帧渲染的并发(density / cover 共用)
两个脚本都走 scripts/lib/still-harness.js:一次 bundle、一次 selectComposition,
之后所有静帧并发渲染(默认 4 路)。
| 并发 | 密度(8 幕 × 2 采样) |
|---|---|
| 1 路 | 30.4s |
| 4 路 | 21.2s |
4 路是实测甜点:再高就被浏览器启动开销吃掉。--concurrency 可调。
> 坑 1:并发时不能给每个 renderStill 传同一个 port。每次渲染会自己起一个
> HTTP server,第二个就报 You specified port 3323 ... but it is not available。
> 串行时传 port 没问题,所以这个坑只在加并发后才暴露。harness 里已改成不传 port。
> 坑 2(会吃满 C 盘):@remotion/bundler 的 bundle() 只 mkdtemp、不删,
> 删除是 CLI 那一层做的(dist/setup-cache.js 返回 cleanup),直接调库就绕过了它——
> 每跑一次静帧留一个 60~90MB 的目录,实测攒到 396 个 / 20.4 GB。harness 现在在
> onDirectoryCreated 就登记(打包中途抛错也能清),close() 真删,
> 并用 process.on('exit') + SIGINT/SIGTERM 兜住 process.exit(1) 这条不走
> finally 的路径。安全阀:只删系统 Temp 下、basename 以 remotion-webpack-bundle-
> 开头的目录;排查时传 keepBundle: true 保留。
空幕定位(版式与数据不匹配)
某几秒画面「只有标题、没有内容」,多半是版式与数据不匹配(换版式时没带数据)。
生成阶段会打印 [错误] 版式与数据不匹配,pipeline.sh selftest 也有对应断言;
已经出片的,用下面这条定位到具体幕:
bash pipeline.sh density <工作目录> --samples 3 # 密度异常低的幕 = 空幕嫌疑
改技能后自测
bash scripts/pipeline.sh selftest # 全量,约 8 分钟(含两次真实生成 + 渲帧)
bash scripts/pipeline.sh selftest --quick # 跳过渲帧/ffprobe,快一些
覆盖链路:文案解析(8 版式齐全、标题不截断「500 万亿」、steps/points ≥3 条、时间轴首尾相接)
→ 预览页 7 尺寸自检 → Token 工程 data.json 断言 → 渲 2 帧 + ffprobe 校验帧数/时长。
当前共 106 项。其中大部分是「踩过的真缺陷」的回归钉子,改代码时不要删,
它们各自对应一次真事故:无时间码文案必须切出同样的幕数与版式、
layout 必须沿用配音时间轴、--gap-scale 必须真的改变总时长、
--no-caption 必须在 custom 下真的关掉字幕、降级必须报出来、
空幕必须阻断而逃生口必须放行、音轨必须从工作目录沿用……
自测目录 D:/ai renwu/video-to-layout-artifacts/selftest,复用 node_modules,第二次起很快。
改了 gen-layout.py / gen-remotion.py / 模板 / pipeline.sh 之后,先跑它再交付。
改代码前先看这三条(都是同一个坑的三种长相)
这个技能的漏洞反复呈现同一个形态:代码看起来对,但那条路根本没被执行到。
改任何东西之前,先确认它不在下面三种陷阱里:
-
加了参数,但没传进所有风格分支 → 参数静默失效。
gen-remotion.py有 5 个风格分支,每个都要单独传。--no-caption就是这样坏的:
只改了 mint 认的Root.tsx,而默认的 custom 读theme.json,于是「不报错也不做事」。
检查法:grep -c "参数名" gen-remotion.py,看是不是每个分支都有一份。 -
加了校验,但写在一个
continue之前的分支里 → 校验永远跑不到。
validate_scenes的降级检查原先写在 token 分支末尾,而 custom 分支开头就continue,
于是默认风格下「数据撑不住被降级」一路绿灯。
检查法:加完校验,构造一个应该被拦下的坏数据跑一遍,看是否真的 exit 1。 -
加了断言,但另写了一份等价逻辑 → 断言和被测代码各自演进,等于没测。
render的走查标记检查内联在pipeline.sh里,selftest 只能照抄一份,
改了一边另一边不知道。改成抽出report_review_done()让 selftest 抽真实函数调用。
检查法:断言里如果出现第二份「判断逻辑」,就该把逻辑抽成函数。
还有一条环境层的:Windows 上 rm -rf 会「部分失败但返回 0」,尤其遇到被占用的文件。
自测里的删除统一走 rmrf()(内部用 node fs.rmSync,绕开 shell 层守卫并复查残留),
不要直接写 rm -rf——否则下游只会报「目标已存在」,排查方向被彻底带偏。
字体不随技能包分发
技能市场对上传包限制「≤200 个文件 / ≤10MB」且拦二进制字体文件,
所以 assets/fonts* 里的 .ttf / .woff2 不进包,只在本地开发时用。
三处 @font-face 的字族栈都带系统兜底("Noto Sans SC", "Microsoft YaHei"),
缺字体时不会报错,只会静默回退系统黑体——文字能看,但那个「圆润」的气质会没。
想恢复原样,把字体放回对应位置即可(文件名必须一致):
| 文件 | 放到 | 说明 |
|---|---|---|
ZCOOLKuaiLe-400.ttf |
assets/fonts/ |
站酷快乐体,SIL OFL 1.1,圆体展示字 |
NotoSansSC-400.woff2 / NotoSansSC-900.woff2 |
assets/fonts-wave/ |
思源黑体正文与重标题 |
JetBrainsMono-700.woff2 |
assets/fonts-wave/ |
等宽,数字与编号 |
三款都是 SIL OFL 1.1,可从 Google Fonts 官方仓库取:
<https://github.com/google/fonts>(ofl/zcoolkuaile、ofl/notosanssc、ofl/jetbrainsmono)。
参考文件
| 文件 | 内容 |
|---|---|
ROADMAP.md |
待办清单:已知但未修的问题(阈值分档、token 死画面、目录结构统一…),每条带实测依据 |
CHANGELOG.md |
版本变更;修过什么坑、为什么这么修 |
references/remotion.md |
Remotion 入口:官方规范索引(v4.0.538)、官方 vs 本站写法差异、升级流程、命令速查 |
references/remotion-project.md |
本站模板:两套风格规格、版式表与自动选版式、参数面板、动效装饰、渲染器、全部踩坑 |
references/remotion-official/ |
官方最佳实践副本(需 sync-remotion 生成);打包只保留 remotion-review-board/,其余官方文档不进包 |
references/jianying.md |
第 ⑤ 步:草稿结构、背景轨、文字动画、草稿位置 |
references/troubleshooting.md |
常见报错、已知限制、视频号解析与限流 |
references/voiceover.md |
可选:配音 + BGM 工序(用户明确要配音时才用) |