mcpField
W
wjs-overlaying-video

wjs-overlaying-video

github.com/jianshuoクリエイティブ・メディア137170 回インストール今日更新
リポジトリ

wjs-overlaying-video について

wjs-overlaying-videoは、動画クリップにカバー、字幕、イラスト、CTAを加える後処理スキルです。完成済み動画や分割スキルから受け取った素材をHyperFramesで合成し、最終エンコード1回で出力します。

主な機能

  1. 01カバー、字幕、章表示、CTAの追加
  2. 02HTML/CSSによる字幕表示
  3. 03指定箇所へのイラストやモーショングラフィックスの配置
  4. 04オーバーレイを単一のHyperFramesプロジェクトに合成
  5. 05最終エンコード1回で動画を出力

活用シーン

  1. 01カット済みクリップから投稿用動画を作成
  2. 02単語ハイライト字幕や章カードを追加
  3. 03冒頭の見どころや終盤にグラフィックとCTAを追加

インストール

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

$npx skills add https://github.com/jianshuo/claude-skills --skill wjs-overlaying-video

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

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
build_hf_clips.py
custom_overlay_recipes.md
example_spec.json
illustration_patterns.md
illustrations.py
scripts/
scaffold.py

詳細

wjs-overlaying-video

動画クリップのポストプロダクション:カバー、字幕、イラスト、CTA、 カスタムモーショングラフィックスを、すべて1つのHyperFramesプロジェクトで構成し、 1回の最終エンコードでレンダリングします。デコード/再エンコードの 連続処理は行いません(処理のたびに品質が低下し、時間もかかります)。

使用する場面

  • /wjs-segmenting-video の後工程 — セグメント化スキルから、トリミング済みクリップとクリップごとのSRTが渡されます。このスキルで、カバー/字幕/イラスト/CTAを加えた、アップロード可能なMP4に仕上げます。
  • 完成した動画にモーショングラフィックスを加えたい場合:オープニングのフック、重要な引用のコールアウト、締めのスローガン、チャプターカード、最初のフレームに使うAI生成カバー。
  • 動画にHTML/CSS品質の字幕を加えたい場合(単語ごとの動的なハイライト、カスタムフォント、大きな縁取り文字、キューごとのシーク)。
  • 特定のフック場面にイラストを重ねたい場合:図解、強調する大きな文字、フローチャート。

使用しない場面:

  • 長い動画をクリップに分割する場合 → /wjs-segmenting-video を使用してください。
  • 元となるSRTを作成する場合 → /wjs-transcribing-audio を使用してください(別の言語が必要な場合は、その後に /wjs-translating-subtitles を使用してください)。
  • 元動画が固定されていないHyperFramesの本格的な制作 → hyperframes を直接使用してください。
  • 微信视频号/抖音へのアップロード(公開APIがありません) → このスキルでMP4を作成し、手動でアップロードします。

このスキルでできること/できないこと

できることできないこと
動画クリップに重ねるすべての要素:カバー、字幕、チャプター、イラスト、CTA動画のカット/トリミング(その場合は /wjs-segmenting-video + /wjs-reframing-video を使用)
クリップごとに1つのHyperFramesコンポジションを作成し、1回の最終エンコードを実行デコード/エンコードを複数回繰り返す処理
cover は出力の文字どおり最初のフレームです(プラットフォームがサムネイルとして自動選択します)ユーザーが別途アップロードするサムネイルファイル
字幕はHTML/CSSです — 何に重ねても白文字を読みやすくするための -webkit-text-strokelibassによる焼き込み(非推奨)
イラスト:再利用可能な stack/hammer パターンと、カスタム対応の拡張手段再利用せず、イラストごとに個別のHTML/CSSを作成
ターゲットのネイティブアスペクト比でAIカバーを再生成(縦向きは1024×1792、横向きは1536×1024)プラットフォーム上で余白が追加されたり、切り抜かれたりする、1024×1536の単一デフォルト

パイプライン

clip.mp4 + clip.zh-CN.burn.srt   (from /wjs-segmenting-video hand-off)
   ↓
1. (Optional) Generate AI cover via gpt-image-2
   make_cover.py --segments S.json --out output/ --size 1024x1792
   cover_NN_slug.png

