Files
AshuaiXsinfo/README.md
T
xiaoshuai 9b63424740 chore: 初始化仓库,提交 vibevideo 源码
- src/: HyperFrames 封装 CLI(hv)、组件、品牌样式、音频(TTS/混音)工具
- projects/: 各项目 project.yaml 与文案
- interactive/: 井字棋演示页
- .gitignore: 忽略 node_modules、构建产物与 HyperFrames 运行期缓存
2026-10-12 18:06:20 +08:00

261 lines
8.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# vibevideo
用 HTML 写视频。一个视频 = 一个 `project.yaml`;组件、模板、素材全站共享。
渲染引擎是 [HyperFrames](https://github.com/heygen-com/hyperframes)(Apache-2.0,HeyGen 开源),
**但它只占整个系统约 5% 的代码**,全部关在 `src/cli/hv.mjs` 里,随时可换。
---
## 30 秒上手
```bash
node src/cli/hv.mjs 体检 # 环境体检
node src/cli/hv.mjs 新建 ep001 # 建项目
$EDITOR projects/ep001/project.yaml # 写内容
node src/cli/hv.mjs 生成 ep001 # 生成 index.html
node src/cli/hv.mjs 看帧 ep001 # 抽几帧看效果(快)
node src/cli/hv.mjs 渲染 ep001 # 出片
```
所有子命令都有英文别名(`hv new` / `hv build` / `hv render` …),中文只是习惯问题。
---
## 一个项目长什么样
```
projects/ep001/
├── project.yaml ← 你唯一要写的文件
├── BRIEF.md ← 选题、受众、一句话信息
├── script.md ← 逐场景台词
├── assets/project/ ← 私有素材(录屏、截图)
└── out/ ← 成片
```
`project.yaml`:
```yaml
title: 三个 Cursor 实用技巧
aspect: 横屏 # 横屏 1920x1080 / 竖屏 1080x1920 / 方形 1080x1080 / 超宽
fps: 60 # 24 / 30 / 60
lang: zh-CN
scenes:
- component: intro
duration: 3.5
props:
kicker: 技巧合集
title: 三个 Cursor 实用技巧
subtitle: 让你写代码少一半时间
- component: codedemo
duration: 8
props:
filename: fib.py
language: python
highlight: "3,4,5" # 高亮这几行
dimOthers: "true" # 压暗其他行
code: |
def fib(n):
if n < 2:
return n
```
跑 `hv 生成` 后,它会编译成时间轴、挂载组件、同步资源。
---
## 命令
| 命令 | 作用 |
|---|---|
| `hv 新建 <名> [--template 模板] [--aspect 画幅]` | 建项目 |
| `hv 生成 <名>` | `project.yaml` → `index.html` |
| `hv 检查 <名>` | 校验参数/时长 + 运行时/对比度/布局检查 |
| `hv 渲染 <名> [--quality 草稿\|正式\|交付]` | 出片到 `out/` |
| `hv 预览 <名>` | 浏览器实时预览 |
| `hv 看帧 <名> [--时间 1,3,5]` | 抽帧,比整条渲染快得多 |
| `hv 说明 <组件>` | 看某组件有哪些参数 |
| `hv 列表 [项目\|组件\|模板]` | 列出已有内容 |
| `hv 体检` | 依赖/字体/组件检查 |
---
## 组件库
| 组件 | 说明 |
|---|---|
| `intro` | 片头:标题、副标题入场 |
| `outro` | 片尾:引导关注 |
| `codedemo` | 代码演示:逐行打字、语法高亮、重点行高亮 |
| `terminal` | 终端:命令逐字输入、输出分类着色 |
| `lowerthird` | 下方字幕条 |
| `textblock` | 文字块,支持 `**加粗**` 和 `[[强调色]]` |
| `cursor` | 鼠标指针:移动、点击涟漪 |
| `cad3d` | **三维钩子建模全过程**:草图→拉伸→切除→圆角 |
| `stress` | 自由度压测:9 项超纲特性的渲染探针 |
`hv 说明 codedemo` 看参数。组件的参数表直接写在组件自己的
`data-composition-variables` 里,带中文标签,`hv 检查` 会校验你传错的名字。
---
## 分辨率与画幅
改 `project.yaml` 的 `aspect` 即可,**组件代码一行不用动**。
```yaml
aspect: 竖屏 # 1080x1920
# 或自定义
width: 2560
height: 1080
```
原理:所有尺寸都写成 `calc(N * var(--u))`,`--u` = 短边 / 1080,
所以 1080p 横竖都是 `1px`,4K 自动放大一倍。
> ⚠️ 组件里写 `width: var(--stage-w)` 是**非法的**(缺单位,浏览器会整条丢弃)。
> 无单位数字用在 `width/height` 上必须补 `* 1px`。这个坑踩过一次。
---
## 写新组件
`src/components/<名字>/<名字>.html`,只有一个硬性要求:
```js
window.__timelines["你的组件名"] = gsap.timeline({ paused: true });
```
**就这一条。** 满足它,组件就能在任何渲染器里跑。
### 两条铁律
1. **画面必须是时间 t 的纯函数。**
给定第 37 帧,直接画出第 37 帧长什么样,不依赖前面播过什么。
这是缓存、并行、秒级拖动预览的基础。
- ✅ `gsap` 时间轴 + `onUpdate` 里重画
- ✅ 3D 每帧手动清空重画
- ❌ `requestAnimationFrame` 累积状态
- ❌ CSS `@keyframes`(真实时间跑,抓到的是任意中间态)
2. **不准 import 渲染引擎的私有 API。**
组件只允许依赖:浏览器 + GSAP + 上面那条注册约定。
违反这条,将来换渲染引擎就得重写。
---
## 当前能力边界(实测结论)
**能做**:WebGL / Three.js、真实布尔运算(CSG)、mix-blend-mode、clip-path、
mask-image、3D 变换、SVG 描边动画、canvas 逐帧重绘、字体排版、音频混流。
**约束只有一条**:确定性渲染(见上面铁律 1)。
这不是某个框架的限制——Remotion 也一样,只是它把这个约束藏在你写 React 组件里,
你察觉不到。
**本机的性能实测**(12 核 i5,1080p):
- 10s @ 30fps → 11.8s(5 个并行 worker,约等于实时)
- 16s @ 30fps → 22s
- 60fps 帧数翻倍,耗时也翻倍
---
## 目录
```
├── src/
│ ├── cli/hv.mjs ★ 适配层:CLI + 时间轴编译(约 600 行)
│ ├── cli/shot.mjs 抽帧
│ ├── components/ 组件库
│ ├── brand/ 设计规范:色板、字体、基准单位
│ ├── templates/ 视频结构套路
│ ├── audio/ Python:TTS 接口 + 程序化音效
│ └── vendor/ gsap / three / highlight.js
├── projects/ 视频项目
├── tools/ 构建 vendor 的脚本
└── node_modules/
```
---
## 音频
配音、点击音、背景音都归这条链路管。**核心设计:场景时长 = 配音时长**,
所以你不用手工对轴——写完台词,跑一条命令,时间轴自己就对了。
### 用法
```bash
python3 src/audio/cli.py 检查 # 音频侧体检
python3 src/audio/cli.py 配音 <项目> # 合成配音 + 对齐场景时长
python3 src/audio/cli.py 音效 <项目> # 合成音效
python3 src/audio/cli.py 混音 <项目> --bgm bgm.mp3 # 混成一条轨
node src/cli/hv.mjs 渲染 <项目> # 音轨自动挂上
python3 src/audio/verify_sync.py <项目> # 验证音画真的对齐
```
在 `project.yaml` 的场景里写 `narration` 即可:
```yaml
- component: codedemo
duration: 8
narration: "第二步拉伸,把这条轮廓沿 Z 轴扫成实体。" # 时长会被这句覆盖
sfx: "pop@1.2" # 第 1.2 秒一个音效
```
跑完 `配音` 后,时长会变成 `朗读时长 + 0.35s 留白`,并回写 `project.yaml`。
### 换 TTS
Provider 是抽象的,换服务只写一个类:
```python
from tts import TTSProvider, AudioClip, register
class MyTTS(TTSProvider):
name = "我的服务"
def synthesize(self, text, voice="", out_dir=None) -> AudioClip:
path = call_your_api(text, voice)
return AudioClip(path=path, duration=real_duration,
text=text, voice=voice, provider=self.name)
register("mine", MyTTS)
```
然后 `配音 <项目> --provider mine`。**画面侧一行都不用动。**
已内置:
| Provider | 说明 |
|---|---|
| `dummy` | 不发声,但产出**精确时长的合法 wav**。链路先跑通,不是 mock |
| `kokoro` | 本地离线 Kokoro-82M,不联网、结果可复现 |
> ⚠️ `duration` 必须真实,不能估算——整条时间轴的对齐都依赖它。
> DummyTTS 的价值就在于:现在就能验证音画链路,不用等真实 TTS。
### 音效是算出来的,不是找来的
`src/audio/sfx/synth.py` 用 numpy 程序化合成:点击、提示音、转场、落位、气泡。
体积为零、参数随便调、能跟着画面的强调色走。
### 验证对齐
`verify_sync.py` 会在每个场景起点铺一个可闻的标记音,渲染成片后逐点测能量峰。
长度对但偏移了照样是错的,所以必须这样测。
---
## 为什么用 HyperFrames
| | HyperFrames | 自己造 / Remotion |
|---|---|---|
| 成熟度 | Apache-2.0,HeyGen 在用 | 从零三个月起步 |
| 许可证 | 免费 | Remotion 按人头 $25/月,含 agent 写码 |
| 并行渲染 | 5 worker 自带 | 自己实现 |
| 对比度/布局检查 | `check` 自带 | 自己实现 |
**但真正的理由是隔离**:引擎的全部知识只存在于 `src/cli/hv.mjs`。
你的组件不知道引擎的存在。哪天要换,改一个文件,组件全部原样保留。