Communitygithub.com

JaviMaligno/agilabs-video-studio

构建文章解说视频的 Remotion 场景——从经过审核的脚本编写 `src/timeline.ts`,挑选并配置动画组件(Counter、BarCompare、FlowDiagram、KeyQuote、StatGrid),添加带有经过测试的 `frameState` 的自定义组件,保持在垂直安全区域内,并通过肉眼检查渲染的静态帧。用于组成、更改或调试使用此插件制作的视频场景。

agilabs-video-studio 是什么?

本技能(AGILabs Video Studio)专为使用 Remotion 构建文章解说视频而设计。它提供了一套完整的视频场景编排方案,核心工作流包括:在 `src/timeline.ts` 中以有序列表形式定义视频场景(可包含 intro、anim、outro 三种类型),通过 `say` 函数精确控制旁白行与时间轴,并使用预置的动画组件(如 Counter、BarCompare、FlowDiagram、KeyQuote、StatGrid)来可视化数据或概念。技能强调“先状态,后绘制”的自定义组件开发模式,要求开发者编写纯函数的 `frameState` 并通过单元测试,确保动画在任何时间点都有确定的状态。同时,技能严格规定了 1080×1920 输出下的垂直安全区域(Stage 范围为 y=280 至 y=1280),并强制要求在渲染最终视频前逐一检查每个场景的静态帧 PNG,以验证布局、文本和动画正确性。该技能适用于 Codex CLI 环境,但所描述的组件库和工作流可复用于任何支持 Remotion 的 AI 代理平台。

兼容平台~Claude Code✓Codex CLI~Cursor
npx skills add https://github.com/JaviMaligno/agilabs-video-studio/tree/HEAD/skills/explainer-animation

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

解说动画

视频是 src/timeline.ts 中一个有序的场景列表。有三种类型:

  • intro:钩子卡片。第一行是钩子,以大字体显示,其单词在朗读时依次点亮(它同时也是自己的字幕)。
  • anim:来自组件库的一个动画组件,与场景中的旁白行同步。
  • outro:结尾卡片,包含 AGILabs 徽标、javieraguilar.ai 以及来自 SETTINGS.articleTitle 的文章标题。

编写时间轴

import type { Cuts } from './types.ts';
import { L, anim, intro, outro, say, videos } from './narrate.ts';

const build = (): Cuts => ({
  main: [
    intro('hook', [say(0.4, 'EN hook line.', 'ES hook line.')]),
    anim(
      'result',
      'BarCompare',
      { bars: [{ label: L('With the reason', 'Con el porqué'), value: 14, display: '14/20', highlight: true },
               { label: L('Without it', 'Sin él'), value: 4, display: '4/20' }], max: 20 },
      [say(0.3, 'First line.', 'Primera línea.'), say(0, 'Second line.', 'Segunda línea.')],
      { min: 4 },
    ),
    outro('outro', [say(0.4, 'More at javieraguilar.ai.', 'Más en javieraguilar.ai.')]),
  ],
});

export const VIDEOS = videos(build);
export const TIMELINE = VIDEOS.en.main;
  • say(at, en, es, spoken?):一行旁白。at 是该行可以开始的最早场景秒数;各行从不重叠(有 0.35 秒的间隔),场景在其最后一行结束后的 0.8 秒或 min 处结束。spoken({ en?, es? })是 TTS 在朗读内容与字幕文本不同时读取的内容(参见 narration)。
  • 节拍。 场景中每一行的开始时间就是一个节拍。组件在节拍 i 上播放步骤 i(柱状图 i 增长,节点 i 出现,卡片 i 弹出),因此请为每个想要揭示的步骤编写一行。在 TTS 之前,节拍来自长度估计;在 npm run tts 之后,它们来自真实音频,因此在叙述后应重新检查静态帧。
  • L(en, es):一个双语言屏幕标签。纯字符串用于两种语言。
  • 场景 ID 必须唯一:行键(<scene>-01,……)用于命名音频文件。
  • anim 上的 opts.chapter 存储在场景上,但尚未被任何组件绘制。
  • 每次更改后运行 npm run lint:检查长度是否在 targetSeconds 的 ±20% 范围内,字幕提示是否超过 0.8 秒或 17 字符/秒,以及组件名称是否缺失在组件库中。

组件目录

所有组件从场景接收 t、beats、dur、lang 和 vertical;下面的 props 来自时间轴。屏幕上显示的数字使用文章的确切形式:每当文章写有 "14/20"、"0.0018" 或 "83%" 时,优先使用 display / 字符串 value 而非计算文本。