2. Scaffold a HyperFrames project per clip
   hf_clip_NN/1080/{index.html, clip.mp4, cover.png, captions.json}

3. Compose: cover scene + body video + caption track + chapter chip
            + 1-2 illustrations at hook moments + CTA scene

4. npm run check (lint + validate + visual inspect)
   npm run render → upload-ready MP4

縦向きの1080×1920、2分間のコンポジションは、MシリーズのMacで約2~3分でレンダリングされます。

色:合成前にHLG/HDRソースをSDRにトーンマッピングする

トーンマッピングするのは、実際にHLG/HDRのソースだけです。 ボディクリップがすでにRec.709 SDRの場合(グレーディング済みのマルチカムレンダーや、S-Log3→709 LUTが適用済みのpolysync出力など)、HLGのトーンマッピング手順を適用すると、正しい色が白っぽくなったり暗くなったりします。build_hf_clips.py の tonemap_to_sdr は color_transfer(_is_hlg_hdr)を調べるようになりました。HLG/PQならトーンマッピングし、それ以外ならトーンマッピングなしで高密度キーフレームの再エンコードを行います。どちらの場合も、HyperFramesに必要な -g 30 の高密度キーフレームエンコードが適用されます。

iPhoneや最新のカメラで撮影した映像は、HLG HDR(bt2020 / arib-std-b67)であることがよくあります。そのままHyperFramesに入力すると、白っぽく(「发白」)レンダリングされるか、安易に --sdr を指定した場合は暗く(「发黑」)なります。また、HDRのx265パスではレンダラーがハングすることがあります。合成前にボディクリップを、zscaleでトーンマッピングした30fpsのh264 SDR(bt709)に変換してください。

検証済みの手順(build_hf_clips.py の tonemap_to_sdr())。npl=203 はmacOSネイティブ(qlmanage)のリファレンスと明るさを合わせ、hable はコントラストを維持します。元の見た目(自然な肌、植物、レンガ)を保ち、白っぽさや暗さが生じません。

# zscale-capable ffmpeg — Homebrew's lacks zscale/tonemap.
# imageio-ffmpeg ships one: .../imageio_ffmpeg/binaries/ffmpeg-macos-aarch64-v7.1
TONEMAP_VF = ("zscale=tin=arib-std-b67:min=bt2020nc:pin=bt2020:t=linear:npl=203,"
              "format=gbrpf32le,tonemap=tonemap=hable:desat=0,"
              "zscale=t=bt709:m=bt709:p=bt709:r=tv,format=yuv420p,fps=30")
# encode: libx264 -crf 18 -color_primaries/-trc/-colorspace bt709
#         -g 30 -keyint_min 30 -movflags +faststart   ← see gotcha below

高密度キーフレームに関する注意点。 HyperFramesはボディ動画をフレームごとにシークします。キーフレームが疎な(GOPが長い)クリップでは、古いフレームで停止することがあります。レンダーログに Video "video" has sparse keyframes という警告が表示されます。シークがすべて正しく行われるよう、SDRクリップは必ず -g 30 -keyint_min 30(1秒ごとにキーフレームを1つ)でエンコードしてください。

確認: レンダーログに No HDR sources detected — rendering SDR と表示されることを確認してください。HDRが検出されたと表示される場合、クリップはトーンマッピングされていません。まずその問題を修正してください。

バージョンスタンプ(すべての出力)

END/CTAシーンの表示中に、右下に 「skill名字 + 版本号」 をスタンプし、各レンダーを生成したパイプラインのバージョンを追跡できるようにします。パイプラインを変更するたびに、build_hf_clips.py の VERSION を更新してください。

#ver-stamp { position: absolute; right: 28px; bottom: 28px; z-index: 30;
  font-size: 20px; color: rgba(150,150,156,0.55); letter-spacing: 0.06em; }
<div id="ver-stamp" class="clip" data-start="{cta_start}" data-duration="{cta_dur}"
     data-track-index="2">wjs-overlaying-video v1.3</div>

標準オーバーレイの種類(6つの基本要素)

各クリップの最終的な合成は、これらをいくつか組み合わせて作成します。エージェントがクリップごとに適切なものを選びます。ポッドキャストのハイライトでは通常6つすべてを使い、注釈オーバーレイを1つだけ表示する場合は1~2個だけ使います。

