Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions .claude/skills/new-promo/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
name: new-promo
description: 做一条没有旁白、按节拍走的短片:15 秒广告、品牌短片、产品宣传、短视频开场(默认竖屏 9:16,带配乐和音效,母带到 -14 LUFS)。用户说「做个短片」「广告片」「宣传片」「15 秒」「抖音」「让人一看就想来买」「punchy」「showreel」时使用。有旁白的讲解用 new-scene。
---

# 做一条短片

先读 `docs/principles.md` 的原理 1(时间来自节拍)、原理 7(声音量出来)和原理 8(按手机上看到的样子验收)。下面每一步都是这三条的具体做法。

## 1. 先定节拍和结构,再画画面

- **总长 = 整数小节**。128 BPM 一小节 1.875 秒,8 小节正好 15 秒;120 BPM 一小节 2 秒。先在 `promo/promo.json` 里排好每场几小节,再写画面。
- **一场一个意思,一拍一个动作**。经典结构(演示片就是这样):冷开场三记砸字(1.5 小节)→ 四拍硬切讲四个卖点(1 小节)→ 16 分音符频闪(半小节)→ 间奏放一句主张(1 小节,`break`)→ 商品轮播(1 小节)→ 铺垫:跑马灯 + logo 描边,最后一拍留空(1 小节,`build`)→ 重拍落版:logo 填色、冲击波、彩屑、字标、标语、CTA(2 小节,`drop`)。
- **能量要有起伏**:`break` 抽掉底鼓,`build` 最后一拍留空,下一个重拍才砸得响。整条都是满能量,反而没有冲击力。

## 2. 画面

- 用 `punch` 包:`Slam`(在 cue 那一帧砸到位)、`Hud`、`Marquee`、`Badge`、`CardStrip`、`PatternText`、`LogoReveal`、`CtaBar`;粒子用 core 的 `<Burst>`。
- **砸字要落在拍上,不是从拍上开始**:`Slam at={cue('x')}` 内部用 `land()`,前 5 帧加速进场,`x` 那一帧到位,同一帧放 `<Sfx name="impact">` 或 `slam`。
- **硬切的场(四拍卖点、频闪)**:每拍换底色 + 换主体 + 一个大字,大字用 `dur={2}` 几乎立刻到位,否则跟不上切换。
- **转场写在 `promo.json` 的 `in` 里**:`iris`、`wipe`、`flood`、`up` 在重拍前播完。一条片子里用两三种就够;需要更强的「推进」可以在场景里自己做(演示片 p1 冲进「不」字里,用主题色铺满)。
- **第 0 帧就要有钩子**:第一帧是默认封面,也是观众决定划不划走的那半秒。第一个 cue 放在 `0:0`,砸字在第 0 帧已经到位(`land()` 在 cue 那一帧落定);`verify` 会查第 0 帧有没有字。
- **字放在安全区里**:竖屏 1080×1920 上,顶部约 200px、底部 1500 以下、右侧 x>940 且 y>840 的按钮栏会被 App 盖住(数值在 `promo.json` 的 `safe`)。右对齐的小字用 `right: 150` 而不是 `72`;大字先算宽度:字号 × 字数,加上落地时的 `squash` 放大(`amt` 0.14 就是 14%)。本来就要出画的装饰(跑马灯、HUD、徽章环形字)用 `<Free>` 包起来。
- **屏幕上的数字从数据算**:「十套风格包」写成 `${zhNum(PACKS.length)}套`,「06 OF 10」用 `pad2()`;`PACKS` 是全部风格包的清单,测试会核对。
- **停留要留够**:八分音符的轮播,滑动 4 帧、静止 3 帧;一句主张至少给一拍让人读。
- **logo 用描的,不用字体代替**:`python tools/trace-logo.py logo.png --split` → `src/brand/logo.json`。截图可以,窗口边框会自动去掉。描完用 `tools/promo-sheet.mjs` 看落版那几帧。
- **展示字体先逐字检查**:风格化中文字体个别字会认不出来。把文案里每个字渲染出来看一遍,认不出就换思源黑体 900。

## 3. 文案

- 卖点写感受和事实,不写绝对化用语:`python tools/copy_lint.py` 必须过(`pytest` 也会查)。
- 产地、价格、「每日上新」「当天到店」这类事实,用户没给就不要写,写进去之前先确认。
- CTA 要能直接行动:到店、扫码、搜索什么、点链接。没有地址或二维码,就在交付时说明缺了什么。

## 4. 声音

