Skip to content

Latest commit

 

History

History

video

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

README.md

操作解説動画 制作パイプライン

TiAb Review Plugin の操作解説動画(YouTube 公開用)を、Playwright による自動収録 + VOICEVOX による自動音声合成 + ffmpeg による自動合成で作るためのパイプライン。要件定義は REQUIREMENTS.md を参照。

ディレクトリ構成

video/
├── REQUIREMENTS.md   要件定義書
├── README.md          本書
├── scenes/            Playwright シーンスクリプト(video/scripts/record.mjs が読み込む)
├── narration/         ナレーション原稿(日本語、チャプターごとに1ファイル)
├── subtitles/         英語字幕ソース(narration と対になるチャプターごとに1ファイル)
├── assets/            サムネイルテンプレート等の静的素材
├── scripts/           パイプライン本体(Node.js ESM, .mjs)
│   ├── config.mjs      共通設定(パス・解像度・VOICEVOX/ffmpeg 接続先 等)
│   ├── record.mjs       収録(シーン → video/build/scenes/<NN-slug>/)
│   ├── tts.mjs           音声合成(原稿 → video/build/audio/<NN-slug>/)
│   ├── assemble.mjs      合成(build/ 一式 → 最終動画・チャプター・字幕・説明文・サムネイル)
│   ├── setup.sh          環境セットアップ(冪等)
│   └── lib/              パーサ・ffmpeg ラッパー等の共通ユーティリティ
└── build/             生成物(git 管理外。.gitignore 済み)

前提環境

  • Node.js 18 以上(package.json の engines 参照)
  • Linux + xvfb(拡張機能を読み込んだ Chromium をヘッド付きで動かして収録するため。Linux の収録コマンドは xvfb-run 経由で実行する)
  • Python3 + py7zr(pip install py7zr) (VOICEVOX エンジンの配布形式が 7z のため、video/scripts/setup.sh の展開に使用)
  • ネットワーク到達性(初回セットアップ時のみ。Playwright の Chromium、ffmpeg、VOICEVOX エンジンをダウンロードする)

使い方(基本の4ステップ)

Windows では xvfb は不要で、node video/scripts/record.mjs をそのまま実行する。 video/scripts/setup.sh は Linux 向けなので、VOICEVOX エンジンは Windows 版 (voicevox_engine-windows-cpu-<版>.7z.001)を video/tools/ に展開し、 展開先で run.exe --host 127.0.0.1 --port 50021 を実行して起動する。 ffmpeg / ffprobe は PATH にあれば使われる。

# 0. 環境セットアップ(初回のみ。以後は冪等なので再実行しても安全)
npm run video:setup

# 1. デモ用拡張機能ビルド(Playwright 収録用。実データ・実アカウントを使わない)
npm run build:demo

# 第9章が映す Web 版デモ(dist-web-demo/ に出力)
npm run build:web:demo

# 2. シーン収録(Playwright + xvfb。LANGUAGE=ja を必ず付与する)
LANGUAGE=ja xvfb-run -a -s "-screen 0 1920x1080x24" npm run video:record

# 3. ナレーション音声合成(VOICEVOX エンジンが起動していること)
npm run video:tts

# 4. 最終合成(動画結合・チャプター・字幕・説明文・サムネイル生成)
npm run video:assemble

生成物は video/build/ 配下にまとまる(後述)。 収録に使うビルドはリポジトリ直下の dist-demo/ と dist-web-demo/ に出力する (どちらも git 管理外。配布しない)。

ffmpeg / ffprobe の実行ファイル指定

video/scripts/setup.sh が video/tools/ 配下に展開した場合、config.mjs は 環境変数 → PATH 上のコマンド の順でしか自動解決しないため、video/tools/ に置いた バイナリを使うときは明示的に環境変数を指定する(setup.sh 実行時にも案内が表示される)。

export FFMPEG_PATH="$(pwd)/video/tools/ffmpeg-master-latest-linux64-gpl/bin/ffmpeg"
export FFPROBE_PATH="$(pwd)/video/tools/ffmpeg-master-latest-linux64-gpl/bin/ffprobe"

Playwright の Chromium 実行ファイル指定

既定では /opt/pw-browsers/chromium があればそれを使い、無ければ Playwright 標準の解決 (開発機で npx playwright install chromium 実行後に使われるパス)にフォールバックする。 別の場所を明示したい場合は PLAYWRIGHT_CHROMIUM_PATH を設定する。

VOICEVOX エンジンの接続先

既定は http://127.0.0.1:50021、話者は四国めたん(ノーマル・話者ID 2)。変更する場合は VOICEVOX_URL / VOICEVOX_SPEAKER を環境変数で指定する。

シーンを1本だけ再収録する

機能追加時などにチャプター1本だけを更新したい場合、対象シーンの video/scenes/NN-slug.mjs (と、必要なら video/narration/NN-slug.md / video/subtitles/NN-slug.md)だけを編集し、 以下のように収録対象を絞って再実行する。