1. cover — フレーム全体を使ったAI画像を最初のフレームにする

カバーは最初のフレームそのものです(アニメーションなし、ズームなし)。そのため、最初のフレームをサムネイルとして自動選択するプラットフォームでは、デザインしたカバーがデフォルトで使われます。必ず ffmpeg -ss 0 -vframes 1 で確認してください — フレーム 0 が黒だと、プラットフォームのサムネイルも黒くなります。

HTML:

<div id="cover" class="clip" data-start="0" data-duration="1.6"
     data-track-index="1" data-layout-allow-overflow>
  <img src="cover.png" alt="" data-layout-allow-overflow />
</div>

CSS:

#cover { position: absolute; inset: 0; background: #0c0d10; overflow: hidden; }
#cover img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }

生成: /wjs-segmenting-video/scripts/make_cover.py を使用します (中間フレームを参照画像として gpt-image-2 images edit を実行します):

# For 1080×1920 vertical output (视频号 / 抖音):
make_cover.py --segments S.json --out output/ --size 1024x1792 [--single N]

# For 1920×1080 horizontal output (YouTube / B站):
make_cover.py --segments S.json --out output/ --size 1536x1024

アスペクト比は出力フレームに合わせる必要があります。 --size 1024x1536(2:3、スクリプトのデフォルト値)は、9:16 の出力ではレターボックス化またはクロップされます。縦向きの場合は、必ず 1024x1792 を指定してください。カバー画像のアスペクト比が視聴者にフレーム全体で表示されるため、不一致は目立ちます。--single N で 1 枚だけ再生成してください。codex プロバイダーはバッチの途中で一時的に失敗することがあります。

Codex の認証が必要です:スクリプトは gpt-image-2-skill 経由で codex CLI を呼び出します。~/.codex/auth.json が存在しない場合、スクリプトはエラーになります。設定方法については gpt-image-2-skill を参照してください。

参照フレームは OUTPUT の向きに合わせる必要があります。 make_cover は output/frame_NN_slug.jpg を読み込み、保持する写真の背景として使用します。横向きの2人用素材から作成した縦向きクリップの場合、デフォルトの frame_NN は横向きの2ショットです。これを 1024x1792 のカバーに渡すと、2人が縦長の画面に不自然に詰め込まれます。まず、すでにクロップ済みの本編クリップから縦向きの話者1人のフレームを切り出し、frame_NN_slug.jpg と置き換えてから、make_cover を実行してください (ffmpeg -ss <t> -i clip_vert.mp4 -frames:v 1 frame_NN_slug.jpg)。これでカバーの構図が本編と一致します。

タイトルを焼き込んだカバー ⇒ アニメーション付きの #hook オープナーを削除します。 make_cover はセグメントのタイトルをカバー画像に焼き込みます(白の塗りつぶし+太い黒の縁取り、顔にかからない位置)。このカバー自体がタイトルカードです。タイトルを二重に表示することになるため、アニメーション付きの #hook オープナーを重ねて実行しないでください(下記のオーバーレイタイプを参照)。どちらか一方を選んでください。make_cover でタイトルを焼き込んだカバーを使う場合は HOOK を空にするか、通常のビデオフレームのカバーとアニメーション付きの hook を組み合わせます。ユーザーが承認した標準設定は、make_cover でタイトルを焼き込んだカバー(AI による描画を加えず、タイトルを映像に焼き込んだクリーンなビデオフレーム)です。

2. caption — キーワード強調キャプション(字幕スタイル 03)、SRT に同期

王建硕に選ばれたスタイル(ユーザー承認済み):字幕スタイル 03「キーワード強調」+ 思源宋体 Noto Serif SC。 白いセリフ体の文字に黒い縁取りを付け、インパクトのある定量的なキーワード (倍数/大きな数量/パーセンテージ)を小さなゴールドのグラデーションブロックで囲みます。 キャプションは固定ゾーン内で垂直方向に中央揃えされるため、1 行と 2 行の キューが切り替わっても視覚的な中心が上下に跳ねません。

