Video giải thích chuyển động có thuyết minh với ToRun MCP và Remotion
Cập nhật lần cuối: 11 thg 11 2026
Bạn có thể làm gì
Một tác nhân lập trình chạy lệnh trên máy tính của bạn, như Claude Code, opencode hoặc Cursor, có thể dùng ToRun để làm một video giải thích ngắn có thuyết minh. Tác nhân viết kịch bản, yêu cầu ToRun tạo hình ảnh cho các cảnh, lời thuyết minh và nhạc nền, rồi kết xuất video trên máy tính của bạn bằng Remotion, một thư viện biến các thành phần React thành video.
Bạn cần gì
- Một công cụ lập trình đã kết nối với máy chủ MCP của ToRun, có thể chạy lệnh và ghi tệp vào một thư mục: opencode, Claude Code hoặc Cursor.
- Node.js 18 trở lên. Lần kết xuất đầu tiên sẽ tải trình duyệt mà Remotion dùng để kết xuất.
- Hạn mức của gói trả phí hoặc số dư trong ví: lệnh gọi API không bao giờ dùng hạn mức miễn phí. Xem lệnh gọi được thanh toán thế nào.
Remotion có giấy phép riêng: miễn phí cho cá nhân và các công ty rất nhỏ, còn các công ty lớn hơn cần giấy phép công ty. Hãy xem remotion.dev/license trước khi dùng cho công việc.
Dự án khởi đầu
Bốn tệp. Hãy nhờ tác nhân tạo chúng trong một thư mục trống, hoặc tự sao chép, rồi chạy npm install ở đó. Các tệp được tạo ra sẽ nằm trong thư mục public bên cạnh package.json.
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 chứa toàn bộ video. Mỗi cảnh có caption hiển thị trên màn hình, văn bản thuyết minh say, prompt cho hình ảnh, tên tệp của hình ảnh và lời thuyết minh, và độ dài tính bằng 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 phát các cảnh lần lượt. Mỗi hình ảnh phóng to chậm, phụ đề trượt lên, lời thuyết minh của cảnh bắt đầu cùng lúc, còn nhạc phát nhỏ suốt cả video:
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)}
/>
);
Tác nhân làm video như thế nào
Kịch bản. Tác nhân viết các cảnh vào
video.json: mỗi cảnh một ý, phụ đề vài từ, một hoặc hai câu để nói. Hãy yêu cầu nó cho bạn xem kịch bản trước khi tạo bất cứ thứ gì.Số dư. Nó gọi
account_balanceđể xem tài khoản có thể chi bao nhiêu. Lệnh gọi này miễn phí.Hình ảnh. Với mỗi cảnh, nó gọi
image_generatevớipromptcủa cảnh vàsize1792x1024, rồi lưu hình ảnh thànhpublic/scene-1.png,public/scene-2.pngvà cứ thế. Nếu hình ảnh chưa xong trong thời gian chờ, công cụ trả về một mã công việc, và tác nhân hỏijob_statuscho đến khi hình ảnh xong.Thuyết minh. Với mỗi cảnh, nó gọi
tts_generatevới văn bảnsaycủa cảnh và một giọng, ví dụnova, rồi lưu âm thanh thànhpublic/scene-1.mp3và cứ thế.Thời lượng. Nó đặt
secondscủa mỗi cảnh bằng độ dài lời thuyết minh cộng thêm khoảng nửa giây.tts_generatetrả về độ dài đó dưới dạngduration_seconds, được đo từ tệp âm thanh. Nếu câu trả lời còn códuration_estimated: true, độ dài chỉ có thể ước tính từ văn bản, nên tác nhân sẽ đo tệp. Để tự kiểm tra một tệp:npx remotion ffprobe -v error -show_entries format=duration -of csv=p=0 public/scene-1.mp3Âm nhạc (không bắt buộc). Nó gọi
music_generatevới một câu lệnh như "calm instrumental background, soft piano",with_vocalsđặt làfalsevàduration_secondsgần với độ dài video. Công cụ trả về một mã công việc; tác nhân hỏijob_statuscho đến khi bản nhạc xong và lưu thànhpublic/music.mp3. Nếu không cần nhạc, hãy xóa dòngmusickhỏivideo.json.Xem trước và kết xuất.
npm run studiomở bản xem trước trong trình duyệt của bạn.npm run renderghi raout/explainer.mp4.
Mọi liên kết tệp mà công cụ trả về đều tải xuống được mà không cần đăng nhập. Hãy giữ nguyên toàn bộ liên kết, kể cả phần sau dấu chấm hỏi:
curl -L -o public/scene-1.png "<the image link>"
Yêu cầu tác nhân của bạn
Làm một video giải thích dài 30 giây về cách tính năng đồng bộ của ứng dụng chúng tôi hoạt động, dùng dự án khởi đầu Remotion từ torun.ai/docs/mcp-motion-explainers. Bốn cảnh. Trước tiên cho tôi xem kịch bản trong video.json. Sau đó tạo một hình ảnh không có chữ cho mỗi cảnh, thuyết minh mỗi cảnh bằng giọng nova, thêm nhạc không lời nhẹ nhàng, căn thời lượng mỗi cảnh theo lời thuyết minh của nó, rồi kết xuất.
Để có video dọc, hãy yêu cầu width 1080 và height 1920 trong video.json, và hình ảnh với size 1024x1792.
Chi phí bao nhiêu
image_generate,tts_generatevàmusic_generatelà các lệnh gọi trả phí, được tính bằng Runs theo cùng mức giá như trong ứng dụng. Một video bốn cảnh có nhạc gồm bốn hình ảnh, bốn lời thuyết minh và một bản nhạc.account_balancevàjob_statusmiễn phí.- Việc kết xuất chạy trên máy tính của bạn. Thay đổi phụ đề, thời lượng hoặc chuyển động rồi kết xuất lại không tốn gì trên ToRun; tạo lại một hình ảnh hoặc lời thuyết minh là một lệnh gọi trả phí mới.
- Khi một lần tạo sẽ tốn nhiều hơn ngưỡng của khóa, công cụ sẽ trả lời bằng giá trước, và tác nhân của bạn sẽ hỏi bạn trước khi chạy lệnh gọi.
- Mọi thứ tác nhân tạo ra cũng được lưu vào thư viện ToRun của bạn.
Mẹo
- Yêu cầu hình ảnh không có chữ. Phụ đề đã mang phần chữ, và mô hình hình ảnh thường viết sai chính tả.
- Giữ mỗi cảnh ở một ý và một hoặc hai câu thuyết minh.
- Chọn một giọng và giữ nguyên trong cả video.
- Giữ âm lượng nhạc trong khoảng
0.1đến0.15để lời thuyết minh vẫn rõ ràng.