キネティックプロモーションビデオ
ビジネスの説明だけから、放送品質の縦型プロモーションビデオを構築します。ビデオは一時停止されたマスタークロックで駆動される単一のHTMLページとして作成され、フレームごとにレンダリングされエンコードされます。エディタ、ストックフッテージ、タイムラインアプリは不要 — そしてすべてのフレームはタイムスタンプから再現可能です。
入力
$brief— それが何か、誰のためのものか、何が違うのか。必須。会話、リポジトリのREADME、または質問から取得します。$reference— オプション。ルックを一致させるべきビデオURLまたはローカルファイル。$brand— オプション。名前、タグライン、URL、色、ロゴ。存在しない場合は、明確にフラグが立てられたプレースホルダーを使用します(決して黙ってブランドを発明し、それを最終版として提示してはいけません)。$duration— オプション。デフォルトは無音で30〜35秒、ナレーション付きで45〜60秒(音声が長さを決定します —references/audio.mdを参照)。Shorts/Reels/TikTokの場合は60秒未満に保ちます。$audio— デフォルト:ナレーション付き、音楽なし。 ユーザーが無音作品を要求しない限り、ナレーションを書き、その周りにタイムラインを構築します。音楽は明示的に要求されない限りオフのままです。
目標
promo.mp4 — 1080×1920、30 fps、H.264、最初の視聴で再生可能かつ正しい — さらに、それを生成したscene.html、それを再現するrender.py、ナレーション作品用のvoice.srt字幕ファイル、およびビートシートとブランド変更のための定数を含むREADME。成功は「ファイルが存在する」ことではありません。それはあなたが見たフレームが、重複するテキストがなく、キューより前に表示される要素がなく、無音の時間がない、きれいに読めることです。
ステップ
1. ツールチェーンを確認する
which ffmpeg ffprobe yt-dlp python3
python3 -m venv .venv && .venv/bin/pip install -q playwright && .venv/bin/playwright install chromium
ffmpegとPlaywright用のChromiumは必須要件です。yt-dlpは参照の分解にのみ必要です。venvを推奨します — ユーザーのシステムPythonは外部管理されている可能性があります。
成功基準: ffmpeg -versionが動作し、.venv/bin/python -c "from playwright.sync_api import sync_playwright; sync_playwright().start().chromium.executable_path"がパスを出力すること。
2. ビジュアルターゲットを確立する
2つのパス — $referenceの有無で選択します。
2a. 参照あり → それを分解する
references/teardown.mdを読んで従ってください。簡単に言うと:ダウンロードし、プローブし、1 fpsのコンタクトシートを抽出し、実際にシート画像を読み、コードを書く前に寸法、持続時間、パレット、タイプ処理、ビート構造、およびモーションボキャブラリを書き留めます。
フレームごとに見ていない参照を決して説明しないでください。ページのタイトルや説明は、そのモーションデザインについては何も教えてくれません。
2b. 参照なし → まず被写体自身の言語を導き出す
default-style.mdをまだ開かないでください。実際の被写体に関する4つの質問に答えるのに2分を費やしてください:
- 物体は何ですか? 本、瓶、請求書、顔、ダッシュボード。これらが動くべきものです。
- それらはどんな光の中にありますか? 読書灯、ショールーム、月光、画面の輝き、窓からの日光。
- それらは物理的にどのように動きますか? 質量のあるものは落ち着きます。紙は回転し、折れます。液体は広がります。データはスナップします。
- 背骨は何ですか? 主張を運ぶ1つの繰り返し要素 — カウントダウン、夜を横切る時計、成長するレシート、埋まっていくルート。理想的には、それは装飾ではなく、主張そのものです。
それらの4つの答えが一貫したルックを与えるなら、それを構築してください。一晩の修復についてのスキンケアプロモは、夜のパレット、セリフ書体、ゆっくりとした落ち着くモーション、および午後10時→午前7時を実行するダイヤルを取得します — 光るリングではありません。
被写体が具体的なものを何も示唆しない場合(ほとんどのB2Bソフトウェア、抽象的なサービス)は、ハウススタイルにフォールバックします。 次にreferences/default-style.mdを読み、そのまま使用します:それは完全で検証済みの仕様です(ダークネイビー+ブルーグロウ、ジオメトリックサンセリフ、ブラーインキネティックタイプ、光る地平線、リングアウトロ)。どのスタイルを使用しているかを1行でユーザーに伝え、参照ビデオを指定することで変更できることを伝え — そして続行します。承認を待って停止しないでください。
構築前の正直さチェック。 ビートのタイミングがテンプレートの±約0.5秒以内で、シグネチャーショット(エコーワードスタック、リングブルーム、地平線+スラブカード、唯一のテキスト移動としてのブラーイン)が残っている場合、あなたは被写体のためにデザインするのではなく、テンプレートをリスキンしたことになります。それは意図的にフォールバックを選択した場合は問題なく、そうでない場合は欠陥です。
成功基準:構築中に参照できる短い書面仕様 — キャンバスサイズ、fps、持続時間、パレットの16進数、フォント、および使用するモーションムーブ。被写体固有の言語を導き出した場合は、背骨と意図的に使用していないムーブも指定します。
3. ビートシートを書く
references/audio.mdを読み、最初に音声トラックを書きます — デフォルトでオンです。音声がタイムラインを所有します:ラインを生成し、測定し、丸い数字に音声を当てはめるのではなく、測定値からビート時間を導き出します。測定されたラインごとのタイミングを保持します — それらは納品時にvoice.srtになります。ユーザーが無音作品を要求した場合にのみこれをスキップします。
構造とコピールールについてはreferences/beats.mdを読み、次にユーザーの実際の差別化要因をビートにマッピングします。中間の3分の1は製品が販売される場所です:それはこのものが優れている具体的な理由を含まなければならず、形容詞ではありません。ミュート再生を想定します — ほとんどのShorts/Reels視聴者は音声を聞きません — そのため、音声のすべての主張はナレーションだけでなく、画面上のテキストとしても伝わる必要があります。
コードを書く前に、ビートシートを表(t範囲→ビート)として書き出します。ペーシングを間違えると、シーンが構築された後に修正するのに非常にコストがかかります。
成果物: $durationに合計される明示的な秒範囲を持つビート表。
成功基準: すべてのビートが製品に関する実際の何かに遡り、範囲が連続しており、0.3秒を超えるギャップがないこと。
4. テンプレートからスキャフォールドする
以下で$SKILLは、このスキル自身のディレクトリ — このSKILL.mdを保持しているものです。パスを推測するのではなく、スキル一覧から解決してください。しばしば読み取り専用でマウントされていますが、すべてがコピーされるため問題ありません。
mkdir -p video && cp "$SKILL/templates/render.py" video/
cp "$SKILL/templates/scene-starter.html" video/scene.html
bash "$SKILL/templates/fetch-fonts.sh" video/fonts # Poppinsサブセット、約40 KB
scene-starter.htmlにはすでにアニメーションハーネス(seek、win、io、pop、swap、draw、シード付きRNG、雰囲気)と、コピーする価値のあるすべてのパターンをカバーする7つの例示ビートが含まれています。ハーネスを書き換えないでください — ルールの下にリストされているバグをエンコードしています。例示ビートを置き換えてください;それより上のすべてを保持します。
成功基準: python render.py --contactが、手を加えていないスターターからcontact.pngを生成すること。
5. シーンを構築する
ビートごとに1つのIIFE、それぞれがwin(layer, tIn, tOut)によってゲートされた独自のlayer()を作成します。内部では、アニメーション化されたすべての要素は、正確に1回のio() / pop() / swap()呼び出しを取得します。
UIモックアップ(ダッシュボード、電話、ラップトップ、カード、チップ)をプレーンなDOM + CSSとして構築し、アニメーション化して取り込みます — それらは純粋なタイプでは伝えられない「実際の製品」の感触をもたらします。チャートはdivとインラインSVGです;チャートライブラリ、画像、外部リクエストはありません。
ハードカットは正当な動きです:正確な時間境界を共有する2つのwin()レイヤーは、steps(1,end)で入力され、意図的な単一フレームカットを与えます — ストーリーワールド→フラットブランドカラーなどのトーンのスナップに適したツールです。他のすべてはイーズします;カットはそれが唯一のものであるために機能します。
実際のマルチカラーロゴは、暗いシーンとフラットなブランド背景の両方で判読不能になることがよくあります — 色を変えるのではなく、小さな白いプレートにアセットを配置します。
ルール: ルールセクションを参照してください。それらはスタイルの好みではありません — それぞれはこのスキルがすでに遭遇した特定の障害です。
成功基準: python render.py --contactがエラーなくレンダリングされること。
6. プレビューループ — すべてのフレームを見る
python render.py --contact # 24フレーム概要、約20秒
python render.py --at 6.2 12.0 19.5 # 特定の瞬間、フル解像度
結果のPNGを画像読み取りツールで読み、検査します。 次に修正して繰り返します。シートがきれいになるまでここで反復します — フルレンダリングは10倍遅いため、フィードバックループとして決して使用しないでください。
2つのビューは異なる質問に答えます。 コンタクトシートは構造用です:タイミング、オーバーラップ、デッドフレーム、ビート順序。フレームあたり270×480であり、暗いまたは低コントラストの作品を体系的に過小評価します — シートで濁って壊れているように見える作品は、フルサイズではしばしばきれいです。コントラスト、パレット、および判読性は--atフレームからのみ判断し、パレットが暗い場合はビートごとに少なくとも1つ、ピクトグラムやイラストごとに1つを取得します — 認識可能性はタイルサイズでは判断できません(ルール12を参照)。シートが誇張したものを修正するために明るさを上げないでください。
各パスで探すべきもの:
- トランジション途中で同時に判読可能な2つのテキスト
- キューより前に表示されるもの(古典的な
fill: 'both'バグ — ルールを参照) - 雰囲気ではなく認識可能な形状として読まれる背景要素
- 積み重なった半透明のブロブ — 重なるアルファレイヤーは可視的な交差弧に合成され、円盤として読まれます;フレームより大きくサイズ設定された、独自のボックス内で溶解するリムを持つ1つの要素を使用します
- デッドフレーム — 画面上に何もない瞬間
- フレームの端に押し込まれたテキストまたはモックアップ、または電話画面には小さすぎるもの
人間によるチェックポイント:必須ではありませんが、ユーザーがいる場合は、フルレンダリングの前にコンタクトシートを見せてください — ペーシングとコピーの修正は今が最も安価です。
成功基準: すべてのフレームがきれいに読め、上記の欠陥がないコンタクトシートを見たこと。
7. レンダリングして検証する
最初にエンコーダをスモークテストします。 約2秒のスライスをレンダリングし — あなたのrender.pyコピーに範囲フラグがある場合はそれを使用し、そうでなければ一時的に持続時間定数を縮小して — 結果をffprobeします。エンコーダとマルチプレクサの問題は、複数分のフルレンダリングの後ではなく、ここで数秒で表面化します。
python render.py # -> promo.mp4、フレームあたり約1スクリーンショット
# ナレーション作品:エンコーダプロセスが終了した後にのみマルチプレクスする
ffmpeg -y -i promo.mp4 -i vo/voice.wav -c:v copy -c:a aac -b:a 192k promo_av.mp4
ffprobe -v error -show_entries format=duration \
-show_entries stream=codec_type,codec_name,width,height,nb_frames \
-of default=noprint_wrappers=1 promo_av.mp4
ffmpeg -y -v error -i promo_av.mp4 -vf "fps=1/1.2,scale=180:320,tile=10x3" -frames:v 1 verify.png
フルレンダリングをバックグラウンドで実行します。 フレームあたり約1スクリーンショットで、50秒のビデオは約1500フレームであり、600秒のコマンドタイムアウトを快適に超えます。mp4が表示されるのではなく、レンダリングプロセスが終了するのをポーリングします:ファイルは実際に終了するずっと前にディスクに存在し、早期にマルチプレクスするとmoov atom not foundが発生します。
ナレーション作品の場合は、ステップ3で測定されたラインごとのタイミングからvoice.srtを書きます — 数値はすでに存在します;これは新しい作業ではなくフォーマットパスです。
verify.pngを読みます — ブラウザではなくエンコードされた出力を検証することがポイントです。次にスクラッチシートとフレームディレクトリを削除します。
成功基準: ffprobeが期待される持続時間、期待される寸法(ユーザーがアスペクトを変更しない限り1080×1920)、およびnb_frames == fps × durationを報告すること;ナレーション作品の場合はオーディオストリームが存在し、持続時間が音声ベッドと一致し、voice.srtが存在すること;verify.pngがビートシートと完全に一致すること。欠落したエンディングを持つ短いファイルは、通常、-shortestが古い音声トラックに遭遇したことを意味します — references/audio.mdを参照してください。
8. 引き渡す
ビデオの横にREADMEを書きます:ビートシート、ブランド変更定数、レンダリングコマンド、および2つのハーネスの落とし穴。次にユーザーに報告します:
- ファイルの場所と正確な仕様(サイズ、持続時間、fps、およびナレーション付きか無音か — さらに再生成できるように使用されたTTS音声)
- ナレーション作品の
voice.srtパス、およびShorts/Reels/TikTokがビデオと一緒にアップロードとして受け入れること - ビート表
- すべてのプレースホルダーと発明された数字を明示的に指摘 — ブランド名、URL、およびモックアップに表示されるメトリック。主張が防御可能である必要がある場合、捏造された数字は公開前に置き換えられなければならないと明確に述べてください。
- 共有可能なリンクはありません — ユーザーがホスティングを要求しない限り、それはローカルファイルです
開くことを提案します:open promo.mp4。
ルール: 明示的な承認なしにコミットまたは公開しないでください。ビデオを生成する副作用としてどこにもアップロードしないでください。
成功基準: ユーザーがファイルパス、プレースホルダー、およびそれを変更する方法を知っていること。
ルール
ハード制約。それぞれは、このパイプラインがすでに生成した障害です。
-
デフォルトでナレーション付き、決して音楽なし。 ユーザーが無音作品を要求しない限り、ナレーションを書き、タイムラインをそれに支配させます。音楽とサウンドデザインは明示的に要求されない限りオフのまま — ライセンスされたトラックを提供することはできず、ストリーミングサイトからオーディオを決して取得しないでください。それでもミュート再生を想定します:すべてのナレーションの主張は画面上のテキストとしてミラーリングされ、ナレーション作品には
voice.srtが同梱されます。無音作品を出荷する場合は、報告時に「無音」と言い、静かな再生がバグとして読まれないようにします。 -
要素ごとに1つのアニメーション、enter → hold → exitをカバー。
fill: 'both'を持つ2つの別々のWAAPIアニメーションは競合します:exitクリップはt = 0から最初のキーフレームを適用し、ビデオ全体で要素を表示したまま固定します。io()ヘルパーはこれを防ぐためだけに存在します。症状:シーンの開始時にすべてが同時に表示され、アニメーションインしません。 -
連続するテキストは、次のテキストが入る前に完全に退出する必要があります。 クロスフェード途中で画面に2つの判読可能な行があると、トランジションではなくレンダリングバグとして読まれます。行Nの退出が行N+1の入り口が始まる前に完了するようにスケジュールします。
-
決定論は交渉の余地がありません。 RNGにシードを与えます。
Date.now()、Math.random()を決して呼び出さないでください。
… (翻訳のため短縮)