候補スタイルは 4 種類(縁取り白文字/質感のある帯/キーワード強調/1 文字ずつ点灯)でした。 ユーザーが03 キーワード強調とセリフ体 scフォントを選びました。これを使用してください。 通常の縁取りスタイル(-webkit-text-stroke: 5px #000、ゴールドブロックなし、サンセリフ体)は、 強調する定量的なキーワードがないクリップ用のフォールバックです。

フォント — <head> で Google Fonts から Noto Serif SC を読み込みます( HyperFrames コンパイラーは指定された Google Fonts のファミリーを自動的に取得してインライン化します。 レンダリングログに Fetched … Noto Serif SC と表示されることを確認してください):

<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@600;700;900&display=swap" rel="stylesheet">

HTML:

<div id="caption" class="clip" data-start="{body_start}"
     data-duration="{body_dur}" data-track-index="4"></div>

CSS(縦型 1080×1920)— 字幕スタイル 03:

#caption {
  position: absolute; left: 0; right: 0; bottom: 240px;
  height: 240px; z-index: 10; overflow: visible;
}
#caption .bubble {
  position: absolute; top: 50%; left: 50%;
  display: inline-block; padding: 0 24px;
  font-family: "Noto Serif SC", "Songti SC", "STSong", serif;
  font-size: 52px; line-height: 1.32; font-weight: 700;
  color: #fff; max-width: 980px; text-align: center;
  -webkit-text-stroke: 2.5px rgba(0,0,0,0.9);
  paint-order: stroke fill;
  text-shadow: 0 2px 8px rgba(0,0,0,0.7), 0 0 2px rgba(0,0,0,0.9);
  letter-spacing: 0.01em;
}
#caption .bubble .hot {        /* gold keyword block */
  color: #1a1206; -webkit-text-stroke: 0;
  background: linear-gradient(180deg, #f3c877, #c79655);
  padding: 2px 12px; border-radius: 9px; margin: 0 3px;
  box-shadow: 0 3px 10px -3px rgba(232,176,99,0.6);
}

キーワードの自動選択(意図的に少なめ)。 ゴールドのブロックが意味のあるものとなり、 煩雑にならないよう、本当に強調すべき数量だけを囲みます。 一般的な「个」/「年」(「一个」、「20年」など)は意図的に除外します。千位区切りのカンマ (「1,000万」)にも対応します。build_hf_clips.py の mark_keywords() で処理します:

_NUM = r"[0-90-9,,一二三四五六七八九十百千两零几]+"
_HOT_RE = re.compile(rf"(?:翻了?{_NUM}?[倍番]|{_NUM}\s*(?:[倍番]|万亿?|亿|%|%))")
# → highlights: 一倍 五六倍 十倍 10倍 50万 800万 1,000万 50% 翻一倍
# render the cue with b.innerHTML = g.html (HTML-escape the non-keyword text)

JS(キューごとに吹き出し 1 つ+GSAP でフェードイン/アウト、すべてコンテナーの中点に配置):

// SRT cues are loaded as inline JSON. Each cue's start/end is offset
// by the cover-scene duration (e.g., 1.5s) so the timing aligns with
// the composition timeline (not the body's own t=0).
const captionEl = document.getElementById("caption");
const groups = JSON.parse(document.getElementById("captions-data").textContent);
const bubbles = groups.map((g, i) => {
  const b = document.createElement("span");
  b.className = "bubble"; b.id = "cap-" + i;
  b.innerHTML = g.html || g.text;   // g.html has <span class="hot"> keyword blocks
  b.style.opacity = "0";
  captionEl.appendChild(b);
  return b;
});
// GSAP xPercent/yPercent for centering (CSS transform would get
// overwritten the moment we tween y).
gsap.set(bubbles, { xPercent: -50, yPercent: -50 });
groups.forEach((g, i) => {
  const el = bubbles[i];
  tl.fromTo(el, { opacity: 0, y: 12 }, { opacity: 1, y: 0, duration: 0.18, ease: "power2.out" }, g.start);
  const exitStart = Math.max(g.start + 0.18, g.end - 0.12);
  tl.to(el, { opacity: 0, duration: 0.12, ease: "power2.in" }, exitStart);
  tl.set(el, { opacity: 0 }, g.end);
});

ソース SRT — インライン化する前に切り出して時間をずらします。 /wjs-transcribing-audio で 作成した単語タイミング付きの .asr.srt を使用してください(火山ストリーミング ASR → build_srt_from_asr.py)。単語ごとのタイミングにより、ずれなくキューを発話音声に 正確に合わせられます。各キューを解析し、すべての start/end にカバーの再生時間を加え、 mark_keywords() を実行して html フィールドを生成し、JSON として <script id="captions-data" type="application/json"> ブロックにインライン化します。

