mcpField
W
wjs-converting-text-to-video

wjs-converting-text-to-video

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

wjs-converting-text-to-video について

wjs-converting-text-to-videoは、中国語の記事を縦型の解説動画に変換するスキルです。記事の構成に合わせたシーンに、TTSナレーション、HyperFramesのアニメーション、抽象的な水彩背景を組み合わせます。

主な機能

  1. 01記事を複数のビジュアルシーンに分割
  2. 02中国語のTTSナレーションを生成
  3. 03HyperFramesとGSAPのアニメーション
  4. 04抽象的な水彩背景を生成
  5. 05縦型MP4動画を出力

活用シーン

  1. 01WeChat公式アカウントの記事を短編動画に変換
  2. 02视频号、抖音、Reels向けの縦型コンテンツを作成

インストール

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

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

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

同梱ファイル

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

scripts/
bootstrap-project.sh
daily-upload-batch.sh
generate-bg.sh
publish-to-youtube.py
retrofit-bg-image.py
strip-dark-scene-bgs.py
synth-sfx.sh
tts.py

詳細

wjs-converting-text-to-video

王建硕風のWeChat公式アカウント記事 article.md を、1080×1920の縦型、30〜90秒の中国語解説ショート動画にします。TTSナレーション + HyperFrames CSS/GSAPアニメーション + 抽象水彩背景 + トランジションSFX。動画号 / 抖音 / 小红书 / Reels向けにMP4を出力します。

このスキルで生成されるもの

項目デフォルト
サイズ1080×1920 縦型(9:16)
長さ30〜90秒
Scene数5〜10
ナレーション火山引擎 Volcano TTS、デフォルトは阿虎の会話風男性ボイス
背景GPT Image 2で生成した抽象水彩(bg.png)+ blur 30 + 暖色の黒い半透明overlay
フォントNoto Sans SC、hero 900、メインテキストは暖色のオフホワイト
出力<article-folder>/<slug>.mp4(video/と同階層で、video/内には置きません)
公開YouTubeに自動アップロードします — Portrait → Shorts、Landscape → 通常のvideo。再レンダリング時は古い動画を置き換えます(蓄積しません)

このスキルが起動するケース

  • ユーザーがすでに article.md を用意していて、「動画にして」「解説動画を作って」「ひととおり説明して」と言った場合
  • ユーザーが /wjs-converting-text-to-video <article-folder> を実行した場合
  • ユーザーが「昨日公開したあの記事を全部動画にして」のような一括処理を依頼した場合

使用しないケース

  • 記事原稿がなく、アイデアだけの場合 → まず /wjs-publishing-wechat で article.md を作成してから実行します
  • 字幕の焼き込み / 翻訳 / 吹き替え差し替えが必要な場合 → /wjs-burning-subtitles / /wjs-dubbing-video / /wjs-localizing-video を使用します
  • 英語 / スペイン語など、中国語以外の動画の場合 → このスキルは中国語TTS(Volcano 火山引擎)に特化しています。中国語以外ではhyperframes内蔵のttsコマンドを使用します(英語のkokoroはまずまずです)
  • 横型16:9の場合 → このスキルのデフォルトは縦型です。横型への変更はユーザーが明確に希望した場合のみ行います

基本原則

動画は記事を視覚的に読み上げるものではなく、記事を視覚的に再構成するものです。

各sceneは独立した視覚的な瞬間です。対比、並列、数字、比喩などを表現します。テキストを画面いっぱいに配置し、太字のゴシック体を使い、重要な文字をオレンジで強調します。背景には抽象水彩を使い、ぼかして柔らかくします。全体のトーンは落ち着いていて、抑制が効き、インパクトのあるものにします。

テンポ > テンプレート。5〜10 sceneの動画で、最初から最後まで「2行で対比」という同じレイアウトが続くなら、それは動画ではなくスライドショーです。現代的な印象は対比から生まれます —— 極端な文字サイズの差、非対称のレイアウト、短いsceneと長いsceneの交互配置、テキストだけのsceneと幾何学要素を含むsceneの交互配置、水彩背景のsceneと明るい色のパンチsceneの交互配置を取り入れます。

デフォルトは平凡です。テンプレート表の上から簡単なものを選ぶだけでは、必ず「単調な2行形式」になります。Step 1b Scene Mix Rule(必須)に従って構成を調整してください。

ワークフロー

Step 1: 5〜10個の視覚的な瞬間を設計する

<article-folder>/article.mdを読み、論証の構成に沿って5〜10個のsceneに分割します(全体の長さは30〜90秒に調整します)。短い記事(要点が1〜2個)は5〜6 scene / 30〜50秒、長い記事は8〜10 scene / 60〜90秒にします。各sceneには、ひとつのナレーションと、明確な視覚的な構成を用意します。

