name: vertical-video-producer description: 手动触发:为 OpenClaw Skill 生成竖版短视频。流程:Phase1 挖掘信息 → Phase2 生成3个风格剧本→ 用户选择→ Phase3 生成音频 → Phase4 渲染视频。支持分次调用(第一次生成剧本,第二次根据用户选择继续)。 disable-model-invocation: true allowed-tools: Read, Write, Edit, Grep, Glob, Bash
Vertical Video Producer(执行入口)
必读引用(唯一权威)
- references/iron-laws.md
- references/workflow-v3.md
- common/production-rules.md
- common/tag-definitions.md
- common/scene-example.md
- references/templates/(6/10/12 场景模板)
- references/templates/timeline-example.md(Phase2 格式参考)
输入参数(调用时传入)
- $ARGUMENTS[0]:目标工具/Skill 名称(必填,作为顶层目录名)
- $ARGUMENTS[1]:GitHub 仓库 URL 或本地路径(必填)
- $ARGUMENTS[2]:输出根目录(可选;默认项目根目录)
⚡ 核心原则:动态时长模式
关键改变:视频时长由音频决定,而非剧本预设。
| 传统模式 | 动态时长模式 ✅ |
|---|---|
| 剧本阶段预设时间 | 剧本只写内容,时间由音频决定 |
| 强制 WPM 校验 | 无 WPM 限制,自然语速即可 |
| 反复调整文案 | 一次写完,直接生成音频 |
| 固定场景时长 | Remotion 根据音频时长动态调整 |
优势:
- 🚀 快速:跳过繁琐的 WPM 调整
- 🎯 自然:语音更自然,不强制加速/减速
- 🔧 简单:减少校验步骤,专注内容创作
执行流程(分次调用模式)
第一次调用:Phase 1-2(信息挖掘 + 剧本生成)
执行内容:
- Phase 1:信息挖掘 + 版本评估
- Phase 2:生成三个风格剧本(功能派/场景派/效率派)
- 输出对比表和选择指引
第一次调用到此结束,等待用户第二次调用。
第二次调用:Phase 3-4(音频 + 视频)
调用参数:
- $ARGUMENTS[0]: Skill 名称(必填,必须与第一次一致)
- $ARGUMENTS[1]: 用户选择(必填,格式:
SELECT=A或SELECT=B或SELECT=C)
执行内容:
- Phase 2e:锁定选定剧本(复制为 timeline.md)
- Phase 3:edge-tts 生成分段音频
- Phase 4:Remotion 开发与合成(必须生成最终视频文件)
简化后的校验
Phase 2 只检查格式:
- ✅ Scene 标题格式正确
- ✅ 必需字段存在(Narration, On-screen Text)
- ⚠️ WPM 仅作参考提示,不强制
Phase 3-4 流程:
- edge-tts 生成音频(记录实际时长)
- Remotion 根据音频时长设置场景 durationInFrames
- 渲染最终视频
输出目录结构
{Skill名称}/
├── 02-timeline-script/
│ ├── versions/ # 多风格剧本版本
│ │ ├── version-a.md # 功能派
│ │ ├── version-b.md # 场景派
│ │ ├── version-c.md # 效率派
│ │ └── comparison.md # 版本对比表
│ ├── timeline.md # 锁定的剧本
│ └── decision.md # 剧本决策记录
├── 03-production/
│ ├── generate-audio.py
│ ├── audio-durations.json # 音频实际时长
│ └── taska-audio/
│ ├── scene_001.wav
│ ├── scene_002.wav
│ └── ...
└── 04-composition/
├── remotion/
│ ├── src/
│ │ ├── scenes/
│ │ ├── {SkillName}Full.tsx
│ │ └── Root.tsx
│ └── package.json
└── output/
└── {Skill名称}-final.mp4 # 最终视频
AI 执行逻辑
判断调用类型
第一次调用特征:
- 参数数量 = 2 或 3
- 第二个参数是 URL 或路径
- 没有包含 "SELECT="
第二次调用特征:
- 参数数量 = 2
- 第二个参数包含 "SELECT="
执行流程
if 第二个参数包含 "SELECT=":
// 第二次调用
锁定剧本 → 生成音频 → 开发 Remotion → 渲染视频
else:
// 第一次调用
信息挖掘 → 生成三版剧本 → 输出选择指引 → 结束
Phase 3:音频生成(简化版)
不再强制 rate 调整,使用自然语速:
# generate-audio.py
SCENES = [
{"id": "1", "text": "口播内容..."},
{"id": "2", "text": "口播内容..."},
...
]
# 使用默认 rate,不强制调整
# edge-tts --voice zh-CN-XiaoxiaoNeural --text "..." --write-to scene_001.wav
记录实际时长到 audio-durations.json:
{
"scene_001": {"duration": 5.2},
"scene_002": {"duration": 6.8},
...
}
Phase 4:Remotion 开发(智能布局 + 动态时长)
⚠️ 铁律:字幕是必需的
每个场景都必须有字幕! 字幕是视频的核心组成部分,不是可选功能。
所有布局组件(Opening、PainPoint、FeatureGrid、CTA 等)都必须:
- 接收
narration参数(口播内容) - 在场景底部渲染黄底黑字的字幕
- 使用
ANIMATION_TIMING.caption控制字幕淡入时机
4.1 布局类型与关键词映射
自动根据场景内容选择布局:
| 布局类型 | 触发关键词 | 效果 |
|---|---|---|
Opening |
"每天一个"、"今天"、"开场" | 噪声背景 + Logo 组装动画 |
PainPoint |
"痛点"、"困境"、"问题" | 左右分栏 + 震动效果 |
Pipeline |
"流水线"、"步骤"、"流程" | 上中下分层 + 连线动画 |
Terminal |
"命令"、"/digest"、"演示" | 终端窗口 + 打字机效果 |
Comparison |
"对比"、"vs"、"提升"、"倍" | 对比卡片 + 数字弹跳 |
FeatureGrid |
"特性"、"功能"、"支持" | 卡片网格 + 依次弹出 |
IconMatrix |
"场景"、"适用"、"人群" | 图标矩阵 + 旋转入场 |
VSTable |
"优势"、"传统" | VS 表格 + 高亮闪烁 |
Steps |
"配置"、"三步"、"快速上手" | 步骤卡片 + 进度条 |
CTA |
"点赞"、"收藏"、"明天见" | 脉冲效果 + 粒子环绕 |
匹配优先级:
- 最后一个场景 → 强制 CTA
- 第一个场景 → 优先 Opening
- 其他 → 按关键词匹配,默认 FeatureGrid
4.2 复制模板并安装依赖
cp -r .claude/skills/vertical-video-producer/remotion-template {Skill名称}/04-composition/remotion
cd {Skill名称}/04-composition/remotion
npm install
⚠️ 关键:必须使用模板中的布局组件!
模板布局组件位置:remotion-template/src/scene-layouts/
禁止行为:
- ❌ 不要自己创建布局组件
- ❌ 不要修改布局组件的字幕渲染逻辑
- ❌ 不要省略
narration参数
预装依赖:
@remotion/noise- 噪声效果@remotion/transitions- 过渡动画@remotion/media- 音频处理
4.3 创建场景组件
必须传递 narration 参数! 每个场景组件都必须把口播内容传给布局组件:
// src/scenes/Scene001.tsx
// ✅ 正确示例:传递 narration 参数
import { Opening } from "../scene-layouts";
import { staticFile } from "remotion";
export const Scene001: React.FC = () => {
return (
<Opening
audioFile="scene_001.wav"
narration="每天一个 Skill,今天给大家介绍 Remotion" // ← 必需!口播内容
title="Remotion"
subtitle="用代码做视频"
brand={BRAND}
/>
);
};
// src/scenes/Scene002.tsx
// ✅ 正确示例:PainPoint 布局
import { PainPoint } from "../scene-layouts";
export const Scene002: React.FC = () => {
return (
<PainPoint
audioFile="scene_002.wav"
narration="传统做视频的痛点,代码能力完全用不上" // ← 必需!
title="痛点"
points={["不会用 GUI 软件", "学一天还是一塌糊涂", "技能无法复用"]}
brand={BRAND}
/>
);
};
// src/scenes/Scene003.tsx
// ✅ 正确示例:FeatureGrid 布局
import { FeatureGrid } from "../scene-layouts";
export const Scene003: React.FC = () => {
return (
<FeatureGrid
audioFile="scene_003.wav"
narration="Remotion 的核心功能,让你用 React 组件来定义视频" // ← 必需!
title="核心功能"
features={[
{ icon: "⚛️", title: "React 组件", desc: "用熟悉的 React 语法" },
{ icon: "🎬", title: "时间轴控制", desc: "精确到帧的动画" },
]}
brand={BRAND}
/>
);
};
❌ 错误示例(缺少 narration):
// 这会导致字幕不显示!
<Opening
title="Remotion"
subtitle="用代码做视频"
// 缺少 narration 参数!
/>
4.4 主组合文件
根据音频时长动态设置场景帧数:
// src/AudioDurations.ts
export const AUDIO_DURATIONS = {
scene_001: 17.66,
scene_002: 16.90,
// ...
};
export const getFramesFromDuration = (duration: number) => Math.ceil(duration * 30);
// src/SkillFull.tsx
import { Sequence } from "remotion";
import { Opening, PainPoint, CTA, ... } from "./scene-layouts";
import { matchLayout } from "./layout-matcher";
export const SkillFull: React.FC = () => {
return (
<AbsoluteFill>
{SCENES.map((scene, index) => {
const Layout = getLayoutComponent(matchLayout(index, SCENES.length, scene.title, scene.subtitle, scene.narration));
return (
<Sequence key={scene.id} from={offsets[index]} durationInFrames={getFramesFromDuration(AUDIO_DURATIONS[scene.id])}>
<Layout {...scene} brand={BRAND} />
</Sequence>
);
})}
</AbsoluteFill>
);
};
4.5 渲染最终视频
mkdir -p ../output
npx remotion render SkillFull ../output/{Skill名称}-final.mp4 \
--codec=h264 \
--pixel-format=yuv420p \
--audio-codec=aac \
--audio-bitrate=192k
Phase 4 完成标准
必须生成:
- ✅
{Skill名称}/04-composition/remotion/- 完整的 Remotion 项目 - ✅
{Skill名称}/04-composition/output/{Skill名称}-final.mp4- 最终视频文件 - ✅ 所有场景组件都传递了
narration参数
验证字幕功能(必须通过):
# 检查场景组件是否包含 narration 参数
grep -r "narration" {Skill名称}/04-composition/remotion/src/scenes/
# 检查布局组件是否包含字幕渲染
grep -r "FFD700\|captionOpacity" {Skill名称}/04-composition/remotion/src/scene-layouts/
验证视频文件:
ls -lh {Skill名称}/04-composition/output/{Skill名称}-final.mp4
⚠️ 只有当以下条件全部满足时,任务才算完成:
- 视频文件存在
- 所有场景组件都传递了
narration参数 - 布局组件包含字幕渲染代码