MarginV/位置に関する注意事項:

  • 縦型(1080×1920):bottom: 240px にすると、字幕が视频号/抖音の下部 UI オーバーレイ(いいね/コメント/共有ボタン)に重なりません。
  • 横型(1920×1080):bottom: 100px、font-size: 48px、 -webkit-text-stroke: 4px が妥当なデフォルト値です。

キャプションの長さ制限。 1080 ピクセル幅で 56px の場合、1 つのキューが約 18 文字を超えると、 不自然に 2 行に折り返されます。これは上流で対処してください。/wjs-translating-subtitles では、 単語間の空白で分割し、続けて句読点で分割して、キューを約 18 文字以内に収める必要があります。 それより長いキューを受け取った場合は、font-size を 48px に下げるか、折り返しを許容してください。

3. chapter — 左上のチャプターチップ(4 秒表示後にフェード)

セグメントを示す控えめなバッジです。本文の開始時に表示し、数秒後にフェードアウトするため、 他の要素と競合しません。

HTML:

<div id="chapter" class="clip" data-start="{body_start}"
     data-duration="{body_dur}" data-track-index="3">
  <span class="dot"></span>
  <span class="text">第一段 · 自然语言才是新代码</span>
</div>

CSS:

#chapter {
  position: absolute; top: 80px; left: 60px; z-index: 9;
  display: inline-flex; align-items: center; gap: 12px;
  padding: 12px 20px;
  background: rgba(12,13,16,0.78);
  border: 1px solid rgba(199,150,85,0.4);
  border-radius: 999px;
}
#chapter .dot { width: 10px; height: 10px; border-radius: 999px; background: #e8b063; }
#chapter .text {
  font-size: 24px; color: #f4f4f5; letter-spacing: 0.04em; font-weight: 600;
}

GSAP:

tl.from("#chapter", { x: -40, opacity: 0, duration: 0.5, ease: "expo.out" }, body_start + 0.4);
tl.to("#chapter", { opacity: 0, duration: 0.4, ease: "power2.in" }, body_start + 4.0);
4. stack イラスト — 右上の縦型リストカード

項目の一覧(例:言語の階層、ワークフローの手順、レベル)を、 右上の暗いカードに表示します。関連するレベル/手順を強調するため、 1 つの項目をアンバーで強調できます。

用途: 話者が説明している間、階層やリストを表示する場合に使用します。 カードは 8〜50 秒間表示します。

HTML:

<div id="ill-stack" class="clip" data-start="{start}" data-duration="{dur}" data-track-index="5">
  <div class="ill-card">
    <div class="ill-card-label">我们写的层级</div>
    <div class="ill-row"><span class="ill-tag accent">自然语言</span></div>
    <div class="ill-row"><span class="ill-tag">Python</span></div>
    <div class="ill-row"><span class="ill-tag">C</span></div>
    <div class="ill-row"><span class="ill-tag">Assembly</span></div>
  </div>
</div>

CSS:(正式版 CSS 全体は references/illustration_patterns.md を参照してください。内容をそのままコピーしてください)

GSAP — 右からスライドイン+行を順番に表示:

tl.fromTo("#ill-stack", { x: 360, opacity: 0 }, { x: 0, opacity: 1, duration: 0.6, ease: "expo.out" }, start + 0.2);
tl.from("#ill-stack .ill-row", { y: 20, opacity: 0, duration: 0.4, stagger: 0.12, ease: "power2.out" }, start + 0.4);
tl.to("#ill-stack", { x: 360, opacity: 0, duration: 0.5, ease: "power2.in" }, end - 0.5);
5. hammer イラスト — 画面中央に大きな数式/テキストを重ねて表示

重要な主張を視覚的に「強く印象づける」、画面中央の大きなテキスト/数式です。 クリップ内で最も引用価値のある瞬間に最適です(例: 「LLM = 编译器」、「Token = 新 GDP」、「AI ≠ 更快的轿子」)。4〜8 秒間表示します。

HTML:

