Video2presentation: セミナー録画 → プレゼンテーション
決定事項と測定値の詳細な履歴は、Video2presentationフォルダ内のMETHODOLOGY.mdにあります。このスキルとMETHODOLOGY.mdで矛盾がある場合は、このスキルに従ってください。新しい教訓はMETHODOLOGY.mdに追加してください。
常に適用されるルール
- 最初に計画と質問。 処理を開始する前に、以下の手順に従った計画をユーザーに伝え、AskUserQuestionを使用して明確化のための質問をしてください(質問リストはステップ0にあります)。回答を得てから作業を開始してください。
- ユーザーへの返信。 ユーザーの言語(通常は英語以外)で、正直に、お世辞なしで返信してください。すべての数値はスクリプトで計算してください。情報は表としてフォーマットし、ユーザーが要求した場合のみプレーンテキストを使用してください。更新のたびにプレゼンテーションリンクを提供してください。
- セキュリティ。
- AssemblyAIキーは
assemblyai_key.txtファイルからのみ読み取ってください。決して印刷したり、クラウド、ノート、アーティファクトにコピーしたりしないでください。 - スピーカーがMiroボードを表示している場合でも、MiroやFigmaの統合は使用せず、ビデオフレームからボードを取得してください。
- 許可なくユーザーのフォルダ内のものを削除しないでください。
_work/ワーキングフォルダも含みます。
- AssemblyAIキーは
- 正確性。
- スピーカーを黙って訂正しないでください。矛盾点や数値の不一致は、タイムコード付きでノートに記録してください。
- スライドの数値はスクリプトで再計算してください。
- 法的な主張を確認し、スライドに短い注意書きを追加してください。
- 最終的な要点は、スピーカーのスライドではなく、統合としてマークしてください。
- ビデオはクラウドにアップロードしません。 ffmpeg、OpenCV、文字起こしはユーザーのコンピュータ(
device_bash)上で実行されます。コンタクトシート、モザイク、個々のフレーム、切り抜きのみがクラウドに送信されます。これらは、確認、認識、またはアーティファクトへのアップロードのために必要なものです。
ファイルの場所
- スキルとプロジェクトフォルダは
Video2presentationです(Coworkの場合は接続フォルダ$HOME/mnt/Video2presentation、Claude Codeの場合はカレントフォルダ)。そのルートには、スキル自体(SKILL.md)とMETHODOLOGY.mdがあります。 pipeline/— スクリプト:transcribe_assemblyai.py— AssemblyAIを使用した文字起こし;extract_slides.py— 1秒あたり1フレーム、ウェブカムマスク、安定セグメント、重複除去、コンタクトシート;mosaic.py— フレームリストから2×2モザイクを作成;crop_images.pyとcrops_<日付>[_<スピーカー>].json— 画像の切り抜き;clean_transcript.py— スライドごとにクリーニングされたトランスクリプトの抜粋。
assemblyai_key.txt— AssemblyAI APIキー。ファイルがない場合があります。ステップ0とステップ1を参照してください。- イベントフォルダ
YYYY.MM.DD 名前/: ビデオ、場合によっては説明(html、またはユーザーのメッセージ内のテキスト)、場合によっては既製のトランスクリプト(sentences.json、transcript.txt)が含まれます。中間ファイルは<イベントフォルダ>/_work/に配置してください。 - スクリプトがない場合は、末尾の「pipeline/ が利用できない場合」を参照してください。
ステップ0. 偵察、計画、質問
- イベントフォルダを調査:
- ビデオ: 解像度、時間、fps(
ffprobe); 別の解像度のコピーがあるかどうか; - 既製のトランスクリプト;
assemblyai_key.txtが存在するかどうか — トランスクリプトを新たに作成する必要がある場合に必要;- イベントの説明(オプション): 名前、Telegramハンドル、トークのトピック。ユーザーのメッセージの最後か、htmlファイルにある場合があります。説明がない場合は、録画自体から名前とトピックを取得してください。
- ビデオ: 解像度、時間、fps(
- 計画を提示し、AskUserQuestionで質問してください。「推奨」オプションは、ユーザーが以前に選択したものです。
- トランスクリプト: 既製のものがあればそれを使用(推奨)、またはAssemblyAIで新しいものを作成。
- スライドノート: スライドが表示されている時間の、軽くクリーニングされたトランスクリプト抜粋(推奨)、逐語的な抜粋、またはタイムコード付きの短い要約。
- 4000文字を超える抜粋(Slidesノートの制限): 「[…]」マークで短くする(前回のユーザーの選択)か、スライドを分割。
- フォーマット「前回と同じ」(推奨): 詳細でオリジナルに近いもの。質疑応答を含むスライドとしてのトーク内の質問; 広告や組織的な部分はスキップ; 全体的な要点と統合リンクリストを最後に。
- スピーカーの数 — 説明から明らかでない場合(話者分離に必要)。
- 最終検証ステップを含むタスクリストを作成。
ステップ1. トランスクリプト
-
既製のトランスクリプト: 最初に確認 — スピーカーラベルと単語レベルのタイムスタンプが存在し、時間がビデオと一致すること。すべて揃っていれば、再実行は不要。
-
AssemblyAIキー。 新しいトランスクリプトの前に、
assemblyai_key.txtがフォルダルートに存在し、空でないことを確認。ファイルがない場合、このステップを続行せず、ユーザーに伝えてください: assemblyai.comにサインアップし、APIキーを取得して、フォルダルートのassemblyai_key.txtに保存する必要があること(またはキーを送信してもらい、そのファイルに保存し、他の場所には繰り返さないこと)。ステップ0の計画でこれをすぐに伝えるのが最善です。 -
新しいトランスクリプト。 低解像度コピーから、再エンコードせずに音声を抽出:
ffmpeg -i in.mp4 -vn -c:a copy audio.m4a。その後:python3 pipeline/transcribe_assemblyai.py audio.m4a <OUT> --key-file assemblyai_key.txt --lang <言語コード> [--speakers --min-speakers N --max-speakers M] --submit-only python3 pipeline/transcribe_assemblyai.py - <OUT> --key-file assemblyai_key.txt --fetch --wait 150 # 完了するまで繰り返し- 英語以外の音声の場合は、録画の言語をサポートするモデルを使用します。2026年10月時点では
universal-2です。 - 話者分離は複数の人が話している場合のみ: 単一の音声では誤ったA/Bラベルが生成されます。
- ドメイン
api.assemblyai.comは許可リスト(設定 → 機能)に含まれている必要があります。プロキシが403を返した場合はユーザーに伝え、回避策を講じないでください。 - 価格 — 1時間あたり約$0.15 + 話者分離で1時間あたり$0.02。
- 英語以外の音声の場合は、録画の言語をサポートするモデルを使用します。2026年10月時点では
-
作業ファイル
transcript_sent.txt: 1行 =N [h:mm:ss] LABEL: 文、Nは1から始まる文番号。config.jsonとoverrides.jsonはこれらの番号を参照します。 -
ラベル → 人。 ホストが人々に話しかける方法(「レオニード、あなたが最初ですね?」)、説明、内容によって照合します。典型的な話者分離のエラー:
- 1人の人物に2つのラベルが割り当てられる;
- トーク全体が1つの発話になる;
- 聴衆からの短い質問が隣の人に帰属される。
修正を
config.jsonに記述:names(ラベル → 名前)、speaker_by_line(行 → 名前)、drop_lines(削除する行)。 -
トークの境界: タイムコードとして記録 — イントロ、各トークとそのQ&A、クロージング。これがデッキのセクション構造です。
ステップ2. フレーム
- 解像度。 2つのコピーがある場合: 低解像度コピーで分析(
--reduce 2)、テキストと切り抜きは高解像度コピーから(ffmpeg -ss <秒> -i hd.mp4 -frames:v 1 hd/NNNNN.jpg)。1つのコピーの場合:--reduce 4(低解像度の場合は--reduce 2); 大きなテキストはフレームから直接読み取れます。 - チャンクで抽出。
device_bashには1回の呼び出しで約180秒の制限があり、バックグラウンドプロセスは存続しません。python3 pipeline/extract_slides.py video.mp4 <OUT> --chunk 1000 python3 pipeline/extract_slides.py video.mp4 <OUT> --skip-extract --reduce 4- ffmpegの速度は大きく変動します: 過去の実行では、実時間の10倍から48倍。最初のチャンクで速度を測定し、0.8 × 制限 × 速度以下のチャンクサイズを選択してください。
- 呼び出しを
timeoutでラップ; 不完全なチャンクはフレーム番号で完了させてください。 - 2つのffmpegプロセスを2コアで並行実行しても高速化しません。
- フレーム
NNNNN.jpg= 秒数N−1。
- ウェブカム。 マスクは自動的に検出されます。Google Meetレイアウト(左に画面共有、右にカメラ)では不要です。スピーカーのカメラウィンドウは通常右上隅にあります — 切り抜きとOCRはそれを避けます。
- レビュー。 すべてのフレームではなく、コンタクトシート(
sheets/)を確認: クラウドに移動して開きます。詳細については、2×2モザイクを使用:python3 pipeline/mosaic.py HD_DIR OUT PREFIX 00165 00179 …。 - アニメーションスライドは選択から外れます: 動く背景は安定セグメントを生成しません。スピーカーのスライド番号(01/16、06/17…)を確認。明示的なフレームリストから作成されたモザイクを使用して、隣接する見つかったスライド間の秒数のギャップを探します。
- ファイルをクラウドに移動する は接続フォルダからのみ機能: VM作業ディレクトリ(
~/w…)から_work/にコピーしてください。
ステップ3. 論理スライド
目標は、ビデオをフレームごとに繰り返すことではなく、プレゼンテーション可能なスライドを取得することです。
- スピーカーのスライド。 テキストを再入力(OCRは草案のみ); グラフやスクリーンショットを切り抜きとして挿入。エディタ(サムネイルストリップ付き)での同じスライド、ズームまたは一時停止は重複です。全画面のものを採用してください。
- ボード(Miro、Excalidraw)。 スライド = スピーカーが話しているボードブロック; 同じブロックのパンやズームは重複です。トークの進行に応じてボードが埋まっていく場合は、最後の完全なフレームを取得し、ブロックごとにスライドに分割してください。
- アプリインターフェース。 結果(テーブル、レポート、ダッシュボード、ボットの返信)が表示された画面を取得し、中間のクリックは取得しないでください。
- スクロールを伴うライブウェブサイトデモ — フレームとしてではなく、トランスクリプトから再話(「スピーカーの言葉からのスライド」)。
- 全画面ウェブカム はスライドではありません。その内容はスピーカーの言葉からのスライドに含めます。
- 質疑応答 — トークの最後に個別のスライド: 質問(質問者の名前付き)と回答の要点。
- ホストによる広告と組織的な部分: スキップ。スピーカー自身の広告(コース、価格)は参照として簡潔に提供します。ユーザーが別途要求した場合を除きます。
- デッキ構造:
- 表紙とアジェンダ;
- 各トークについて: セクションタイトルスライド、内容、質問、「リンクとサービス」スライド;
- 全体的な要点 — 「統合」とマークし、スピーカー間の矛盾を含める;
- 統合リンクリスト。
- 各スライドについて、トークプレフィックス(
l-、v-…)付きのID、ソースフレーム、画面上のインターバルを記録。
ステップ4. 画像
- 切り抜き座標をフルサイズフレーム上で
pipeline/crops_<YYYY-MM-DD>[_<スピーカー>].jsonに保存、フォーマット{"name": ["00556.jpg", x0, y0, x1, y1]}。 - 切り抜き:
python3 pipeline/crop_images.py FRAMES crops.json OUT --scale 2 --jpg(低解像度の場合)、--scale 1 --jpg(高解像度の場合)。 - フレームやウェビナー資料内の個人データ(名前、連絡先、ログイン情報、通信内容)を確認したり隠したりしないでください。そのまま引き継ぎます。
ステップ5. テキスト、OCR、QR、数値
- OCR。 ユーザーのコンピュータでは、tesseractは通常英語のみです。他の言語の場合はクラウドを使用:
apt-get install tesseract-ocr-<言語コード>、カメラウィンドウを白く塗りつぶし、フレームを1.5倍にアップスケール、tesseract -l <言語コード>+eng。結果をフレームと照合: 名前は歪みます。 - QRコード: 高解像度フレームで
cv2.QRCodeDetector、結果はqr.json。 - 数値。 スライドからの行の合計、パーセンテージ、「1件あたり約1,700」などの数値をスクリプトで再計算。矛盾を修正せず、次のように記録: 「行の合計は60.3k、スライドには64kとある」。ラベルのないグラフから読み取った値は「≈」でマーク。
ステップ6. リンク
- ソース: フレーム内のアドレス、QRコード、音声、イベントの説明(Telegramハンドル)。ASRは名前を歪めます(invoice.com → 「インボイシン」、Gwen → Qwen、JLM → GLM)。そのため、正しいスペルはフレームまたは検索から取得してください。
- 検証。 WebSearchまたはWebFetchですべてのリンクを確認し、確認日をスライドフッターに記載。Product Hunt(producthunt.com)やThere's An AI For That(theresanaiforthat.com)などのカタログは、サービスの公式アドレスと説明をすぐに提供します。
- 開かなかった場合 — curlやミラーで回避しないでください。t.me は頻繁に429を返します: 再試行せず、「スライドのアドレスですが、ページが開きませんでした」とマーク。
- 見つからなかった場合 — そのように伝える: 「サービスが見つかりませんでした」、「リンクは後で約束されました」。
- 配置場所。 各トークの最後のスライド: 1行の注釈付きリンク。そのノートには、より詳細: それが何か、ウェブサイトからの価格、スピーカーがどのように使用しているか、何が見つからなかったか。デッキの最後に — 統合スライド: カテゴリ別のスピーカーとツール。
ステップ7. ノート — トランスクリプト抜粋
- インターバル。
ranges.json—[["スライドID", 開始秒, 終了秒], …]、録画全体にわたってギャップや重複がないこと。スライド変更フレームと意味によって境界を設定: スピーカーは多くの場合、切り替え前に次のスライドについて話し始めます。 - 生成:
python3 pipeline/clean_transcript.py <イベントフォルダ>/_workはnotes.jsonを作成し、ノートの長さを出力(3700より長い場合は<-- LONG)。 - 軽いクリーニング:
- スクリプト内のASRエラー辞書
REPL: 一般的な置換は維持し、ウェビナー固有のものを追加; - 文頭のフィラーワード(スクリプト内の
FILLリスト)は自動的に削除; overrides.jsonで文を1行ずつ修正("行番号": "テキスト"またはnull)、スピーカーの意味と語順を維持; 不明瞭な部分は「[録音では聞き取れません]」とマーク;- オーバーライドのある行は再度クリーニングされません。
- スクリプト内のASRエラー辞書
- 生成後の確認: 繰り返し文がないこと(オーバーライドが隣接行を複製する可能性があります)、および失われた行がないこと(インターバル内のすべての行がノート内にあるか、
drop_linesで意図的に削除されていること)。 - ノートのフォーマット(デッキの言語で記述):
録音抜粋 1:31:02–1:33:05: [1:31:02] アルチョム: … [1:31:09] スタニスラフ: … 1~2行の備考: ボードのテキスト、数値の不一致、名前の明確化。 - 4000文字制限。 制限を超える場合は、ステップ0でユーザーが決定したように、「[…]」で短縮するか、スライドを分割。
ステップ8. Claude Slidesでの組み立て
- Slidesタイプ(Artifactクイックスタート、インテントスライド)からアーティファクトを作成し、そのSKILL.mdに従ってください。経験上重要なこと(2026年10月時点のタイプルール):
- 最初に、完全な順序とセクション(各トークに1セクション)を含む
deck.jsonを作成...
- 最初に、完全な順序とセクション(各トークに1セクション)を含む
...(以降は翻訳対象外)