Yearly Pay yearly and get 2 months free on every paid plan. See plans
登录 免费开始

用 ToRun MCP 和 Remotion 制作带旁白的动态讲解视频

最后更新于 11 11月 2026

你可以做什么

能在你的电脑上运行命令的编程代理,例如 Claude Code、opencode 或 Cursor,可以借助 ToRun 制作一段带旁白的简短讲解视频。代理会编写脚本,向 ToRun 请求场景图像、旁白和背景音乐,然后用 Remotion 在你的电脑上渲染视频。Remotion 是一个把 React 组件变成视频的库。

你需要什么

  • 一个已连接 ToRun MCP 服务器、能运行命令并在文件夹中写入文件的编程工具:opencode、Claude Code 或 Cursor。
  • Node.js 18 或更高版本。第一次渲染时会下载 Remotion 用来渲染的浏览器。
  • 付费套餐的额度或钱包余额:API 调用从不使用免费额度。参见调用如何付费。

Remotion 有自己的许可证:对个人和非常小的公司免费,更大的公司需要公司许可证。在工作中使用之前,请查看 remotion.dev/license。

入门项目

四个文件。让你的代理在一个空文件夹中创建它们,或者自己复制,然后在那里运行 npm install。生成的文件会放在 package.json 旁边的 public 文件夹中。

package.json:

{
  "name": "motion-explainer",
  "private": true,
  "scripts": {
    "studio": "remotion studio src/index.ts",
    "render": "remotion render src/index.ts Explainer out/explainer.mp4"
  },
  "dependencies": {
    "@remotion/cli": "4.0.539",
    "react": "18.3.1",
    "react-dom": "18.3.1",
    "remotion": "4.0.539"
  }
}

video.json 包含整个视频。每个场景都有显示在屏幕上的 caption、旁白文字 say、图像的 prompt、它的图像和旁白文件名,以及以 seconds 表示的时长:

{
  "width": 1920,
  "height": 1080,
  "fps": 30,
  "music": { "file": "music.mp3", "volume": 0.12 },
  "scenes": [
    {
      "caption": "Every team has a backlog.",
      "say": "Every team has a backlog, and it only grows.",
      "prompt": "flat illustration of a wall covered in sticky notes, soft morning light, no text",
      "image": "scene-1.png",
      "narration": "scene-1.mp3",
      "seconds": 5.5
    },
    {
      "caption": "Sort it by what users feel.",
      "say": "Sort it by what your users feel, not by what is loudest.",
      "prompt": "flat illustration of hands arranging cards into three columns on a desk, no text",
      "image": "scene-2.png",
      "narration": "scene-2.mp3",
      "seconds": 6
    },
    {
      "caption": "Ship the top one this week.",
      "say": "Then ship the top one this week.",
      "prompt": "flat illustration of a small paper boat leaving a harbour at sunrise, no text",
      "image": "scene-3.png",
      "narration": "scene-3.mp3",
      "seconds": 5
    }
  ]
}

src/index.ts:

import {registerRoot} from 'remotion';
import {Root} from './Root';

registerRoot(Root);

src/Root.tsx 会依次播放各个场景。每张图像缓慢放大,字幕从下方升起,场景的旁白随之开始,音乐则在整个视频中轻声播放:

import React from 'react';
import {AbsoluteFill, Composition, Html5Audio, Img, interpolate, Series, staticFile, useCurrentFrame} from 'remotion';
import data from '../video.json';

// "say" and "prompt" are the script: the narration text and the image prompt. Rendering reads the rest.
type Scene = {image: string; narration?: string; seconds: number; caption?: string; say?: string; prompt?: string};
type Video = {width: number; height: number; fps: number; music?: {file: string; volume: number}; scenes: Scene[]};

const video = data as Video;
const clamp = {extrapolateLeft: 'clamp', extrapolateRight: 'clamp'} as const;
const framesOf = (scene: Scene) => Math.round(scene.seconds * video.fps);

// One scene: the image slowly zooms in, the caption rises in, the narration plays from the scene's start.
const SceneView: React.FC<{scene: Scene; frames: number}> = ({scene, frames}) => {
  const frame = useCurrentFrame();
  const fade = interpolate(frame, [0, 10, frames - 10, frames], [0, 1, 1, 0], clamp);
  const zoom = interpolate(frame, [0, frames], [1, 1.08]);
  const rise = interpolate(frame, [8, 26], [40, 0], clamp);
  return (
    <AbsoluteFill style={{backgroundColor: 'black', opacity: fade}}>
      <Img src={staticFile(scene.image)} style={{width: '100%', height: '100%', objectFit: 'cover', transform: `scale(${zoom})`}} />
      {scene.caption ? (
        <div
          style={{
            position: 'absolute', left: '6%', right: '6%', bottom: '8%', transform: `translateY(${rise}px)`,
            color: 'white', fontFamily: 'Arial, sans-serif', fontSize: video.height / 18, fontWeight: 700,
            textShadow: '0 2px 16px rgba(0,0,0,0.7)',
          }}
        >
          {scene.caption}
        </div>
      ) : null}
      {scene.narration ? <Html5Audio src={staticFile(scene.narration)} /> : null}
    </AbsoluteFill>
  );
};

