wshuyi/remotion-video-skill6 files

Remotion Video

|

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 视频的框架。

核心概念

  1. Composition - 视频的定义(尺寸、帧率、时长)
  2. useCurrentFrame() - 获取当前帧号,驱动动画
  3. interpolate() - 将帧号映射到任意值(位置、透明度等)
  4. spring() - 物理动画效果
  5. <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,生成速度极快,音色克隆效果优秀。

配置

  1. 注册 https://www.minimax.io (国际版)或 https://platform.minimaxi.com (国内版)
  2. 获取 API Key
  3. 在 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.py

Remotion 音频同步

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 → 场景组件 → 视频渲染

关键思想:

  1. 音频决定时长:每个场景的持续时间由音频长度决定
  2. 场景即章节:一个概念 = 一个场景 = 一段音频
  3. 配置即真理: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}

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.

Use this skill