mcpField
V
video-talkcraft

video-talkcraft

github.com/vincentwei1021クリエイティブ・メディア1.9k517 回インストール2 週間前に更新
リポジトリ

video-talkcraft について

video-talkcraftは、Claude CodeやCodexでナレーション動画を制作するためのスキルです。台本、音声の単語単位の位置合わせ、意味に基づくショット設計、モーションデザイン、Remotionでのレンダリングを扱います。

主な機能

  1. 01ナレーション音声の単語単位の位置合わせ
  2. 02意味注釈とショット計画
  3. 03再生デモ付きモーションレシピカード
  4. 04カメラワークとビジュアルスタイルの規則
  5. 05Remotionレンダリングと動画チェック

活用シーン

  1. 01録音済み音声から解説動画を制作
  2. 02意味のまとまりごとに映像とショットを計画
  3. 03字幕とナレーションのタイミングを調整
  4. 04素材を確認しレンダリング結果を検査

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/vincentwei1021/video-talkcraft --skill video-talkcraft

インストール後、Claude Code で/video-talkcraftを入力して読み込まれたか確認します。

詳細

video-talkcraft — 口播视频 skill

三大来源合体:管线(配音→字级时间戳→Remotion)+ 词汇(108 张动效配方卡,全配可播 demo + 自包含 tsx)+ 镜头(七层模型反 PPT 系统)+ 视觉语言(Apple 范式默认版)。

核心范式:解说词驱动画面,每句都要有活的画面响应(相机极缓推拉/已有元素的变化), 但新元素只在语义拍边界进场,禁止机械的"一句一个新元素"(一句一元素是堆积型凌乱的制度根源; 分镜按语义段落切,排版预算见 cinematography.md §4.5); 一个节拍只有一个主角,说完就让位;字幕句边界 = 全片时间锚点。

流程

① 文案 → ② 配音输入+时间戳(本机CPU) → ③ 素材 → ④ SHOTBOOK 层矩阵 → ⑤ 实现(全局系统 → 首镜先做先确认 → 其余镜头)
                                → ⑥ 渲染 → ⑦ 三重验收(机器闸全过 + 1 轮审片修 P0/P1)→ ⑧ 交付(可选续审 ≤3 轮)

⓪ 开工体检:统一依赖 + 画幅与视觉语言