const Explainer: React.FC = () => (
  <AbsoluteFill style={{backgroundColor: 'black'}}>
    <Series>
      {video.scenes.map((scene, i) => (
        <Series.Sequence key={i} durationInFrames={framesOf(scene)}>
          <SceneView scene={scene} frames={framesOf(scene)} />
        </Series.Sequence>
      ))}
    </Series>
    {video.music ? <Html5Audio src={staticFile(video.music.file)} volume={video.music.volume} loop /> : null}
  </AbsoluteFill>
);

export const Root: React.FC = () => (
  <Composition
    id="Explainer"
    component={Explainer}
    width={video.width}
    height={video.height}
    fps={video.fps}
    durationInFrames={video.scenes.reduce((sum, scene) => sum + framesOf(scene), 0)}
  />
);

代理如何制作视频

  1. 脚本。 代理把场景写进 video.json:每个场景一个要点,几个字的字幕,一两句要说的话。请让它在生成任何内容之前先把脚本给你看。

  2. 余额。 它调用 account_balance 查看账户可以花费多少。这次调用是免费的。

  3. 图像。 对于每个场景,它用该场景的 prompt 和 size 1792x1024 调用 image_generate,并把图像保存为 public/scene-1.png、public/scene-2.png 等。如果图像在等待时间内没有准备好,工具会返回一个任务 ID,代理会不断查询 job_status,直到图像准备好。

  4. 旁白。 对于每个场景,它用该场景的 say 文字和一个声音(例如 nova)调用 tts_generate,并把音频保存为 public/scene-1.mp3 等。

  5. 时间。 它把每个场景的 seconds 设为其旁白的时长再加约半秒。tts_generate 会以 duration_seconds 返回这个时长,它是根据音频文件测得的。如果回应中还写着 duration_estimated: true,说明时长只能根据文字估算,因此代理会测量文件。要自己检查一个文件:

    npx remotion ffprobe -v error -show_entries format=duration -of csv=p=0 public/scene-1.mp3
    
  6. 音乐(可选)。它用类似“calm instrumental background, soft piano”的提示词、设为 false 的 with_vocals 以及接近视频时长的 duration_seconds 调用 music_generate。工具会返回一个任务 ID;代理会查询 job_status 直到音轨准备好,并把它保存为 public/music.mp3。如果不需要音乐,请从 video.json 中删除 music 这一行。

  7. 预览和渲染。 npm run studio 会在你的浏览器中打开预览。npm run render 会写出 out/explainer.mp4。

工具返回的每个文件链接都无需登录即可下载。请保留完整链接,包括问号后面的部分:

curl -L -o public/scene-1.png "<the image link>"

让你的代理来做

使用 torun.ai/docs/mcp-motion-explainers 上的 Remotion 入门项目,制作一段 30 秒的讲解视频,介绍我们应用的同步是如何工作的。四个场景。先在 video.json 中把脚本给我看。然后为每个场景生成一张不含文字的图像,用 nova 声音为每个场景配旁白,加入轻柔的器乐音乐,让每个场景的时长与其旁白一致,然后渲染。

如果要制作竖屏视频,请在 video.json 中要求 width 为 1080、height 为 1920,并要求图像的 size 为 1024x1792。

费用是多少

  • image_generate、tts_generate 和 music_generate 是付费调用,以 Runs 按与应用相同的价格计费。一个带音乐的四场景视频需要四张图像、四段旁白和一首音轨。
  • account_balance 和 job_status 是免费的。
  • 渲染在你的电脑上进行。修改字幕、时间或动画后重新渲染,在 ToRun 上不产生任何费用;重新生成图像或旁白则是一次新的付费调用。
  • 当单次生成的费用超过密钥阈值时,工具会先回复价格,你的代理会在运行调用之前先询问你。
  • 代理生成的所有内容也会保存到你的 ToRun 资源库中。

提示

  • 要求生成不含文字的图像。文字由字幕承载,而图像模型经常把文字写错。
  • 每个场景只讲一个要点,旁白一两句即可。
  • 选定一个声音,并在整个视频中使用它。
  • 把音乐音量保持在 0.1 到 0.15 之间,让旁白保持清晰。
遇到问题? 论坛通常几小时内就有回复;支持团队会在 24 小时内回复。 在论坛提问 联系支持