返回 html-video
2026-05-26-spec-02-template-metadata.md
根目录 / research / 2026-05-26-spec-02-template-metadata.md
1 # RFC-02:Template Metadata 格式
2
3 > **Status**: Draft v0.1
4 > **Date**: 2026-05-26
5 > **Depends on**: [RFC-01](./2026-05-26-spec-01-engine-adapter.md)
6 > **Scope**: 跨 engine 的 template 描述。让 agent 可以基于 intent 检索、选 engine、填变量、render。
7
8 ---
9
10 ## 设计目标
11
12 1. **Engine-agnostic 描述 + engine-specific 入口** —— metadata 跨引擎统一,但每个 template 物理上属于某个 engine
13 2. **Agent 可读检索** —— `category` `tags` `bestFor` 让 agent 用 intent → template 匹配
14 3. **Inputs 强类型** —— 用 JSON Schema,agent 可以 introspect 该填啥
15 4. **License 可追溯** —— 不让用户被未知 license 模板 surprise(CC-BY-NC 的素材不能商用等)
16 5. **预览资产指引** —— 静态预览图 / 视频示例位置统一,前端 / studio / agent 都能 render template card
17
18 ---
19
20 ## 文件位置
21
22 每个 template 是一个目录,metadata 文件名固定 `template.html-video.yaml`:
23
24 ```
25 templates/
26 └── data-bar-chart-race/
27 ├── template.html-video.yaml # 本 RFC 定义的 metadata
28 ├── source/ # engine-native 源代码(HF: index.html / Remotion: src/Composition.tsx / MC: src/scenes/)
29 ├── preview.png # 静态预览图(必备)
30 ├── preview.mp4 # 短预览视频(可选,<3MB)
31 └── README.md # 人类可读说明(可选)
32 ```
33
34 ---
35
36 ## 完整 schema(YAML 形态,给作者用)
37
38 ```yaml
39 # template.html-video.yaml
40 spec_version: 1 # RFC 版本号,将来 breaking change 用
41
42 # === 标识 ===
43 id: data-bar-chart-race # kebab-case,全局唯一(namespace 同 npm scope,例 @html-video/template-XXX 内部就用 XXX)
44 name: Bar Chart Race # 人类可读
45 description: >
46 Animate ranking changes over time. Bars race left-to-right, length scales
47 with value, color stays consistent per entity.
48
49 # === 引擎归属(必填)===
50 engine: motion-canvas # 物理上写在哪个 engine 里。一个 template = 一个 engine
51 engine_version: ^3.0.0 # peerDep semver
52 source_entry: source/scene.tsx # 相对本目录,engine-specific entry point
53
54 # === 跨 engine 检索元数据 ===
55 category: data-viz # 一级分类(见下方枚举)
56 subcategory: chart-animation # 自由
57 tags: # 自由文本,agent 用 fuzzy match
58 - chart
59 - race
60 - ranking
61 - data-driven
62
63 best_for: # 短句意图描述,跟 EngineAdapter.bestFor 同思路
64 - "Compare entity rankings over time"
65 - "Sales / GitHub stars / user growth race"
66 - "Data journalism style social shorts"
67
68 not_for: # 反向标记,避免 agent 错配
69 - "Static infographics (use html-anything instead)"
70 - "Talking-head explainer (use motion-canvas free-form)"
71
72 # === 输出能力(adapter capabilities 的子集 / 期望值)===
73 output:
74 formats: [mp4, webm]
75 default_format: mp4
76 resolution:
77 default: { width: 1920, height: 1080 }
78 supported_aspects: ["16:9", "1:1", "9:16"]
79 fps:
80 default: 60
81 supported: [30, 60]
82 duration:
83 type: variable # variable = 跟 inputs 走;fixed = 模板固定时长
84 min_sec: 5
85 max_sec: 60
86 alpha: false
87 audio:
88 supported: true
89 expected_inputs: [bgm] # 模板内部 reference 的 audio key
90
91 # === 用户输入(最关键的部分)===
92 inputs:
93 schema: # JSON Schema Draft 2020-12
94 type: object
95 required: [data, title]
96 properties:
97 data:
98 type: array
99 description: Time series of rankings
100 items:
101 type: object
102 required: [date, entities]
103 properties:
104 date:
105 type: string
106 format: date
107 description: ISO date string
108 entities:
109 type: array
110 items:
111 type: object
112 required: [name, value]
113 properties:
114 name: { type: string, description: Entity label }
115 value: { type: number, description: Ranking value (higher = better) }
116 color: { type: string, description: Hex color, optional }
117 minItems: 2
118 title:
119 type: string
120 maxLength: 80
121 subtitle:
122 type: string
123 maxLength: 160
124 duration_per_step_sec:
125 type: number
126 minimum: 0.5
127 maximum: 5
128 default: 1.5
129 bgm_path:
130 type: string
131 description: Optional background music file path
132
133 examples: # 至少 1 条,agent 用作 fewshot
134 - data:
135 - date: "2024-01-01"
136 entities:
137 - { name: "Open Design", value: 100 }
138 - { name: "Hyperframes", value: 4500 }
139 - date: "2025-01-01"
140 entities:
141 - { name: "Open Design", value: 8000 }
142 - { name: "Hyperframes", value: 18000 }
143 - date: "2026-05-01"
144 entities:
145 - { name: "Open Design", value: 50000 }
146 - { name: "Hyperframes", value: 21000 }
147 title: "GitHub Stars Race · 2024-2026"
148
149 # === License & Attribution ===
150 license:
151 spdx: Apache-2.0 # SPDX id
152 attribution_required: false
153 redistribution_allowed: true
154 commercial_use: true
155 notes: ~ # 可选自由文本
156
157 # 模板里用到的第三方素材(字体 / 图标 / sample video / sample audio)
158 assets_attribution:
159 - name: "Inter font"
160 license: SIL-OFL-1.1
161 url: https://fonts.google.com/specimen/Inter
162 - name: "sample-bgm.mp3"
163 license: CC-BY-4.0
164 author: Joe Doe
165 url: https://...
166
167 # === 作者 / 维护 ===
168 author:
169 name: nexu-io
170 url: https://github.com/nexu-io
171 contact: open-design@nexu.io
172 maintainers:
173 - github: joeylee12629-star
174 contributing:
175 url: https://github.com/nexu-io/html-video/blob/main/CONTRIBUTING.md
176
177 # === 版本历史 ===
178 version: 0.1.0 # 模板 semver,独立于 spec_version 和 engine_version
179 changelog:
180 - { version: 0.1.0, date: 2026-05-26, notes: Initial release }
181
182 # === 预览(前端 / studio / agent 用)===
183 preview:
184 poster: preview.png # 必备
185 loop: preview.mp4 # 可选,3 MB cap
186 thumbnail: preview-thumb.webp # 可选,前端 grid 用,<100KB
187
188 # === 渲染参考(让 agent 评估代价)===
189 performance:
190 reference_render: # 1080p / 60fps / 10s 视频在标准硬件的渲染时间,作者填
191 duration_sec: 10
192 render_wall_clock_sec: 22
193 machine: "M2 MacBook Air"
194
195 # === 标签:social-share quick links(generate share-ready 缩略)===
196 share_optimized_for:
197 - twitter-card
198 - xiaohongshu-9-16
199 ```
200
201 ---
202
203 ## 一级分类(受控枚举)
204
205 `category` 字段必须从下表选一项。新增需 PR + RFC。
206
207 | Category | 说明 | 典型 engines |
208 |---|---|---|
209 | `data-viz` | 数据可视化(chart race / line / bar / map / network) | MC, Revideo, HF |
210 | `social-shorts` | 短视频(Twitter / TikTok / 小红书 9:16) | HF, Remotion, Revideo |
211 | `product-demo` | 产品 demo / SaaS landing video / launch | HF, Remotion |
212 | `explainer` | 教程 / 知识 / 对话 / 数学 | MC, Revideo |
213 | `marketing` | 营销 / 广告 / 倒计时 / promo | HF, Remotion |
214 | `intro-outro` | logo reveal / intro / outro / branding bumper | HF, Revideo |
215 | `ambient` | 循环背景 / loop / VJ / live-art | HF, Revideo |
216 | `documentary` | 长篇叙事 / b-roll + 字幕 | Remotion |
217 | `presentation` | slides 风(参照 Prezi 动效) | HF, Remotion |
218 | `transition` | 单场转场组件(不独立成片) | HF, Revideo |
219
220 ---
221
222 ## JSON Schema 形态(给程序用)
223
224 YAML 是给作者写的。`@html-video/core` 启动时会编译成 JSON 形态供 agent / studio / CI 用,并用 [Ajv](https://ajv.js.org/) 强校验。
225
226 ```ts
227 // core/template-schema.ts
228 import { JSONSchemaType } from 'ajv';
229
230 export interface TemplateMetadata {
231 spec_version: 1;
232 id: string;
233 name: string;
234 description: string;
235 engine: string;
236 engine_version: string;
237 source_entry: string;
238 category: TemplateCategory;
239 subcategory?: string;
240 tags: string[];
241 best_for: string[];
242 not_for?: string[];
243 output: OutputCapabilities;
244 inputs: { schema: object; examples: object[] };
245 license: LicenseInfo;
246 assets_attribution?: AssetAttribution[];
247 author: { name: string; url?: string; contact?: string };
248 maintainers?: { github: string }[];
249 contributing?: { url: string };
250 version: string;
251 changelog?: ChangelogEntry[];
252 preview: { poster: string; loop?: string; thumbnail?: string };
253 performance?: { reference_render: PerformanceRef };
254 share_optimized_for?: string[];
255 }
256 ```
257
258 ---
259
260 ## Agent 检索 / 选 engine 流程(说明性)
261
262 ```
263 用户意图 → agent 调 html-video 的 search/match
264
265 [Stage 1] tag/category fuzzy match
266 ↓ 候选 N 个 template
267 [Stage 2] 看每个 template.engine 在用户机器上 adapter 是否安装可用
268 ↓ 过滤掉不可用的
269 [Stage 3] 按 license tier × renderSpeedHint × bestFor 排 top 3
270
271 agent 把 top 3 + preview poster URL 给用户选
272 ↓ 用户确认
273 [Stage 4] 用 inputs.schema introspect 缺哪些参数
274 ↓ agent 跟用户对话补齐
275 [Stage 5] 调 EngineAdapter.render()
276 ```
277
278 ---
279
280 ## License 列举(agent 决策用)
281
282 `license.spdx` 是必填的 [SPDX identifier](https://spdx.org/licenses/)。Agent 在以下场景必查:
283
284 - 用户标记"商用" → 过滤掉非 commercial-use 的
285 - 用户标记"二改重新发布" → 过滤掉 NoDerivatives 的
286 - 跨国用户 → 提示 attribution requirement
287
288 允许的 license 白名单(v0.1):
289
290 | SPDX | 商用 | 修改 | Attribution | html-video 收录 |
291 |---|---|---|---|---|
292 | Apache-2.0 | ✅ | ✅ | needed | ✅ default |
293 | MIT | ✅ | ✅ | needed | ✅ |
294 | CC0-1.0 | ✅ | ✅ | none | ✅ |
295 | CC-BY-4.0 | ✅ | ✅ | needed | ✅ |
296 | CC-BY-SA-4.0 | ✅ | ✅ (must SA) | needed | ⚠️ 须给用户 share-alike 警告 |
297 | CC-BY-NC-4.0 | ❌ | ✅ | needed | ⚠️ 仅个人/非商用入口 |
298 | GPL-3.0 | ✅ | ✅ (copyleft) | needed | ⚠️ 须给用户 copyleft 警告 |
299 | Other | — | — | — | ❌ 拒绝收录 |
300
301 ---
302
303 ## 关键设计决策记录
304
305 ### 为什么 inputs 用 JSON Schema 不用 TS interface
306
307 JSON Schema 可以**运行时**校验 + 序列化传给 agent。TS interface 编译后丢失,agent 拿不到。
308
309 ### 为什么 engine 字段必填且单值
310
311 强制每个 template 物理归属一个 engine,**避免维护多 engine 实现的同名 template**(容易漂移、维护成本翻倍)。如果某个 idea 适合多个 engine 实现,就发布为多个 template id(`bar-chart-race-mc` / `bar-chart-race-hf`),各自独立。
312
313 ### 为什么 preview.poster 必填
314
315 agent 跟用户对话时一定要能 paste 一个截图让用户秒选;没 poster 的 template 不收录。这是入门门槛,不是限制——降低用户决策成本。
316
317 ### 为什么 performance 是可选 + 作者填
318
319 中心化 benchmark 不现实(硬件 / 版本太多),让作者自报 reference 数字(hardware / engine version 标清),agent 用作 ranking tie-breaker 即可。
320
321 ---
322
323 ## Open Questions(v0.2 待定)
324
325 1. **i18n** —— `name` `description` 多语言?建议 v0.2 加 `i18n: { zh-CN: { name, description } }`,v0.1 默认英文
326 2. **变量 group** —— 复杂模板(>20 字段)需要分组渲染 UI;JSON Schema 标准的 `x-ui-order` / `x-ui-group` 扩展?
327 3. **依赖图** —— 一个 template 能不能 include 另一个 template(transition + main 组合)?v0.1 不支持,每模板独立
328 4. **变量来源** —— 是否预设 "data source" 类型让用户 paste CSV / Google Sheet URL?v0.1 只接 inline,外部数据靠 agent 自己拉
329 5. **A/B 变体** —— 一个模板出多版(不同色 / 不同 motion)共享同源代码?v0.2 加 `variants` 字段
330
330 lines MARKDOWN