mcpField
D
document-to-narration

document-to-narration

github.com/jwyniaクリエイティブ・メディア179976 回インストール35 週間前に更新
リポジトリ

document-to-narration について

document-to-narrationは、文書を動画用のナレーション原稿、音声、単語単位の字幕に変換するスキルです。固定的な分割ではなく、文章の論旨の流れに沿ってシーンを構成します。

主な機能

  1. 01文書をシーンに分割
  2. 02話し言葉向けに文章を調整
  3. 03ナレーション音声の生成
  4. 04単語単位の字幕作成
  5. 05シーンのタイミング抽出

活用シーン

  1. 01エッセイを動画ナレーションに変換
  2. 02TTS音声向けのテキスト準備
  3. 03動画と同期する字幕の作成
  4. 04Remotion用シーン時間の設計

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/jwynia/agent-skills --skill document-to-narration

インストール後、Claude Code で/document-to-narrationを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

.gitignore
data/
adaptation-rules.json
references/
spoken-adaptation-guide.md
scripts/
extract-scene-boundaries.py
full-pipeline.ts
narrate-full.py
narrate-scenes.py
split-to-scenes.ts
transcribe-full.py
transcribe-scenes.ts
tts/
requirements.txt
setup-venv.sh
tts/model/
.gitkeep

詳細

文書からナレーションへ

文章を、単語単位の正確なタイミングを備えたナレーション付き動画スクリプトに変換します。

基本原則

解釈するのはエージェント、導くのは文書です。 固定テンプレートに基づいて機械的に分割するのではなく、このスキルではエージェントの判断を活用して、内容が自然に息継ぎし、論を展開し、話題を切り替える箇所を見つけます。シーンの区切りは、あらかじめ決められた構成ではなく、文書の論旨の流れによって決まります。

このスキルを使う場面

次のような場合にこのスキルを使います。

  • ブログ記事やエッセイを動画ナレーションに変換する場合
  • TTS 音声の生成用にコンテンツを準備する場合
  • 長文コンテンツを視聴しやすいシーンに分割する場合
  • 動画用に単語単位で同期した字幕を作成する場合

次のような場合にはこのスキルを使わないでください。

  • コンテンツがすでにシーン/スクリプト形式になっている場合
  • リアルタイムの音声合成が必要な場合(この処理はバッチ処理です)
  • 会話や複数話者のコンテンツを扱う場合(単一の音声のみ対応)