- 配乐由 `tools/score.py` 按同一张节拍网格写:调 `key`、每场 `energy` 就能改编曲。
- 音效写在场景里,跟着画面的动作:砸字 `slam`/`impact`、切换 `whip`、刀光转场 `swish`、跑马灯进场 `impact`、铺垫 `riser`(结束在重拍:`at = 重拍 - 45`)+ `revcrash`(`at = 重拍 - 15`)、logo 描边 `shimmer`、落版 `impact` + `chime`。
- `node tools/promo.mjs` 跑完看两样:`score.py` 打印的总线 RMS(鼓约 0.37、贝斯约 0.23、音乐约 0.15、混响 0.02–0.08,混响或 pad 比鼓响就是糊了),`audio-report.py` 的分段响度(`break` 和 `build` 应该比 `groove` 低几 LU,`drop` 最响)和 `out/audio/` 的波形。

## 5. 交付

1. `npm run verify`(包含短片两关:在手机上读得到、两次渲染一致)。不过的话看 `out/verify/promo.png` 的红框。
2. `node tools/promo.mjs` → `out/Promo-mixed.mp4` 和上传用封面 `out/Promo-cover.png`(`promo.json` 的 `cover` 指定哪一帧),报告里响度 -14 ±1 LUFS、真峰值 ≤ -1 dBTP。
3. 从成片里抽帧再看一遍(不是只看 Studio):`ffmpeg -ss <秒> -i out/Promo-mixed.mp4 -frames:v 1 x.png`。
4. 交付时说清楚:哪些文案是你写的、没确认过;缺了哪些素材(地址、二维码、真实产品图)。
2 changes: 1 addition & 1 deletion .claude/skills/new-scene/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ description: 在 explainer-kit 里新写一场讲解(旁白 + 画面)。用

# 写一场

动手前先过一遍 `docs/principles.md` 的六条原理;遇到组件里没有现成答案的需求,从原理推(比如「要和某个词同步」就是原理 1:用 `w()` 取帧)。
动手前先过一遍 `docs/principles.md` 的八条原理;遇到组件里没有现成答案的需求,从原理推(比如「要和某个词同步」就是原理 1:用 `w()` 取帧)。

## 1. 先写旁白,再想画面

