アプリの宣伝動画を Python で量産する: Playwright で画面録画 → VOICEVOX で読み上げ → Pillow と ffmpeg で合成
2026-10-03 ・ @apkderete
個人開発のアプリの宣伝用に、縦長(1080×1920)のショート動画を 19 本作りました。動画編集ソフトは使わず、全部 Python のスクリプトです。1本直すたびに手で編集し直さなくて済むのが一番の利点でした。
構成はこうです。
アプリ(開発サーバー) ─ Playwright + CDP screencast ─→ 画面のコマ(jpg) + 各コマの時刻
│
VOICEVOX エンジン(ローカル) ─ 文ごとに wav ─────────────┤
▼
Pillow で 1 コマずつ描く(背景・スマホ枠・字幕・クレジット)
│ rawvideo を標準入力へ
▼
ffmpeg(libx264) → 効果音と声を adelay + amix → loudnorm
1. 画面を録る: CDP の screencast を使う
Playwright の動画録画は画質が粗いので、Chrome DevTools Protocol の Page.startScreencast でコマを直接受け取りました。各コマの受け取り時刻も一緒に保存しておくと、後で「何秒目の画面」を正確に取り出せます。
ハマりどころが2つありました。
device_scale_factor=3にしても screencast のコマは等倍のままでした。Chrome の起動引数に--force-device-scale-factor=3を付けると、390×844 の画面が 1170×2532 で撮れます。- 見本データの日付が「今月」基準だったため、撮る日によって画面が変わりました。
page.clock.install()で時計を固定して解決しました。
2. 声を作る: VOICEVOX のエンジンを HTTP で叩く
VOICEVOX はエンジンをローカルで起動すると 127.0.0.1:50021 で HTTP API が使えます。/audio_query で読み上げ用のパラメータを作り、話速や音量を書き換えて /synthesis に渡すと wav が返ってきます。同じ文はハッシュをキーにキャッシュしておくと、作り直しが速くなります。
声の長さが場面より長いときは、場面のほうを伸ばして録画をゆっくり再生しています。字幕と声がずれないので、これが一番手間が少なかったです。
VOICEVOX のキャラクターを動画に使うときは、利用規約どおりクレジット(例:「VOICEVOX:ずんだもん」)を画面に入れます。
3. 1コマずつ描いて ffmpeg に流し込む
Pillow で 1 コマずつ画像を作り、tobytes() を ffmpeg の標準入力に -f rawvideo -pix_fmt rgb24 で流しています。中間ファイルを作らないので速いです。
音は、効果音と声それぞれに adelay で開始時刻を付けて amix で混ぜ、最後に loudnorm で音量をそろえました。
ハマりどころ:
- ffmpeg を
subprocessで呼ぶとき、標準入力を閉じないと待ち続けて止まることがありました。stdin=subprocess.DEVNULLとtimeoutを付けています。 - 音が映像より短いと尻切れになるので、
apad=whole_dur=で映像の長さまで無音を足しています。
4. 「掛け合い」動画にする
途中から、2人のキャラクターが掛け合う形式に変えました。せりふを (話者, 文) の並びで書いておけば、話している人の名札と字幕、声が自動でつながります。
ここでの失敗は、せりふで「設定6の確率は17%」と言っているのに、画面がまだその数字に届いていなかったことです。録画を時間どおりに流すと、言い終わってから数字が映ります。せりふが画面の数字を指すときは、その数字が映っている瞬間で画面を止めるようにしました。
結果と反省
- 作るのは速くなりました。せりふを1行直して1コマンドで作り直せます。
- ただ、動画を作ることと見られることは別でした。ギャンブル系の内容は TikTok でほぼ再生されず、X でも表示は伸びませんでした。題材と出す場所の相性は、作る前に確かめるべきでした。
作っているアプリ
- カチマケ(パチンコ・競馬の収支管理): https://kame6493-del.github.io/kachimake-site/
- DIAMOND NINE(野球シミュレーション): https://diamond-nine-baseball.com/
- X: https://x.com/apkderete