<div id="ill-hammer" class="clip" data-start="{start}" data-duration="{dur}" data-track-index="6">
  <div class="ill-h-content">
    <div class="ill-h-eq">
      <span class="ill-h-left">LLM</span>
      <span class="ill-h-equals">=</span>
      <span class="ill-h-right">新编译器</span>
    </div>
    <div class="ill-h-foot">自然语言 → Python → 汇编</div>
  </div>
</div>

GSAP — スケールポップで登場+各パーツを順番に表示+スケールしながらフェードアウト:

tl.fromTo("#ill-hammer", { scale: 0.85, opacity: 0 },
  { scale: 1.0, opacity: 1, duration: 0.45, ease: "back.out(1.6)" }, start);
tl.from("#ill-hammer .ill-h-left", { x: -40, opacity: 0, duration: 0.4, ease: "expo.out" }, start + 0.2);
tl.from("#ill-hammer .ill-h-equals", { scale: 0, opacity: 0, duration: 0.4, ease: "back.out(2)" }, start + 0.4);
tl.from("#ill-hammer .ill-h-right", { x: 40, opacity: 0, duration: 0.4, ease: "expo.out" }, start + 0.6);
tl.from("#ill-hammer .ill-h-foot", { y: 20, opacity: 0, duration: 0.4, ease: "power2.out" }, start + 0.8);
tl.to("#ill-hammer", { scale: 1.05, opacity: 0, duration: 0.45, ease: "power2.in" }, end - 0.45);

(正式版 CSS 全体は references/illustration_patterns.md を参照してください)

6. cta — チャンネルの CTA を含むエンドカード

最後の 3 秒間に表示する、ブランドを反映したアウトロです。チャンネル名には (全体の指示に従って)王建硕を使用してください。CTA 欄にゲストの名前を 入れてはいけません。

HTML:

<div id="cta" class="clip" data-start="{cta_start}" data-duration="3.24" data-track-index="1">
  <div class="cta-line-1">关注王建硕</div>
  <div class="arrow">↓</div>
  <div class="cta-line-2">微信公众号 · 视频号</div>
  <div class="cta-foot">聊 AI · 聊创业 · 持续更新</div>
</div>

CSS/GSAP: references/illustration_patterns.md を参照してください。

レガシータイプ(単一動画に一度だけオーバーレイを適用する場合)

spec.json + scaffold.py のワークフローでは、以下の旧式オーバーレイタイプもサポートしています。上記の完全なポストプロダクションワークフローを経ずに、既存の動画を1本だけ装飾したい場合に便利です。

  • quote — 全幅のキネティックタイポグラフィ。上部または下部にグラデーションを表示します。 オープニングのフックや重要な引用のコールアウトに最適です。
  • slogan — position: bottom と大きめの文字を指定した quote の別名です。 クロージングのスローガンに最適です。
  • callout — 隅に表示する小さな注釈パネルです。チャプターラベル、ローワーサード、「掲載実績」などの注記に最適です。
  • custom — 自由にカスタマイズできます。Claude が overlays/<name>.html フラグメントファイル内にオーバーレイの HTML/CSS/GSAP を記述します。 references/custom_overlay_recipes.md を参照してください。

ワークフロー A — セグメンテーション後のプリセット(最も一般的)

/wjs-segmenting-video から直接作業を始めていて、各クリップに標準の cover + caption + chapter + illustrations + CTA の処理を適用したい場合に使用します。

ステップ 1 — 適切なアスペクト比で AI カバーを生成する
# For vertical 9:16 output (视频号 / 抖音):
python3 ~/.claude/skills/wjs-segmenting-video/scripts/make_cover.py \
    --segments segments.json --out output/ --size 1024x1792 --single 1
# Verify segment 1's cover; then batch:
python3 ~/.claude/skills/wjs-segmenting-video/scripts/make_cover.py \
    --segments segments.json --out output/ --size 1024x1792
ステップ 2 — 各クリップ用の HyperFrames プロジェクトをスキャフォールドする

hf_clip_NN/1080/ に以下を配置します。

  • index.html — コンポジション(テンプレートから作成。references/post_segmentation_template.html を参照)
  • clip.mp4 — output/clip_NN_slug.mp4 からコピー
  • cover.png — output/cover_NN_slug.png からコピー
  • captions.json — output/clip_NN_slug.zh-CN.burn.srt から生成。 各キューの開始時刻と終了時刻に +cover_duration を加算し、字幕を本文独自のタイムラインではなく、コンポジションのタイムラインに合わせます。

