Remotion template-prompt-to-video:从一句标题到 TikTok 竖屏 AI 视频的全链路实战指南
【免费下载链接】remotion🎥 Make videos programmatically with React项目地址: https://gitcode.com/GitHub_Trending/re/remotion
本篇指南基于 Remotion 官方模板template-prompt-to-video(包名template-ai-video),完整讲解如何用一条 CLI 命令,将故事标题和主题转换为包含剧本、AI 图像、配音与逐字时间轴的竖屏短视频。读完后,你将掌握该模板的完整生成流水线、Timeline 数据结构的每一类字段,以及基于 Remotion 源码的渲染原理与远程部署改法。
模板定位与整体结构
该模板位于 packages/template-prompt-to-video,目标是生成适配 TikTok 或 Instagram 的高质量 AI 视频。它的核心思路是:把"做视频"拆成两个阶段——
- 离线生成阶段(Node/Bun CLI):调用 OpenAI 生成故事剧本与图像描述,调用 DALL·E 生成 1024x1792 的竖版配图,调用 ElevenLabs 合成带逐字时间戳的语音,最后把所有素材编排进一份
timeline.json。 - 渲染阶段(Remotion):React 组件读取 Timeline,把每个元素映射为
<Sequence>序列,输出 1080x1920、30fps 的竖屏视频。
目录结构上,CLI 与渲染端完全分离:
- cli/cli.ts:命令行入口,串联整个生成流水线;
- cli/service.ts:OpenAI / ElevenLabs 的 API 调用层;
- cli/timeline.ts:把故事素材编排成 Timeline;
- src/Root.tsx:动态注册每个故事为一个 Composition;
- src/components/:
AIVideo、Background、Subtitle、Word四个渲染组件; - src/lib/types.ts:用 Zod 定义的 Timeline 数据契约;
- public/content/history-of-venus/:内置演示故事,含 8 张配图、8 段语音、
descriptor.json与timeline.json。
快速上手:预览与渲染演示故事
无需任何 API Key 即可预览并渲染仓库内置的演示故事(history-of-venus)。按 README 的三步操作:
安装依赖
npm install启动预览(Remotion Studio)
npm run dev渲染视频
npx remotion render对应地,package.json 中的脚本为dev: remotion studio、build: remotion bundle、gen: bun cli/cli.ts,此外还有upgrade: remotion upgrade与lint: eslint src && tsc。
渲染行为由 remotion.config.ts 控制,其中做了三项配置:
Config.setRspack(true); // 使用 Rspack 作为打包器 Config.setVideoImageFormat("jpeg"); // 帧间图片采用 jpeg 编码 Config.setOverwriteOutput(true); // 输出文件已存在时直接覆盖由于setVideoImageFormat指定为jpeg,渲染 1080x1920@30fps 的长视频时磁盘占用会比默认 png 帧显著更小。
创建新故事:配置环境变量与选择音色
生成新故事前需要配置两个环境变量(README 中同时提示可参考模板内的.env.example):
OPENAI_API_KEY= ELEVENLABS_API_KEY=从 cli/cli.ts 的源码可以看到降级策略:程序先读取--api-key参数或OPENAI_API_KEY环境变量,若都缺失,则通过prompts库在终端弹出密码式输入框交互式索取;ElevenLabs Key 同理。也就是说,不创建.env文件也能使用,只是每次运行都要手动输入。
选择 ElevenLabs 音色
配音音色在 cli/service.ts 的generateVoice()中硬编码:
const voiceId = "21m00Tcm4TlvDq8ikWAM";按 README 的说明,替换为你喜欢的 Voice ID 即可。获取方式有两种:调用 ElevenLabs 的 Voice 列表 API,或直接打开 ElevenLabs 网页端任意音色页面,从 URL 中voiceId=之后截取 ID,例如:
https://elevenlabs.io/app/voice-library?voiceId=aTxZrSrp47xsP6Ot4Kgd这里aTxZrSrp47xsP6Ot4Kgd就是 Voice ID。
运行生成命令
npm run gen运行后终端会提示依次输入故事标题(Title)与主题(Topic)。标题可以是模糊或冗长的长句,第一张幻灯片的短标题会基于它生成;主题则用于约束叙事风格,README 给出的效果好主题包括 History(历史)、Interesting facts(冷知识)、ELI5(通俗解释)等。
CLI 本身也支持非交互传参。从 cli/cli.ts 的 yargs 定义可以看出,generate子命令(同时是默认命令)提供三个选项:
| 选项 | 别名 | 类型 | 说明 |
|---|---|---|---|
--api-key | -k | string | OpenAI API Key |
--title | -t | string | 故事标题 |
--topic | -p | string | 故事主题(如 Interesting Facts、History) |
因此脚本化调用可以写成bun cli/cli.ts --title "The history of Venus" --topic "History" -k sk-...,跳过所有交互提示。
CLI 生成流水线:四步把标题变成时间轴
generateStory()是整个模板的编排核心,其执行顺序在 cli/cli.ts 中可以逐段对应:
第一步:生成故事剧本。调用openaiStructuredCompletion(getGenerateStoryPrompt(title, topic), StoryScript)。从 cli/service.ts 的提示词可以看到约束:剧本必须 8~10 句,遵循叙事最佳实践,输出一段无格式、无换行的连续英文文本。结构化输出由 openaiStructuredCompletion() 实现:它使用gpt-4.1模型,把 Zod Schema 通过z.toJSONSchema()转成 JSON Schema,并以response_format: { type: "json_schema", strict: true }强制模型输出符合契约的 JSON,返回前再用schema.parse()二次校验。
第二步:生成图像描述。提示词 getGenerateImageDescriptionPrompt() 要求模型为故事生成 5~8 条"非常详细"的图像描述,且每条描述必须对应故事中连续的 1~2 句话,句子顺序与原文保持一致——这保证了后续图像、文本与语音三者天然对齐。每条内容项会被分配一个uuidv4()作为唯一标识uid,成为图像文件、音频文件与 Timeline 引用之间的关联键。
第三步:生成图像与语音(逐条串行)。对每个内容项:
generateAiImage()调用 DALL·E 3,尺寸取自 constants.ts 中的IMAGE_WIDTH x IMAGE_HEIGHT(1024x1792,与 TikTok 竖屏接近的 9:16 比例),以 base64 落盘为<uid>.png;失败时最多重试 3 次、每次间隔 1 秒,重试状态会实时显示在ora转圈提示上;generateVoice()调用 ElevenLabs 的textToSpeech.convertWithTimestamps,把 MP3 存为<uid>.mp3,并取回CharacterAlignmentResponseModel——其中包含逐字符的characters、characterStartTimesSeconds、characterEndTimesSeconds三个数组。这是后文"逐字字幕对齐"的关键数据源;若响应缺少时间戳会直接抛错终止。
第四步:编排并保存 Timeline。调用createTimeLineFromStoryWithDetails()生成 Timeline,随后contentFs.saveTimeline()落盘。
所有产物由 ContentFS 类统一管理,输出目录为public/content/<slug>/,其中 slug 由标题小写化、非字母数字字符替换为连字符生成(如 "The History of Venus" →the-history-of-venus)。目录内固定包含:
public/content/<slug>/ descriptor.json # 剧本 + 图像描述 + 逐字音频时间戳 timeline.json # 渲染端直接消费的 Timeline images/<uid>.png # DALL·E 3 配图 audio/<uid>.mp3 # ElevenLabs 配音仓库内置的 public/content/history-of-venus/ 就是一份完整产物,可直接打开 timeline.json 对照学习。
Timeline 数据结构:Elements、Text、Audio 三大块
README 的 "Technical overview" 指出:Remotion 依据 Timeline 渲染视频,它由 Elements、Text 和 Audio 三块组成;Elements 定义幻灯片背景,并携带进出场转场(如 blur)与激活期动画(如 scale、rotate);Text 与 Audio 的特殊之处在于它们是同步的。
这些描述在 src/lib/types.ts 中有精确的 Zod 契约:
const TimelineSchema = z.object({ shortTitle: z.string(), elements: z.array(BackgroundElementSchema), text: z.array(TextElementSchema), audio: z.array(AudioElementSchema), });各字段含义如下表:
| 类型 | 字段 | 说明 |
|---|---|---|
Timeline | shortTitle | 首屏标题卡显示的短标题 |
elements | 背景幻灯片数组,按时间顺序排列 | |
text | 字幕文本段数组 | |
audio | 配音片段数组 | |
BackgroundElement | startMs/endMs | 片段起止时间(毫秒) |
imageUrl | 图像文件名(不含扩展名,即 uid) | |
enterTransition/exitTransition | 可选,取值fade|blur|none | |
animations | 可选,动画数组 | |
TextElement | startMs/endMs | 字幕显示时间窗 |
text | 字幕文本(通常为不超过 14 字符的词组) | |
position | top|bottom|center | |
animations | 可选,动画数组 | |
AudioElement | startMs/endMs | 音频播放时间窗 |
audioUrl | 音频文件名(不含扩展名) | |
ElementAnimation | type/from/to | 当前仅支持scale,from/to为缩放系数 |
startMs/endMs | 动画在片段内的局部时间窗 |
值得注意的是imageUrl与audioUrl只存 uid 而不存完整路径——因为渲染端会按固定规则拼接(下文 utils.ts 的getImagePath/getAudioPath)。这一约定也是远程部署需要改动的位置之一。
Timeline 生成算法:时长累加、交替缩放与 14 字符切段
createTimeLineFromStoryWithDetails()(cli/timeline.ts)把离散的素材"焊接"成一条连续时间线,几个值得注意的设计:
背景时长由语音决定。每个内容项的背景时长取该段语音最后一个字符的结束时间乘以 1000(毫秒),各背景首尾相接累加durationMs,保证画面切换严格卡在语音句尾,不会出现图文声错位。
进出场统一使用 blur 转场,缩放动画交替方向。每个背景元素的enterTransition与exitTransition均设为"blur";缩放动画由zoomIn布尔位交替控制:偶数段从 1.5 缩放回 1(推远),奇数段从 1 放大到 1.5(推近),形成呼吸感。
字幕按 14 字符切段,并用逐字时间戳精确对齐。文本先按空格拆词,累计长度超过MaxSentenseSizeChars = 14就切出一个TextElement;每段的startMs/endMs取自该段覆盖字符在characterStartTimesSeconds/characterEndTimesSeconds中的首尾值加上当前片段的durationMs偏移。这正是 README 所说"Text 与 audio 是同步的"的具体实现。
字幕动画带受控随机性。getTextAnimations()(cli/timeline.ts)以 60% 概率给字幕一段 300ms 的入场缩放(起始缩放值在 0.5~0.7 间随机),另有 50% 概率追加 200ms 的 1.25→1 回弹。源码中这两处Math.random()都显式禁用了@remotion/deterministic-randomness规则——因为随机发生在CLI 生成阶段并固化进 JSON,渲染端读取的是确定值,不破坏 Remotion 渲染的确定性要求。这是一个很好的实践范式:把随机性前移到数据生成层,而非渲染层。
渲染端实现:Composition 自动发现与三大组件
Composition 的发现。src/Root.tsx 用getStaticFiles()扫描静态目录,把所有以timeline.json结尾的文件名取第二段(即 slug)作为 Composition ID 动态注册。这意味着 CLI 每生成一个新故事,Studio 与渲染器就会自动多出一个可渲染的组合,无需改任何代码。每个 Composition 配置为1080x1920、fps = 30(见 constants.ts),Schema 为{ timeline: TimelineSchema.nullable() },defaultProps先给timeline: null,再由calculateMetadata调用 loadTimelineFromFile() 拉取content/<slug>/timeline.json、按startMs排序,并以最后一段的endMs计算总帧数,加上 1 秒(30 帧)的INTRO_DURATION开场。
开场标题卡。AIVideo.tsx 用前 30 帧渲染timeline.shortTitle:Bree Serif 字体、120px 字号、黄底黑字加粗边框、全大写——典型的信息流"钩子"封面样式。
背景与转场。Background.tsx 中,图片宽度设为画布高度(1920),按 9:16 比例计算显示尺寸并叠加EXTRA_SCALE = 0.2的基础放大(避免缩放动画露出画面边缘);随后查找当前时刻命中的scale动画段,按线性进度插值出缩放值。模糊转场由 calculateBlur() 计算:入场后前 1000ms 内从满模糊线性衰减到 0,出场前 1000ms 反向递增,最终映射到maxBlur = 25像素的 CSSblur()滤镜。
字幕弹入。Subtitle.tsx 用 5 帧、damping: 200的 spring 驱动入场,Word.tsx 再叠加两层渲染——一层 20px 黑色描边、一层白色填充,配合scale/translateY插值实现"弹出"效果;字号通过@remotion/layout-utils的fitText约束在画布宽度 80% 以内,超过 120px 上限则按适配值缩小。
预挂载。背景与音频的<Sequence>都设置了premountFor={3 * FPS},即提前 3 秒挂载组件,让图片在入场前就完成加载、音频在播放前完成解码,消除转场时的加载闪烁。
部署为远程渲染服务
README 的 "Deploying on a remote server" 一节给出了把模板改成远程服务的三步改法,结合源码可以精确定位改动点:
- 照常部署 Remotion 渲染器与模板 bundle(
remotion bundle产物 + 渲染 Worker)。 - 让 Root.tsx 支持外部 Timeline。当前实现是从本地静态目录发现 slug 并读取文件;远程场景下改为接收以 prop 形式传入的 timeline URL,
calculateMetadata里把loadTimelineFromFile()换成对该 URL 的fetch即可——AIVideo组件本身只依赖注入的timelineprop,无需改动。 - 素材改走 URL。CLI 生成 Timeline 时,
imageUrl/audioUrl目前存的是本地 uid 文件名(渲染端经 getImagePath() 拼成content/<proj>/images/<uid>.png);远程部署需要先把images/与audio/上传到对象存储,并在createTimeLineFromStoryWithDetails()中把这些字段写成可访问的 URL。
版本管理与参考入口
- 遇到渲染问题时,README 建议先升级 Remotion 获得修复:
npx remotion upgrade(对应 package.json 的upgrade脚本); - 该模板源码维护在 Remotion Monorepo(包名
template-ai-video),本仓库路径为 packages/template-prompt-to-video; - 想要定制生成逻辑(如更换模型、调整切段策略、换转场类型),入口是 cli/timeline.ts 的
createTimeLineFromStoryWithDetails()——README 的 Technical overview 一节明确将其列为定制点; - 想换字体、改开场卡样式,改动 AIVideo.tsx 与 Word.tsx 即可;
- 完整的 Timeline 字段契约以 src/lib/types.ts 为准,手工编写或二次处理
timeline.json时可逐字段对照; - 授权方面,按 README 提示,部分企业主体使用 Remotion 需要公司授权,条款见仓库根目录 LICENSE.md。
小结
template-prompt-to-video演示了一个可复制的"AI 内容 → 结构化时间轴 → React 渲染"范式:LLM 负责叙事与分镜(结构化 JSON 输出 + Zod 校验),图像模型与 TTS 服务产出按 uid 关联的素材,逐字时间戳把字幕、背景、音频三者锁在同一时间轴上,Remotion 端则通过静态文件发现 +calculateMetadata把 JSON 变成逐帧确定的画面。其最值得借鉴的工程细节包括:随机性前移到生成层、时长以语音为准累加、素材引用与存储路径解耦——最后一点也正是把它改造为远程渲染服务时唯一必须触碰的耦合面。
【免费下载链接】remotion🎥 Make videos programmatically with React项目地址: https://gitcode.com/GitHub_Trending/re/remotion
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考