テンプレート一覧 —— 6カテゴリ、計16種類を必要に応じて組み合わせます:

A. Hero / Punch(強いコントラストのクライマックス。動画ごとに1つ以上、長さは4秒以下)
テンプレート用途
A1. 全画面1文字hero1〜3文字のクライマックスワードを画面いっぱいに表示。文字サイズは280〜400px
A2. アウトラインhero中抜き文字 -webkit-text-stroke: 4px #f5efe5; color: transparent;
A3. カラーフリップパンチ背景全体を明るい色(オレンジ / 赤 / 金 / エメラルドグリーンなど)に変え、白抜き文字を配置
A4. グラデーションテキストhero大きな文字に background: linear-gradient(...); -webkit-background-clip: text; を指定
B. Contrast / 対照(コントラスト構成。動画ごとに1〜2個、長さは5〜8秒)
テンプレート用途
B1. 2行対照 + strikethrough「以前はX、今はY」「AではなくB」—— 動画全体で最大2つ
B2. 左右分割の対照画面を2分割(縦線で区切ってもかまいません)
B3. 対角線の対照左上 ↔ 右下に配置し、中央に大きな余白を設ける
C. List / 構造(複数項目の並列。動画ごとに1〜2個、長さは6〜10秒)
テンプレート用途
C1. カードを横にN個並べる3〜5個を並べ、暖色のダークブラックと単色の枠線を使用
C2. キーワードを縦に積み重ねる6〜8個の並列項目。大きな連番01〜08を加えてもかまいません
C3. 正規のグリッド2×2 / 3×2のグリッドで、各セルにアイコン + ラベルを配置(縦型は横幅が限られるため、横に4列並べると窮屈です)
C4. 階段状 / ずらしたリスト項目ごとにmargin-leftを増やす
D. Stat / データ(数字のクライマックス。動画ごとに1つ以上、長さは4〜6秒)
テンプレート用途
D1. 数字ticker0 → Nのスクロールアニメーション(gsap.to({textContent}))
D2. 数字 + ラベルメインの数字は200〜400px、説明は60〜80px
D3. プログレスバー / タイムライン横向きのprogress bar + ノード
E. Quote / Climax(名言で締める。動画ごとに1〜2個、長さは6〜10秒)
テンプレート用途
E1. 段落レベルのhero text60〜100pxの名言を左揃えで配置し、左側にemphasis barを置く
E2. 大きな引用符 + 本文大きな半透明の開始引用符を背景の装飾として使う
F. 装飾 / 幾何学(テンポに変化をつけるための任意要素)
テンプレート用途
F1. グリッド + spinner / プログレスバー複数処理が並行する画面
F2. 吹き出し ↔ 応答登場人物Aが話し、登場人物Bが行動する

各sceneのナレーションは3〜12秒にします(短いパンチは3〜4秒、長いブレスは10〜12秒。すべてを5〜7秒にしないでください)。全sceneの合計は30〜90秒とし、90秒を超えないようにします。記事が短い場合は動画も短くします。5 scene × 6秒 = 30秒でも問題ありません。

Step 1b: Scene Mix Rule(必須)

5〜10個のsceneの設計後、以下のチェックリストで確認してください。ひとつでも満たさない項目があれば、戻って調整します。

構成比の必須ルール
  • A / D / C / Eカテゴリのsceneが1つ以上
  • B1テンプレート(2行strikethrough)は2つ以下 —— これまで最も乱用されやすかったテンプレートです
  • A3カラーフリップscene(明るい背景に白抜き文字)が1つ以上
  • 異なるテンプレートタイプを4種類以上使用(A/B/C/D/E/Fのうち4カテゴリ以上)
  • 同じカテゴリのsceneを3つ以上連続させない
テンポの必須ルール
  • sceneの長さの幅が6秒以上(最短は4秒以下、最長は9秒以上)
  • 「短 → 長 → 短」または「長 → 短」のテンポ切り替えを2回以上入れる
  • 文字サイズの幅が240px以上(最大のheroは320px以上、最小は80px以下)
レイアウトの必須ルール
  • 中央揃えでないsceneを2つ以上入れる(隅への配置、対角線配置、左揃え、階段状配置など)
  • 画面の60%以上を余白にするsceneを1つ以上入れる(呼吸の間)
  • 幾何学的な装飾(太線、色面、矢印、ドット、大きな連番など)を含むsceneを1つ以上入れる
配色の必須ルール
  • 大半のsceneではbackground:の色を指定しません —— 水彩のbg-imageを見せます。無地の背景色を使うのはA3カラーフリップのみです
  • カラーフリップsceneの色をオレンジ / 青 / 白だけにしません(深紅 / 深い金色 / エメラルドグリーン / 青松色 / 暗い紫なども使用できます)
  • emphasisには少なくとも2〜3色を使います(技術用語は青、価値を表す言葉は金、成長を表す言葉は緑、警告を表す言葉は赤)