Expand Down
5 changes: 4 additions & 1 deletion .claude/skills/publish/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ publish 会在渲染前先跑 `tools/verify.mjs`(验收不过就停,不会

- 渲染一定要 `--gl=angle`(`tools/render.mjs` 已带上),纯软件 GL 慢约 5 倍。
- 超过 10 分钟的渲染脱离终端跑并看日志:Git Bash `nohup node tools/publish.mjs --formats all > out/publish.log 2>&1 &`;PowerShell 用 `Start-Process` 并重定向输出。
- edge-tts 原始响度只有约 -24 LUFS,必须经过 `tools/mix.py`(publish 已包含)到 -16 LUFS。
- edge-tts 原始响度只有约 -24 LUFS,必须经过 `tools/mix.py`(publish 已包含)。它用 `tools/master.py` 做两遍线性母带(测 → 加增益 → 限幅 → 再测),目标是 `kit.config.json` 的 `loudness.target`(默认 -16),某个画幅可以单独设(`formats.9x16.loudness`,默认 -14,给短视频平台)。真峰值在编码后的 AAC 上测,超过 -1 dBTP 会自动降天花板重做。
- 出片后跑 `python tools/audio-report.py out/<id>-mixed.mp4 --target -16` 看分段响度和波形。
- 渲染报 `kill EBADF`、而且只在范围里有音频时才崩:是某个 WAV 用 Python `wave` 模块写的(最简文件头)。用 `tools/kit.py` 的 `wav_bytes()` 重写;`pytest` 会查出 `public/` 里不合格的 WAV。
- 没有旁白的短片、广告用 `node tools/promo.mjs`(见 `new-promo` skill)。
- 只重做某几场的配音:`python tts/gen.py s03 s05`,其他场沿用旧时间轴;然后 `publish --skip-tts`。
- 换了真配音后 cue 位置都会变,交付前按 `review` skill 再审一遍。
21 changes: 20 additions & 1 deletion .claude/skills/review/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,28 @@ node tools/listen.mjs s05:block s05:out:30 # 一段

输出每个声音离哪个 cue 多少帧(如 `hit+0`)。`--mock` 时旁白是静音的,列出来的全是音效和配乐节拍。核对:每个点击、落地、砸字都有声音,且偏差在 ±3 帧内;没有意外的长时间噪声。

## 量声音(成片)

```bash
python tools/audio-report.py out/Explainer-mixed.mp4 --target -16 # 讲解片
python tools/audio-report.py out/Promo-mixed.mp4 --promo --target -14 # 短片
```

整体响度要在目标 ±1 LU,真峰值 ≤ -1 dBTP(在编码后的文件上测)。看分段响度:某一场比相邻的响 3 LU 以上,多半是那场音效没配平(演示片 s08 的方波金币就是这样查出来的);短片的 `break`、`build` 应该比 `groove` 低,`drop` 最响。再看 `out/audio/*_wave.png`:一条平带说明压过头或混响糊了。

## 看短片

```bash
node tools/promo-sheet.mjs # 第 0 帧、每个 cue 定住后、转场中点、封面各一格;遮挡区涂红,出安全区的字框红
node tools/promo-sheet.mjs 230 236 239 243 # 指定帧,看一段动作(比如轮播停没停住)
```

短片不要用 `<Freeze>` 拼图看(转场有预卷,会差几帧);这个工具是逐帧直接渲染的。

## 交付前的检查清单

- `npm run verify` 全部通过(类型、源文件一致、每场每种画幅可渲染、两次渲染逐字节一致)
- `npm run verify` 全部通过(类型、源文件一致、每场每种画幅可渲染、两次渲染逐字节一致、短片每个 cue 可渲染且可复现)
- 成片跑过 `audio-report.py`:响度在目标上、真峰值 ≤ -1 dBTP、没有哪一场突然响一截
- 改过的每场都拼帧看过,主画幅和 9x16 各一遍
- 音效位置用 `listen` 核对过
- 真配音(Windows 上 `python tts/gen.py`)后再看一遍:cue 位置会变,写死的帧数会错位
8 changes: 5 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
# explainer-kit

Remotion 讲解视频模板:旁白里写 `[[cue]]` 标记,edge-tts 的逐词时间戳驱动字幕和动画。用法见 `README.md`。
Remotion 讲解视频模板:旁白里写 `[[cue]]` 标记,edge-tts 的逐词时间戳驱动字幕和动画。没有旁白的短片 / 广告走节拍网格(`promo/promo.json`,`src/promo/`,`node tools/promo.mjs`)。用法见 `README.md`。

- 面向用户的文档用简体中文;代码、命令、标识符用英文。
- 本机是 Windows(渲染、真 TTS 验证在那里做);云端沙箱只做重构、类型检查、`--mock` 时间轴和单测。沙箱里也能看画面和听音效:`tools/` 的渲染脚本会自动用 `/opt/pw-browsers` 预装的 headless shell(`node tools/strip.mjs s01:quote`、`node tools/overview.mjs`、`node tools/listen.mjs s05`),只看,不提交 `out/`。
- 做事的原则:往原理去理解,随后一通百通;可交付、可重复、稳定输出,才是 AI 赋能的前提。项目的六条原理见 `docs/principles.md`,动手前先对照它想清楚,新需求优先从原理推,不要只套组件。交付前跑 `npm run verify`(`--quick` 只做类型和一致性检查),它不过就不算完成。
- 常做的事有项目 skill(`.claude/skills/`):`new-scene` 写一场、`review` 审片、`new-style-pack` 新风格包、`publish` 出片。反复手动做的步骤,优先收成 `tools/` 脚本或补进这些 skill。
- 做事的原则:往原理去理解,随后一通百通;可交付、可重复、稳定输出,才是 AI 赋能的前提。项目的八条原理见 `docs/principles.md`,动手前先对照它想清楚,新需求优先从原理推,不要只套组件。交付前跑 `npm run verify`(`--quick` 只做类型和一致性检查),它不过就不算完成。
- 常做的事有项目 skill(`.claude/skills/`):`new-scene` 写一场、`review` 审片、`new-style-pack` 新风格包、`publish` 出片、`new-promo` 做短片。反复手动做的步骤,优先收成 `tools/` 脚本或补进这些 skill。
- 不在仓库里提交字体文件和渲染产物(`out/`、`public/fonts/`)。
- 场景要播的 WAV 一律用 `tools/kit.py` 的 `wav_bytes()` 写:Python `wave` 模块的最简文件头会让 Windows 上的 Remotion 渲染崩溃(只报 `kill EBADF`)。
- 声音和画面一样要验:出片后看 `tools/audio-report.py`(响度、真峰值、分段响度)。广告文案过 `tools/copy_lint.py`。
Loading
Loading