paper2reel について
paper2reelは、論文のポスター、スライド、ブログ、動画をセクション単位で対応付けるビューアを作るスキルです。paper2assets、paper2blog、paper2videoの成果物を使うワークフロー向けで、固定の操作テンプレートとパッケージ検査機能を備えます。
主な機能
- 01セクション単位のメディア対応付け
- 02ポスター優先表示とキーボード操作
- 03タイムラインに基づく動画・スライドクリップ
- 04二言語ブログペインとスライドサムネイル
- 05ダウンロードパッケージと検査
活用シーン
- 01ポスター、スライド、ブログ、動画で論文を閲覧
- 02ポスターのセクションから対応メディアへ移動
- 03最終成果物をローカル閲覧用にパッケージ化
- 04納品前にシーク動作と表示を確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ microsoft/ researchstudio --skill paper2reelインストール後、Claude Code で/paper2reelを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
README.mdassets/section_modal_contract.jsonscripts/build_poster_slides_view.pybuild_reel_from_paper.pybuild_section_media_from_timeline.pycheck_reel_package.pyreel_downloads.pyserve_reel.py詳細
paper2reel - アラインメント済みアーティファクトビューアー
paper2reel は、既存のアーティファクトの上に閲覧レイヤーを構築します。
paper2poster、ppt-master、paper2blog、paper2video の出力を置き換えたり変更したりするものではありません。
重要なアーティファクトは次のとおりです。
content_alignment.json
paper2assets の assets/meta/sections.json にある正規のセクション ID を、次の項目に対応付けます。
- ポスターの DOM ターゲット。通常は
[data-section="<id>"]です。 - スライドのフレームターゲット。通常は 1 つ以上のスライドインデックスです。
- 最終版 DOCX と同じ paper2blog コンテンツからレンダリングされたブログブロック。
- paper2video での納品に使う最終版の Bottom-Bar 字幕付き MP4 とタイムラインから切り出した動画クリップ。
このサイドカーにより、ラッパー UI で Poster/Slides/Blog を切り替え、納品物自体に見えるエンジニアリング用タグを追加せずに、対応する場所へ移動できます。
ビューアー UX 契約
ユーザー向けビューアーでは、元のポスターをデフォルト画面として維持する必要があります。
reel.html を最初に開いたときは、ポスター自体だけを表示してください。
常時表示のラッパーバー、タブストリップ、ポスター外の目に見える説明は表示しません。ラッパーのコントロールは、キーボードショートカットとセクションの操作から見つけられるようにします。
vで上部のセクションメニューを切り替えます。hでキーボードショートカットのヘルプを切り替えます。aでポスターに既存の音声コントロールを切り替えます。sで全画面表示を切り替えます。dでポスターのデバッグ/ホバーコントロールを切り替えます。- ポスターのセクションをダブルクリックすると、大きなモーダルが開きます。
- ポスターのタイトルをクリックまたはダブルクリックすると、論文全体のモーダルが開きます。
手法のフォーカスカードは、data-section="method-1"…"method-N" の識別子を維持してください。カードに独自のアラインメントエントリーがある場合はそれを使い、ない場合はホバーまたはダブルクリック時に、正規の method の動画、ブログ、スライドを開きます。ポスターの拡大縮小や過去のラスタオーバーレイが適用されている場合も含め、実際にクリックされたカードをハイライトしてください。クリック可能にするためにカードの名前を変更したり、小節用のクリップを新たに作ったりしないでください。
モーダルでは、セクションメディアレイアウトを使用する必要があります。左側に Bottom Bar 字幕が焼き込まれた動画、右側にブログを配置し、EN/中文の切り替え、動画の下のスライドサムネイル、ドラッグ可能な分割線を備えます。これを Poster/Slides/Video/Blog のタブ式ビューアーに置き換えないでください。タブ式ビューアーではセクション単位の操作契約が損なわれるため、後退となります。
動画のシーク機能は契約の一部であり、ホスティングの詳細ではありません。モーダルの動画プログレスバーとスライドサムネイルから、実際に再生位置を移動できる必要があります。以下の Range 対応 paper2reel サーバーを使ってリールを配信し、テストしてください。python -m http.server は、MP4 の Range リクエストに 206 Partial Content ではなく 200 OK で応答することがあるため、プレビューや QA 環境として合格扱いにしないでください。
このビューアーのテンプレートは意図的に固定されています。手作業で調整された Attention リールから抽出した UI の正式な契約として、assets/section_modal_contract.json を扱ってください。契約には、固定のセクションモーダルテンプレート、デフォルトで非表示のトップバー、v/h/a/s/d ショートカット、デバッグ用不透明度スライダー、字幕が焼き込まれた動画再生、ダウンロードメニュー、ドラッグ可能な分割線、スライドサムネイル、EN/CN のブログペイン、タイムラインに紐付いたセクションクリップが含まれます。エージェントにこの HTML の再設計を依頼しないでください。ビルダーが行えるのは、固定テンプレートへのデータ入力と、自己完結型アセットのコピーのみです。
入力と出力
完全なバンドルでは、paper2reel はユーザーに納品するものと同じ v2 出力を読み込む必要があります。
<poster_outdir>/ # poster.html, poster.pdf/png/pptx, manifest.json, assets/
<blog_outdir>/ # blog_zh.docx, blog_en.docx, manifest.json, assets/
<video_outdir>/ # video*.mp4, manifest.json, assets/
古いサンプルファイルや旧 exports/ フォルダーからリールを作成しないでください。ビューアーとタイムラインに紐付いたすべてのクリップには、字幕が追加された黒い Bottom Bar に焼き込まれている最終版の <video_outdir>/video.mp4 を使用する必要があります。VTT サイドカーを Reel ビューアーに添付しないでください。動画の各フレームにすでに含まれているテキストが重複して表示されます。
ユーザー向けビューアーは、v2 リールバンドル内に直接構築してください。
<reel_outdir>/
reel.html
content_alignment.json
manifest.json
assets/
poster/
media/
slides/
blog/
meta/
reel_downloads.json
downloads/ # materialized mode only
このディレクトリは、ローカルで配信できる程度に自己完結している必要があります。reel.html と content_alignment.json はバンドルのルートに配置し、ポスター、動画、スライド、ブログのアセットは assets/ 以下に配置します。reel.html だけをコピーしないでください。
ポスター+スライドの基本ビューアー
リポジトリのルートから実行します。
python skills/paper2reel/scripts/build_poster_slides_view.py \
--poster-dir <poster_outdir> \
--slides-dir <slide_png_or_svg_dir> \
--script-json <optional_audio_script_json> \
--section-slide-map <optional_section_slide_map.json> \
--blog-outline-en <blog_outdir>/assets/meta/outline_en.json \
--blog-outline-zh <blog_outdir>/assets/meta/outline_zh.json \
--blog-figures-dir <blog_outdir>/assets/figures \
--download-poster-dir <poster_outdir> \
--download-blog-dir <blog_outdir> \
--download-video-dir <video_outdir> \
--download-mode materialized \
--outdir <reel_outdir>
スクリプトは次のファイルを書き出します。
<viewer_outdir>/
reel.html
content_alignment.json
manifest.json
assets/
poster/
poster.html
assets/
figures/
fonts/
logos/
qr/
audio/
slides/
slide_01.png
...
blog/
figures/
...
meta/
reel_downloads.json
downloads/ # materialized mode only
all_final.zip
poster_final.zip
blog_final.zip
video_final.zip
reel.html はポスターを iframe 内に表示するため、ポスター自体のインタラクションはそのまま維持されます。ラッパーはセクションのクリックを監視し、一時的なハイライトを適用するだけです。スライドは、安定したスクロールとハイライトを実現するため、画像フレームとしてコピーされます。
paper2blog のアウトラインがある場合は、両言語のアウトラインと図のディレクトリを渡します。ビルダーはブログの図を blog/figures/ にコピーし、セクション単位の EN/CN ブロックを content_alignment.json に保存します。ブラウザーのゲートでは、それらのブロックが必須です。
ダウンロードディレクトリが指定されている場合、ビルダーは常にスキーマ paper2reel.downloads.v1 の assets/meta/reel_downloads.json を書き出します。その archives オブジェクトには all、poster、video、blog の4つだけが含まれ、各アーカイブには label、filename、バンドル相対の POSIX path、ZIP の arcname、正確なバイト数 size を含む重複排除済みの files エントリーが定義されます。
ダウンロードの提供方法には、明示的に2つのモードがあります。
materializedはスタンドアロンのデフォルトです。assets/downloads/{all,poster,video,blog}_final.zipを保持し、完全なfile://ダウンロード動作を提供します。on_demandは永続的な ZIP を書き出しません。相対リンク__download__/{all,poster,video,blog}を使用し、serve_reel.pyがマニフェストから解決して、一時的な ZIP レスポンスとしてストリーミングします。file://で直接開いた場合、オンライン専用のこれらのリンクは表示されません。
どちらのモードでも、.claude/、ネストされたダウンロードディレクトリ、バックアップ、内部ファイルは除外されます。Video アーカイブには、video.pptx、video.mp4、任意の字幕サイドカー、そして編集者に PPTX2Video を案内するルートの README.txt が含まれます。この README がない過去のマニフェストも引き続き有効です。ダウンロードサーバーは、古いバンドルを書き換えることなく、正規の案内を video_final.zip に挿入します。新しいマニフェストでは all_package_version: paper2reel.all.v2 と指定されます。その場合、All アーカイブは reel.html、content_alignment.json、assets/poster、assets/media、assets/slides、assets/blog、assets/ui、assets/fonts 以下の Reel ランタイム、最終版の Poster PDF/PPTX、Video MP4/PPTX、EN/CN の Blog DOCX ファイルを含む、単体で動作するオフライン Reel になります。ルートの poster.html と poster.png は重複して含めず、ビルド/キャッシュツリーとネストされた ZIP も除外されます。all_package_version のない過去のマニフェストでは、従来どおり重複排除したユニオン方式が維持され、ダウンロード可能な状態も保たれます。呼び出し元が別の指定を明示しない限り、Portal のジョブには on_demand を、スタンドアロンのオフラインバンドルには materialized を使用してください。
タイムラインに基づくセクションメディア
paper2video が timeline.json を生成している場合は、モーダル動画クリップ、スライドクリップ、セクションのタイミングの信頼できる情報源として使用します。推測した MP4 のタイムスタンプからセクション動画を切り出さないでください。セクションクリップは、タイムライン上のスライドクリップ全体をつないで構成し、最後に短い無音のフリーズ区間を追加します。これにより、モーダルが動きや思考の途中で止まるのを防ぎます。
python skills/paper2reel/scripts/build_section_media_from_timeline.py \
--viewer-dir <reel_outdir> \
--timeline <video_outdir>/assets/meta/timeline.json \
--video <video_outdir>/video.mp4 \
--allow-burned-subtitle-video \
--section-tail-seconds 0.9
次の内容が更新されます。
<viewer_outdir>/content_alignment.json
<viewer_outdir>/reel.html # inline ALIGNMENT refreshed when present
<viewer_outdir>/assets/media/video.mp4 # final playback copy with baked Bottom Bar
<viewer_outdir>/assets/media/clips/<section>.mp4
<viewer_outdir>/assets/media/slide_clips/slide-XX.mp4
決定的な Reel ブートストラップでは、選択されたソースが最終版の Bottom Bar レンダリングであることを示すため、--allow-burned-subtitle-video を渡します。このモードでは、ビルダーは動画ソースと字幕配信の両方に burned_bottom_bar を記録し、サイドカーの CC トラックは公開しません。
timeline.json には、明示的なセクションマッピングがすでに含まれている必要があります。デッキで標準のポスター ID ではなくスライド ID を使用している場合は、まず python -m pptx2video.build_timeline --section-map ... でタイムラインを作成してください。
必須のハードゲート
ベースビューアーとタイムライン対応メディアを構築した後、リールパッケージチェッカーを実行してください。これは必須のゲートであり、任意のスモークテストではありません。
python skills/paper2reel/scripts/check_reel_package.py \
<reel_outdir> \
--browser \
--file-browser \
--screenshot <reel_outdir>/assets/meta/previews/reel_browser_gate.png \
--report <reel_outdir>/assets/meta/reports/reel_qa_report.json
paper2reel を完了としてマークする前に、チェッカーに合格する必要があります。チェッカーは、納品するビューアーが古いタブ切り替え式ビューアーではなくセクションモーダル UI であることを検証し、実際のブラウザーで、デフォルトのポスターのみの表示、ショートカット、ダブルクリックによるセクションモーダル、字幕が焼き込まれた動画クリップ、スライドのサムネイル、ブログテキストが機能することを確認します。また、MP4 のバイト範囲リクエストへの対応も検証し、実際の動画シークを実行します。後のスライドサムネイルをクリックすると sectionVideo がサムネイルのタイムスタンプに移動すること、さらに、進行状況バーから直接シークする操作が全編動画とセクションクリップの両方で成功することを確認します。
同じ最終版の reel.html は、file:// から直接開く場合やダブルクリックで開く場合にも対応する必要があります。直接オープンモードでは、コピーしたポスター HTML を iframe.srcdoc で埋め込み、ポスターのベース URL を assets/poster/ に設定し、MathJax などのポスター描画リソースを assets/poster/ 以下にローカライズします。また、焼き込み済みの Bottom Bar を維持しながら、セクションモーダル、ポスターへのホバー、ショートカット、ブログペイン、スライドサムネイルからのシーク、動画への直接シークを、サーバーを起動せずに引き続き利用できるようにします。これはユーザー向け機能の同等性を実現する経路であり、HTTP プロトコルを置き換えるものではありません。file:// では HTTP 206 Range ヘッダーがないため、Range/シークの検証には引き続き --browser が必要で、直接オープン時の実行環境は別途 --file-browser で検証します。
チェッカーは assets/section_modal_contract.json を読み込みます。必須のゴールデン契約機能がひとつでも欠けている場合は、ブロッキング ERROR となります。対象となるのは、ショートカットのハンドラー、デバッグスライダー、paper2poster 標準のデバッグ用 bbox/サイズオーバーレイ、アイコンと All | Poster | Video | Blog リンクを備えたゴールデンダウンロードピル、セクションレールのホバースタイル、Bottom Bar 字幕が焼き込まれたセクションクリップ、重複するサイドカートラックがないこと、ブログ画像、固定のテンプレート/バージョンマーカーです。
ダウンロードの QA では、マニフェストの明示的な delivery フィールドに従います。materialized モードでは、既存のアーカイブ境界を使って 4 つの ZIP を検証します。on_demand モードでは、永続化された ZIP を拒否し、マニフェスト内のすべてのパス、分類、ソースファイル、バイトサイズを検証します。また、重複を排除した All の和集合を確認し、各動的エンドポイントに対して HTTP HEAD を実行します。
チェッカーに失敗した場合は、まずエージェントの修正タスクとして扱ってください。ビューアーを修正し、ゲートを再実行します。このステージを PASS としてマークしたり、ユーザーにページを目視確認して問題を見つけてもらったりしないでください。
人間によるプレビューでは、ブラウザーゲートと同じ Range 対応サーバーを起動します。
python skills/paper2reel/scripts/serve_reel.py <reel_outdir> --port 8900
http://127.0.0.1:8900/reel.html を開きます。Range リクエストに対して動画のリクエストが 206 Partial Content を返さない限り、このプレビューは忠実なものとは見なされません。on_demand バンドルの場合、このサーバーは /__download__/{all,poster,video,blog} も実装し、リクエストごとに ZIP を作成します。バンドル内にアーカイブを書き込むことはありません。
ローカルですばやく共有する場合は、ユーザーが <reel_outdir>/reel.html をダブルクリックして開くこともできます。assets/ フォルダーを削除したり、HTML ファイルだけをコピーしたりしないでください。直接オープンモードでも v2 バンドルのフォルダー構造が必要です。
出力マニフェスト
<reel_outdir>/manifest.json には "layout": "v2-assets" と、reel.html、content_alignment.json、assets/poster/、assets/media/、assets/slides/、assets/blog/、assets/meta/reel_downloads.json のルート相対パスを含める必要があります。materialized バンドルでは assets/downloads/ もインデックスに含めます。QA のスクリーンショットとレポートは assets/meta/previews/ と assets/meta/reports/ 以下に保存します。
必須の入力が見つからない場合や、ビューアーが必要な成果物を読み込めない場合は、QA レポート/マニフェストに ERROR を記録して停止します。ポスター、動画、スライド、ブログコンテンツの欠落を隠す不完全なビューアーを、警告なしに構築しないでください。
PDF または不完全なバンドルからのブートストラップ
ユーザーが PDF、arXiv/リンク入力、または不完全な v2 バンドルを指定して paper2reel を実行した場合は、まず共有バンドルを調べ、上流のどのステージがすでに完了しているかを判断します。未完了のステージは、リールを組み立てる前に、それぞれの完全なスキルを実行して完了させる必要があります。
paper2assets -> paper2poster -> paper2blog -> paper2video -> paper2reel
調査と決定的な最終組み立てには、ブートストラップヘルパーを使用します。
python skills/paper2reel/scripts/build_reel_from_paper.py <paper.pdf-or-bundle> --dry-run
次に、そのステージレポートに従います。
-
paper2assetsにpaper_spec.mdがない場合は、paper2assetsスキルを完全に実行します。extract_pdf.pyの出力だけをpaper2assetsパッケージとして扱わないでください。Step 4 はモデル駆動であるためです。 -
paper_spec.md、text.txt、figures.json、metadata.jsonがすでに存在し、manifest.json、assets/meta/sections.json、またはassets/meta/narration.jsonのみが欠けている場合は、ヘルパーが--run-missingを使用して、これらの決定的なパッケージファイルを更新できます。 -
paper2posterが未完了の場合は、測定済みのフィルループ、レンダリング/エクスポート、html2pptx への引き渡し、成果物ゲートを含め、paper2posterスキルを完全に実行します。 -
paper2blogが未完了の場合は、実際の EN/CN のアウトライン、DOCX の生成、図の埋め込み、ブログ QA ゲートを含め、二言語対応のpaper2blogスキルを完全に実行します。 -
paper2videoが未完了の場合は、ppt-master、音声、タイムライン、キャプション、ビジュアルハイライト、video.mp4、video_no_subtitles.mp4、video.pptx、動画 QA ゲートを含め、paper2videoスキルを完全に実行します。 -
すべての上流ステージに合格したら、ヘルパーにリールを組み立てさせます。
python skills/paper2reel/scripts/build_reel_from_paper.py \ <paper.pdf-or-bundle> \ --run-missing \ --download-mode materializedポータルから呼び出す場合は、
--download-mode on_demandを渡す必要があります。ステージングされたリールが最終共有バンドルに同期された後、ヘルパーはその最終バンドルを基にマニフェストを再構築し、過去のassets/downloads/ディレクトリがあれば削除します。
ヘルパーは一時ステージングディレクトリでビューアーを構築し、リールの成果物のみを v2 バンドルにコピーして戻します。これにより、build_poster_slides_view.py --outdir のクリーン処理で、既存のポスター、ブログ、動画、または paper2assets の出力が削除されるのを防ぎます。v2 の契約に従い、バンドルのルートには reel.html と content_alignment.json を配置し、リールのサポートアセットは assets/poster/、assets/media/、assets/slides/、assets/blog/ と assets/meta/reel_downloads.json の下に配置します。assets/downloads/ が追加されるのは、マテリアライズドモードの場合のみです。
ヘルパーがステージのブロックを報告した場合は、そこで停止し、該当する完全なスキルステージを完了させます。リールチェッカーに合格させるために、簡易的なポスター、動画、ブログのアウトライン、スライド画像、または HTML ページを作成しないでください。
アラインメントのルール
paper2assets の明示的なセクション ID を優先します。
titleproblemmotivationcontributionmethoddataset-benchmarkkey-resultablation-studyheadline-numberstakeawayfailure-modes-limitationsなど、論文固有のカスタム ID
スライドの場合は、利用可能であれば script.json を渡します。スクリプトの sections[*].id と順序を使えば、PPTX にメタデータを埋め込まずにスライドを識別できます。
スライドの自動マッチングの精度が低い場合は、オーバーライド JSON を渡します。
{
"method": [4, 5],
"key-result": [7, 9],
"takeaway": [10]
}
次のコマンドでオーバーライドを使用します。
python skills/paper2reel/scripts/build_poster_slides_view.py \
--poster-dir <paper2poster_outdir> \
--slides-dir <slide_png_or_svg_dir> \
--script-json <script.json> \
--section-slide-map <map.json> \
--outdir <viewer_outdir>