前提条件

  • Deno がインストールされていること(https://deno.land/)
  • Python 3.12 と venv のサポート
  • 音声変換用の ffmpeg
  • whisper-cpp(@remotion/install-whisper-cpp 経由でインストール)
  • tts/model/ に TTS モデル があること(サイズの都合で Git には含まれていません。以下の「モデルのセットアップ」を参照)

パイプライン全体

方法は 2 つあります。シーンごと(旧方式)とナレーション全体(推奨)です。

ナレーション全体のパイプライン(推奨)

音量とペースを一定に保つため、単一の音声ファイルを生成します。

Document (.md)
    ↓ [agent interprets scene breaks]
Scene .txt files (01-scene-name.txt, 02-scene-name.txt, ...)
    ↓ [TTS via narrate-full.py - SINGLE PASS]
full-narration.wav (one consistent audio file)
    ↓ [Whisper via transcribe-full.py]
full-narration.json + full-narration.vtt (word-level timing)
    ↓ [extract-scene-boundaries.py]
Scene timing boundaries for video composition
シーンごとのパイプライン(旧方式)

シーンごとに別々の音声を生成します。音量にばらつきが生じることがあります。

Scene .txt files
    ↓ [TTS via narrate-scenes.py - MULTIPLE PASSES]
Scene .wav files (volume may vary between scenes)
    ↓ [concatenate]
Combined audio (may have clipping at boundaries)

警告: シーンごとの TTS では、音量レベルやペースの異なる音声が生成されます。これらを連結すると、聞こえるほどの音量の跳ね上がりやクリッピングが発生します。代わりに、ナレーション全体のパイプラインを使用してください。

クイックスタート

ナレーション全体のパイプライン(推奨)
cd .claude/skills/document-to-narration
source tts/.venv/bin/activate

# 1. Split document into scenes (manual or scripted)
deno run --allow-read --allow-write scripts/split-to-scenes.ts input.md --output ./output/

# 2. Generate single audio file
python scripts/narrate-full.py ./output/scenes/

# 3. Transcribe with word-level timestamps
python scripts/transcribe-full.py ./output/full-narration.wav

# 4. Extract scene boundaries for video timing
python scripts/extract-scene-boundaries.py ./output/scenes/ ./output/full-narration.json --typescript
シーンごとの旧パイプライン
# 1. Split document into scenes
deno run --allow-read --allow-write scripts/split-to-scenes.ts input.md --output ./output/

# 2. Generate audio per scene (may have volume inconsistencies)
source tts/.venv/bin/activate
python scripts/narrate-scenes.py ./output/scenes/

# 3. Transcribe (DEPRECATED: transcribe-scenes.ts requires whisper-cpp)
# Use transcribe-full.py instead after concatenating audio

手順

ステップ 1:セットアップ(初回のみ)
Python 仮想環境の作成
cd .claude/skills/document-to-narration/tts
python3.12 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
TTS モデルのセットアップ

ファインチューニング済みの音声モデル(約 7.8GB)は、サイズの都合で Git には含まれていません。 Qwen3-TTS のモデルファイルを tts/model/ に配置してください。

tts/model/
├── config.json
├── generation_config.json
├── model.safetensors      # Main model weights
├── tokenizer_config.json
├── vocab.json
├── merges.txt
└── speech_tokenizer/
    └── ...
Whisper のインストール(まだインストールされていない場合)

@remotion/install-whisper-cpp パッケージがインストールを処理します。

import { installWhisperCpp, downloadWhisperModel } from '@remotion/install-whisper-cpp';

await installWhisperCpp({ to: './whisper-cpp', version: '1.5.5' });
await downloadWhisperModel({ model: 'medium', folder: './whisper-cpp' });
ステップ 2:文書の準備

このスキルは、次のような文書で最も効果を発揮します。

  • 見出し構成(H1、H2)が明確な Markdown 文書
  • セクションが明確に分かれ、論旨がよく構成されている文章
  • 自然に音読できるコンテンツ
ステップ 3:パイプラインの実行
deno run -A scripts/full-pipeline.ts /path/to/essay.md --output ./output/essay-name/
ステップ 4:出力の確認
output/essay-name/
├── scenes/
│   ├── 01-opening-hook.txt      # Scene script
│   ├── 01-opening-hook.wav      # Generated audio
│   ├── 01-opening-hook.vtt      # Word-level captions
│   ├── 02-core-argument.txt
│   ├── 02-core-argument.wav
│   ├── 02-core-argument.vtt
│   └── ...
└── manifest.json                # Complete timing data

シーン境界の判断基準

エージェントは、次の判断基準を使ってシーンの区切りを特定します。

強い境界(ほぼ必ず分割)
  • H2 見出しの変更
  • 「ここが重要です」/「要点は」など、論点を切り替える文
  • 主要な比喩の導入
  • 明示的な列挙(「第一に……」「第二に……」)
  • 視点の大きな変化
中程度の境界(分割を検討)
  • 短い段落の後に長い段落が続く場合(またはその逆)
  • 例から原則への移行
  • 段落の冒頭が「しかし」/「とはいえ」/「一方で」の場合
  • 質問とそれに対する回答のパターン
弱い境界(通常はまとめる)
  • 同じ例の中で段落が切り替わる箇所
  • 同じ主張を裏付ける一連の証拠
  • オチや意外な事実を明かすまでの盛り上がり
シーンの長さの目安
  • 目標:シーンあたり 100~300 語(音声で 30~90 秒)
  • 最小:50 語(極端に短いシーンを避けます)
  • 最大:500 語(認知的な負荷を避けます)

アンチパターン

段落スライサー

パターン: 各段落や見出しの区切りで機械的に分割すること。 問題: 論旨の流れを無視します。シーンが断片的になり、つながりが失われます。 対策: 構造上の単位ではなく、修辞上のまとまりを探します。複数の段落がひとつのシーンを構成することもよくあります。

文字の壁

パターン: セクション全体をひとつのシーンとして扱うこと。 問題: TTS 音声が長くなりすぎます。自然な息継ぎの間が失われます。 対策: 100~300 語を目標にします。セクション内で自然に間を置ける箇所を見つけます。

逐語的な書き起こし

パターン: 話し言葉向けに調整せず、書かれたテキストをそのままコピーします。 問題: 書き言葉の慣習は、話すときにはうまく機能しません。括弧書き、複雑な句読点、入れ子になった節は、TTSや聞き手を混乱させます。 対処法: 調整ルールを適用します。頭の中で声に出して読んでみます。

過剰な調整

パターン: 作者の語り口が失われるほど、内容を書き換えます。 問題: 仕上がりが元の作者らしくなくなります。 対処法: 語り口を保ち、表現の仕組みを調整します。作者が修辞疑問文を使っている場合は、そのまま残します。

利用可能なスクリプト

scripts/split-to-scenes.ts

Markdown ドキュメントを解析し、シーンごとのテキストファイルを出力します。

deno run --allow-read --allow-write scripts/split-to-scenes.ts input.md --output ./output/
deno run --allow-read --allow-write scripts/split-to-scenes.ts input.md --output ./output/ --adapt
deno run --allow-read scripts/split-to-scenes.ts input.md --dry-run

オプション:

  • --output - シーンファイル用のディレクトリ(存在しない場合は作成されます)
  • --adapt - 話し言葉向けの調整ルールを適用します
  • --dry-run - ファイルを書き込まずにシーンの区切りをプレビューします

出力: 番号付きの .txt ファイルと初期状態の manifest.json

scripts/narrate-full.py (推奨)

すべてのシーンファイルから、単一の TTS 音声ファイルを生成します。音量と話すペースが一貫した音声になります。

python scripts/narrate-full.py ./output/scenes/
python scripts/narrate-full.py ./output/scenes/ --force
python scripts/narrate-full.py ./output/scenes/ --speaker jwynia
python scripts/narrate-full.py ./output/scenes/ --output ./custom/path/audio.wav

オプション:

  • --force - 出力ファイルが存在する場合も再生成します
  • --speaker - 話者名(デフォルト:jwynia)
  • --output - カスタム出力パス(デフォルト:../full-narration.wav)

出力: シーンの親ディレクトリに単一の full-narration.wav

scripts/narrate-scenes.py (レガシー)

各シーンファイルの TTS 音声を個別に生成します。連結時に音量の不一致が生じることがあるため、推奨しません。

python scripts/narrate-scenes.py ./output/scenes/
python scripts/narrate-scenes.py ./output/scenes/ --force
python scripts/narrate-scenes.py ./output/scenes/ --speaker jwynia

オプション:

  • --force - 出力ファイルが存在する場合も再生成します
  • --speaker - 話者名(デフォルト:jwynia)

出力: 各 .txt ファイルと同じディレクトリに .wav ファイル

scripts/transcribe-full.py (推奨)

Python の openai-whisper を使って、単語単位のタイムスタンプ付きで音声を書き起こします。

python scripts/transcribe-full.py ./output/full-narration.wav
python scripts/transcribe-full.py ./output/full-narration.wav --model large-v3
python scripts/transcribe-full.py ./output/full-narration.wav --output-dir ./captions/

オプション:

  • --model - Whisper モデル:tiny、base、small、medium、large、large-v2、large-v3(デフォルト:medium)
  • --output-dir - 出力ディレクトリ(デフォルト:音声ファイルと同じディレクトリ)

出力:

  • 単語単位のタイムスタンプを含む .vtt ファイル
  • Remotion 用のキャプション配列を含む .json ファイル

依存関係: Python 環境に openai-whisper が必要です:

pip install openai-whisper
scripts/extract-scene-boundaries.py

シーン冒頭のフレーズを照合して、トランスクリプトからシーンのタイミング境界を抽出します。

# Human-readable table
python scripts/extract-scene-boundaries.py ./output/scenes/ ./output/full-narration.json

# JSON output
python scripts/extract-scene-boundaries.py ./output/scenes/ ./output/full-narration.json --json

# TypeScript for Video.tsx
python scripts/extract-scene-boundaries.py ./output/scenes/ ./output/full-narration.json --typescript

オプション:

  • --json - JSON 配列として出力します
  • --typescript - Video.tsx の scenes 配列用に TypeScript コードとして出力します

出力: シーン番号、スラッグ、開始時刻、再生時間

scripts/transcribe-scenes.ts (非推奨)

非推奨: インストールされていない可能性がある whisper-cpp バイナリが必要です。代わりに transcribe-full.py を使用してください。

whisper-cpp を使って、シーンごとの音声ファイルを書き起こします。

deno run --allow-read --allow-write --allow-run scripts/transcribe-scenes.ts ./output/scenes/

出力: 単語単位のタイムスタンプを含む .vtt ファイル

scripts/full-pipeline.ts

パイプライン全体を実行します。

deno run -A scripts/full-pipeline.ts input.md --output ./output/project-name/

オプション:

  • --output - 出力ディレクトリ(必須)
  • --adapt - 話し言葉向けの調整を適用します
  • --skip-tts - 音声生成をスキップします(テキストのみ)
  • --skip-transcribe - Whisper による書き起こしをスキップします

出力形式

manifest.json
{
  "source": "appliance-vs-trade-tool-draft.md",
  "created_at": "2024-01-15T10:30:00Z",
  "total_scenes": 9,
  "total_duration_seconds": 420,
  "scenes": [
    {
      "number": 1,
      "slug": "popcorn-opening",
      "word_count": 185,
      "audio_duration_seconds": 55.2,
      "files": {
        "text": "scenes/01-popcorn-opening.txt",
        "audio": "scenes/01-popcorn-opening.wav",
        "captions": "scenes/01-popcorn-opening.vtt"
      },
      "captions": [
        { "text": "Two", "startMs": 0, "endMs": 180, "confidence": 0.98 },
        { "text": "people", "startMs": 180, "endMs": 450, "confidence": 0.97 }
      ]
    }
  ]
}
VTT 形式
WEBVTT

00:00.000 --> 00:00.180
Two

00:00.180 --> 00:00.450
people

00:00.450 --> 00:00.720
walk

00:00.720 --> 00:01.100
into

話し言葉向けの調整

--adapt を有効にすると、このスキルは書き言葉の慣習を話し言葉に適した表現に変換します:

書き言葉話し言葉
括弧内の補足ダッシュで区切る、または別の文にする
"e.g.""for example"
"i.e.""that is"
長く入れ子になった節複数の文に分ける
セミコロンピリオド
*強調*文脈に応じた強調

維持するもの:

  • 作者の語り口とトーン
  • 修辞疑問文
  • 意図的な反復
  • 重要なフレーズや印象的な言い回し

統合

remotion-designer との連携
  • manifest のシーンリストを remotion-designer に渡します
  • 各シーンが視覚デザインの単位になります
  • 単語単位のタイミングをテキストアニメーションに使用します
Remotion コンポジションとの連携
import { Audio, useCurrentFrame, Sequence } from 'remotion';
import manifest from './output/manifest.json';

// Use scene durations for Sequence timing
{manifest.scenes.map((scene, i) => (
  <Sequence
    from={accumulatedFrames}
    durationInFrames={scene.audio_duration_seconds * fps}
  >
    <Audio src={staticFile(scene.files.audio)} />
    <CaptionRenderer captions={scene.captions} />
  </Sequence>
))}

技術メモ

WAV 形式への変換

Whisper には 16kHz モノラル WAV が必要です。パイプラインが自動的に変換します:

ffmpeg -i input.wav -ar 16000 -ac 1 output_16khz.wav
TTS モデル

ファインチューニング済み音声モデル(約7.8GB)は tts/model/ に同梱されています。カスタム話者埋め込みを使用した Qwen3-TTS を使用します。

パフォーマンス
  • TTS:1文あたり約5~30秒(Apple Silicon MPS または NVIDIA CUDA)
  • Whisper:モデルサイズに応じてリアルタイムの約0.5~2倍
  • エッセイ全体(約2000語):処理時間は合計約10~20分

このスキルでできないこと

  • 映像を生成する(remotion-designer を使用してください)
  • リアルタイム音声合成
  • 複数話者の対話
  • コンテンツの主張を編集または改善する
  • 機械的な読み上げ用の調整を超える編集

コメント