Communitygithub.com

suxrobGM/logistics-app

Playwrightでアプリを操作し、ffmpegで編集して、ショートティーザー/デモ動画を撮影・編集・納品します。ストーリーボード作成、認証付きキャプチャ、PIIレビュー、アニメーションタイトルカード、MP4 + GIFを生成。「ティーザーを作って」「デモ動画を録画して」「製品ローンチ動画」「README用の画面録画」に使えます。

logistics-app とは?

このスキルは、実際に動作するアプリケーションのプロダクションクオリティのティーザー動画(約30〜45秒)を自動生成します。ストーリーボードの作成、Playwrightによる実際のUI操作の自動撮影、ffmpegによる映像編集、そしてREADME用のMP4とパレット最適化GIFの納品までを一貫して行います。製品動画のように見せるために、画面録画ではなく、シーンの演出、フレーミング、リズムを重視します。Claude Code環境で動作し、認証済みのキャプチャやPIIの確認、アニメーションタイトルカードの生成もサポートしています。このスキルは、本番環境への副作用(メール送信、公開投稿、課金など)を引き起こす可能性があるため、すべての操作について明示的なユーザーの同意を得ることが必須です。また、動画のすべてのフレームに個人情報が含まれていると想定し、事前にポリシーを決定し、最終カットで厳密にレビューします。

対応✓Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/suxrobGM/logistics-app/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ボタンだけではなく、セッション/ワーカーを強制終了します。UIボタンは効果がない場合があります)、読み返して停止状態を確認し、最終メッセージでユーザーにその回数も含めて明確に伝えます。

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

実際のアカウントには、名前、メール、住所、書類が含まれます。事前にユーザーとポリシーを決定します。

  • フレームで囲む(デフォルト) – アイデンティティを除外するショットを構成します。ffmpegの段階で永続的なクローム(サイドバー、アバター、アカウントメニュー)をクロップします。
  • 撮影前に非表示にする – カメラが回転する前に、問題のあるカードに page.evaluate で visibility: hidden を設定します。決して撮影して後で編集で回避しようとしないでください。
  • シード済みデモアカウントを使用する – 存在する場合、最もクリーンです。

最終カットのすべてのフレームを納品前にフル解像度でレビューします。コンタクトシートではなく、フレームです。

ワークフロー

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

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

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

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

2. セットの準備

撮影後にではなく、常に事前にキュレーションします。

  • データが最も見栄えの良いビューを選びます。完全なチャート、アクティブなリスト、認識可能な名前、ゼロでないカウント。スピナー、空の状態、または半分レンダリングされたチャートは決して撮影しません。
  • 初期化スクリプトまたはプリロールの page.evaluate を介して localStorage を使用して、UIの状態(パネル幅、折りたたみ/展開、フィルター、スクロール位置)を意図的に設定します。
  • トースト、バナー、Cookieバーはカメラ外で閉じます。
  • データを落ち着かせます:networkidle に加えて数秒待ちます。

3. 撮影

画面録画ではなく、CDPスクリーンキャスト(Page.startScreencast)を使用します。これは、画面上のページを約100fpsでキャプチャするため、ユーザーが行っていることは一切フレームに表示されず、マシンは使用可能なままです。Playwrightが操作をドライブし、CDPが撮影します。reference/rig.js がこれを実装しています。

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

外部ウィンドウの撮影(アプリ自体が開くブラウザ、デスクトップアプリ)は、OSキャプチャ(gdigrab/x11grab/avfoundation、リグ内の recordDesktop)が必要です。これにより、ユーザーの作業を含む画面上のすべてがキャプチャされます。ユーザーに警告し、席を離れるように依頼し、結果をフレームごとにレビューします。プライベートなものをキャプチャした場合は削除します。

4. タイトルカード

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

アプリのモーション言語(独自のイージングカーブと期間)に合わせます。プロジェクトに確立されたカードスタイルがない場合は、デザインする前に frontend-design スキルを読み込みます。

5. 編集

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

6. レビューしてから納品

  • コンタクトシートと、1秒ごとのフル解像度フレームを生成します。
  • PII、クリップされたパネル、読み取り不可能なテキストがないかフレームを読み取ります。
  • プローブを使用して、期間と寸法を確認します。
  • MP4と、README用にパレット最適化されたGIF(GitHubはコミットされたMP4をインライン再生しません)を納品します。GIFは約10MB未満に保ちます。
  • 依頼がない限り、サイレントのままにします。音楽のライセンスは取得できません。その旨を伝えます。

苦労して得た知見

プッシュインに 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を解析します。)

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

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

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

成果物

パス、期間、寸法を報告します。ユーザーからまだ必要なもの(音楽、GitHubネイティブの動画アップロード)を明確に述べ、撮影中にユーザーが知っておくべきことが発生した場合は開示します。

関連スキル