Communitygithub.com

suxrobGM/jobpilot

Playwright でアプリを操作し、ffmpeg で編集して、短いティーザー/デモ動画を撮影・編集・納品します。ストーリーボード、認証付きキャプチャ、PII レビュー、アニメーションテロップ、MP4 + GIF を提供。「ティーザーを作って」「デモ動画を録画して」「製品ローンチ動画」「README 用のスクリーンレコード」といったタスクに最適。

jobpilot とは?

このスキルは、実際のアプリケーション UI を Playwright で駆動し、CDP スクリーンキャストと ffmpeg を使って製品品質のティーザー動画(約 30~45 秒)を自動生成します。単なる画面録画ではなく、ストーリーボードの作成、シーンの演出、フレーム構成、PII のレビュー、アニメーションテロップの挿入まで一貫して行います。出力は MP4 と GitHub README 向けのパレット最適化 GIF です。対応プラットフォームは Web アプリ全般で、認証状態の保存・再利用、ローカルネットワークアクセスチェックの回避、外部ウィンドウの OS レベルキャプチャなど、実運用で直面する様々な状況を想定した堅牢な実装が特徴です。ユーザーに明示的な同意を得ながら操作を行い、フレーム単位での PII チェックを徹底するなど、安全性にも配慮しています。全てのスクリプトは ESM モジュールとして `reference/` に用意されており、実行環境に ffmpeg のインストールが不要な設計です。

対応✓Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/suxrobGM/jobpilot/tree/main/.claude/skills/teaser-video

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

ティーザー動画

起動中のアプリに対して、ローンチ品質のティーザー(約 30~45 秒)を生成します。ストーリーボードを作成し、実際の UI を操作して撮影し、ffmpeg で編集して、MP4 と README 用の GIF を納品します。

出力は製品動画のように見える必要があり、単なる画面録画ではいけません。違いは解像度ではなく、演出、フレーミング、リズムといった「演出面」にあります。

参考実装は reference/ にあります。疑似コードではなく、実際に動作する ESM モジュールです。プロジェクト内ではなく、スクラッチディレクトリでセットアップしてください:

cp -r <skill>/reference/* "$SCRATCH/" && cd "$SCRATCH" && npm install
export TEASER_BASE_URL="https://the-app.example.com"

reference/package.json は "type": "module" と 3 つの依存関係を宣言しています。すべてのスクリプトは TEASER_BASE_URL を読み取るため、特定のアプリにハードコードされていません。

何よりも先に:2 つの絶対ルール

1. 現実世界に影響を及ぼす副作用を、明示的一件承認なしに絶対にトリガーしないこと。

実アプリの撮影は、実際のボタンをクリックすることを意味します。その中には、メールを送信する、公開投稿する、課金する、サードパーティにフォームを送信する、といった操作が含まれる可能性があります。アプリに触れる前に:

  • ストーリーボード内の、マシンを離れるすべてのアクションを列挙する。
  • それぞれのアクションが何をするのか、何回実行されるのかをユーザーに正確に伝え、回数について合意を得る。
  • 不可逆的なステップの手前で停止するモード(プレビュー、ドライラン、撮影してから拒否できる確認ゲート)を優先する。

ライブシステムに対する読み取り専用のプローブなど存在しません。既に実行中のセッション内で「診断目的」のクリックが行われれば、実際に発火します。アクションが機能しなかった理由をデバッグしている場合、次のクリックは 機能する と想定し、その準備をしてください。

意図しない発火が発生した場合:プロセスをそのソースで直ちに停止し(UI のボタンだけでなく、セッション/ワーカーを kill する。UI ボタンは効かない可能性がある)、停止した状態を読み戻して確認し、最後のメッセージでユーザーにその回数も含めて明確に伝えてください。

2. すべてのフレームが公開され、PII で満たされていると想定すること。

実際のアカウントには、名前、メールアドレス、住所、書類が含まれています。事前にユーザーと方針を決めてください:

  • フレームで回避する(デフォルト) - 個人情報を除外するようにショットを構成する。ffmpeg の段階で、永続的なクローム要素(サイドバー、アバター、アカウントメニュー)をクロップする。
  • 撮影前に非表示にする - page.evaluate で該当するカードに visibility: hidden を適用してからカメラを回す。撮影して後でカットで回避しようとしない。
  • シードされたデモアカウントを使用する - デモアカウントが存在する場合、最もクリーン。

納品前に、最終カットのすべてのフレームをフル解像度で確認する。コンタクトシートではなく、フレームそのものを確認してください。

ワークフロー

1. スカウトとストーリーボード

最初に、すべての候補画面の認証済みスクリーンショットを取得します(reference/scout.js)。それらを確認します。次に、ストーリーボードテーブルを作成します:ビート、ショット、視聴者が 感じる べきこと、長さ。担当が割り当てられていないショットは、撮影前にカットされます。

効果的なアーク:緊張(問題) → 啓示(製品) → 証明(実際に動作する) → 規模(結果) → 信頼(実行環境) → クロージング。

ストーリーボードに、どの画面に PII が含まれ、どの画面が安全かを記録してください。

2. セットの準備

撮影前に、決して撮影後に、セットをキュレーションします:

  • データが最も美しく見えるビューを選択する - 完全なチャート、アクティブなリスト、認識可能な名前、ゼロでないカウント。スピナー、空の状態、描画途中のチャートは絶対に撮影しない。
  • UI の状態(パネル幅、折りたたみ/展開、フィルター、スクロール位置)を、init スクリプトまたはプレロールの page.evaluate で意図的に設定する。
  • トースト、バナー、クッキーバーはカメラ外で消す。
  • データが落ち着くのを待つ:networkidle に加えて数秒。

3. 撮影

CDP スクリーンキャスト(Page.startScreencast)を使用します。画面録画ではありません。これはページをオフスクリーンで約 100fps でキャプチャするため、ユーザーの操作がフレームに映り込まず、マシンも使用可能なままです。Playwright が操作を駆動し、CDP が撮影します。reference/rig.js がこれを実装しています。

  • ショットごとに 1 つの被写体。「アプリ全体」ではなく、対象をフレーミングする。
  • カメラの動きは ページ内でのスクリプト化されたイージングスクロール(cameraScroll)から行い、フィルターは使用しない。以下の zoompan 警告を参照。
  • カーソルは演技者です。注入された SVG カーソル(cursorClick)を使用します。これはイージングで滑らかに動き、押下時にディップします。または非表示にします。アイドル時のドリフトは避ける。
  • 各テイクをリハーサルし、フレームを抽出し、確認し、調整してから本番撮影する。

外部ウィンドウの撮影(アプリ自体が開くブラウザ、デスクトップアプリ)には、OS レベルのキャプチャ(gdigrab/x11grab/avfoundation、rig 内の recordDesktop)が必要です。これは 画面上のすべて、ユーザー自身の作業も含めてキャプチャします。ユーザーに警告し、離れるよう依頼し、結果をフレーム単位で確認し、プライベートな情報が映り込んだ場合は削除します。

4. タイトルカード

静的画像ではなく、ブラウザ内で撮影したアニメーション HTML/CSS。カードのマークアップを アプリ自身のオリジン上の 空白ページに注入し(これにより Web フォントとパレットが解決される)、スクリーンキャストします。実際のページから表示フォントを取得し(getComputedStyle)、既知の良好な等幅スタックを固定します(スニッフィングはしません)。

アプリのモーション言語(独自のイージングカーブと持続時間)に合わせます。プロジェクトに確立されたカードスタイルがない場合は、事前に frontend-design スキルをロードしてください。

5. 編集

reference/build.js は各セグメントを共通のサイズ/フレームレートに正規化し、クロスフェードでチェーンします。カットは約 2.5~4 秒ごとに保ちます。決定的な瞬間は通常速度で余裕を持たせ、その周りのルーチン部分は圧縮します。

6. レビュー、そして納品

  • コンタクトシート および 秒ごとのフル解像度フレームを生成する。
  • フレームを PII、クリップされたパネル、読みにくいテキストがないか読み取る。
  • プローブで長さと寸法を確認する。
  • MP4 に加えて、README 用のパレット最適化 GIF を納品する(GitHub はコミットされた MP4 をインラインプレイしません)。GIF は約 10 MB 未満に抑える。
  • 指定がない限り無音で納品する。音楽のライセンスは取得できません。そのように伝える。

苦労して得た知見

プッシュインに zoompan を使用しないこと。 フレームごとにズームを再計算し、クロップウィンドウを整数ピクセルに丸めるため、画像が行ったり来たりと震えます。地震のように見えます。ロックされたショットと、実際のページ内スクロールを使用してください。どうしてもプッシュインが必要な場合は、まずソースを 4 倍にプリスケールし、フレームデルタ測定(reference/jitter.js)で確認してください。ロックされた静的ショットは平均デルタが 1.0 未満であるべきです。目に見える揺れは 3 以上になります。

アニメーションが落ち着くと、スクリーンキャストはフレームの発行を停止します。 アニメーションが 1.2 秒で終了するカードは、1.2 秒のクリップになります。tpad=stop_mode=clone:stop_duration=N でパディングし、-t で希望の長さにカットしてください。

固定レートではなく、タイムスタンプ付きフレームをアセンブルすること。 スクリーンキャストのフレームには metadata.timestamp が付属します。実際のフレームごとの持続時間で ffmpeg concat リストを作成し、その後一定の 60fps にリサンプリングします。固定間隔を想定すると、ジッターが発生します。

ffmpeg-static を dev 依存関係として、モジュールパスで解決すること。 ffmpeg が PATH にあることに依存しないでください。(ffprobe はそのパッケージに含まれていません。代わりに ffmpeg -i の stderr を解析してください。)

認証済みキャプチャ: 一度ヘッド付きでログインし、storageState を保存し、すべてのテイクで再利用します(reference/login.js)。セッションは期限切れになります。同じ日に撮影を終えるか、再実行してください。また、UI 設定の localStorage もそこでシードしてください。

デプロイされたオリジンからの localhost へのアクセスは、Chrome のローカルネットワークアクセスポリシーによってブロックされます。 ページがローカルサービスにアクセスする必要がある場合は、--disable-features=LocalNetworkAccessChecks,PrivateNetworkAccessChecks,BlockInsecurePrivateNetworkRequests を指定して起動してください。

永続的なクローム要素は固定オフセットでクロップすること。 ビューポート幅 1920 + railWidth で撮影し、crop=1920:1080:railWidth:0 でサイドバーを除去して正確に 1080p に収めます。

成果物

パス、長さ、寸法を報告し、ユーザーからまだ必要なもの(音楽、GitHub ネイティブ動画アップロード)を明確に述べ、撮影中にユーザーが知るべき出来事があれば開示します。

関連スキル