反単調性のセルフチェック
  1. すべての scene のスクリーンショットをサムネイルに縮小して横に並べます — ひと目で見分けられますか?8 個が同じに見えるなら → 作り直します
  2. 1、4、7 番目の scene で、視覚的な密度に違いはありますか?密度の高いものも、極めてシンプルなものもあるべきです
  3. 「メタリズム」はありますか?たとえば、A で始まり → B/C が 3 つ続いて展開し → D でクライマックス → E で締めくくる、といった構成です。直線的に並べるより、ドラマチックな展開になります
Step 2: narration_chunks.json を作成する
[
  {"id": "s01", "text": "我们以前,是 AI 的领导。现在,我们就是它的维修工。"},
  {"id": "s02", "text": "..."}
]

ナレーションの詳細を書く:

  • article.md よりも口語的で、短く歯切れよくします。TTS が自然に間を取れるよう、読点や句点を多めに使います
  • 数字と英語が混在しても問題ありません(「Claude Code」、「100 倍」など)。Volcano はどちらも読み上げられます
  • 括弧書きの注釈や ...、ダッシュ —— は使いません(TTS が「ダッシュ」と読み上げてしまいます)
  • article.md 内の **太字の Markdown** は削除し、プレーンテキストだけを残します
  • 百姓網に関する事実を削除します:article.md に「百姓網」「百姓網は現在 X 人」「百姓網の従業員」などが出てきた場合は、削除するか一般化します(「百姓網は現在 158 人」→「現実には人間はほとんどいません」)。これは古い情報なので、動画に含めないでください。同様に、visuals にも「百姓網」というラベルや「158 人」という統計を表示しないでください。詳しくは [[no-baixing-facts]] を参照してください
Step 3: TTS ナレーションを生成する
cd <article-folder>/video
python3 tts_narration.py

スクリプトでは、デフォルトで zh_male_ahu_conversation_wvae_bigtts(阿虎対話)を使います。チャンク間に 0.35s の無音を挿入し、narration.mp3 と timing.json を出力します。

Volcano TTS の注意事項(実際に起きた問題):

  • resource volc.service_type.10029 を使い、speaker は zh_*_*_bigtts を選びます
  • emotion / emotion_scale は絶対に渡さないでください — ほとんどの _bigtts 音声は data: null を返して無音で失敗します
  • kokoro は絶対に使わないでください(hyperframes に組み込まれている tts)— 中国語の品質が低く、ユーザーは明確に受け入れていません
  • zh_male_jieshuonansheng_mars_bigtts は避けてください — 「Claude Code」などの英語の固有名詞が含まれると、幻覚を繰り返します

代替音声(おすすめ順):

  • zh_male_ahu_conversation_wvae_bigtts(阿虎対話)— デフォルト。自然な口語です
  • zh_male_M392_conversation_wvae_bigtts — 同じ wvae シリーズです
  • zh_male_wennuanahu_moon_bigtts(温暖阿虎)— より温かみがあり、アナウンス調です
  • zh_male_silang_mars_bigtts(思朗)— 落ち着いた思索的な声で、ドラマチックな印象が強いです
  • zh_male_baqiqingshu_mars_bigtts(霸气)— より力強い声です

音声を切り替えるには、python3 tts_narration.py --voice zh_male_silang_mars_bigtts を実行します

Step 4: 水彩背景画像を生成する

bg-image はビジュアルの基調となる、ぼかした抽象水彩です。article の illustration.png は使わないでください — 手描きの図は細部が多すぎるため、ぼかすと均一な暗色の泥のようになり、見た目は真っ黒なままです。必ず専用の抽象水彩を生成してください。

~/.claude/skills/wjs-converting-text-to-video/scripts/generate-bg.sh <article-folder> <theme>

<theme> は記事のテーマに合わせて選びます:

themeカラーパレット用途
personal明るく温かみのある黄色、柔らかなコーラルピンク、テラコッタ、セージグリーン、クリーム個人、手作り、温かみ
techクールなティール、エレクトリックブルー、深い紫、ミント、白AI、技術、データ
reflectionセージグリーン、くすんだブルー、ラベンダー、パール、クリーム内省、静けさ
warningバーントオレンジ、深紅、マスタード、チャコール警告、緊張感
growthフレッシュグリーン、ゴールド、柔らかな黄色、スカイブルー成長、複利効果
abstractラベンダー、ダスティローズ、セージ、柔らかなアンバー抽象、思索

出力先:<article-folder>/video/bg.png(1088×1920、約 3MB)。