LANGUAGE=ja xvfb-run -a -s "-screen 0 1920x1080x24" \
  node video/scripts/record.mjs 05-ml-screening

npm run video:tts -- 05-ml-screening   # 原稿を変更した場合のみ(未変更のcueは自動でスキップされる)
npm run video:assemble                 # 常に build/ 全体から再生成する

引数はシーンファイルのフルネーム(05-ml-screening)でも、番号部分(05)だけでも一致する。 引数を省略すると、それぞれ video/scenes/ video/narration/ 配下の全ファイルが対象になる。

tts.mjs は原稿本文と話者IDのハッシュを video/build/audio/<key>/index.json に保存しており、 変更の無い cue は再生成をスキップする。assemble.mjs は毎回 video/build/ 全体から作り直す (各シーン mp4・最終 mp4・チャプター・字幕・説明文・サムネイルをすべて再生成する)。

ナレーション原稿・字幕・シーンスクリプトの対応関係(CONTRACT)

1チャプター = 1シーンスクリプト(video/scenes/NN-slug.mjs)+ 1ナレーション原稿 (video/narration/NN-slug.md)+ 1英語字幕ソース(video/subtitles/NN-slug.md)が基本単位。 NN はチャプター番号(2桁ゼロ埋め)、slug はシーン名。

ナレーション原稿の形式(video/narration/NN-slug.md)

---
scene: "NN"
slug: slug-name
title: チャプタータイトル      # chapters.txt・description.txt に使われる
target_seconds: 90            # 目安秒数(パイプラインは参照のみ、強制はしない)
---

## cue 01
<!-- action: 画面操作の補足メモ(TTSには渡らない。HTMLコメントは自動で除去される) -->
実際にTTSで読み上げる本文。複数行に分けて書いても、合成時は半角スペースで
1つの発話として結合される。

## cue 02
...

英語字幕ソースの形式(video/subtitles/NN-slug.md)

ナレーション原稿と同じ ## cue NN 形式。title 等の frontmatter は無くてもよい。 cue 番号(n)はナレーション原稿・シーンスクリプトの ctx.cue(n) と対応させる。

シーンスクリプトの CONTRACT(video/scenes/NN-slug.mjs)

CONTRACT の全文と ctx API の詳細は video/scripts/record.mjs の先頭コメントに記載している (実装時はそちらを一次情報として参照すること)。要点:

  • export default { id, slug, title, narration?, storageSeed?, pageQuery?, async run(ctx) {...} }
  • narration(省略可): 使用するナレーション原稿・字幕ソースのキー。省略時は ${id}-${slug}。 ナレーション無し(映像のみ)のシーンにしたい場合は narration: null を指定する。
  • storageSeed(省略可): 収録前に chrome.storage.local へ流し込む初期状態。ログイン画面を スキップしたい場合は { demo_signed_in: true } を指定する(video/scenes/examples/00-smoke.mjs 参照)。
  • run(ctx) の中で ctx.cue(n) を呼んだタイミングが、そのナレーション cue の発声開始時刻の 目安として記録される。ctx.newSegment(page) を呼ぶと、以後の ctx.page/ctx.cue() は 新しいタブ(例: フルテキストページ)を基準に切り替わる。

サンプル: video/scenes/examples/00-smoke.mjs(スモークテスト用。 narration を 01-intro に向けて音声付きで最後まで通す)。video/scenes/ 直下ではなく examples/ サブディレクトリに置いているのは、record.mjs のシーン列挙が拡張子 .mjs のファイルのみを 対象とし、サブディレクトリを無視するため(npm run video:record を引数無しで実行しても スモークテストは収録対象に含まれない)。実際のチャプター01〜11のシーンは video/scenes/NN-slug.mjs として追加済み。

タイミング精度についての注意

  • ctx.cue(n) が記録するのは「その瞬間の壁時計時刻」を、アクティブなセグメント(ページ)が 作られた瞬間からの相対秒数に変換した値。sub秒(1秒未満)オーダーの誤差が生じうる。
  • assemble.mjs は各キューの音声を「その cue の想定発声時刻」と「直前の cue 音声終了 + 最短間隔(MIN_CUE_GAP_SEC = 0.3秒)」の遅い方に配置するため、多少のタイミングのズレは 自然に吸収される(早口で操作してもナレーションが重ならない)。
  • ナレーション音声の合計がシーン映像より長くなった場合は、映像の最終フレームを複製して 引き伸ばす(tpad)。逆に映像がナレーションより長い場合は、映像の自然な尺がそのまま使われる。

操作ツアーの解説動画(npm run video:tours)

アプリ内の操作ツアー(src/lib/guide/tours/)を、ツアー1本につき1本の短い動画にする。 上の4ステップ(手書きのシーンと原稿から作る長い解説動画)とは別の経路で、映像も原稿もツアーそのものから作るので、 シーンスクリプトやナレーション原稿を書く必要がない。招待した参加者へ「最初に見てほしい動画」として渡す用途を想定している。