依赖只有一份:所有口播工程的 remotion/node_modules 与 workbench/node_modules 都是指向 <skill根>/runtime/node_modules 的软链,版本只在 runtime/package.json 里钉(@remotion/* 全家同号)。新片开工第一件事(用户 2026-09-15 定版:每次新做视频都要检查更新):

bash <skill根>/runtime/check-runtime.sh --upgrade   # 装好共享依赖 + 共享无头浏览器 + 链好 workbench;Remotion 有新版就全家升级并冒烟渲 1 帧,不过自动回滚

末行 [runtime] OK 才往下走;升了级要把 runtime/package.json / package-lock.json 的改动提交。制作中途不升级(母版段缓存不认依赖版本,混版本渲出的段不一致)。 工程目录里永远不跑 npm install(会写进共享目录);要加包 → 加进 runtime/package.json,npm ci,再提交。

画幅与视觉语言(开工先定,全流程引用)
  • 画幅默认横屏 1920×1080(用户偏好);明确要发抖音/竖屏渠道才用 1080×1920
  • 视觉语言:用户明确点了风格就按用户的来(整套 token 替换);没点时先从口播稿判定领域、派生本片风格档 (references/design-language.md §0:读全稿答"讲什么 / 对谁讲 / 什么口吻"→ 领域 → 风格档表给底色策略 / accent / 字体气质 / 材质与卡造型 / 图表语言 / 素材气质 / 能量档, 写进 SHOTBOOK §0 G0 风格档),骨架仍是 Apple 范式(一个强调色/一个投影/底色交替分幕/两档字重/ 默认幕底:浅 pastel-mesh-flow / 深 mesh-flow-dark——12 款幕底见 §1.1,代码 template/motion-systems/backdrop.tsx), 派生本片 token 落成 theme.ts。对任何风格都成立的只有一条:禁止逐场景随手取色。 卡是中性 UI,进片必须蒙皮:动效卡的 demo / tsx 是无风格的中性呈现,复制进工程后按风格档改皮(颜色 / 字体 / 圆角 / 材质 / 图表坐标轴与标记 / 占位图形)、 不改运动命门(时序 / 缓动 / 几何比例 / 层级),契约与反例见 design-language §0.4;SHOTBOOK 每镜写蒙皮行

① 口播稿

  • 每句一个信息点,钩子在第一句(数字/冲突);13 句 ≈ 95s
  • 数字一律汉字(时间戳按文本逐字锚定,197747 无法与"十九万七千"的读音对位);英文品牌词直接写(中英混合对齐已验证)
  • 先调研核实事实,列"事实红线清单"(不可说错的数字/未验证数据不引用)

② 配音输入 → 预剪 → 字级时间戳(默认本机 CPU;可选 Fish Audio)

默认使用成品配音:真人录音或任何 TTS 皆可,输入 = 一条完整配音(wav/mp3)+ 与之逐字一致的口播稿。 没有录音且用户选择合成时,可使用下面方案 B;它不会替换已有配音或默认的本机 CPU 流程。

②-0 配音预剪(真人录音必做;TTS 只会压气口,可跳过)——剪掉口水词(嗯 / 呃 / 那个…)、结巴重说、过长停顿, 在做时间戳之前跑(时间戳做完再动音频 = timing / beats 全体错位;预剪 → 时间戳 → 一切后续,顺序不可换):

python3 scripts/voice_trim.py audio/raw.wav script.json --dry-run --words-out audio/asr_words.json   # 先只出报告:逐条 [filler / repeat / pause] 起止 + 共剪多少;ASR 结果落盘
python3 scripts/voice_trim.py audio/raw.wav script.json --words audio/asr_words.json --out audio/full.wav \
    [--video dh/host_raw.mp4 --video-out dh/host.mp4]               # 用户认可后落盘(吃回上一步的词表,不再跑一次 ASR);同一条录音的人物视频用同一 EDL 剪(音画逐帧同长)
  • 稿子是真值,宁漏勿错:只剪 ASR 里稿子没有的插入段——全由口水词表拼成的(filler)、等于紧邻稿文的结巴 / 重说(repeat); ASR 听错稿子里的字永不剪(剪它就剪掉了那个字的真实发音);稿外改了措辞的重说只报告,--cut-unmatched 才剪。 词表来源缺省跑 FireRed(与下面时间戳同一后端),也吃剪映等导出的逐字 SRT(--srt)或词级 JSON(--words); 句级 SRT 定位不到口水词,脚本会警告。无稿时只剪整 token 文字恰为保守词表(嗯 / 呃 / 额 / um…)的口水词——按规范化文字精确比对、不认同音(五≠唔、饿≠呃); 紧邻重复的短词无稿只报告不剪(逐字 token 下分不清叠词与结巴)。剪同录的透明 webm 时会解一帧核 alpha,帧数对但透明丢了也算 FAIL。
  • 停顿:≥0.7s 的静音压到 0.35s(留段内最安静的一窗真实房间音,不补数字零),首尾留白 0.25 / 0.5s; 切点先找能量谷、再吸附 30fps 帧网格;cuts.json 是 EDL(含源 → 新时间轴映射表,剪人物视频与后续任何对账都吃它)。
  • dry-run 报告必须给用户过目再落盘:标了「切点未落在静音里,听一下」的条目让用户听那一处;脚本只删不合成,不改语速不改音高。 剪完人物视频后跑 ③ 的 preflight.py --media-only 核时长——同一 EDL 只对同一条录音成立。

字级时间戳(对预剪后的 audio/full.wav):

# 方案 A(已有录音,本机 CPU 对齐):
pip install zhconv pypinyin sherpa-onnx soundfile numpy   # 默认后端 FireRedASR2-CTC int8 的全部依赖
# 首次:下载模型 767MB(model.int8.onnx + tokens.txt)放 ~/.cache/koubo/<模型名>/,地址见脚本头注释
python3 scripts/timestamps_cpu.py audio/full.wav script.json audio/timestamps.json
# 备选(免手动下模型):pip install faster-whisper 后加 --backend whisper(首跑自动下载 460MB)
python3 scripts/make_timing.py audio/timestamps.json remotion/src/timing.json

# 方案 B(可选生成配音 + 时间戳,默认请求 s2.1-pro-free;可用性以 Fish Audio 为准):
pip install requests python-dotenv   # 还需 ffmpeg,生成前会检查
# 在 .env 配置 FISH_AUDIO_API_KEY=xxx 及可选 FISH_AUDIO_REFERENCE_ID=xxx(见 .env.example)
python3 scripts/tts_fishaudio.py script.json audio/full.wav audio/timestamps.json --timing-out remotion/src/timing.json
  • tts_fishaudio.py:默认 --mode sentence 每句一次请求,插入 --pause-sec 指定的真实静音(默认 0.25 秒);--mode stream 整稿一次请求、保留自然停顿。两者均接收 UTF-8 SSE,生成结束才写文件,不提供流式播放。先解码 PCM 再拼接,偏移按采样数计算。空音频、缺失/无效对齐或文本不匹配时报错;只忽略标点、大小写与字符宽度,不自动转换数字读法。中文用接口字级锚点,英文词内插值;match/ok 仅确认文本映射,仍需试听。合成后若再预剪,须重新运行 CPU 对齐。
  • timestamps_cpu.py:ASR 词级时间戳 → 与口播稿字符级对齐(CJK 是可靠锚点, 匹配键=繁简归一+无声调拼音,同音字不算错;拉丁词各家 ASR 都常拼错,在锚点间插值)→ 每句 match 质检,<0.90 标出人工听核。默认后端 FireRedASR2-CTC(尾部最稳、零误报;整段喂入 ~200s 崩、内存平方涨,脚本默认按静音切 ≤75s 段再加回偏移,--chunk-sec 可调),备选 faster-whisper; 各后端横评数据与模型下载地址见脚本头注释。
  • timestamps.json schema:{sr, total, sentences:[{i,text,start,end,match,ok,words:[{text,start,end}]}]} ——words 为 CJK 逐字 + 拉丁整段 token(标点跳过);满足此 schema 的任何对齐工具都可替换。
  • make_timing.py:转成 timing.json(chars 与文本逐字符 1:1,标点零时长),供 tSay/msSay 锚点查询
  • 配音自查(耳听):无爆音/截断/误读;句间留 ~0.3s 气口,时间锚点更稳
②-1 语义标注(2026-09-22 起必做,正主 references/semantic-annotation.md)

口播稿逐句标「这句在做什么」,落成工程根 semantics.json——这是选卡的需求侧产物,没有它,选卡只剩输入类型一道过滤、剩下几十张卡靠印象挑(2026-09-21 那支片的「我是万里」就这样拿到了全库能量最低的文字卡,库里 P0 的人名条没人查到)。

python3 <skill根>/scripts/semantic_annotate.py --init     # 从 timestamps.json 出骨架(带词法提示 hint)
#   逐句填 sem(26 词封闭词表,见 taxonomy.md「语义索引」)/ weight(main 主句 = 允许进新元素、sub 陪衬句)/ entities / need
python3 <skill根>/scripts/semantic_annotate.py --stats    # 校验:与时间戳逐字一致 · 词表封闭 · 词法硬规(我是X→自我介绍、点赞订阅→号召、数量词→数据)· 每镜至少一个 main

标注是派生物:预剪 → 时间戳 → 标注 → 其后一切;改稿或重剪后 text 对不上就是标注失效,重新 --init。 ②-1 早于分镜,所以 shot 字段此时是空的(SHOTBOOK 还没写):④ 写完后必须回填一次,否则下游的镜头级覆盖核不到任何东西——

python3 <skill根>/scripts/semantic_annotate.py --sync-shots   # ④ 之后:按 SHOTBOOK 标题的起止秒(或 shots.json)回填 shot,只动 shot,标注不丢

③ 素材

  • 素材一覧は②-1 の意味ラベル付けから生成し、思いつきで決めません:need が 証拠 の文は実際のページをキャプチャ/出典を探し、身份 にはアバターとアカウント情報、量化 にはグラフまたはデータソースが必要です。entities 内の人名/ブランド名/場所/URL は検索語です。
  • 各カットに素材モードを先に付けます(複数選択・組み合わせ可):B-roll(実写動画)/图片(写真/ポスター/イラスト)/截图(ウェブページ/インターフェースの証拠画面)/ 纯动效——たとえば「B-roll を背景に、スクリーンショットの証拠カードを重ねる」などです。ニュース/情報系の話題では証拠を優先します。Playwright によるリアルタイムのスクリーンショットは、汎用的な B-roll より情報量があります。 4 種類は taxonomy.md の入力タイプコード V/图/截图/文に対応します。SHOTBOOK の各カットには 素材:V(パス)· 图(パス)· 文 の形式で 1 行記載します(形式は④を参照)。
  • 実写素材は必須であり、任意ではありません(2026-09-06 の必須ルール):全編で B-roll/画像が 0 点の場合、動效と話者だけになり、視聴者には「PPT の説明」に見えてしまいます。 preflight.py で SHOTBOOK を照合します。V/图のカットが 0 件なら即 FAIL、V/图のカットの比率が 1/3 未満なら WARN です(証拠系の題材でスクリーンショットを中心にする場合など、SHOTBOOK に根拠を記載してください)。 「本編では B-roll を使わない」を設計上の決定として記載することは禁止です。動画素材が見つからなければ画像に切り替えます(Pexels の写真原本 → Pixabay)。画像も見つからない場合に限り、「未完了/未収集リスト」に追加します。 画像と動画は同じソース、同じキーを使用します。収集仕様(最低解像度、保存先ディレクトリ、登録)については references/broll-sources.md の「配图采集」を参照してください。
  • 実画像必須ルール:話題にキャプチャ可能な実在ページ(製品公式サイト/GitHub/ドキュメント/ギャラリー)がある場合、本編のブラウザー/ページ系カットでコードによるモックをスクリーンショットとして見せることは禁止です。カードデモ内の灰色バーの偽 UI はプレースホルダーです。 本編ではカードの「再利用ガイド」に従い、全体を <img> の実画像に置き換える必要があります。モックで表現してよいのは実在する対応物がない UI の例示に限り、SHOTBOOK の各カットに「実画像がない理由」を記載してください。関連ルール:話者が「このような完成映像/効果」について説明するとき、サンプル画面には実際の完成映像の一部を使用してください(既存の完成映像を埋め込んだ <OffthreadVideo>/実機の内部録画をトリミングしたもの、muted)。 「長いページ/ダッシュボード/パラメーター画面」について説明するときは、Playwright の全ページ縦長スクリーンショットを使用してください(拡大鏡/巡回系の動效には、実画像の座標をそのまま使用します)。 実画像上の注釈座標はすべて機械計測し、目視での推定は禁止です。ページ要素は DOM の getBoundingClientRect、完成画像はピクセル単位で計測します(目視による ±30px のずれで、円や枠が別の要素を囲んでしまいます)。スクロール/移動する実画像では、注釈(円/枠/pill)をコンテンツ座標系に固定し、コンテンツと一緒に動かしてください。画面上の固定位置に置くと、ずれの原因になります。
  • 動画だけのカットにはテーマフレームを付けます(2026-09-07 ユーザー最終決定):カット内の唯一の主体が動画 1 本(画面録画/B-roll 1 本/他者の完成映像やインタビューの引用)の場合、フレームなしで全面に貼ることも、白いカードにそのまま置くことも、偽のプレーヤーを付けることも禁止です(進行バー/再生ボタン/タイムコードは一切付けないでください。実際のプレーヤーでないものは嘘になります)。動画エリアは template/components/theme-frame.tsx の <ThemeFrame kind> で囲む必要があります。8 種類(レトロなブラウザーウィンドウ/雑誌の額縁/35mm フィルム/ポラロイド/設計図/ノート/切手のギザギザ/二重のヘアライン)から、動画のトーンに合わせて 1 種類を選び、1 本の動画で使うのは 1 種類だけにします。SHOTBOOK のスキン行に記載してください。複数動画のカード(bed-echo-blur の前景/split-60-40-story の左枠/gallery-wall-dolly)の動画エリアにも同じフレームを付けます。 フレームは形状とフレーム自体の装飾の連携だけを担当し、全体の出入りとごく緩やかなズームはカット側で制御します。フレーム内の映像には一切加工を加えません(フィルター/拡大縮小/フェードイン・フェードアウトなし)。 例外は 2 つだけです。動画を主体ではなく背景レイヤーとして使う場合(bed-echo-blur/§1.2 の実写背景レイヤー)と、製品インターフェースのカード(chat-gpt/claude-code 系。スキンがコンテンツそのもの)の場合です。 静止画像にはフレームを付けません(フレームは「これは動画です」という意味なので、画面が動かないと一目で不自然です。画像には media-pop-in/slow-push-in を使用してください)。ルールと 8 種類の一覧は design-language §1.3 を参照してください。
  • ウェブページは静止画像として貼らない:資料/素材を探す際に使用可能と判断したウェブページは、本編で静止スクリーンショットを画面に貼ることを禁止します。手持ちカメラで撮影するように見せてください——スクロール(evidence-scroll-tour◆:ページ高の約 10%/s で一定速度で上方向にスクロールし、重要な行に近づいたら事前に減速して 1〜2 秒停止)/巡回(stage-keyframe-tour◇:長いページを台の上に動かさず置き、カメラが各注目箇所に順番に寄ります)/拡大(magnifier-detail:円形の拡大鏡で一瞬見せてから戻し、pip-zoom-box◈:切り出した部分を長時間表示します)/線を引く(highlighter-sweep:文全体をなぞる、ink-underline◇:単語に下線を引く、scribble-annotation:丸や矢印で注釈する、corner-bracket-frame◈:領域を枠で囲む)。1 画面に収まるページでも、最低限 slow-push-in の動きを付けてください。 1 カットにつき主な撮影手法は 1 つ:スクロール/巡回中に拡大鏡を出したり、その場で線を引いたりしないでください。順序は「スクロールする → 停止する → 線を引く/拡大する → 再びスクロールする」です。撮影手法の選択表は shot-design.md §2④「网页拍摄」を参照してください。素材は broll-sources.md「网页拍摄素材采集」の仕様に従って保存します。Playwright の2×全ページ縦長スクリーンショットと、同一セッションで DOM を実測した対象座標 JSONを用意します(拡大鏡/線引き/停止位置にはすべてこの座標を使い、前述の「目視での推定禁止」に従います)。 撮影はすべて Remotion で縦長スクリーンショット上に作成します(シークセーフで、音声に合わせて位置を固定できます)。ブラウザーの画面録画は使用しません(フレームレートが安定せず、遅延読み込みや固定ヘッダーが不自然になり、文字単位のタイムスタンプに合わせられないためです)。唯一の例外は、1 画面に収まり、脇役としてのみ使う小さなスクリーンショット(media-pop-in で複数枚を重ねるうちの 1 枚など)です。静止画としてカードに入れてもかまいませんが、Ken Burns 効果を付ける必要があり、撮影対象の主体にはできません。
  • B-roll と指定したカットでは、英語のビジュアルコンセプト語を 2〜3 個挙げ、Pexels + Pixabay API の両方を並行検索します。候補は assets/broll/ に保存します。 ソースの分類とライセンス上の禁止事項(クレジット不要の素材のみ使用)については references/broll-sources.md を参照してください。
  • 調査記録:重要な事実を裏付けるソースページを 1 つずつスクリーンショットで保存し、sources.md のリンクとローカルのスクリーンショットを必ず対応させます (リンクだけ保存し、証拠を残さないことは禁止です)。本編で引用する際は、保存済みスクリーンショットを画面上の証拠として優先し、micro 段階の出典行を添えてください。
  • B-roll/スクリーンショットと、それに対応する話者素材(収録済み/デジタルヒューマンの完成映像)がある場合、話者は必ず常時表示の角バッジに縮小します—— 円形アバターバッジ(host-shrink-to-chip◆)または背景を切り抜いた人物を、左下/右下に配置します。話者を画面から切り替えたり、人物で画面全体を占めたりしてはいけません。 2 つの配置方法とすべての必須条件は references/host-footage.md §5 に従い、カットのプリセットは shot-design.md §2⑦を参照してください。
  • Manim のグラフ:--transparent --format=mov の後、必ず VP9 webm に変換してください(-c:v libvpx-vp9 -pix_fmt yuva420p)。
  • すべてを public/ に保存し、レンダリング時のリモート取得は禁止します。
  • ③の最後に必ず素材チェックを実行します(host-footage/broll-sources の素材導入に関する必須ルールをアサーションにします。答えはすでに reference に書かれていますが、適切なタイミングで確認されていなかったことが、2026-09-06 の振り返りで見つかった 2 つの大きな問題に共通する失敗パターンでした。そのためドキュメントを追加するのではなく、作業開始前の必須実行に変更します):
python3 scripts/preflight.py --media-only --host remotion/public/dh/host.webm --fps 30 --voice audio/full.wav
# 查:人物素材 r/avg 帧率一致(VFR)· 源片无重复帧签名 · 素材 fps = 成片 fps(不等→成片改成素材 fps,禁 -r 直转)·
#     时长与配音逐帧对齐 · 真实宽高比 · 素材盘点(实拍/图片/网页长图数)· sources.md 在册。任一 FAIL 不进 ④

④ SHOTBOOK(必ず作成し、実装前にレビュー)

まず references/shot-design.md を使い、各ショットの三面レイヤー作業シート(背景面/主体面/文字面 + 各面の動き + 7種類のショットタイププリセット)を記入してから、references/cinematography.md §4 に従ってレイヤー表に展開します。例は references/shotbook-example.md を参照してください。 各シーン:意図を一文で記述 + 主体の引き継ぎ線 + ビートごとのレイヤー表(ビートは文字単位のタイムスタンプにアンカーを置く。各行の動作は必ず「誰に合わせるか」に答えられること)。 各ショットに必ず「素材:」行を記載(機械可読。preflight でファイルを個別に stat します):- 素材:V(public/broll/gpu.mp4)· 図(public/stills/a.jpg, public/stills/b.jpg)· スクリーンショット(public/pages/gh/page.png)· 文—— V/図/スクリーンショットは必ずパスを括弧で指定し、「未収集」と記載した場合は FAIL です。純粋なモーションショットも 素材:文 と記載してください。 「## 未完了/未収集リスト」セクションを必ず記載: 「今回は X を作らない」場合は、必ずどちらかに分類します。§0 の設計決定(+根拠)に記載するか、本セクション(+ブロック理由 + 代替ソースを試したかどうか)に記載します; 「なし」は許可しますが、セクションを省略してはいけません。このセクションは、「未完了」が設計原則として取り繕われ、疑問視されない前提になってしまうことを防ぐためのものです(2026-09-06 の振り返りで判明した真の失敗モード)。 G0 に「サンプルショット:sNN」を1行記載(⑤-1 で最初のショットを先行実装して確認するために使用):デフォルトは s01。s01 が章タイトルカード/空ショットなど主体の動きがないショットの場合は、カード、字幕、人物、または素材を含む最初のショットを選びます。 ビートは機械検証可能でなければなりません:画面上の強調はすべて remotion/beats.json ({t, anchor, sentence, what})に記録します。t はすべて timing.json/atChar() から取得し、近似秒数を手入力することは禁止です——手入力の誤差は静止画 QA では検出できません。SHOTBOOK のビート表と beats.json は一致させ、 機械ゲートでは scripts/beat_lint.py により timestamps.json を |Δ|≤0.1s で検証します。 ビートに達するまで表示しない:語句アンカーに達していない数字/図形は完全に見えない状態(opacity 0)にする必要があります。 暗くしたり、薄く表示したりして「予告」することは禁止です。行内の数字は、その後に続く文字と合わせて gate する必要があります(「わずか __ 種類」の空白だけが残るのも同様に不具合です)。 ショット開始時に画面を空にしない:ショット開始から最初のモーションアンカーまでの空白が 1.5s を超える場合は、画面を支える素材を用意します (実際の b-roll/前のショットからの要素の継続/実際の素材壁)。語句アンカーを待つだけの空のキャンバスは禁止です。 ショット終端の保護帯:語句アンカーに連動する動きの着地点がショットの終点から <0.7s の場合は、前倒しするか、次のショットに移します。着地点がトランジションに飲み込まれるためです; beat_lint.py --shots shots.json で機械的に ≥0.5s の最低ラインをチェックします。 幕レベルのトランジションイベントも beats.json に記録:shape wipe/幕切り替えの遮蔽ピークの時刻も、語句アンカーから生成して表に記載します——絶対秒数を手入力したトランジションイベント表は機械検証の仕組みから外れ、静止画 QA も beat_lint も検出できません。 レイアウト予算(表全体については cinematography.md §4.5):分割は意味段落ごとに行い、各ショットの primary visual job は1つにします; 文字のみのショットには必ず添え図形を付けます(2026-09-07 のユーザーフィードバック「文字のモーションだけを積み上げるのは単調すぎる」):素材行が「文」だけのショット(章タイトルカードを除く)には、レイヤー表に「G5 線画の概念図 ← X を説明するため Y を描く」という行を追加します(references/schematic.md、コードは template/motion-systems/schematic.tsx)。preflight は行がない文字のみのショットに WARN を出します;ピボット文(「しかし今回はXではない」のような転換/問いかけ)の動きは、その文が開始させる次のショットに配置します。同時に画面に表示する主体グループは最大3つ(重みを下げて残す要素も含める)、各ショットで象限を1つ以上空けます。hero の造形は1画面に1つです。 レイアウト規則(表全体については references/layout.md):予算は「いくつ配置するか」を、規則は「どこに、どの大きさで、どう揃えるか」を定めます—— SHOTBOOK の各ショットにレイアウト行(列の幅 + グループのバウンディングボックス + 配置基準 + 文字階層)を記載します。スタイル確定フレームでは debugOverlay を有効にし、9項目を確認します。いずれか1項目でも失敗した場合は P1 です; 1文だけの hero は中央に配置しますが、顔に重ねてはいけません(B-roll 内の顔も含みます。縦方向の配置は顔を避けた三分割線を使います)。 レイアウトのローテーション(2026-09-21 のユーザーコメント:完成映像が「人物が左下、素材が中央のボックス、右側に文字、という配置に固定されすぎている」;表全体については cinematography.md §4.5 第9項):ショットごとの表を書く前に、G0 に「レイアウトリズム表」を記載します (| ショット | 人物の形態・位置 | 素材コンテナ | メインカード |。形式は cinematography.md §4 を参照)——同じ表示カード(素材表示/データ/カメラワーク系)を3ショット連続で使わず、作品全体の 1/3 以下にします(6ショット以上の作品)。人物の形態(上半身/左下隅のバッジ/右下隅のバッジ/分割画面の枠内/切り抜いて隅に配置/一時的に退場)と素材コンテナ(全面裁ち落とし/フレーム内/分割画面の枠/背景ベッド/複数画像の配置/3D 壁/長いページ)は、3ショット連続する前に少なくともどちらかを変えます。同じ B-roll を隣接する2ショットで使ってはいけません。単一の B-roll は shot-design.md §2⑦ の7種類から選び、隣接ショットでは異なる形式にします。複数素材は §2④′ の関係表に従います。preflight はリズム表とスキン行を照合し、連続使用/使用比率に FAIL を出します。ローテーションは構図を変えることであり、動きを増やすことではありません——各形式の内部では、カメラのごく緩やかなズームイン/アウトだけにします。 各ショットに必ず「カード選択行」を記載(②-1 の意味 → カード。候補は機械が提示):

python3 <skill根>/scripts/card_match.py --out qa/card-candidates.md   # 语义 × 素材行 × 卡索引 → 每镜每个主句语义的可行候选 + 排序理由 + 落选理由

実行前に semantic_annotate.py --sync-shots を実行して、shot を再設定してください(②-1 の時点ではショット割りがまだありません)。候補表では意味に基づいて候補を列挙するほか、素材を宣言したショットには素材を引き継ぐ候補を個別の行で提示します——V/図/スクリーンショットを宣言しているのに表示/カメラワーク系カードで引き継いでいない場合、素材の貼り付けだけと見なされ、preflight は FAIL と判定します。 SHOTBOOK の各ショットに - カード選択行:<意味> → <カード>、<意味> → <カード> と記載します(選択したカードが実際にスキン行に存在すること。preflight が照合します); 候補以外のカードを選ぶ場合は、そのショットに - 意味の逸脱:自己紹介 ← 冒頭ですでに身元を伝えたため、s11 では繰り返さない という行を記載します(理由は4文字以上。preflight はこの行があれば許可します)。 レイヤー表のビート行に意味の列を追加します(値は semantics.json から取得し、別の語を作らないこと)。こうすることで、「このビートでは何をしているか」がショット割りで機械可読になります。 カード選択時に必ずカードの知見を読むこと:選んだカードごとに、references/cards/<slug>.md の「既知の落とし穴」と「配置のセルフチェック」を各項目ごとにレイヤー表の自検列へ転記します。 実装後は項目ごとに確認します(例:ファインダー/円形注釈/下線系カードでは、注釈が対象を囲んでいるか必ず確認する;gooey-morph は画像にのみ使用し、文字には使用しない。人物がいない場合は中央に配置する;chapter-title-card は各章に1つのテーマカラーと、その章の内容に関連する線画モチーフを用意し、SHOTBOOK に章のテーマ行を記載する——4枚の章タイトルカードが同じ色、同じ模様では、「ページをめくる」ではなく「また同じか」と感じられます)。 カードの知見が SHOTBOOK にないなら、カードを読んでいないのと同じです。 モーションの語彙は 108枚のレシピカードから選びます。2段階のフィルターでは、どちらも references/taxonomy.md の機械生成インデックスを使います(各カードの frontmatter にある「入力/意味/素材形態/位置/props」の5項目を元に、scripts/cards_index.py --write で生成し、--check で検証します。インデックスを手動編集しても無効です):まずショットの入力で絞り込みます(人物/V/図/スクリーンショット/文/界面/シーン——「入力タイプインデックス」)。次にナレーションの意味で絞り込みます(自己紹介/データ/比較/列挙/引用/呼びかけ……26語の固定語彙表——「意味インデックス」。各カードは、そのカード専用に設計された意味の下にのみ記載されています)。インデックスで ◦ が付いているカードには、差し替え可能な内容を持つ prop がありません(リソース/スキン系しか公開されていないか、何も公開されていません)。文言やデータを変更するには tsx を編集します(各カードの再利用ガイドにある props の行に、変更箇所が記載されています)。◉◎ 新しいカード2バッチ(29枚)の md の冒頭には、別途「入力タイプ」表と「よくある場面」4項目があります。次に references/taxonomy.md の分類インデックス → references/cards/<slug>.md のパラメーターと落とし穴 → template/cards/<slug>.tsx の自己完結型 Remotion ソースコード(実装時はこちらを正とし、プロジェクトにコピーして CONFIG を変更するだけで使えます)の順に確認します。demos/<slug>/index.html は同じ画面の HTML プレビューです(open gallery/index.html で一覧を一画面に表示でき、demo はスクロールして表示されると自動再生します。★付きの実践カードには、テンプレート内の制作元が template/motion-systems|components に別途あります)。 忠実度の鉄則:使用するカードはすべて、プロジェクト内に src/cards/<slug>.tsx として実在していなければなりません (template からコピーして CONFIG を変更)。md だけ読んで、カード名から想像して「似た」簡易版を手書きすることが、最大の失敗要因です (バウンス/打撃/密度がすべて失われ、ファインダーの括弧の向きを逆に描き、名刺が色面に変わります)。機械ゲートでは scripts/card_lint.py により slug ごとの存在と類似度を検証します(≥0.55。CONFIG/文言の変更は許容範囲です)。 スキンは書き直しではありません:コピーしたカードは中立的な UI です。SHOTBOOK §0 のスタイル設定に従ってスキンを変更する必要があります——色はすべて theme token に置き換え、フォントスタックとウェイト、角丸/ボーダー/シャドウ/素材感、チャートカードの座標軸/グリッド/マーカー/数字フォント、カード系のプレースホルダー図形は実素材またはスタイル化した図形に置き換えます——変更するのはスキン層のみで、タイミング/イージング/幾何学的比率/階層は変更しません; 各ショットのレイヤー表の横にスキン行(カード名 → 変更したスキン内容)を記載します。同じ作品内では、同種のカードに同じスキンを使います。仕様、例外(製品インターフェースカードはスキンを変更しない、意味を表す色相は変更しない)、悪い例については design-language §0.4 を参照してください。 card_lint の 0.55 はスキン変更の余地を残すための値です。スキン変更なら通過しますが、動きを書き直すとスコアが下がります。 3段階の鉄則:入場 0.2~0.8s → hold(静止していて構いません——画面の動きはシーンカメラのごく緩やかなズームイン/アウトが担います)→ 退場 0.15~0.5s。入場は常に退場より強くします。同じ画面の強調は、同時に1つだけにします。 モーションを選んだら、その効果音も付けること:各カードには demos/_lib/sfx-map.js に cue 表({t, name, vol, rate?, clip?}。t はカード内の相対秒)が用意されています—— カードを選ぶ際に、その cue をショットのレイヤー表に転記します(絶対秒に換算します。vol は完成映像の基準で再設定し、≤0.35にします。 demo ライブラリの上限 0.65 は試聴用の基準であり、完成映像用の基準ではありません)。実装時は⑤の sfx 手順に従って反映します。 適用範囲:主要モーションの入場はすべてカバーし、demo ライブラリと同程度の密度にします (各カードにつき 2~6 個、約 0.4 個/s。100s の作品なら約 40~50 個)——「少なく、的確に」とは、1つのポイントに効果音を重ねないこと、音量を抑えること(≤0.35)、連続的に明らかになる演出(ゆっくりしたズーム/フォーカス/文字の順次表示)、名言の文字のみのカード、ロゴの幕引きで余白を残すこと、トランジションでは着地点ではなく勢いをつける部分だけに音を付けること、締めのチャイムや強い打撃音を避けることを指します。カバー範囲を削ることではありません。実際のサンプル(pk- プレフィックス)を優先します。 cue の file 名は ls public/sfx/ を正とすること(pk: キー名のコロンはエクスポート時に pk- になります。 キーによってはプレフィックスがもともと付いているため、pk-transition-transition-soft のような二重名になることがあります。名前が間違っていると、レンダリング時に 404 エラーで失敗します)。 ④→⑤ 闸:SHOTBOOK 写完先过 preflight 全量,再进实现(③ 的素材体检 + SHOTBOOK 对账,任一 FAIL 挡住 ⑤):

python3 scripts/preflight.py --shotbook SHOTBOOK.md --host remotion/public/dh/host.webm --fps 30 --voice audio/full.wav --shots remotion/shots.json --semantics semantics.json
# SHOTBOOK 对账:每镜有「素材:」行 · V/图/截图 的文件都在盘上 · 零 V/图 镜头 FAIL · 占比 <1/3 WARN · 「未完成 / 未采集清单」节在册 · shots.json 与镜头 id 一致
#           · 版式轮换:同卡连用 ≥3 镜 / 呈现卡 >1/3 / 节奏表连续 3 镜同形态同容器 FAIL;版式行复制 / 素材相邻复用 / 缺节奏表 WARN

⑤ 实现(Remotion)

先装全局系统再写场景(代码 template/motion-systems/,规范正主 cinematography.md §2,运动做减法): 只装 G1 CameraRig(每场景一条极缓推进或拉出的 scale 曲线,1.00→1.04~1.06 或反向,不做 x/y/旋转/模糊,impulses 留空,shots.ts 表驱动) 与 G3 让位状态机(Live demoteAt = 下一主体锚点 / Defocus,idle 关、落定即静置;demoteAt 是降权留守不是退场—— 元素压暗缩小后仍占着原槽、计入同屏预算,新主体不得摆进它的位置;旧名 retireAt 仍可用但已 deprecated,2026-09-06 因名字误导出过 P0 文字相撞); G2 视差、G4 分幕色温可选、默认不装;主体 idle / 环境呼吸 vignette / 扫光 / 曝光脉冲 / 相机脉冲一律不做。 G5 线稿示意图(schematic.tsx + icons.ts)只给纯文镜装:DrawPath / DrawIcon / Connector / Node / Plate / Panel / Cross / Tick / Traveller / Label, 全部 abs 秒驱动、机器一笔画、线到哪亮哪、一套皮;图标缺什么跑 python3 scripts/fetch_icons.py <slug,...> --merge --out <工程里 icons.ts 的路径>(不带 --out 写的是库内 template/motion-systems/icons.ts;Iconify lucide = ISC + 部分 Feather MIT,根目录 THIRD_PARTY_NOTICES.md 随 icons.ts 一起拷进工程并登记进 sources.md)。

每个镜头边界必须有明确转场处置,禁止裸切:运动承接六式(lead/tail 重叠 12–16 帧 + ShotFade, 代码 template/motion-systems/transitions.tsx)或 caret/shape-wipe 轻量式,选型见 cinematography.md §3; 一个边界只用一式。空间/流程叙事段落可改用长镜头世界画布(longtake.tsx,cinematography.md §3.5)。 template/components/ 是即取即用件:Subtitles 整句硬现版(chunks 由 props 注入)/FlowerWord 花字/SmashWord 砸字/HighlightSweep 荧光笔/PencilDraw 铅笔手绘/Mascot 吉祥物/NumberRoll。 底部字幕:素排、无标点(正主 design-language.md §5):跟读字幕不加任何动效、整句硬现,不含任何句读(数字/型号间的半角点号除外,停顿靠拆卡); 唯一例外 keyword-pop-highlight 关键词弹出且全片 ≤3 次(motion-systems 版 keywords prop 有此上限自检)。 音效落地:node scripts/sfx_dump.mjs remotion/public/sfx 把库里采样解码成 mp3 → SHOTBOOK 抄来的 cue 表落成一张 sfx.ts(绝对秒),场景里 <Audio src={staticFile(...)} startFrom/volume> 逐条摆; 音效电平比人声低 ~12dB、同帧最多一条 cue。 anime.js v4 / three.js 走 anime-remotion.ts / three-anime.ts 桥(seek-safe,工程铁律见 cinematography.md §6:零 Math.random、初始 opacity:0、lead 补偿收敛一处)。

⑤-1 首镜先做先确认(2026-09-09 用户定版——其余镜头动工前的必经关)

不再"整片做完再看第一镜":全片共性(蒙皮 / 字幕样式 / 相机幅度 / 卡的密度 / 音效电平)在一镜上就判得出,做完 13 镜再改是 13 倍返工; "改一行就整渲"的冲动也集中在这一段(2026-09-08 issue #24:9 次整片直渲全发生在制作完、验收前)。

  1. 先搭全合成骨架:remotion/ 目录建好后先 bash <skill根>/runtime/link-runtime.sh <本片工程>(node_modules 软链到统一依赖、package.json 依赖版本抄 runtime——不自建、不 npm install), 然后 entry / 全片时长 / shots.ts + shots.json 全表(时间来自 SHOTBOOK 与 timestamps,此时就写全)/ 全局系统(G1 相机、G3 让位、幕底、字幕、theme) / src/sfx.ts 先建成空表(export const CUES: SfxCue[] = [])——⑤-2 拆解契约的六个文件(shots / scenes/index / Subtitles / sfx / timing / camera)骨架期就要齐, 工作台一接入就是多轨、之后每落一条 cue 轨上就多一块;缺 sfx.ts 进不了多轨、工作台右下角会点名缺哪个契约模块(@kbsrc/* 真实 / stub 的别名在 dev server 启动时定,后补文件要重跑 link-project.sh 并重启); 其余镜头一律占位——MainVideo-example.tsx 里 SCENES[shot.id] 查不到就落到 PlaceholderScene(只露幕底 + 字幕,不写任何动效)。 骨架搭全是为了 render_shots 的段表 / 时长断言原样生效,不为首镜开豁免。
  2. 只实现样板镜(SHOTBOOK G0「样板镜」行,默认 s01):按 SHOTBOOK 全量落地——蒙皮行、卡 tsx 复制、音效 cue、转场处置——不做"先糙后精"。
  3. 渲一条有声单镜预览给用户看:只渲这一段的视频和这一段的音频,不渲整条音轨(其余镜头都是占位,整条音轨没意义;2026-09-09 demo v4 实测 6s 首镜 47s 出预览:段 34s + 段音频 10s):
# 在工程 remotion/ 目录下
node <skill根>/scripts/render_shots.mjs --shots shots.json --only s01 --seg-audio --preview-dir out/preview
open out/preview/s01.mp4        # 打开给用户看

--seg-audio 的段音频只活在这条预览里(渲完即删);拼装 / 交付仍走 --audio 整条音轨,纪律 A 不破。 4. 问一次,等回答(AskUserQuestion 类工具,两个选项,不替用户决定):「样板确认,继续做其余镜头」/「先改样板」。 改完重渲同一条预览再问;样板未确认前不得动其余镜头。确认后样板镜的蒙皮 / 字幕 / 相机幅度就是全片基线,其余镜头不得另起一套。

布局红线(数值表 design-language.md §5;几何总纲 references/layout.md)
  • 字幕位置 / 宽度 / 字号 / 常驻件方位按画幅取 design-language §5 表(竖屏常驻件必须左下,右缘是抖音点赞栏)
  • 横屏内容主列 ≤1440px 居中、边距 action-safe 96 / 标题 160(design-language §3;栏跨度与吸附见 layout.md §1)
  • 通用:切镜后 ≤10 帧必须有主视觉入场;深色场景隐藏全局顶部标题; 文字不叠截图文字(加白底卡);卡片文字防裁切(预留 padding)
  • 人物在场:先跑 scripts/face_bbox.py 实测人脸安全区(口径 host-footage.md §3), 任何文字/卡片/字幕及其背景全时刻不得进入;主信息面板放人物对侧
⑤-2 工作台实时看板(骨架搭完就开,制作全程常开)

把 ⑧ 里"交付后才打开工作台"前移到这里:⑤-1 合成骨架搭完(shots.json + Main.tsx 落盘)就接入并打开工作台,用户此后随时能看到 "做到哪了、哪镜什么状态"、能播当前实时成片、能点单镜有声预览;agent 每存一次盘预览就刷新,不用等成片(workbench/docs/live-pipeline.md)。 打开即多轨(2026-09-21 用户定版:制作中的看板与交付面是同一个多轨工作台,不是单轨"进度台")——骨架一搭完,时间线上就是 字幕 / 转场标记 / 全部镜头(占位镜画斜纹)/ 幕底 / 配音 / 逐条音效各轨;agent 每落一条 cue、每实现一镜、每改一句字幕,对应轨上 ≤3s 出现 (shots / 场景 / sfx.ts / 字幕一落盘就按稳定 id 自动同步,用户改过的不覆盖);进度轨 + 每个镜头 clip 的角标显示 占位 / 已实现 / 已渲 / 已过闸。

bash <skill根>/runtime/check-runtime.sh                                # 依赖统一在 runtime/,这一步顺手把 workbench/node_modules 链好(不在 workbench 里 npm install)
cd <skill根>/workbench
bash scripts/link-project.sh <本片工程>       # kbsrc → remotion/src;public/ 先清掉指向别的工程的旧链接再逐项软链;末行必须打印「拆解契约 OK」——否则多轨进不去,它会说缺哪个文件
npm run dev &                                                          # 已在跑就跳过;链接变了要重跑一次 npm run gen;契约文件是后补的要重启 dev server(别名启动时定)
sleep 4 && curl -s http://localhost:5199 | grep -q '动效工作台' && echo "工作台 OK" || echo "FAIL: 工作台未起"
open 'http://localhost:5199/?tracks'                                   # 进来就是多轨(字幕 / 转场 / 逐镜 / 幕底 / 配音 / 音效 + 进度轨),之后自动跟盘;?live 同义
                                                                       # 契约不全不会装任何工程,右下角点名缺哪个模块——回 ⑤-1 补齐;单轨"成片(实时)"2026-09-21 已下线
  • 状态清单 pipeline.json(工程根):工作台 dev server 按盘上产物实时推导每镜状态(占位 / 已实现 / 已渲 / 已过闸、场景比段新 = 过期), 不依赖 agent 记得写;agent 只在盘上推不出的事上落一笔,都走 node <skill根>/scripts/pipeline_state.mjs(在工程根或 remotion/ 下执行):

    时机命令说明
    ⑥⑦ 某镜机器闸 + 审片过--pass s03,s04进度轨变绿;--pass all 整片
    审片发现未清缺陷--issue "s07|P1|字幕带压到人脸安全区 12.3–12.8s"进度块红点 + 镜头面板列出;修完 --clear-issues s07
    阶段与推断不符时--stage ⑥⑦平时不用,阶段按产物自动推
    任何时候想核对不带参数重算并落盘,末行打印摘要
  • 半成品不盖页:Vite 报错不再罩住整个工作台,右下角一条提示 + 画面停在上一版;单 clip 渲染出错只把那一格画红; 页面打开时工程就是坏的,修好后成片卡自动恢复(载入失败不缓存)。只有 Player 里容错——render_shots / 工作台导出走 Remotion CLI 时任何卡抛错就是渲染失败,不会把红色错误画面当成片。

  • 看板画布 = 工程原尺寸(Root.tsx 的 width / height / fps 必须写数字字面量,写变量会按默认 1920×1080@30 并在 npm run gen 时告警)。

  • --pass 绑定当时的产物:段被删 / 场景之后又改过 → 该镜回到推导状态并提示"曾通过",重渲复核后再 --pass。

  • Main 组件别调 getInputProps()(Remotion Player 里必抛,看板上那一格会红):debug / sfxSolo 之类开关改成组件 props(Composition defaultProps), 或守卫 typeof window !== 'undefined' && !(window as any).remotion_isPlayer。

  • 看板只看不驱动:不提供"点按钮触发某一步"的接口,skill 仍是主控。

  • 拆解契约(工作台多轨 + 逐镜调参吃这几个导出,template/motion-systems/ 都有样例): shots.ts 导出 SHOTS(每镜 lead / tail 帧数、hardOut)+ shotSequence;scenes/index.ts 导出 SCENES(镜头 id → 场景组件)与 SCENE_PARAMS(镜头 id → 该场景的 PARAMS); 每个场景 export const PARAMS = [...] as const satisfies readonly ParamField[],场景内 const p = useParams(shot.id, PARAMS) 取值——表里只放语境级参数 (文案 / 颜色 / 字号 / 位置 / 入场方向),词锚时刻、时长、缓动、几何比例、层级是命门不进表(design-language §0.4 同一条线); Subtitles.tsx 导出 Subtitles + phrases()(全部可见字幕段:已扣静音区、已做显示映射)+ SubtitleLine(单句静态渲染,Subtitles 自己也用它画); Environment.tsx 导出 Environment(幕底画布,画在所有镜头之下)与 Overlays(幕级覆盖:黑震切帧 / 落幕等,画在镜头之上、字幕之下),Main.tsx 从这里取; params.ts 照模板抄(useParams / ParamsProvider / OVERRIDES),remotion/overrides.json 建成 {}; 配音文件放 public/narration.wav(模板 MainVideo-example.tsx 的约定;工作台多轨的配音块按 narration / full 解析,别叫别的名字——2026-09-21 实测叫错名字多轨整条静音、导出丢人声); 实拍底床用 template/components/bed.tsx 的 Bed(根元素 className="wb-bed")——工作台右键导透明通道时按这个标记把底床去掉,只留动效本体。 overrides.json 归工作台写、agent 永不改:用户在面板改的键以它为准,agent 只改 tsx 默认值——这就是双写冲突的解法; 渲染(render_shots / 工作台导出)读同一份,所以定版前要看一眼它是否为空(用户调过参就以调过的为准交付,交付说明里写明)。 契约是否齐全由机器闸 python3 scripts/workbench_contract_lint.py <工程根> 核(⑥⑦ 六条闸之一;样板镜阶段加 --allow-placeholder)。

⑥⑦ 渲染 + 三重验收(机器闸全过 → 1 轮审片 → 交付)

迭代纪律(管着本节全部循环)
  • 闸报 FAIL 先读闸怎么量的,再动手改(闸源码就在 scripts/):motion_check 的静止判定是 freezedetect n=0.003 d=0.8 (逐帧平均像素差 >0.3% 才算动)——平滑渐变、漂背景、纯透明度呼吸都不产生像素变化,相机缩放才改每一个像素; sfx_check --mix 量的是 [t−0.05, t+0.45] 0.5s 窗。读闸还能判出闸本身够不够得着 (句间气口 <0.3s 而音效电平上限 −22dB 时,UNMASKED 在数学上不可达)——这种"输入决定、不是本片可修"的结论必须早下。
  • 静帧优先:render_stills.mjs 约 10s 一批,能答掉九成"这个改动对不对"(落点/遮挡/文案/配色/层级/取景); 整渲只留给跑机器闸和静帧查不了的时域缺陷(抖动/闪烁/音画/freezedetect)。
  • 改哪段渲哪段、复审改动攒批再渲:--only s2,s4 / --changed sNN 只渲改动段, 复核时逐段对时间戳确认"改的段是新的、没改的段沿用缓存",别习惯性 --all;多批 P0/P1 攒成 1~2 批再渲。

⑥-0 渲染前静态预检(零母版成本)——三道把返修拦在母版前:

python3 scripts/beat_gap_check.py remotion/beats.json remotion/shots.json   # 空台预检(advisory,从节拍表推算)
# 每条 WARN 都要答得出"这窗里什么在动":正常答案只有一个——该镜的相机极缓推拉覆盖了这窗(不补 idle/呼吸层);相机确在动就 --ok 声明
cd remotion && python3 ../scripts/freeze_probe.py --shots shots.json          # 静止探针:每镜 3 个时点渲「相隔 0.8s 的两帧」,按 freezedetect 同款 yuv420p 均差判
# 量的是真实合成像素(beat_gap_check 看不见持续运动);15 镜 90 张静帧实测 ~2 分钟,替掉"渲 10~20 分钟母版才知道 S06 过不了静止闸"。
# 2026-09-06 v4 实测:45 个采样点与母版 freezedetect 一致 98%(1 个 mafd 0.72 的边界点多报);是单点粗筛,不是全覆盖

清单二·状态切换窗:人物轨道每个 half↔chip 切换点、每个 wipe 时刻 ±0.5s 列入静帧抽样点—— 字幕带换位与人物几何过渡的穿越冲突(黑字压黑衣)只藏在这种窗口里,句级/锚点抽帧都错过。 字幕带换位必须等几何过渡完成再切(half→chip 延后 ~0.45s 落位)。

⑥-1 静帧抽样(一次 bundle 批量渲,比逐张 npx remotion still 快一个量级):

# 两个 node 渲染脚本都必须在工程 remotion/ 目录下执行(Remotion 模块从工程自己的 node_modules 解析,
# 并自动加载工程的 remotion.config.ts——webpack alias / publicDir / browserExecutable 与 CLI 渲染一致;
# 吃 inputProps 的合成给 --props @props.json;素材是符号链接时 --public-dir 指向解引用同步后的目录)。
# 首跑没装浏览器会联网下载 Chrome Headless Shell(~95MB);离线机先 npx remotion browser ensure,或 --browser 指向本机 headless shell
# 中间帧格式:renderMedia 不读 remotion.config.ts,此前永远是默认 JPEG-80——render_shots 现在透传配置里显式设的
# setVideoImageFormat / setJpegQuality / setCrf,命令行 --image-format png|jpeg --jpeg-quality 95 --crf 18 再覆盖,
# 生效值打在日志首行 `encode: …`。默认建议 jpegQuality 95:2026-09-06 白底片同段三格式实测,jpeg95 对 png 的 PSNR 47.9dB、
# jpeg80 46.0dB,静态窗噪声底三者相同(2.83/2.83/3.07),渲染时间差异小于本机运行间噪声——格式按画质定不按速度定;
# 深底渐变片的色带(用户实测 raw_mean 0.67→0.39)本次没有对应素材复测,深底风格档仍按 png。
node <skill根>/scripts/render_stills.mjs --times 2.0,7.2,...   # 抽样点=每镜入/出+关键锚点+状态切换窗

⑥-1.5 定渲染节奏(样板镜已在 ⑤-1 由用户确认,这里不再单独渲首镜给用户看;首渲仍禁止直接 --all——先问节奏): 若 ⑤-1 之后改过全局系统(theme / 字幕 / 相机 / 幕底),先 --only s01 --seg-audio --preview-dir out/preview 重出样板镜,自查与确认版一致即可,不必再问用户。 然后问一次(AskUserQuestion 类工具,两个选项,不替用户决定):

  1. 整片渲:⑥-2 的 --all --parallel 4 --concat … --audio … --mux …;
  2. 逐镜节奏:--only s02 --audio … --preview-dir out/preview 渲一镜、开给用户看一镜、等用户说"继续 / 改"再下一镜, 改动用 --changed sNN;全部看完再 --concat + --mux 拼装。 两种节奏最后都要过 ⑥-2 的帧数断言与 ⑦ 三重验收——"用户看过"不等于"验收过",机器闸与独立审片照做。

⑥-2 分段渲染母版制——按镜头切段、段内单进程连续渲(段内光栅自洽;多 tab 并发会产生周期性相位抖动), 段间 --parallel 4 实测比单进程快 1.3~1.8×(本机负载不同两次分别 253→139s、307→230s,4 镜 900 帧); 整条音轨没有光栅问题,--audio-concurrency 4(默认)实测 419s→175s(116s 片),imageFormat:'none' 只省 6%——浏览器逐帧 seek 才是音轨渲染的主成本; 段边界都是切镜点,K 段并行:

# 在工程 remotion/ 目录下执行。首渲:K 段并行 + 拼装 + 整条音轨 + 混音
node <skill根>/scripts/render_shots.mjs --shots shots.json --all --parallel 4 \
     --concat out/assembled.mp4 --audio out/full-mix.wav --mux out/vN.mp4
# 改一个镜头 → 只重渲该段±邻段(lead/tail 交叠波及邻镜边缘)再拼装
node <skill根>/scripts/render_shots.mjs --shots shots.json --changed s14 \
     --concat out/assembled.mp4 --audio out/full-mix.wav --mux out/vN+1.mp4
npx remotion render src/entry.ts <Comp> out/sfx-solo.wav --props='{"sfxSolo":true}' --codec=wav

音画对齐三条硬纪律(脚本内建断言,缺一必错位):音轨整条不分段(视频段全 muted, 音轨单渲一次交付时混入——每段各带 AAC 再拼会因编码器前导延迟逐段错位); 段边界取整与 Sequence 同规则(差 1 帧=画面节拍整体偏 33ms);帧数断言(每段实数帧+ 拼装总帧数精确相等,不等即 FAIL)。音轨缓存过三关才复用:时长 == 合成时长、素材/inputProps/时序配置文件 (beats.json、cues.json、src/sfx.ts 等文件名含 sfx|cue|beat|audio|sound|timing)三项指纹未变、非半截临时文件; 指纹看不见的改动(音量常量写在组件里)用 --force-audio。 修复验证同理只渲受影响段过闸(freezedetect 单段可跑),不整渲。

# —— 关卡 1 机器闸:六条命令一次跑完,全 PASS 才进关卡 2 独立审片 ——
python3 scripts/motion_check.py out/vN.mp4 --baseline remotion/public/dh/host.webm --window <t>,<人物区 W:H:X:Y>
                                                  # 画面健康:静止段 + 抖动。抖动先查重复帧签名(人物区周期性近零差 = 素材帧率病,
                                                  # 处方在 preflight,--concurrency=1 治不了),再查并发光栅;--baseline 同窗量源片,
                                                  # 素材自带的噪声降 WARN——有人物素材的片必给,人物区窗必加
python3 scripts/sfx_check.py out/sfx-solo.wav cues.json                  # 音效在场(峰值 ≥−45dBFS)
python3 scripts/sfx_check.py --mix out/vN.mp4 audio/full.wav cues.json --timestamps audio/timestamps.json
                                                  # 音效可听(掩蔽分级);--timestamps 数气口,UNMASKED 门槛不超过气口数并打印"不可达"原因
python3 scripts/card_lint.py remotion/src <slug,slug,...>                # 卡片保真(复制自 template/cards)
python3 scripts/beat_lint.py remotion/beats.json audio/timestamps.json --shots remotion/shots.json --anchors anchors.json
                                                  # 词落点 |Δ|≤0.1s + 镜尾保护带 ≥0.5s + label 只许 [A-Za-z0-9_-](进文件名/JS 字符串)
python3 scripts/workbench_contract_lint.py <本片工程根>                      # 工作台拆解契约(⑤-2):六个契约文件 · SCENES/SCENE_PARAMS 与 shots.json 对账 ·
                                                  # 每镜 PARAMS + useParams · params.ts / overrides.json · Subtitles 的 phrases/SubtitleLine——
                                                  # 漏一个导出 = 用户打开工作台才发现按钮灰 / 面板空;⑤-1 只有样板镜时加 --allow-placeholder
# 评审材料抽帧:每句 2 帧 + 动效锚点帧(anchors.json 从 beats.json 导出)
# 连拍三帧对只抽 anchors.json 里标了 "burst": true 的锚点——状态切换(两态翻转/换场/砸入落位)
# 与高风险区域必须标;其余锚点只抽定妆帧。
# motion_check 的抖动闸只量 ≤12 个 18s 间隔的固定裁剪窗、快速运动窗还跳过——窗外的短闪烁它看不见,
# 所以连拍不是可选项;--bursts 全抽只在抖动闸报警需人眼定位时用。同一份 anchors.json 也喂给 motion_check
python3 scripts/qa_extract.py out/vN.mp4 audio/timestamps.json /tmp/qa_vN 540 anchors.json
python3 scripts/motion_check.py out/vN.mp4 --anchors anchors.json   # 锚点 t+0.6s 各加一窗,结尾打印实际覆盖窗数
# 评审拼图:帧目录拼 3×4 网格(评审先整版浏览、可疑帧再回原目录单张放大)
python3 scripts/contact_sheet.py /tmp/qa_vN /tmp/qa_vN_sheets

机器闸口径备忘:音效两查要求工程主音轨支持 {!getInputProps().sfxSolo && <Audio .../>}; 可听度 MASKED>50% 或 UNMASKED 少于 max(3, 片长/30s) 即 FAIL("81/81 在场但全被人声掩蔽"是典型翻车), 良品口径:转场/边界 cue 落句间 ~0.3s 气口出声;shots.json = 分镜表导出的 [{"id","start","end"}](与 shots.ts 同源)。

关卡 2 独立审片(协议正主 references/review-protocol.md,评审 subagent 被派时必须先读它): 必须派全新上下文的 subagent——不许制作者自评、禁止 fork/复用制作对话当"评审"、禁止对同一评审做 followup 复审 (fork 出来的评审继承制作者视角,对照物又是制作者自己写的 SHOTBOOK,形成自证闭环)。 派发 / 等待 / 判活 / 重派按 review-protocol §1.6 的 harness 无关原语表(PACKET / DISPATCH / FAN-OUT / WAIT / LIVENESS / RE-DISPATCH,Claude Code · Codex · headless 各一列);WAIT 以落盘 REVIEW.md 的结束行为准,不以子代理完成通知为准。 制作者自己的首轮版式过目也委托子代理(只回文字缺陷清单,几十张图的图像 token 不进主上下文)。 备齐协议 §1.2 的材料五件套,评审按 rubric 出 P0/P1/P2 清单,修完 P0 + P1 才算过关;返修按协议 §3 给量测数字、只渲受影响段。 关卡 3 规则合规:cinematography.md §5 八条逐镜核 + 交付前终检(调试 overlay 关、成片缩到 390px 宽可读),条目见 review-protocol.md §2。 审片循环:机器闸全过后只做 1 轮独立审片 → 修 P0/P1 → 即交付,同时问用户是否续审(自动轮次封顶 3 轮) 并打开动效工作台(⑧);遗留 P2 清单随交付物。细则 review-protocol.md §4。

⑧ 交付

两遍 loudnorm(单遍是动态模式,会压音效瞬态;且 loudnorm 内部升到 192kHz,不加 -ar 会把 96k 漏进 AAC——2026-09-06 实测):

# 第一遍量测(只看 stderr 的 JSON)
ffmpeg -i out/final.mp4 -af "loudnorm=I=-15:TP=-1.5:LRA=11:print_format=json" -f null - 2>&1 | sed -n '/^{/,/^}/p' > /tmp/ln.json
# 第二遍线性归一(measured_* 从 /tmp/ln.json 抄:input_i / input_tp / input_lra / input_thresh / target_offset)
ffmpeg -i out/final.mp4 -c:v copy \
  -af "loudnorm=I=-15:TP=-1.5:LRA=11:measured_I=<input_i>:measured_TP=<input_tp>:measured_LRA=<input_lra>:measured_thresh=<input_thresh>:offset=<target_offset>:linear=true" \
  -ar 48000 -c:a aac -b:a 192k delivery.mp4

听一遍确认配音无爆音/截断、音效不压人声不叠帧(loudnorm 之后音效相对电平会变)—— agent 自己听不了成品,sfx_check.py --mix 就是耳听的机器替身:交付前必须对 delivery.mp4 重跑一次; 简介附素材来源行(用了库内采样时加 sfx 来源,见 demos/_lib/sfx/ATTRIBUTION.md)。

交付时工作台应已自 ⑤-2 起常开(没开就按 ⑤-2 那段接入并打开;不要等用户问;与"是否继续自动审改"的询问同时给出, 见 ⑥⑦ 审片循环制度)——给用户一个剪映式界面做人工微调,并 pipeline_state.mjs --pass … 把过闸镜头钉绿:

bash <skill根>/runtime/check-runtime.sh          # 统一依赖体检 + 链好 workbench/node_modules(不在 workbench 里 npm install)
cd <skill根>/workbench
bash scripts/link-project.sh <本片工程>          # 链接本片工程(机器本地符号链接,不进库;手写 ln -sfn 循环不会替换指向旧工程目录的链接——2026-09-15 实测 logos 仍指上一支片)
mkdir -p public && for f in <本片工程>/remotion/public/*; do ln -sfn "$f" "public/$(basename "$f")"; done
npm run dev &                                     # 浏览器打开 http://localhost:5199/?tracks 并告知用户(多轨面;?live 同义)
sleep 4 && curl -s http://localhost:5199 | grep -q '动效工作台' && echo "工作台 OK" || echo "FAIL: 工作台未起——禁止用 remotion studio 代替"

防误操作:交付给用户的界面只能是这个工作台(页面标题「TalkCraft Workbench · 动效工作台」,上面那行断言就是核验)。 npx remotion studio(工程内)或工作台的 npm run studio 是开发者调参入口,不是交付面,不得用它代替工作台; npm run dev 必须从 <skill根>/workbench 执行(别在本片工程目录里起)。

工作台打开(?tracks)就是多轨:逐句字幕(文本可改)/ 幕级覆盖 / 转场标记 / 逐镜参数化镜头 / 幕底 / 配音 / 逐条音效——自 ⑤-2 起它就在,交付时只是全部变绿; 交付面必须是这个多轨面,不是单轨「成片(实时)」(2026-09-21 用户反馈:成片做完打开的还是单轨进度台,没法分轨改背景 / 动效 / 字幕 / 音效); 镜头里每个场景 PARAMS 声明的文案 / 颜色 / 字号 / 位置 / 入场方向逐项可调(词锚节拍、时长、缓动、相机固定), 改动写回本片 remotion/overrides.json,渲染读同一份;改完点「导出成片」(内置 Remotion 渲染,遵守单并发纪律)。详见 workbench/README.md / GUIDE.md ⑥。 接入按真实路径解析、契约模块缺哪个只降级哪个(workbench/kbsrc.map.mjs);本 skill 产出的工程按 ⑤「拆解契约」六个文件写就能拆—— 拆解按钮灰着,先看 npm run gen 末行报的是缺哪个契约文件,别去补造 promo 形态的 PromoScenes / Host。 发布时**推荐(非强制)**在简介 @ 一下本 skill 作者——对作者是最好的支持: X @VincentWei93 · 抖音 @Vincent · 小红书 @Vincent。 有建议、反馈欢迎扫 README「微信讨论群」小节的二维码进交流群。

目录路由

要做什么看哪里
定视觉语言(色板/字阶/间距/字幕规范)· 幕底 12 款references/design-language.md(Apple 范式默认版;§1.1 幕底菜单 → template/motion-systems/backdrop.tsx)
排版:放哪 / 多大 / 怎么对齐(栅格 · 间距令牌 · 居中 · 字阶 · 碰撞 · 校验九项)references/layout.md
给镜头做背景/主体/文字分层设计references/shot-design.md(三面工作单 + 七型预设)
镜头方法论/反PPT/SHOTBOOK格式/验收references/cinematography.md(+ shotbook-example.md)
审片:关卡 2 材料五件套 / rubric / 缺陷分级 · 关卡 3 · 返修纪律 · 审片循环references/review-protocol.md(评审 subagent 必读)
转场(六式代码)/ 长镜头template/motion-systems/transitions.tsx / longtake.tsx(cinematography.md §3、§3.5)
纯文字镜配线稿示意图(G5:词汇 · 语义图形词典 · 节拍纪律 · 落位自检)references/schematic.md → template/motion-systems/schematic.tsx + icons.ts(scripts/fetch_icons.py 抓 Iconify lucide)
视频容器边框八式(单视频镜不裸贴、不装假播放器;任何卡的视频区可包)template/components/theme-frame.tsx(规则与选式:design-language §1.3)
选动效/查参数和坑references/taxonomy.md(两道生成索引:输入类型 / 语义)→ references/cards/ → template/cards/(tsx 源码)+ demos//gallery/(预览)
口播稿逐句语义标注(②-1)· 选卡候选表 · 卡索引生成references/semantic-annotation.md · scripts/semantic_annotate.py · scripts/card_match.py · scripts/cards_index.py
找素材 · 配图采集 · 网页拍摄素材采集(全页 2× 长图 + DOM 坐标 JSON)references/broll-sources.md
开工体检(③ 素材期 --media-only / ④→⑤ 闸全量:人物素材帧率·重复帧·时长·比例 + SHOTBOOK 素材对账·未完成清单)scripts/preflight.py
静止探针(母版前用真实合成帧差预判 freezedetect)scripts/freeze_probe.py
人物素材(输入规格 / CPU 抠像 / 人脸安全区)· 与 B-roll 同屏怎么摆references/host-footage.md + scripts/face_bbox.py
新增配方卡references/demo-spec.md,验证 node scripts/verify-demo.mjs <slug>
可复制代码template/cards/(108 卡逐卡自包含 tsx)、template/motion-systems/(极缓推拉相机/让位/桥)、template/components/(字幕/花字/铅笔/吉祥物)
成片后人工微调 / 导出workbench/(剪映式工作台:多轨时间线 + 全卡参数化 + 成片拆解 + Remotion 渲染导出)
制作全程实时看板(⑤-2 起常开、打开即多轨且自动跟盘:进度轨 / 镜头状态角标 / 阶段栏 / 单镜预览 / 半成品不盖页)· 状态清单workbench/docs/live-pipeline.md · scripts/pipeline_state.mjs(--pass / --issue / --stage;状态按产物自动推)
字级时间戳(本机 CPU)scripts/timestamps_cpu.py(FireRedASR2-CTC 默认 / faster-whisper 备选,+ 口播稿逐字对齐)→ scripts/make_timing.py
一键配音+时间戳(Fish Audio 免费层)scripts/tts_fishaudio.py(基于 s2.1-pro-free 流式 TTS,生成 full.wav + timestamps.json + 可选 timing.json)
配音预剪(口水词 / 结巴重说 / 过长停顿,时间戳之前跑;同一 EDL 剪人物视频)scripts/voice_trim.py(②-0;稿子为真值只剪稿外插入段;词表来源 ASR / 逐字 SRT / 词级 JSON;cuts.json EDL 含时间轴映射)
闸报 FAIL 了怎么办 · 怎么少烧母版⑥⑦「迭代纪律」三条——先读闸怎么量的再改 · 静帧优先 · 改哪段渲哪段、复审改动攒批
机器闸(画面健康 / 保真 / 词落点+镜尾 / 音效)scripts/motion_check.py(静止段+并发光栅抖动双判定)/ scripts/card_lint.py(卡片须复制自 template/cards)/ scripts/beat_lint.py(词落点对 timestamps + --shots 镜尾保护带)/ scripts/sfx_check.py(solo 在场 + --mix 可听度)
渲染提速(分段母版 / 批量静帧 / 空台预检 / 评审拼图)· 首镜先做先确认(⑤-1)scripts/render_shots.mjs(段渲+拼装+音轨混入+帧数断言;--changed sNN 单镜头迭代 53s;--only s01 --seg-audio --preview-dir 样板镜有声预览、不渲整条音轨(⑤-1);⑥-1.5 问用户整片还是逐镜)/ scripts/render_stills.mjs(一次 bundle 批量 still)/ scripts/beat_gap_check.py(渲染前空台预检)/ scripts/contact_sheet.py(QA 帧拼 3×4 网格)
动效配套音效逐卡 cue 表 demos/_lib/sfx-map.js(口味纪律见 references/demo-spec.md「Demo 硬性要求」第 8 条);制作端 node scripts/sfx_dump.mjs 导出采样

コメント