组件用途属性
Counter一个标题数字,从下往上计数,标签在其下方value: number、label: Label、prefix?、suffix?、decimals?(0-8)、beat?(计数开始的节拍索引,默认为 0)
BarCompare2-5 个水平柱状图,每拍增长一个;高亮的柱状图使用强调渐变bars: { label: Label; value: number; display?: string; highlight?: boolean; at?: number }[]、max?(刻度顶部,默认为最大值)、unit?(当没有 display 时附加)
FlowDiagram按顺序出现的方框和箭头;broken 边为虚线且弱化,带有一个间隙nodes: { id; label: Label; at?: number }[]、edges: { from; to; label?: Label; broken?: boolean }[]、`layout: 'vertical'
KeyQuote关键句子,逐词显示,强调的词语使用强调颜色并带有下划线text: Label、emphasis?: string[](单词或短语,忽略大小写和标点进行匹配)、beat?
StatGrid2-4 个数字卡片,每拍弹出一个stats: { value: string; label: Label; at?: number }[]

at 在柱状图、节点或卡片上将其固定到场景的某一秒,而不是其自己的节拍。没有节拍或 at 的步骤在上一个步骤之后 0.6 秒跟随。每个组件都经过适配,使其在场景结束时完成:最后一帧始终显示完成状态。

要查看整个库:运行 npm run studio 并打开 Gallery-vertical,或渲染其静态帧。

添加自定义组件

当脚本标记一个场景为“自定义”时:

  1. 先有状态,并附带测试。 编写 src/anim/<Name>.state.ts,包含 props 类型和一个纯函数 frameState(props, t, beats, dur),该函数返回纯数字和字符串。那里不要有 Remotion 或 React 的导入。基于 src/anim/steps.ts(sceneTime、stepTimes、fit、progress、beatAt、easeOutCubic)构建它,使其是彻底的:在任何 t 上都有有限值,进度字段在 [0, 1] 内,在其第一个节拍之前不可见,在 t = dur 时处于结束状态。
  2. 在 test/anim.test.ts 的 CASES 中添加一个案例(并测试其自身行为),然后运行 npm test。它在 t < 0、每个节拍、dur 和超过 dur 的情况下检查状态,包括缺失、延迟和非常短的节拍。
  3. 绘制它 在 src/anim/<Name>.tsx 中:调用 frameState,在 <Stage vertical={...}> 内部绘制,使用来自 src/brand.ts 的品牌令牌(C、GRADIENT、alpha)和 Inter 字体。不要使用新的颜色或字体。
  4. 注册它 在 src/anim/index.ts 的 ANIMS 中。保持 ANIMS 为名称的纯对象字面量:npm run lint 从源代码中读取它们。
  5. 可选地,在 src/Gallery.tsx 中添加一个示例。如果它是可复用的,它应回到插件的模板中。

生成的图像。 大多数场景应通过代码绘制(来自品牌令牌的 SVG 和 CSS)。如果一个场景确实需要生成的图像(背景、基础图像、纹理瞬间),可以使用图像模型(例如 Codex CLI 的图像生成功能)来生成。将其保存在 public/ 下,使用 staticFile() 加载,保持在品牌调色板内,并检查静态帧。

垂直安全区域(1080x1920)

  • 平台 UI 覆盖顶部约 220 像素和底部约 380 像素;两侧保持 72 像素。
  • 动画仅在 Stage 内部绘制:从 y = 280(AGILabs 徽标下方)到 y = 1280(两行字幕上方),宽 936 像素。Stage / stageOf() 提供该框。
  • 字幕位于 Stage 下方,最多两行,每行约 32 个字符,宽度约 900 像素。

静态帧:仔细检查它们

在渲染视频之前渲染静态帧并读取每个 PNG。一次干净的 lint 或构建不会显示溢出、裁剪的文本或错误的数字。

npx remotion still src/index.ts Main-EN-vertical out/stills/f0150.png --frame=150

为每个场景在其最后一步播放后拍摄一张静态帧,再加上每约 3 秒(30 fps 下每 90 帧)一张。检查:仅 AGILabs 徽标作为品牌元素,没有超出安全区域的内容,没有文本框溢出的文本,每个数字与文章一致,并且屏幕上的字幕与当时动画显示的内容匹配。

Individual skills in this repo

This repo contains 3 individual skills — each has its own dedicated page.

相关技能