Specification
- Skill ID
- wshuyi/remotion-video-skill/remotion-video
- Publisher
- wshuyi
- Repository
- remotion-video-skill
- Installs
- 0
- Files
- 6
- Synced
- Sep 23, 2026
How to use it
Open any RiverX project, open the Skills panel in the chat, and search for this identifier. The files are fetched from the source repository at install time.
wshuyi/remotion-video-skill/remotion-videoInstalls these files- README.md
- README_CN.md
- SKILL.md
- scripts/generate_audio_edge.py
- scripts/generate_audio_minimax.py
- templates/audioConfig.ts
What this skill tells the agent
Remotion Video
用 React 以编程方式创建 MP4 视频的框架。
核心概念
- Composition - 视频的定义(尺寸、帧率、时长)
- useCurrentFrame() - 获取当前帧号,驱动动画
- interpolate() - 将帧号映射到任意值(位置、透明度等)
- spring() - 物理动画效果
- <Sequence> - 时间轴上排列组件
快速开始
创建新项目
npx create-video@latest选择模板后:
cd <project-name>
npm run dev # 启动 Remotion Studio 预览项目结构
my-video/
├── src/
│ ├── Root.tsx # 注册所有 Composition
│ ├── HelloWorld.tsx # 视频组件
│ └── index.ts # 入口
├── public/ # 静态资源(音频、图片)
├── remotion.config.ts # 配置文件
└── package.json基础组件示例
最小视频组件
import { AbsoluteFill, useCurrentFrame, useVideoConfig } from "remotion";
export const MyVideo = () => {
const frame = useCurrentFrame();
const { fps, durationInFrames } = useVideoConfig();
return (
<AbsoluteFill style={{ backgroundColor: "white", justifyContent: "center", alignItems: "center" }}>
<h1 style={{ fontSize: 100 }}>Frame {frame}</h1>
</AbsoluteFill>
);
};注册 Composition
// Root.tsx
import { Composition } from "remotion";
import { MyVideo } from "./MyVideo";
export const RemotionRoot = () => {
return (
<Composition
id="MyVideo"
component={MyVideo}
durationInFrames={150} // 5秒 @ 30fps
fps={30}
width={1920}
height={1080}
/>
);
};动画技巧
interpolate - 值映射
import { interpolate, useCurrentFrame } from "remotion";
const frame = useCurrentFrame();
// 0-30帧:透明度 0→1
const opacity = interpolate(frame, [0, 30], [0, 1], {
extrapolateRight: "clamp", // 超出范围时钳制
});
// 位移动画
const translateY = interpolate(frame, [0, 30], [50, 0]);spring - 物理动画
import { spring, useCurrentFrame, useVideoConfig } from "remotion";
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const scale = spring({
frame,
fps,
config: { damping: 10, stiffness: 100 },
});Sequence - 时间编排
import { Sequence } from "remotion";
<>
<Sequence from={0} durationInFrames={60}>
<Intro />
</Sequence>
<Sequence from={60} durationInFrames={90}>
<MainContent />
</Sequence>
<Sequence from={150}>
<Outro />
</Sequence>
</>AI 语音解说集成
为视频添加 AI 语音解说,实现音视频同步。支持两种方案:
| 方案 | 优点 | 缺点 | 硬件要求 | 推荐度 |
|---|---|---|---|---|
| MiniMax TTS | 云端克隆、速度极快(<3秒)、音质优秀 | 按字符计费 | 无 | ⭐⭐⭐ 首选 |
| Edge TTS | 零配置、免费 | 固定音色、无法自定义 | 无 | ⭐⭐ |
方案选择流程
1. 首选 MiniMax TTS
- 检测 API Key 是否配置
- 测试调用是否正常(余额充足)
- 如果成功 → 使用 MiniMax
2. MiniMax 不可用时
→ 退回 Edge TTS(使用预设音色 zh-CN-YunyangNeural)方案一:MiniMax TTS(推荐)
云端 API 方案,无需本地 GPU,生成速度极快,音色克隆效果优秀。
配置
- 注册 https://www.minimax.io (国际版)或 https://platform.minimaxi.com (国内版)
- 获取 API Key
- 在 MiniMax Audio 上传音频克隆音色,获取 voice_id
API 差异
| 版本 | API 域名 | 说明 |
|---|---|---|
| 国际版 | api.minimax.io | 推荐,稳定 |
| 国内版 | api.minimaxi.com | 需国内账号 |
⚠️ 常见错误:api.minimax.chat 是错误的域名,会返回 "invalid api key"。请确认使用上表中的正确域名。
生成脚本
使用 scripts/generate_audio_minimax.py 生成音频,支持:
- 断点续作:已存在的音频文件自动跳过
- 实时进度:显示生成进度,避免茫然等待
- 自动更新配置:生成完成后自动更新 Remotion 的场景配置
# 设置环境变量
export MINIMAX_API_KEY="your_api_key"
export MINIMAX_VOICE_ID="your_voice_id"
# 运行脚本
python scripts/generate_audio_minimax.py价格参考(2025年)
| 模型 | 价格 |
|---|---|
| speech-02-hd | ¥0.1/千字符 |
| speech-02-turbo | ¥0.05/千字符 |
⚠️ MiniMax TTS 踩坑经验
| 问题 | 原因 | 解决方案 |
|---|---|---|
invalid api key | 使用了错误的 API 域名 | 国际版用 api.minimax.io,国内版用 api.minimaxi.com |
config.ts 语法错误 Syntax error "n" | Python 脚本在 f-string 中用 ",\\n".join() 产生了字面量 \n 而非真正换行 | 见下方「Python 生成 TypeScript 注意事项」 |
| 长时间无进度显示 | 后台执行命令看不到输出 | 前台执行脚本,或用 tail -f 实时查看日志 |
Python 生成 TypeScript 注意事项
❌ 错误写法:在 f-string 中使用 \n 会产生字面量字符
# 这会在生成的文件中写入字面的 \n 字符串,而非换行!
content = f'export const SCENES = [{",\\n".join(items)}];'✅ 正确写法:分开处理字符串拼接
# 先用真正的换行符拼接
scenes_content = ",\n".join(items) # 在 f-string 外部拼接
# 再放入模板
content = f'''export const SCENES = [
{scenes_content}
];'''方案二:Edge TTS
无需特殊硬件,完全免费,适合不需要克隆音色的场景。
安装
pip install edge-tts推荐语音
| 语音 ID | 名称 | 风格 |
|---|---|---|
| zh-CN-YunyangNeural | 云扬 | 专业播音腔(推荐) |
| zh-CN-XiaoxiaoNeural | 晓晓 | 温暖自然 |
| zh-CN-YunxiNeural | 云希 | 阳光少年 |
生成脚本
使用 scripts/generate_audio_edge.py 生成音频:
python scripts/generate_audio_edge.pyRemotion 音频同步
import { Audio, Sequence, staticFile } from "remotion";
// 音频配置(根据生成的时长)
const audioConfig = [
{ id: "01-intro", file: "01-intro.mp3", frames: 450 },
{ id: "02-main", file: "02-main.mp3", frames: 600 },
];
// 计算起始帧
const sceneStarts = audioConfig.reduce((acc, _, i) => {
if (i === 0) return [0];
return [...acc, acc[i - 1] + audioConfig[i - 1].frames];
}, [] as number[]);
// 场景渲染
{audioConfig.map((scene, i) => (
<Sequence key={scene.id} from={sceneStarts[i]} durationInFrames={scene.frames}>
<SceneComponent />
<Audio src={staticFile(scene.file)} />
</Sequence>
))}教程类视频架构(场景驱动)
教程、讲解类视频的核心架构:音频驱动场景切换。
架构概览
音频脚本 → TTS 生成 → audioConfig.ts → 场景组件 → 视频渲染关键思想:
- 音频决定时长:每个场景的持续时间由音频长度决定
- 场景即章节:一个概念 = 一个场景 = 一段音频
- 配置即真理:
audioConfig.ts是音画同步的单一数据源
audioConfig.ts 模板
参见 templates/audioConfig.ts,包含:
- SceneConfig 接口定义
- SCENES 数组
- getSceneStart() 计算函数
- TOTAL_FRAMES 和 FPS 常量
场景切换 Hook
import { useCurrentFrame } from "remotion";
import { SCENES } from "./audioConfig";
// 根据当前帧号返回场景索引
const useCurrentSceneIndex = () => {
const frame = useCurrentFrame();
let accumulated = 0;
for (let i = 0; i < SCENES.length; i++) {
accumulated += SCENES[i].durationInFrames;
if (frame < accumulated) return i;
}
return SCENES.length - 1;
};
// 使用
const sceneIndex = useCurrentSceneIndex();
const currentScene = SCENES[sceneIndex];主场景组件模式
import { AbsoluteFill, Audio, Sequence, staticFile, useVideoConfig } from "remotion";
import { ThreeCanvas } from "@remotion/three";
import { SCENES, getSceneStart, TOTAL_FRAMES } from "./audioConfig";
export const TutorialVideo: React.FC = () => {
const { width, height } = useVideoConfig();
const sceneIndex = useCurrentSceneIndex();
const currentScene = SCENES[sceneIndex];
return (
<AbsoluteFill style={{ backgroundColor: "#1a1a2e" }}>
{/* 3D 内容 */}
<ThreeCanvas width={width} height={height} camera={{ position: [0, 0, 4], fov: 50 }}>
{/* 根据 sceneIndex 渲染不同场景 */}
{sceneIndex === 0 && <Scene01Intro />}
{sceneIndex === 1 && <Scene02Concept />}
{sceneIndex === 2 && <Scene03Demo />}
</ThreeCanvas>
{/* 音频同步 - 每个场景一个 Sequence */}
{SCENES.map((scene, idx) => (
<Sequence key={scene.id} from={getSceneStart(idx)} durationInFrames={scene.durationInFrames}>
<Audio src={staticFile(`audio/${scene.audioFile}`)} />
</Sequence>
))}
{/* UI 层:标题 + 进度 */}
<div style={{ position: "absolute", top: 40, left: 0, right: 0, textAlign: "center" }}>
<h1 style={{ color: "white", fontSize: 42 }}>教程标题</h1>
</div>
<div style={{ position: "absolute", bottom: 60, left: 60 }}>
<span style={{ color: "white" }}>{currentScene?.title}</span>
</div>
{/* 进度条 */}
<div style={{ position: "absolute", bottom: 30, left: 60, right: 60, height: 4, backgroundColor: "rgba(255,255,255,0.2)" }}>
<div style={{ width: `${((sceneIndex + 1) / SCENES.length) * 100}%`, height: "100%", backgroundColor: "#3498DB" }} />
</div>
</AbsoluteFill>
);
};Root.tsx 使用动态帧数
import { Composition } from "remotion";
import { TutorialVideo } from "./TutorialVideo";
import { TOTAL_FRAMES } from "./audioConfig";
export const RemotionRoot: React.FC = () => {
return (
<Composition
id="Tutorial"
component={TutorialVideo}
fps={30}More from wshuyi
Use this skill in a project
Open any RiverX project, search the Skills panel for wshuyi/remotion-video-skill/remotion-video, and install it in one click.