references/build_hf_clips.py のビルドスクリプトを使うと、すべてのセグメントを一度に処理できます。segments.json、ILLUSTRATIONS 辞書(クリップごとのイラスト。ステップ 3 を参照)、テンプレートを読み込み、レンダリング可能なプロジェクトを5つ出力します。

ステップ 3 — クリップごとにイラストを定義する

各クリップについて、フックとなる場面を1~2つ特定し、stack または hammer を選択します。

ILLUSTRATIONS = {
    1: [
        # The language hierarchy as a stack card during the opening
        {"key": "stack", "pattern": "stack", "body_start": 0.3, "body_end": 9.0,
         "label": "我们写的层级",
         "rows": [
             {"text": "自然语言", "accent": True},
             {"text": "Python",   "accent": False},
             {"text": "C",        "accent": False},
             {"text": "Assembly", "accent": False},
         ]},
        # The hammer at the most quotable moment
        {"key": "hammer", "pattern": "hammer", "body_start": 10.8, "body_end": 14.6,
         "left": "LLM", "equals": "=", "right": "新编译器",
         "foot": "自然语言 → Python → 汇编"},
    ],
    # ... clips 2-5
}

タイムスタンプは本文基準(カバーシーンの再生時間の後)です。ビルドスクリプトは GSAP の位置を出力する際にカバー分のオフセットを加算します。

ステップ 4 — ビルドしてレンダリングする
python3 references/build_hf_clips.py    # scaffolds all projects
for n in 01 02 03 04 05; do
  cd "hf_clip_$n/1080"
  npx hyperframes lint
  npx hyperframes validate
  npx hyperframes render
  cd ../..
done