⚠️ 画像は必ず video/ ディレクトリ内に置いてください — ../illustration.png は使えません。hyperframes render はディレクトリをまたぐ相対パスを解決できず、真っ黒にレンダリングされます。

Step 5: HyperFrames のコンポジションを作成する(index.html)

timing.json を読み込み、各チャンクの start/end に合わせて scene を設計します。縦画面 1080×1920 の構成:

<html><head><script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/gsap.min.js"></script>
<style>
  html, body {
    width: 1080px; height: 1920px; margin: 0; overflow: hidden;
    background: #0e0b08;
    font-family: 'Noto Sans SC', 'PingFang SC', 'Heiti SC', sans-serif;
    font-weight: 900;
    color: #f5efe5;
    letter-spacing: -0.02em;
    -webkit-font-smoothing: antialiased;
  }
  #bg-image {
    position: absolute; inset: 0;
    background-image: url('bg.png');
    background-size: cover;
    background-position: center;
    filter: blur(30px) brightness(0.65) saturate(0.85);
    z-index: 0;
    transform: scale(1.1);
  }
  #bg-overlay {
    position: absolute; inset: 0;
    background: rgba(14, 11, 8, 0.28);
    z-index: 1;
  }
  .scene { position: absolute; inset: 0; overflow: hidden; opacity: 0; z-index: 2; }
  #s1 { opacity: 1; }
  /* ... scene-specific styles ... */
</style></head>
<body>
  <div id="root" data-composition-id="main" data-start="0" data-duration="<total+2>" data-width="1080" data-height="1920">
    <div id="bg-image"></div>
    <div id="bg-overlay"></div>
    
    
  </div>
  <script>
    /* GSAP timeline: paused + register to window.__timelines['main'] */
  </script>
</body></html>
🎬 最初のフレームのルール(必須)

動画の t=0 には、次の要素を含める必要があります:

  1. bg-image が完全に表示されている — opacity は常に 1 とし、決してフェードインさせません(CSS のデフォルトで表示されるため、GSAP で opacity を変更しないでください)
  2. タイトル要素が表示されている — s1 の主要なタイトル要素に tl.from({y:30, scale:0.95}) を使うのは問題ありませんが、tl.from({opacity:0}) は使わないでください。t=0 で黒画面になってしまいます
  3. s1 を A3 color-flip にしない — bg-image が覆われ、最初のフレームで水彩が見えなくなります。color-flip は s2 以降に使います
色彩系统

主文字 / 锚定色(design system,全片一致):

角色值用法
主文字#f5efe5 暖奶白hero / 主要内容
二级文字(副标题、caption)#f5efe5 + opacity: 0.7 + 小字号不要用灰色(#8a7e72 在水彩底上看不清)。用 opacity + 缩字号做 hierarchy
划掉文字本身#f5efe5 + opacity: 0.5 + strikethrough line不要用 #6d635a 暗灰 — 在水彩底上看不清。改用 opacity 弱化 + 橙色 strike line
装饰大编号(01-08)#f5efe5 + opacity: 0.18 或 #e87a3e + opacity: 0.35不要用 #2b2620 等深灰(水彩底上完全消失)
Outline 描边#f5efe5 4-8px stroke + color: transparentA2 空心字
默认 fallback bg#0e0b08 深暖黑被 bg-image + overlay 覆盖;color-flip 不用

核心原则:所有文字用 #f5efe5 cream 或 #e87a3e 橙系(accent palette),用 opacity + size 做 hierarchy,不用色相变化。灰色是黑底时代的遗物,水彩底上一律不用。 详见 [[no-low-contrast-text]]

Color-flip 背景 palette(A3,不只是橙/蓝/白):

hex适合
#e87a3e 经典橙警示、强调、climax punch
#6b9bc4 亮蓝数据、技术 climax
#f5efe5 暖奶白收尾、安静的反差
#c45c3e 深红警告、错误 climax
#d4a040 深金成就、价值 climax
#5a8c6a 翠绿增长、复利、生命力
#4a8a8a 青松冷静、长期主义
#7a5a8a 暗紫智慧、神秘 climax
#c48a8a 暗粉柔软、人性

color-flip 上的文字用 #0e0b08 或 #f5efe5 反相。

Emphasis / Accent palette(不只是橙):

hex适合
#e87a3e 橙默认 emphasis
#6b9bc4 蓝数据、技术词、AI
#d4a040 金价值、成就
#5a8c6a 翠绿增长、好结果
#4a8a8a 青松长期、稳定
#c45c3e 深红警告、反差
#8a7aaa 暗紫抽象、智慧
#c48a8a 暗粉柔软、人性化

整片 emphasis ≥ 2-3 种,根据 scene 主题选 accent。

