音声ファイル管理基盤の整備
前日にナレーション機能を組み上げて音声ファイルが一気に増えた。152ファイルがpublic/audio/に居座り、git statusを叩くたびに差分が画面を埋め尽くした。このまま放置するとリポジトリが膨張して開発速度を落とすので、音声ファイルの管理基盤を一日がかりで整備した。
public/audio/ から audio-assets/ への移行
まずファイルの置き場を変えた。public/audio/ にあった152個の音声ファイルをプロジェクトルートの audio-assets/ に移動し、.gitignore に audio-assets/ を追加してリポジトリの追跡対象から外した。
audio-assets/
├── elevenlabs/ # ElevenLabs生成音声
├── gcp/ # Google Cloud TTS (Chirp 3 HD)
└── voicevox/ # VOICEVOX生成音声
これで git add . しても音声ファイルがステージングに乗らなくなった。
dev server middleware の作成
音声をpublicから外したので、ローカル開発時に音声が404になる。この問題を dev-audio.ts ミドルウェアで解決した。
// server/middleware/dev-audio.ts
// /audio/ へのリクエストを audio-assets/ から配信
開発サーバー起動中のみ動作し、本番ビルドには含まれない。defineEventHandler でリクエストパスを見て、audio-assets/ ディレクトリから該当ファイルを返す仕組み。
runtimeConfig に audioBaseUrl を追加
nuxt.config.ts の runtimeConfig.public に audioBaseUrl を追加した。
| 環境 | 値 | 解決先 |
|---|---|---|
| 開発 | "" (空文字) | localhost + dev middleware |
| 本番 | R2 CDNのURL | Cloudflare R2 |
環境変数 NUXT_PUBLIC_AUDIO_BASE_URL で本番URLを注入する。Cloudflare Pagesの環境変数として設定した。
useAudioUrl composable
音声URLの組み立てロジックが各コンポーネントに散らばっていたので、useAudioUrl composableに集約した。
const { resolveUrl } = useAudioUrl()
const src = resolveUrl('/audio/elevenlabs/chapter01/scene01.wav')
audioBaseUrl が空なら相対パス、値があればCDNのフルURLを返す。呼び出し側はどの環境で動いているか意識しなくてよい。
narrationChapter01.ts のパス統一
台詞データファイルの narrationChapter01.ts では、音声ファイルのパスがハードコードされていた。/audio/elevenlabs/chapter01/prologue_001.wav のような文字列が55行並んでいた状態。
AUDIO定数を定義してテンプレートリテラルに書き換えた。
const AUDIO = {
ELEVENLABS: '/audio/elevenlabs/chapter01',
VOICEVOX: '/audio/voicevox/chapter01',
} as const
// Before: "/audio/elevenlabs/chapter01/prologue_001.wav"
// After: `${AUDIO.ELEVENLABS}/prologue_001.wav`
パスのプレフィックスが1箇所に集まったので、ディレクトリ構造を変えても定数の値を直すだけで済む。
Cloudflare R2 へのアップロード
ElevenLabs音声59ファイルとVOICEVOX音声をR2バケットにアップロードした。
--remote フラグの罠
wrangler r2 object put を実行して「アップロード完了」と表示されたのに、本番のR2バケットにファイルが見つからない。ダッシュボードを何度リロードしても0 objects。
原因は --remote フラグの付け忘れだった。wranglerはデフォルトでローカルのR2エミュレータに書き込む。ローカルの .wrangler/state/ にファイルが溜まっているのを見つけて気づいた。
# NG: ローカルエミュレータに書き込まれる
wrangler r2 object put bucket/path/file.wav --file=./file.wav
# OK: 本番R2に書き込まれる
wrangler r2 object put bucket/path/file.wav --file=./file.wav --remote
前日のR2アップロードでも同じミスをしていた。2日連続で踏んだので、もう忘れない。
環境変数の設定
Cloudflare Pagesのプロジェクト設定から NUXT_PUBLIC_AUDIO_BASE_URL を設定した。値はR2バケットのカスタムドメインURL。
これでデプロイ時にNuxtがruntimeConfigにCDN URLを注入し、useAudioUrl経由で全コンポーネントがR2から音声を取得する。
DEPLOY.md の作成
音声ファイルのアップロード手順、環境変数の設定、デプロイコマンドをDEPLOY.mdにまとめた。次に音声を追加するときの手順書として残した。
主な記載内容:
- R2への音声アップロードコマンド(--remote フラグ付き)
- Cloudflare Pages環境変数の一覧
pnpm deploy:cloudflareの実行手順- audio-assets/ のディレクトリ構成ルール
学んだこと
- wranglerのデフォルトはローカル:
r2 object putもd1 executeも、--remoteなしだとローカルエミュレータに向く。「成功した」と表示されても本番には届いていない。コマンドの出力を鵜呑みにせず、ダッシュボードで実物を確認する癖をつける - 音声ファイルはGit管理しない: 152ファイル・数十MBのバイナリがリポジトリに入ると、cloneが遅くなり、diffが見づらくなり、CI/CDの転送量が増える。最初から外部ストレージに置くのが正解
- URLの解決層を1つ挟む: composableで環境差分を吸収しておくと、配信元がR2からS3に変わっても呼び出し側は無傷
今日の構成変更まとめ
| 変更 | Before | After |
|---|---|---|
| 音声ファイル配置 | public/audio/ (Git追跡) | audio-assets/ (.gitignore) |
| ローカル配信 | Nuxtのpublic配信 | dev-audio.ts middleware |
| 本番配信 | public配信 | Cloudflare R2 CDN |
| URL解決 | ハードコード | useAudioUrl composable |
| パス定義 | 文字列リテラル55行 | AUDIO定数 + テンプレートリテラル |
| 手順書 | なし | DEPLOY.md |