2:30 のクリップは約3分でレンダリングされます。出力先:hf_clip_NN/1080/renders/*.mp4。

ワークフロー B — 単一動画にカスタムオーバーレイを適用する(レガシー spec.json)

既存の動画が1本あり、タイトルカードや注釈、ローワーサードなどのアドホックなオーバーレイをいくつか追加したい場合に使用します。

spec.json のスキーマ
{
  "source_video": "../path/to/source.mp4",
  "duration": 135.4,
  "size": "1920x1080",
  "name": "clip_01_animated",
  "overlays": [
    {"id": "o1", "type": "quote", "start": 8.0, "duration": 6.0,
     "position": "top", "lines": ["代码不存在错误", "只存在意图错配"],
     "accent": [false, true]},
    {"id": "o2", "type": "callout", "start": 30.0, "duration": 5.0,
     "anchor": "top-right", "text": "FRP 概念"},
    {"id": "o3", "type": "slogan", "start": 122.0, "duration": 13.4,
     "lines": ["改 prompt", "不改 AI 生成的代码"], "accent": [false, true]}
  ]
}
フィールド必須備考
source_videoはいソース MP4 へのパス。プロジェクト内に source.mp4 としてシンボリックリンクが作成されます。
durationはいコンポジション全体の長さ(秒)。ソース動画に合わせてください。
sizeいいえWIDTHxHEIGHT(デフォルトは 1920x1080)。
overlays[].typeはいquote、slogan、callout、または custom。
overlays[].startはい開始時刻(秒)。
overlays[].durationはいオーバーレイの表示時間。
スキャフォールドとレンダリング
python3 ~/.claude/skills/wjs-overlaying-video/scripts/scaffold.py spec.json
cd <name> && npm run check && npm run render

出力チェックリスト

クリップの完成とする前に、以下を確認してください。

  • フレーム 0 がカバーになっている(黒画面ではない) — ffmpeg -ss 0 -vframes 1 out.mp4
  • 字幕が音声と同期している(音声を再生しながら数秒分を確認)
  • すべてのイラストが、対応する発話のタイミングに合わせて表示・終了している
  • CTA が正しくレンダリングされている(ゲストの名前ではなく 关注王建硕)
  • npx hyperframes lint && npx hyperframes validate の両方に合格する
  • npx hyperframes inspect でレイアウトのはみ出しがない
  • 全体の長さがソースクリップ、カバー、CTA の各再生時間の合計と一致している

よくある間違い

  • カバーのアスペクト比 ≠ 出力のアスペクト比です。 1024x1536(make_cover.py のデフォルトサイズ)は 2:3 で、9:16 の出力ではレターボックス表示になるか、クロップされます。縦向きの場合は必ず --size 1024x1792 を指定してください。
  • 字幕の配置は行数によってずれます。 高さを固定したコンテナ内で中央を基準に配置(translate(-50%, -50%))すると、1 行と 2 行の字幕の視覚的な中央位置がそろいます。下端を基準に配置しないでください(上方向に広がります)。
  • GSAP は CSS の transform による中央配置を上書きします。 CSS で transform: translate(-50%, -50%) を設定してから y をトゥイーンすると、GSAP が transform を置き換え、中央配置が崩れます。代わりに gsap.set(el, { xPercent: -50, yPercent: -50 }) を使ってください。こうすると、xPercent/yPercent が以降の y/x のトゥイーンと組み合わされます。
  • HTML/CSS の字幕の上に libass の字幕を焼き付けること。 出力動画ごとに字幕システムは 1 つだけ選んでください。このスキルの HTML/CSS 字幕を使う場合は、/wjs-segmenting-video で字幕を焼き付けないでください。ハンドオフパッケージを使って未加工のクリップをリクエストしてください。
  • フレーム 0 が黒くなります。 カバーシーンの不透明度が 0 からフェードインする場合、最初のフレームは文字どおり黒くなり、プラットフォームのサムネイルも黒くなります。カバーは静止状態で配置し(不透明度のトゥイーンを使わず)、ffmpeg -ss 0 -vframes 1 で確認してください。
  • CTA のチャンネル名はゲストの名前です。 必ず 王建硕 を使ってください。ゲスト名はメタデータ内の説明文に記載し、画面上の CTA には表示しないでください。
  • アスペクト比が合わず、object-fit: cover によってカバー画像がクロップされること。 正しいアスペクト比でカバーを再生成する(ステップ 1 を参照)か、object-fit: contain と暗い背景を使ってレターボックス表示にしてください。

他のスキルとの連携

  • /wjs-segmenting-video — 通常はこれが前段のスキルです。カット、クロップ、SRT の分割が完了した後、このスキルで処理します。ハンドオフパッケージは clip_NN.mp4、clip_NN.zh-CN.burn.srt、segments.json です。
  • /wjs-transcribing-audio + /wjs-translating-subtitles — SRT がない場合は、先にこれらを実行してください。正確な字幕タイミングには、単語単位の Whisper または Volcano/豆包 ASR の出力が推奨されます。
  • hyperframes — 基盤となる合成フレームワークです。このスキルは、実証済みのポストプロダクションパターンをまとめた薄いラッパーです。hyperframes スキルの内容はすべて適用されます(プレビュー、レンダリング、トランジション、音声連動など)。custom オーバーレイを作成するときは、必ずこちらを読んでください。
  • hyperframes-cli — プロジェクトで使用する CLI コマンドです(init、lint、validate、inspect、render)。
  • gpt-image-2-skill — カバー生成ツールです。make_cover.py は codex CLI 経由でこれを呼び出します。~/.codex/auth.json の codex 認証が必要です。
  • /wjs-uploading-video — このスキルで MP4 を生成した後の後続スキルです。メタデータファイルのタイトル、説明、タグを使って、レンダリング結果を YouTube にアップロードします。

ファイルと参照資料

  • scripts/scaffold.py — ワークフロー B のスキャフォルダー(アドホックなオーバーレイ向けのレガシー spec.json)
  • references/post_segmentation_template.html — ワークフロー A のテンプレート:プレースホルダーを置換して使う、標準的な cover + caption + chapter + illustration + CTA の合成構成
  • references/build_hf_clips.py — ワークフロー A の複数クリップビルダー。segments.json とクリップごとのイラスト辞書を読み込み、クリップごとにプロジェクトを 1 つずつスキャフォールドして内容を設定します
  • references/illustration_patterns.md — stack および hammer のイラストパターンに関する標準的な CSS / GSAP
  • references/custom_overlay_recipes.md — 再利用可能な custom オーバーレイのレシピ(ターミナルデモ、レイヤースタック図、矢印付きコールアウト)
  • references/example_spec.json — ワークフロー B のサンプル

コメント