字体系统(竖屏 1080 宽)
项值
字重hero 900 / 主文 800 / 二级 600-700 / caption 500
字距hero -0.04em 到 -0.06em / 主文 -0.02em / caption 0
Punch hero (A1/A2,1-3 字)280-400px
短句 hero (4-6 字)160-240px
长句 hero (7-10 字)100-150px
卡片内容56-130px
副标题40-72px
Caption / 序号 / 标签20-40px
布局系统(反居中惯性)
布局CSS 关键适合
居中flex; center; center;A 类 hero,但 ≤50% scene
左对齐贴顶padding: 80px 80px 0 80px;E 类金句、长 quote
右下角锚定position: absolute; right: 80px; bottom: 80px;落款、climax 词
对角线top-left / bottom-rightB3 对角对照
网格display: grid; grid-template-columns: repeat(2, 1fr);C3(竖屏 2×N 而非 3×N)
阶梯每项 margin-left: calc(60px * var(--i));C4 错位列表
贴底 + 上方留白position: absolute; bottom: 60px; 上方空白呼吸 scene
边角小元素文字小贴一角,其他全空极简 / 留白 punch

Padding:撑满型 40-80px,呼吸型 120-200px。不要所有 scene 都用同一个 padding。

几何装饰元素

每隔几个 scene 用一个:

  • 粗短线 8-16px × 40-200px,emphasis bar,橙色
  • 左侧 emphasis bar 6px × 100%,配长 quote
  • 大数字编号 01-08,list 项序号(淡灰、巨大、装饰性)
  • 大引号字符 " 半透明超大置左上
  • 横向分隔线 2-4px 奶白 30% 透明
  • 圆点 / 方块 12-20px、橙色,list bullet
  • 箭头 ➜ 或自绘 SVG
Scene 转场(4 种 + 混用规则)

不要全片都 blur crossfade。每 4 个转场必须 ≥2 种类型。

T1. Blur crossfade(默认柔和)

  • 0.6s,sine.inOut
  • 后 scene opacity: 0, filter: blur(24px) → opacity: 1, filter: blur(0)
  • 前 scene 同时 fade-out + blur

T2. White flash cut(punch 切,最现代)

  • 0.18s 总长:60ms 白闪 → 切 → 40ms 新 scene scale 1.05 → 1
  • 适合:进入 A 类 hero、D 类 stat、climax 切换
tl.to('.flash', { opacity: 1, duration: 0.06, ease: 'none' }, T - 0.06)
  .set(prevScene, { opacity: 0 }, T)
  .set(nextScene, { opacity: 1 }, T)
  .to('.flash', { opacity: 0, duration: 0.12, ease: 'power2.out' }, T)
  .from(nextScene, { scale: 1.05, duration: 0.25, ease: 'expo.out' }, T);

T3. Scale push(推进感)

  • 0.55s,前 scene scale: 1 → 0.85,后 scene scale: 1.15 → 1
  • 适合:从概览推到细节

T4. Color flash cut(橙/蓝闪一下,强烈节奏)

  • 0.22s 总长:80ms 全屏橙 → 切 → 40ms 收
  • 适合:进入 A3 color-flip 或关键转折
  • 全片最多 2 次

flash overlay 在 HTML 里加 <div class="flash"> 全屏定位、默认 opacity 0、z-index 100。

入场动画规则
  • 每个 scene 的每个元素都用 tl.from(...) 入场(y/opacity/scale)
  • 入场 stagger 0.1-0.3s;首元素 t = scene.start + 0.3 起
  • ≥3 种不同 ease(power3.out / back.out(1.3) / expo.out / elastic.out(1, 0.5))
  • 不要 gsap.to({opacity: 0}) 退场 — 转场已处理。只有最后 scene 可 fade-to-black
  • 整片必须用到 ≥3 种 Modern Motion Techniques
Modern Motion Techniques

平庸视频和现代视频的差别一半在排版、一半在 motion。下面 7 种每片必须用 ≥3 种(特定 scene 用,不要全片堆)。

