動画をAIに読ませて「編集の型」を数値で取り出し、その型どおりのショート動画を自動で書き出すまで。実際に構築したときの記録なので、つまずいた箇所もそのまま残しました。
下の3本は、この手順で実際に書き出した動画です。左から順に、作業が進んだ順に並んでいます。いきなり完成品を目指さず、8秒だけ作って目で確かめるのがこの手順の要です。
AIに動画を「描かせない」。AIが書くのは動画を描くプログラムで、943フレームを実際に描くのは手元のパソコンです。だから枚数が増えてもAIの利用量は増えません。
そして音声が先、映像が後。先に喋らせて、その実際の長さに映像を合わせます。逆順にすると、テロップと声がずっとズレたままです。
| 必要なもの | 確認コマンド | 無ければ |
|---|---|---|
| Node.js 20以上 | node -v | 公式サイトから |
| Python 3.9以上 | python3 -V | macOSに標準 |
| ffmpeg | ffmpeg -version | brew install ffmpeg |
| Pillow | python3 -c "import PIL" | pip install pillow |
| 日本語フォント | ls ~/Library/Fonts/ | Google Fontsから |
| VOICEVOX | ls -d /Applications/VOICEVOX.app | 公式サイトから |
構築時の実測は Node v25.2.1 / Python 3.10.4 / Pillow 12.2.0 / ffmpeg 9.0.1 / VOICEVOX 0.25.0 でした。
ブラウザで https://aistudio.google.com/apikey を開きます。トップページから探すより確実です。無ければ「Create API key」を押し、出てきた AIza で始まる39文字をその場でコピーします。
英語のページなので、ブラウザの自動翻訳が効いていると表示が崩れてボタンの位置が変わります。
画面がおかしいと思ったら、まず翻訳を切ってください。アドレスバー右の翻訳アイコンから「英語のまま表示」。
請求先アカウントを紐づけていない無料枠では、送ったデータがサービス改善に使われる可能性があります。
自分だけが映っている動画なら無料枠で構いません。第三者が映っている・声が入っている動画は、課金枠に切り替えてから送ってください。ここは後から取り消せません。
キーを .env に書き(chmod 600 と .gitignore を忘れずに)、使えるモデルを一覧します。外部パッケージは要りません。Node標準の機能だけで動きます。
$ node --env-file=.env scripts/list-models.mjs
HTTP 404 This model is no longer available to new users.
一覧に載っていても、使えるとは限りません。必ず短いテキストを1回投げ、実際に応答するか確かめてから本番に使ってください。
参考にした記事が名指ししていた最新のFlashモデルは、テキストなら36秒で応答するのに、動画を投げると5回リトライして全部これでした。
HTTP 503 This model is currently experiencing high demand.
混んでいたのは動画処理の側でした。ひとつ前の世代のFlashモデルに落としたら15.7秒で通り、解析の質も落ちませんでした。あわせて -latest のような別名は中身が最新モデルを指すため同じ混雑に巻き込まれます。モデル名は世代を固定して指定してください。
初回のリクエストが120秒経っても返らず、失敗したと思って中断しました。放置していたら正常に完了していました。
無料枠では最初の1発が待たされることがあります。すぐ失敗と判断せず、待つかバックグラウンドで走らせてください。
(eval):1: command not found: timeout
シェルに頼らず、スクリプトの中で打ち切る仕組み(JavaScriptなら AbortSignal.timeout)を持たせるほうが確実です。あわせて503と429は必ず起きるので、5秒 → 10秒 → 20秒 → 40秒 → 60秒で再挑戦する処理を最初から入れておきます。
$ node --env-file=.env scripts/analyze-youtube.mjs "<URL>" <モデル> <保存名>
カットの平均の長さ、総カット数、切り替えの合図、テロップの位置と文字数、起承転結の秒数配分、オチの作り方まで返ってきます。手元のファイルは、いったんGoogleにアップロードしてから解析します(上限2GB、保管は48時間)。
公開値(映像258トークン/コマ × 1コマ/秒 + 音声32トークン/秒 = 約290トークン/秒)で計算して「1時間の動画は枠に入らない」と判断しました。
実測は1秒あたり91〜97トークン。約3分の1でした。
実用的な見積もり式 = 動画の分数 × 0.67円
| 解析した動画 | 入力トークン | 所要 | 費用 |
|---|---|---|---|
| ショート動画 60秒 | 5,828 | 15.7秒 | 1.3円 |
| ショート動画 3本(TikTok) | 6,208〜6,574 | 30〜238秒 | 各1.8〜2.0円 |
| 14秒の区間を5コマ/秒 | 5,395 | — | 1.4円 |
| Zoom録画 76分52秒 | 419,808 | 105.4秒 | 51.3円 |
一度アップロードした動画に別の質問を投げたら、入力トークンがまた全額(419,808)計上されました。アップロードは省けても、動画の読み込みは毎回やり直しです。
「あれも聞けばよかった」でもう51円。聞きたいことは1回のプロンプトに全部詰めてください。
全編を1コマ/秒で解析すると「テロップは白背景に青枠」程度しか返りません。
14秒だけを5コマ/秒で解析したら、「上段は青枠・下段は赤枠」「アニメーションなしで1フレームで出現」「古い枠は消えずに残る」まで取れました。料金はほぼ同じ(1.4円)です。短い区間なら密度を上げてもトークンが増えません。
インストール直後、日本語テロップを描こうとしてこうなりました。
[AVFilterGraph] No such filter: 'drawtext'
$ ffmpeg -buildconf | grep -i freetype # → 何も出ない $ ffmpeg -filters | grep subtitles # → 字幕フィルタも無い
解決策は、ffmpegで文字を描くのをやめることです。Pillowで1枚ずつ絵を描き、ffmpegには「フレームをつなぐ」だけをさせます。結果的にこちらのほうが良く、可変フォントの太さを細字から極太まで選べるので、参考にした記事の著者が困っていた「フォントが太すぎて文字が潰れる」問題を最初から回避できます。
f = ImageFont.truetype(FONT, 80)
f.set_variation_by_name("Black") # Thin〜Black1回目の書き出しで、テロップの2行目が枠の下に飛び出していました。これはコードを読んでも気づけません。目で見るしかありません。
「8秒だけ作って人間が確認する」手順が効くのは、このためです。直したあとは、文字の幅を測って収まるまでフォントサイズを段階的に下げる処理を入れておきます。
「歯医者で歯茎の状態を褒められた」+「わ」のように、最終行に1文字だけ残って不格好になりました。
最終行が1〜2文字になる場合はフォントを2ポイントずつ縮めて組み直す処理を入れると解消します。日本語では頻発するため、最初から入れておくのが早道です。
「テロップが表示されていない」と誤解しましたが、切り出した時刻がちょうどカットの切り替わりの瞬間でした。
確認用のフレームは、切り替わりから1〜2秒ずらした時刻で切り出してください。
$ open -a VOICEVOX $ curl -s http://127.0.0.1:50021/version
アプリはインストール済みなのに応答がありませんでした。アプリを起動していないだけでした。VOICEVOXは起動して初めて窓口が開きます。
起動から応答までに数秒かかるので、繋がるまで待つ処理を入れておくと安定します。
ここが一番大事なところです。映像のタイミングを先に決め打ちすると、テロップと声が必ずズレます。
実際にやると、項目ごとの長さは4.4秒・3.8秒・4.6秒とバラバラです。それが正しい状態です。喋り終わったら次へ進む、自然な作りです。
解析で「話す速度は標準の1.2〜1.3倍」「発話の間の無音が極限までカットされている」と分かったので、速度1.25倍・発話前後の無音0.02秒・台詞の間隔0.06秒に設定しました。数値を自分で決めず、解析結果から取ってください。それがこの方法の意味です。
商用利用は無料ですが、使ったキャラクター名の表記が条件です。
VOICEVOX:四国めたん
「VOICEVOX」を「VOICEBOX」と書き間違えていた例がありました。誤字で済まず、条件を満たしていない扱いになりかねません。キャラクターごとに個別の規約もあるので、公開前に確認してください。
| 試したこと | 結果 | 代わりにやったこと |
|---|---|---|
ffmpegの drawtext で日本語を描く | 機能が入っていないビルドで、そもそも使えない | Pillowで描いてffmpegでつなぐ |
| 最新世代のFlashモデルに動画を投げる | 5回リトライして全部503 | ひとつ前の世代。15.7秒で完了 |
| 公開値からトークン数を計算 | 実測と3倍ずれた | 短い動画で1本実測してから計算 |
timeout コマンドで打ち切る | macOSに入っていない | スクリプト内で打ち切る |
| 既製の専用ソフト(ゆっくりMovieMaker4) | Windows専用。macOS非対応で対応予定も無し | 手元の環境で組む(この手順) |
.env に直接書き、chmod 600 と .gitignore を必ず設定する| やりたいこと | コマンド |
|---|---|
| 使えるモデルを一覧する | node --env-file=.env scripts/list-models.mjs |
| モデルが今応答するか調べる | node --env-file=.env scripts/probe.mjs <モデル> |
| YouTubeの動画を解析する | node --env-file=.env scripts/analyze-youtube.mjs "<URL>" <モデル> <保存名> |
| 一部の区間だけ細かく解析する | node --env-file=.env scripts/analyze-segment.mjs "<URL>" <開始> <終了> <プロンプト> <コマ数> |
| 手元のファイルを解析する | node --env-file=.env scripts/analyze-file.mjs <ファイル> <モデル> <保存名> |
| VOICEVOXを起動する | open -a VOICEVOX |
| 動画を書き出す | python3 build/make_with_voice.py |
| 確認用に1フレーム切り出す | ffmpeg -y -ss <秒> -i <動画> -frames:v 1 out.png |
このマニュアルは実際にやったところまでしか書いていません。次は未検証です。