npm run build:demo
LANGUAGE=ja xvfb-run -a -s "-screen 0 1920x1080x24" npm run video:tours                    # 参加者向けの既定3本
LANGUAGE=ja xvfb-run -a -s "-screen 0 1920x1080x24" npm run video:tours -- ml-start         # ツアー ID を指定
npm run video:tours -- --skip-capture ml-start   # 撮り直さず、直前のスクリーンショットから作り直す

Windows では xvfb は不要で、node video/scripts/tour-videos.mjs fulltext-page のようにそのまま実行できる。

  • 映像: scripts/guide-tour-check/ のシナリオ(npm run check:tours と同じもの)がデモビルドの上でツアーを最後まで操作し、 手順ごとに残すスクリーンショットを使う。手順ごとに最初の1枚を1画面にし、縦長(サイドパネル)は左に画面・右に文言、 横長(全文の判定ページ)は上に画面・下に文言で並べる。シナリオで通らなかった手順(skipIf で飛ばされる分岐)は入れない。 最初の1枚がダイアログの開いた瞬間になる手順は、video/scripts/tour-videos.mjs の SHOT_OVERRIDES で、ツアー・手順ごとに使う画像のラベルを指定できる。 指定したラベルの最初の1枚を使い、画像が無い場合はエラーになる。 fulltext-page は動画用シナリオ fulltext-page-video で撮る(video/scripts/tour-videos.mjs の CAPTURE_SCENARIOS)。 確認用シナリオは判定を保存して PDF の無い次の候補へ進み、終盤が PDF の無い画面になるため、動画用では理由を選ばずに進めて 同じ文献の PDF を保つ。このため動画には手順 reason-info(除外を押さなかったときの説明)は入らない
  • 原稿: ツアーの定義の手順の順番と、ja の messages.json のカードの文言。英字は VOICEVOX が読めないので、 video/scripts/tour-videos.mjs の READINGS(PDF → ピーディーエフ など)で音声だけ読み替える(画面の文言はそのまま)。 ツアーの文言に新しい英字が入ったら READINGS に足すこと(足さないとアルファベットを1字ずつ読む)
  • 追従: ツアーの手順や文言を変えたら作り直すだけで動画も追従する。check:tours が落ちる変更は、動画の撮り直しも要る合図
  • 出力: video/build/tours/<ツアーID>.mp4 と字幕 <ツアーID>.srt(途中の画面・音声・区切りの MP4 は video/build/tours/<ツアーID>/)。 同じ文言・話者の音声は作り直さない
  • --skip-capture が使えるのは直前に撮ったツアーだけ(check:tours は実行のたびにスクリーンショットを消すため)
  • Playwright 同梱の Chromium と版が合わない環境(Claude Code web のコンテナなど)では、 PLAYWRIGHT_CHROMIUM_PATH=/opt/pw-browsers/chromium を付ける(check:tours も同じ変数を見る)

YouTube に上げ直したあとに直す場所

YouTube は公開済み動画のファイルを差し替えられないため、上げ直すと動画の ID が変わる。

  • 長編: docs/index.html・docs/help.html の埋め込みと YouTube リンク、src/lib/guide/topics.ts の GUIDE_VIDEO_ID を更新する。
  • 章の時刻が変わった場合: video/build/chapters.txt を出どころとして、GUIDE_VIDEO_CHAPTERS の開始秒を更新する。
  • ツアー動画: docs/help.html の #getting-started-tours の一覧で、該当するツアーの動画リンクと長さ(日英の両方)を更新する。

生成物一覧(video/build/, git 管理外)

パス 内容
scenes/<NN-slug>/segment-K.webm 収録した生の映像セグメント(record.mjs)
scenes/<NN-slug>/meta.json セグメント・キュー時刻等のメタデータ(record.mjs)
audio/<NN-slug>/cue-NN.wav 合成したナレーション音声(tts.mjs)
audio/<NN-slug>/index.json cue ごとの音声メタ・再合成スキップ用ハッシュ(tts.mjs)
scenes/<NN-slug>.mp4 シーン単体の完成動画(映像+ナレーション、assemble.mjs)
final.mp4 全シーンを結合した最終動画(assemble.mjs)
chapters.txt YouTube 説明欄に貼るチャプタータイムスタンプ
timeline.json シーンごとの尺・cue 配置時刻の記録
subtitles-en.srt 英語字幕(YouTube の字幕トラックとしてアップロード)
description.txt YouTube 説明欄用テキスト(チャプター・リンク・クレジット込み)
thumbnail.png サムネイル(video/assets/thumbnail.html を撮影)
tours/<ツアーID>.mp4 / .srt 操作ツアーの解説動画と字幕(tour-videos.mjs)
tours/<ツアーID>/ ツアー動画の途中のファイル(画面 PNG・音声 WAV・手順ごとの MP4)

これらはすべて video/build/ から再生成可能なため git 管理しない (.gitignore の video/build/ / video/tools/ を参照)。