1. Kinetic Typography(字符 stagger 入场)—— A 类 hero
<h1 class="kinetic">维 修 工</h1>
tl.from('.kinetic span', {
  y: 180, opacity: 0, rotateX: -90,
  duration: 0.7, stagger: 0.06,
  ease: 'back.out(1.4)',
  transformOrigin: '50% 100%',
}, T);
2. Camera Punch(推近 / 拉远)—— A3、D 类
tl.from(scene, { scale: 1.15, opacity: 0, duration: 0.5, ease: 'expo.out' }, sceneStart);
3. Mask Reveal(clip-path 揭示)—— E 类 quote
.reveal { clip-path: inset(0 100% 0 0); }
tl.to('.reveal', { clipPath: 'inset(0 0% 0 0)', duration: 0.9, ease: 'expo.inOut' }, T);
4. Number Ticker(数字のカウントアップ)—— D1
<div class="ticker" data-end="3600">0</div>
const ticker = document.querySelector('.ticker');
const obj = { val: 0 };
tl.to(obj, {
  val: parseInt(ticker.dataset.end),
  duration: 1.8, ease: 'power2.out',
  onUpdate: () => { ticker.textContent = Math.round(obj.val).toLocaleString(); },
}, T);
5. Outline → Fill(中抜き文字を塗りつぶす)—— A2
.morph { -webkit-text-stroke: 4px #f5efe5; color: transparent; }
tl.to('.morph', { color: '#e87a3e', webkitTextStrokeColor: '#e87a3e', duration: 0.5, ease: 'power2.out' }, T);
6. Letter Highlight Sweep(キーワードに光を走らせる)—— E クラスのクライマックス語
<span class="sweep"><span class="sweep-bg"></span>搭档</span>
.sweep { position: relative; display: inline-block; padding: 0 8px; }
.sweep-bg { position: absolute; inset: 0; background: #e87a3e; transform: scaleX(0); transform-origin: left; z-index: -1; }
tl.to('.sweep-bg', { scaleX: 1, duration: 0.5, ease: 'power3.inOut' }, T);
tl.to('.sweep', { color: '#0e0b08', duration: 0.1 }, T + 0.25);
7. Background Color Punch(背景色のフラッシュ)—— 動画全体で1〜2回
tl.to(scene, { backgroundColor: '#e87a3e', duration: 0.08 }, T)
  .to(scene, { backgroundColor: '#0e0b08', duration: 0.4, ease: 'power2.out' }, T + 0.1);

Strike-through アニメーション:::after ではなく、実際の DOM <span class="strike-line"> を使用します。擬似要素と CSS 変数は、hyperframes の一部のレンダリング経路では動作しません。

<span class="strike">领导<span class="strike-line"></span></span>
.strike-line { position: absolute; left: -10px; right: -10px; top: 56%; height: 10px; background: #e87a3e; transform: scaleX(0); transform-origin: left; }
tl.to('.strike .strike-line', { scaleX: 1, duration: 0.55, ease: 'power2.inOut' }, T);
Step 6: SFX を追加
~/.claude/skills/wjs-converting-text-to-video/scripts/synth-sfx.sh <article-folder>/video

video/sfx/{tick,chime,bell}.mp3 を生成します:

  • tick.mp3 — 80ms、1.2kHz のサイン波、トランジション用(scene 切り替えの0.3秒前ごと)
  • chime.mp3 — 220ms、880+1320Hz の二音、対話やリスト項目を強調するとき(任意)
  • bell.mp3 — 1.5s の低音の鐘、最後のクライマックス語が出るとき(動画全体で最大1回)

timeline への追加:

<audio id="aud-narration" src="narration.mp3" data-start="0" data-duration="<total>" data-track-index="0" data-volume="1"></audio>

<audio id="aud-tick-s02" src="sfx/tick.mp3" data-start="<scene2.start - 0.3>" data-duration="0.1" data-track-index="2" data-volume="0.55"></audio>


<audio id="aud-chime-s08-1" src="sfx/chime.mp3" data-start="<T>" data-duration="0.3" data-track-index="3" data-volume="0.45"></audio>
<audio id="aud-bell-s12" src="sfx/bell.mp3" data-start="<climax-T>" data-duration="1.6" data-track-index="4" data-volume="0.55"></audio>

⚠️ 各 <audio> に id が必要です。そうでないと、レンダリング時に音が出ません(hyperframes で必須です)。

異なる track-index 同士は競合しません。同じ track 内では時間が重ならないようにしてください。

SFX は控えめに使用:トランジションの tick は必須です。chime / bell は装飾なので、scene の内容がシンプルな場合は追加しません。bell は動画全体で1回だけにしてください。

Step 7: Lint + Inspect + Render(必ずこの順序で実行)
cd <article-folder>/video

# 必跑 1:linter(必须 0 errors)
npx hyperframes lint

# 必跑 2:layout inspect 找溢出(必须 0 errors)
npx hyperframes inspect --at 1,8,15,25,35,45,55,65

# 推荐:snapshot 看排版
npx hyperframes snapshot --at <t1>,<t2>,<t3> .

# 渲染(lint + inspect 都通过才能跑)
# ⚠️ 输出到上级目录,与 video/ 平行 —— 最终 MP4 不放 video/ 里
npx hyperframes render --quality standard --fps 30 --output ../<slug>.mp4

inspect を必ず実行する理由:縦型動画の幅は1080と狭く、3〜4文字の hero でも、280〜400px のフォントサイズでははみ出しそうになります。毎回必ず inspect を実行し、エラーが0件の場合のみ render できます。

はみ出しの修正:

  • フォントサイズを小さくする(inspect に具体的な提案が表示されます)
  • 長い hero は改行する(「蓄積できない」→ 2行の「蓄積」/「できない」)
  • white-space: nowrap は、文字数 × フォントサイズが画面幅未満であることを確認できた場合のみ使用する
  • .em が reveal-wrap 内ではみ出す場合は、.em に line-height: 1 を追加する

レンダリング品質:

  • --quality draft 約30秒でレンダリング — 反復作業用
  • --quality standard 約1.5分 — デフォルト。公開に十分です
  • --quality high 約3分 — 大画面やビジネス用途向け
Step 8: プレビュー

出力先:<article-folder>/<slug>.mp4(video/ と同じ階層で、video/ の中ではありません。video/ は中間ファイル用です)。

open <article-folder>/<slug>.mp4 でユーザーにプレビューを表示します。動画チャンネルに自動アップロードしないでください(ユーザーが先に編集や調整をしたい場合があります)。

Step 9: YouTube に公開(cron で自動実行。render のフローには含めない)

新しい動画の render 完了後、すぐにはアップロードしません —— YouTube には1日の quota 制限があり(デフォルトでは1日6件、アップロードごとに1600 quota ポイント)、レンダリングが多いと quota に達してしまいます。

方法:cron が毎日10:00に daily-upload-batch.sh を自動実行し、まだアップロードされていない MP4 を最大5件選び(記事の日付が古い順)、アップロード後に .youtube.json に記録します。

cron は登録済み(一度だけ実行すればよく、繰り返す必要はありません):

0 10 * * * /Users/jianshuo/.claude/skills/wjs-converting-text-to-video/scripts/daily-upload-batch.sh

手動で実行する場合(wjs-converting-text-to-video のフロー内では実行しないでください — cron に任せてください):

~/.claude/skills/wjs-converting-text-to-video/scripts/daily-upload-batch.sh
# 或单个文章立即上传
~/.claude/skills/wjs-converting-text-to-video/scripts/publish-to-youtube.py <article-folder>

各アップロードスクリプトの動作:

  1. MP4 が縦向きか横向きかを検出 → 縦向きのタイトルには #shorts を追加し、横向きは通常の video として扱う
  2. title は article.md の H1 から、description は冒頭の数段落から取得
  3. <article-folder>/.youtube.json を確認:存在する場合は古い動画の削除を試みてから新しい動画をアップロードします(youtube.force-ssl scope が必要ですが、現在の token にはこの scope がないため、削除をスキップして新しい動画をアップロードします)
  4. .youtube.json に記録

詳しくは memory を参照してください:[[auto-publish-youtube]]

ディレクトリ構成

<article-folder>/
├── article.md
├── illustration.png            # 用户原始示意图,不直接用作 bg
├── <slug>.mp4                  # ⭐ 最终视频(与 video/ 平行,不放 video/ 里)
└── video/                      # 所有中间产物
    ├── narration_chunks.json   # 5-10 个 scene 的旁白文本
    ├── tts_narration.py        # bootstrap 复制进来
    ├── narration.mp3           # 合并的全段 TTS
    ├── narration/              # 单段 mp3 (s01..sN)
    ├── timing.json             # 每段 start/end/duration
    ├── bg.png                  # GPT Image 2 生成的水彩背景
    ├── sfx/{tick,chime,bell}.mp3
    ├── index.html              # HyperFrames composition
    ├── hyperframes.json
    ├── meta.json
    ├── package.json
    └── snapshots/              # 渲染前快照

Skill 自身のファイル

~/.claude/skills/wjs-converting-text-to-video/
├── SKILL.md
└── scripts/
    ├── bootstrap-project.sh        # init video/ 目录 + 复制 helper + 生成 sfx
    ├── generate-bg.sh              # 调 GPT Image 2 生成抽象水彩 bg.png
    ├── tts.py                      # Volcano TTS narration 生成
    ├── synth-sfx.sh                # tick/chime/bell 合成 (ffmpeg)
    ├── retrofit-bg-image.py        # 给已有视频补 bg-image 层
    ├── strip-dark-scene-bgs.py     # 剥离 scene-level 暗色 bg,让 bg-image 透出
    └── publish-to-youtube.py       # 自动上传 MP4 到 YouTube(portrait→Shorts),可替换已有上传

アンチパターン

単調さを避ける(最重要 — 「平板な語り口」の原因)
避けること理由
すべての scene で B1 の2行 strikethrough を使う過去最大の失敗パターンです。B1 は動画全体で最大2回まで
すべての scene を中央揃えにする活気がなくなります。中央揃え以外を2つ以上にする
すべての scene のフォントサイズをほぼ同じにするサイズの差は240px以上必要です
すべての scene を5〜7秒にする長さの差は6秒以上必要です
動画全体で blur crossfade だけを使う4つのトランジションごとに2種類以上を使う
動画全体で color-flip を使わないA3 を1つ以上使うことは必須です
動画全体で幾何学的な要素を使わない1つ以上の scene に太線/大きな番号/引用符を追加する
動画全体で tl.from({y, opacity}) だけを使うModern Motion Techniques を3種類以上使う
すべての scene に要素を詰め込む1つ以上の scene では余白を60%以上確保する
すべての scene に background: の色を追加するbg-image を覆い、水彩画像を生成した意味がなくなります。通常の scene では bg を指定せず、A3 の color-flip のみ単色を使います
color-flip / emphasis にいつもオレンジだけを使うaccent を2〜3種類以上使う
二次テキスト/strike/装飾にグレーを使う水彩背景ではグレーのコントラストが低く、見えなくなります。#f5efe5 のクリーム色を使い、opacity で弱めてください(詳しくは [[no-low-contrast-text]] を参照)
コンテンツ/エンジニアリング
避けること理由
Kokoro で中国語 TTS を使う中国語の品質が低く、ユーザーが明確に受け入れていません
Volcano TTS に emotion パラメーターを渡す_bigtts の音声では data: null が返り、無音で失敗します
zh_male_jieshuonansheng_mars_bigtts を使う英語の固有名詞を含むと、繰り返し幻覚が発生します
serif フォント(Songti / 宋体 / Noto Serif)を使うインパクトに欠けます
記事全体を画面に貼り付けるそれは PPT です。動画は1画面につき1つのビジュアル上の瞬間を見せます
10 scene または90秒を超える視聴者の注意力が続きません
短い記事を無理に90秒まで引き延ばす記事が短ければ30〜50秒にします。無理に長くすると水増しになり、内容が浅くなります
scene ごとにフォントや配色のスタイルを変えるスタイルがぶれます。デザインシステムは固定し、テンプレートを変化させます
strike に ::after の擬似要素と CSS 変数を使うhyperframes のレンダリング経路では動作しません。実際の DOM <span class="strike-line"> を使用します
最後の scene 以外で gsap.to({opacity: 0}) を使うhyperframes では退場アニメーションは禁止されています。退場にはトランジションを使います
chunk ごとに chime を追加するうるさくなります
bg url に ../illustration.png を使うhyperframes の render ではディレクトリをまたぐパスが解決されず、真っ黒にレンダリングされます。bg.png は video/ 内に配置してください
<audio> に id を付けないrender 時に無音になります。各 <audio> に必ず id="..." を指定してください
s1 を A3 の color-flip にする最初のフレームで bg-image が見えません。color-flip は s2 以降に配置します
s1 のタイトル要素すべてに from({opacity: 0}) を指定する最初のフレームが黒くなります。s1 の主要要素はデフォルトで opacity: 1 にし、y/scale のみ動かします

よくある落とし穴

  • ナレーションのテキストを変更したのに TTS が変わらない(キャッシュが使われている) → tts_narration.py は chunk id ごとに個別の mp3 をキャッシュします。テキストを変更しても id が変わらない場合、古い音声が再利用されます(症状:テキストが長くなったのに、timing.json の長さが前のバージョンとまったく同じです)。再合成する前に rm -rf narration/ narration.mp3 timing.json を実行してください
  • ナレーションに「——」のダッシュを使う → TTS が「破折号」と読み上げます。削除して句点または読点に置き換えてください
  • 特定の chunk が異常に長い(>3 chars/s) → Volcano が幻覚的なループを生成します。音声を変更するか、短く分割してください
  • scene の長さがナレーションの長さより短い → 次の scene に切り替わるときにナレーションが途切れます。scene はナレーション全体と 0.3s のバッファをカバーする必要があります
  • 黒背景の大きな文字が opacity: 0 でも見える → s1 以外の .scene に opacity: 0 のデフォルト設定があるか確認してください
  • .reveal-wrap 内の .em が少しはみ出す(上下に数 px) → .em に line-height: 1 を追加してください
  • スナップショットの字形がレンダリング結果と一致しない → 現在はどちらも Noto Sans SC を使用しているため、通常は一致します

依存関係

  • HyperFrames CLI (npx hyperframes) — コンポジションの lint/inspect/snapshot/render
  • GPT Image 2 (~/.claude/skills/gpt-image-2-skill/) — bg.png を生成します。--provider codex は ChatGPT の認証を使用します
  • Volcano TTS — VOLC_TTS_APPID / VOLC_TTS_ACCESS_TOKEN は ~/code/.env にあります
  • ffmpeg — SFX の合成、音声の結合、アスペクト比の検出
  • YouTube uploader (~/.claude/skills/wjs-uploading-video/) + ~/.config/youtube/token.json にある OAuth トークン —— Step 9 で自動公開します

コメント