srt-remotion-video
SRT Remotion Video - 主流程编排
将 SRT 字幕文件转换为 Remotion 视频的完整工作流。
工作流概览
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────────────┐ ┌──────────┐
│ 获取 SRT │ → │ 依赖预检 │ → │ 项目初始化│ → │ 生成分镜 │ → │ 并行 Creator 规划实现 │ → │ 合成视频 │
│ 文件路径 │ │ / 首次安装│ │ │ │ │ │ scene-plan + code │ │ │
└──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────────────────┘ └──────────┘
项目目录结构
<skillRoot>/
├── SKILL.md
├── template/
├── references/
│ ├── storyboard-parser.md
│ └── scene-component-creator.md
└── scripts/
├── ensure-template-deps.js
├── init-project.js
├── generate-storyboard.js
├── generate-creator-scenes.js
├── generate-scenes-registry.js
├── scene-registry-utils.js
├── validate-project.js
└── validate-scene-plan.js
<srtDir>/
├── your-file.srt
└── remotion-video-projects/
└── {yyyy-mm-dd-hh-mm-ss}/
Path Contract
主流程和所有 SubAgent 统一使用以下绝对路径约定:
skillRoot: 当前srt-remotion-videoskill 目录的绝对路径templateRoot:{skillRoot}/templatereferencesRoot:{skillRoot}/referencesscriptsRoot:{skillRoot}/scriptssrtPath: 用户提供的 SRT 文件绝对路径projectBaseDir:{dirname(srtPath)}/remotion-video-projectsprojectRoot: 当前项目实例目录,格式为{projectBaseDir}/{projectName}/
强制要求:
- SubAgent prompt 中必须写入展开后的绝对路径,不要只传变量名
- 阶段协议文档只能从
referencesRoot读取 - 脚本只能从
scriptsRoot执行 - 所有运行态状态必须由主 Agent 显式传递
执行流程
步骤 0: 获取 SRT 文件
- 询问用户 SRT 文件路径
- 如果用户给的是相对路径,主 Agent 必须先自行判断该文件是否存在
- 若存在,主 Agent 必须先解析为绝对路径,再将解析后的绝对路径作为
srtPath - 若不存在,必须明确反馈用户路径无效,并要求提供正确路径
- 后续所有步骤统一使用最终确认过的绝对路径
srtPath
步骤 1: 依赖预检与项目初始化
关键:模板始终从 skill 内部复制,所有工作在字幕目录下的独立项目目录进行。
1.0 依赖预检(首次使用时自动安装)
执行:
node "{skillRoot}/scripts/ensure-template-deps.js" "{templateRoot}"
脚本会:
- 检查
{templateRoot}/package.json和{templateRoot}/package-lock.json - 检查模板关键依赖是否已安装
- 若未安装,则在
{templateRoot}下执行一次npm install - 若已安装,则直接跳过安装
- 安装或校验失败时返回明确错误,并停止主流程
1.1 默认行为:创建新项目
执行:
node "{skillRoot}/scripts/init-project.js" --srt-path "{srtPath}"
脚本会:
- 确保
{dirname(srtPath)}/remotion-video-projects/存在 - 创建新的项目目录
remotion-video-projects/{yyyy-mm-dd-hh-mm-ss}/ - 从
{skillRoot}/template/复制模板文件;若模板依赖已安装,则一并复制已安装依赖 - 输出项目信息 JSON
1.2 用户指定项目路径(仅当用户明确指定时)
如果用户明确指定了项目路径,则直接使用该路径作为 projectRoot,跳过默认目录推导。
1.3 记录关键路径
从脚本输出或用户指定路径获取:
projectRootskillRoottemplateRootreferencesRootscriptsRootsrtPath
后续所有步骤都使用这些绝对路径。
步骤 2: 生成分镜脚本
必须使用 SubAgent 执行此步骤。
主 Agent 负责:
- 计算并展开绝对路径:
storyboardReference = {referencesRoot}/storyboard-parser.mdstoryboardScript = {scriptsRoot}/generate-storyboard.js
- 启动一个 SubAgent
- 在 prompt 中写入实际绝对路径值
SubAgent prompt 模板:
你正在执行 srt-remotion-video 工作流的“分镜生成阶段”。
首先读取以下参考协议并严格按其步骤执行:
- {storyboardReference}
输入参数:
- skillRoot: {skillRoot}
- projectRoot: {projectRoot}
- srtPath: {srtPath}
重要:
1. 所有路径都已展开为绝对路径,不要自行猜测
2. 需要执行的脚本位于 {storyboardScript}
3. 完成后必须按参考协议中的“完成后返回”契约,返回结构化结果
主流程必须等待返回结果,并读取 storyboard.json 验证结构正确。
步骤 3: 使用 SubAgent 规划并实现场景组件
读取 storyboard.json,获取所有场景数据。
3.0 前置准备:计算分组
const SCENES_PER_CREATOR = 5;
const sceneCount = storyboard.scenes.length;
const creatorCount = Math.ceil(sceneCount / SCENES_PER_CREATOR);
creatorId 生成规则固定为:
const creatorId = `creator-${String(index + 1).padStart(2, '0')}`;
示例:
- 第 1 个 creator:
creator-01 - 第 2 个 creator:
creator-02 - 第 10 个 creator:
creator-10
主流程必须始终使用该格式,不要省略前导零,不要改成其他命名方式。
3.1 规划 Creator 任务
主流程负责调度:
- 为每个 creator 生成
scenesDataPath = {projectRoot}/scene-plans/{creatorId}.scenes.json - 为每个 creator 计算:
creatorIdplanPath = {projectRoot}/scene-plans/{creatorId}.jsonvalidateScript = {scriptsRoot}/validate-scene-plan.js
- 对每个 creator 执行:
node "{scriptsRoot}/generate-creator-scenes.js" \
"{projectRoot}/storyboard.json" \
"{creatorId}" \
"{SCENES_PER_CREATOR}" \
"{scenesDataPath}"
- 主 Agent 将
{scenesDataPath}的绝对路径写入 SubAgent prompt - 并行启动全部 creator SubAgent
3.2 并行启动所有 Scene Creator
每个 Creator 的 SubAgent prompt 模板:
你正在执行 srt-remotion-video 工作流的“场景规划与实现阶段”。
首先读取以下参考协议并严格按其步骤执行:
- {referencesRoot}/scene-component-creator.md
输入参数:
- skillRoot: {skillRoot}
- projectRoot: {projectRoot}
- creatorId: {creatorId}
- planPath: {planPath}
- scenesDataPath: {scenesDataPath}
- validateScript: {validateScript}
重要:
1. 所有路径都已展开为绝对路径,不要自行猜测
2. 当前 creator 的场景事实源只允许来自 {scenesDataPath}
3. 规划阶段只使用 {scenesDataPath}、{projectRoot}/cartoon-ui-style-guide.css、{projectRoot}/cartoon-ui-style-guide-reference.md 和 {skillRoot}/../remotion-best-practices/SKILL.md
4. 先生成 scene-plan JSON,再执行校验,校验通过后再编写场景组件
5. `beatPlan` 默认一段字幕对应一个 beat;如果相邻 segments 明显属于同一句连续表达,可以合并
6. 合并仅允许发生在相邻 segments 之间,禁止跳跃式组合
7. `beatPlan` 只声明 `segments` 和 `action`;实际时间必须从 scenesData[].segments 的 `relativeStart / relativeDuration` 推导
8. 场景主节奏必须绑定 scenesData[].segments[].relativeStart / relativeDuration
9. 默认保留宿主背景,在透明根层上围绕画面中部或中上区域组织主视觉;不要重建整屏背景
10. 组件接口固定为 React.FC<{ segments: Segment[] }> 且使用默认导出
11. 只负责产出 {projectRoot}/src/scenes/SceneXXX.tsx;若文件不存在则创建,若已存在则仅修改自己负责的场景文件
12. 不要手改 {projectRoot}/src/compositions/Main.tsx 或 generated-scenes.ts
13. 完成后必须按参考协议中的“完成后返回”契约,返回结构化结果
14. `remotion-best-practices` 与当前 skill 同级,固定入口为 {skillRoot}/../remotion-best-practices/SKILL.md
3.3 等待所有 Creator 完成
确认所有目标场景组件文件均已生成。
componentResults仅用于任务完成反馈,不作为最终注册文件组装或总时长计算的真实来源。
步骤 4: 合成视频
4.1 生成场景注册文件
普通视频生成流程不得重写 {projectRoot}/src/compositions/Main.tsx。
模板默认输出规格:1920x1080 / 30fps。
generated-scenes.ts的totalDurationInFrames由generate-scenes-registry.js生成,不要手改Main.tsx中的msToFrames必须使用useVideoConfig().fps动态获取帧率,不能硬编码FPS常量
执行:
node "{scriptsRoot}/generate-scenes-registry.js" \
"{projectRoot}" \
"{projectRoot}/storyboard.json"
运行时契约固定:
- 每个
SceneXXX.tsx都通过默认导出暴露组件 generated-scenes.ts负责保存start、duration、segments、ComponentMain.tsx负责用<Component segments={scene.segments} />把分段数据传给场景组件
4.2 Root.tsx 总时长同步
Root.tsx 是模板只读文件,不需要也不允许在普通流程中重写。
要求:
Root.tsx必须保持从generated-scenes.ts读取totalDurationInFrames- 不要在流程中重复手算或手填总帧数
4.3 校验项目产物完整性
渲染前必须执行:
node "{scriptsRoot}/validate-project.js" \
"{projectRoot}" \
"{projectRoot}/storyboard.json"
校验失败时必须停止流程,不得继续渲染。
4.4 执行渲染
cd "{projectRoot}"
npx remotion render Main out/output.mp4
步骤 5: 完成
通知用户:
- 视频已生成
- 输出路径:
{projectRoot}/out/output.mp4 - 场景数量: N
- 视频时长: X 秒
测试模式与重新渲染
当主视频生成流程(步骤 0–5)完成后,用户可以请求"测试模式"或"重新渲染"。
这两个操作都假定
projectRoot已存在且场景组件已生成完毕。
测试模式
当用户说"测试模式"、"预览模式"或类似表述,并要求添加音频时执行。
TM.0 获取音频文件
- 如果用户已提供音频文件路径,验证文件存在并解析为绝对路径
audioPath - 如果用户未提供音频文件路径,必须向用户询问
- 验证文件存在,若不存在则反馈用户并停止
TM.1 添加音频到时间轴
- 确保
{projectRoot}/public/目录存在 - 将音频文件复制为
{projectRoot}/public/audio.mp3
mkdir -p "{projectRoot}/public"
cp "{audioPath}" "{projectRoot}/public/audio.mp3"
-
修改
{projectRoot}/src/compositions/Main.tsx:- 在 import 行添加
Audio和staticFile:import { AbsoluteFill, Audio, Sequence, staticFile, useCurrentFrame } from "remotion"; - 在
<AbsoluteFill style={{ backgroundColor: ... }}>的直接子级最前面添加:<Audio src={staticFile("audio.mp3")} />
- 在 import 行添加
-
启动 Remotion Studio 供用户预览:
cd "{projectRoot}"
npx remotion studio
TM.2 完成通知
告知用户 Remotion Studio 已启动,音频已添加到时间轴,可在浏览器中预览。
重新渲染
当用户说"重新渲染"、"再次渲染"或类似表述时执行。
RR.0 移除时间轴上的音频
渲染最终视频前,必须确保 Main.tsx 中不存在音频组件:
- 读取
{projectRoot}/src/compositions/Main.tsx - 检查是否包含
<Audio标签 - 如果存在:
- 移除
<Audio src={staticFile("audio.mp3")} />这一行 - 从 import 语句中移除
Audio和staticFile(如果它们不再被其他代码使用)
- 移除
- 如果不存在,跳过此步骤
RR.1 执行渲染
cd "{projectRoot}"
npx remotion render Main out/output.mp4
RR.2 完成通知
通知用户视频已重新渲染,输出路径为 {projectRoot}/out/output.mp4。
高分辨率 / 高帧率渲染
当主视频生成流程已经完成,用户要求生成 4K、60fps 或其他高于默认配置(1080p 30fps)的版本时执行。
这个操作假定
projectRoot已存在且场景组件已生成完毕。
关键原则:设计分辨率与输出分辨率分离
场景组件中的所有元素(卡片、图标、文字等)使用绝对像素值,基于 1920x1080 设计。直接将 Root.tsx 的 width/height 改为 3840x2160 会导致所有元素在画面中占比缩小。正确做法是保持设计分辨率 1920x1080 不变,通过 Remotion 的 --scale 参数放大输出分辨率。
常见错误(禁止使用)
| 错误做法 | 后果 |
|---|---|
| 改 Root.tsx 的 width=3840 height=2160 | 所有场景元素占比缩小一半 |
用 --width 3840 --height 2160 CLI 参数 |
同上 |
| 只改 fps 不改 totalDurationInFrames | 视频只有前半段有内容,后半段空白 |
Main.tsx 中硬编码 const FPS = 30 |
改了 Root.tsx fps 后场景时序错乱 |
HR.0 确认用户需求
解析用户需求为具体的输出参数:
| 用户需求 | Root.tsx 修改 | generated-scenes.ts 修改 | 渲染命令 |
|---|---|---|---|
| 4K / 超清 | 不改 | 不改 | --scale 2 |
| 60fps | fps={60} |
totalDurationInFrames 按比例换算 |
无需 scale |
| 4K 60fps | fps={60} |
totalDurationInFrames 按比例换算 |
--scale 2 |
HR.1 修改帧率(仅当用户要求高帧率时)
- 修改
{projectRoot}/src/Root.tsx中的fps值:
fps={60} // 从 30 改为 60
- 修改
{projectRoot}/src/compositions/generated-scenes.ts中的totalDurationInFrames:
// 帧数 = 原帧数 × (新fps / 原fps)
// 例如 30→60fps: 1572 × 2 = 3144
export const totalDurationInFrames = {原帧数 × 新fps / 原fps};
关键:{projectRoot}/src/Root.tsx 中 <Composition> 的 width 和 height 不要修改,必须保持 1920 和 1080。分辨率放大由渲染时的 --scale 参数完成,而非修改设计分辨率。
Main.tsx中的msToFrames必须使用useVideoConfig().fps动态获取帧率。如果发现Main.tsx中有硬编码的FPS常量,必须先修复为useVideoConfig().fps,否则 fps 变更后场景时序会完全错乱。
HR.2 校验项目
node "{scriptsRoot}/validate-project.js" \
"{projectRoot}" \
"{projectRoot}/storyboard.json"
校验失败时必须停止,不得继续渲染。
HR.3 执行渲染
cd "{projectRoot}"
npx remotion render Main out/output-4k.mp4 --scale 2
--scale 2:将 1920x1080 的设计画布放大 2 倍渲染为 3840x2160- 矢量元素(文字、SVG)会以更高分辨率渲染,画质更清晰
- 画面布局与 1080p 完全一致,不存在元素缩小的问题
- 如果不需要 4K 只需 60fps,去掉
--scale 2即可
HR.4 完成通知
通知用户:
- 输出路径:
{projectRoot}/out/output-4k.mp4 - 输出分辨率、帧率、时长
- 如有 fps 修改,提醒用户 Root.tsx 中的 fps 已从 30 改为目标值
数据结构参考
storyboard.json
interface Storyboard {
totalDuration: number;
sceneCount: number;
scenes: {
id: string;
startTime: number;
duration: number;
segments: {
text: string;
relativeStart: number;
relativeDuration: number;
}[];
semanticTags?: string[];
visualHint?: string;
}[];
}
scene-plan JSON
interface ScenePlanCard {
sceneId: string;
goal: string;
layout: string;
visualCore: string;
surface: string;
emphasis: string;
screenShouldShow: string[];
beatPlan: {
segments: number[];
action: string;
}[];
}
SceneComponentResult
interface SceneComponentResult {
sceneId: string;
componentPath: string;
componentName?: string;
planPath?: string;
}
Resources
template/
- 轻量模板项目,随 skill 一起分发
- 首次使用时在模板目录执行依赖安装,后续项目复用模板依赖
references/
storyboard-parser.md:分镜生成阶段协议scene-component-creator.md:场景规划与实现阶段协议
scripts/
ensure-template-deps.js:检查模板依赖,必要时执行首次安装init-project.js:根据srtPath初始化项目generate-storyboard.js:根据 SRT 和 groups.json 生成 storyboard.jsongenerate-creator-scenes.js:根据 storyboard.json 为指定 creator 生成 scenesData JSONgenerate-scenes-registry.js:生成generated-scenes.tsscene-registry-utils.js:registry 和校验共用工具validate-project.js:渲染前完整性校验validate-scene-plan.js:校验 scene-plan JSON 结构与 segment 绑定
执行清单
主流程
- 获取用户提供的 SRT 绝对路径
- 运行
ensure-template-deps.js检查模板依赖,必要时完成首次安装 - 运行
init-project.js --srt-path创建项目 - 获取
projectRoot、skillRoot、templateRoot、referencesRoot、scriptsRoot - 使用
references/storyboard-parser.md生成storyboard.json - 验证
storyboard.json结构正确 - 计算 Creator 分组
- 使用
references/scene-component-creator.md并行生成 scene-plan 与场景组件 - 运行
generate-scenes-registry.js - 运行
validate-project.js - 执行渲染
测试模式
- 获取用户提供的音频文件绝对路径
- 复制音频到
{projectRoot}/public/audio.mp3 - 修改
Main.tsx添加<Audio>组件 - 启动 Remotion Studio 供用户预览
重新渲染
- 检查
Main.tsx是否存在<Audio>标签 - 若存在则移除
<Audio>及相关 import - 执行渲染
高分辨率 / 高帧率渲染
- 解析用户需求为具体的输出分辨率、帧率和 scale 值
- 确认
Main.tsx中msToFrames使用useVideoConfig().fps而非硬编码常量 - 仅当需要高帧率时:修改
Root.tsx的fps和generated-scenes.ts的totalDurationInFrames - 确认
Root.tsx的width/height保持 1920/1080 不变 - 运行
validate-project.js校验 - 使用
--scale 2(4K时)执行渲染
注意事项
- 所有路径必须使用绝对路径
- SubAgent prompt 中必须传入实际路径值,不能只传变量名
- 模板资源位于
{skillRoot}/template - 模板以轻量形式分发,首次使用时必须先完成
template/依赖预检 - 默认项目目录位于
{dirname(srtPath)}/remotion-video-projects Main.tsx、Root.tsx属于受保护宿主层- 场景组件必须真实消费
segments validate-project.js失败时不得继续渲染