Gadeninアプリを入れる

プロダクト読了 9 分

台本を書くとYouTube動画が自動生成するシステム作った話 - バイブコーディング4時間

Gadeninのブランド認知向上のため、YouTubeチャンネルを開設しようと思った。まだ作成前で公開していないが、とりあえず台本をChatGPTなどで雑に書いて、それをインプットすると絵コンテ・アニメーション・字幕付きの動画を出力する仕組みを作った。動画編集ソフトは使わず細かなカット編集まであえてシステムで行う。

Gadeninのブランド認知向上のため、 YouTube チャンネルを始めることにした。まだ現時点ではチャンネルも開いていない。というのも、そもそも知らなかったのだが、Google Workspace を契約し、そのアカウントで YouTube 自体を開こうと思ったら、仕様上YouTubeそのものが使い得ない制約条件があった。まず試用期間は YouTube が使えない。それを回避するためには、試用期間終了後に支払う予定の金額を30ドル以上 Google に事前に支払っておく必要があるらしい。そしてそれだけでなく、 48 時間はそもそも YouTube自体にアクセスができないらしい。なんかはじめてみたんだが、 youtube.com/oops という URL にリダイレクトされ、このアカウントでは YouTube サービスを利用できません、みたいな文言が出てくるページに飛ばされる。

という状態だったが、とりあえず待ってる間に動画を作る仕組みだけは先に作ろうと思い、Claude code でバイブコーディングしたらソッコーめちゃくちゃ良いのができた。以前も動画生成システムを1年くらい前に作っていたんだが、今回のやつはもっとクオリティーが高いシステムができて満足。何よりも、ほぼ自動でアニメーション動画をエージェンティックに作る土台ができ、しかもそれを再現性高い状態で利用できるようなシステムができたから。

台本をテキストで書くと絵コンテが返ってきて、収録すると字幕付きの mp4 が出てくる。 書いたのはほぼ全部 Claude Code で、自分がやったのは指示と判断と、見た目の参考画像を 1 枚用意したことだ。

この記事はその仕組みの話だ。

週1本を1年、編集ソフトなしで回す

基本的には顔出しはしない方針で、Gadeninアプリの素材をうまいこと動画に混ぜられるようにすることで、自然にネタは出来上がるというのがもっとも僕にとってコストが低い。YouTube ってネタを考えるのができなくなって離脱していく人が大半だから、そういう意味でも僕にとって最もコストが低い仕組みができていること自体がアドバンテージだ。 声は自分。画面は Gadenin の実際の栽培記録と、アニメーションのスライド。

1 本 10 分、週 1 本、金曜 19 時。ネタ帳は 52 週分ある。

問題は自分の時間だ。動画にかけられる時間の上限は週 3 時間と仮定して週1投稿を目指す。

当然カット編集すらやってる余裕はない。

最終的に、動画生成のシステムを「台本 (データ)」と「キット (コード)」の2つに分けた。台本はChatGPTをベースに自分が書く。というより自分の言葉で喋る。絵は AI エージェント (Claude Code) が組む。といってもお絵かきクオリティーは現時点ではClaude codeはひどい仕上がりなので、ChatGPT に素材を作らせて、それを Claude code に素材集として組ませるようにした。見た目はスクリプトで統一感が出るように縛りをいれる。

このシステムの素晴らしい点は、台本を入力するとほぼ9割完成形の動画が生成される点。

台本を書くと、絵コンテが返ってくる

全体の流れが図1だ。上から、自分がやること、AI エージェントがやること、スクリプトがやること。 図 1。僕・AI エージェント・スクリプトの 3 段に分けた工程のブロック図。ネタ帳、台本、絵コンテ、検査と絵コンテ画像、絵コンテを見る、声のレコーディング、言い直しやフィラーの削除、書き出し、予約投稿、YouTube の順に曲線の矢印でつながり、絵コンテを見る工程から台本へ戻る点線がある

自分がやるのは 1・2・5・6・9 の 5 つ。ネタ帳に仮タイトルを書き、台本を書き、絵コンテを見て、声を録り、最後にサムネを選んで予約投稿する。絵コンテを組むのは AI エージェント。検査と書き出しはスクリプトだ。

台本はフリーテキストで、決まりは 3 つしかない。空行で区切った 1 段落が 1 場面。## 見出し が章。> で始まる行は絵の希望で、書かなくてもいい。

エージェントが組んだ絵コンテは 1 枚の画像になる。ep001 は 38 段落、38 場面。

ep001 の絵コンテ画像。38 場面のコマが 4 列に並び、写真の場面、章扉、図解、比較、手順、アプリの画面収録、締めの画面が見える

見て、気に入らなければ台本を直す。絵コンテは手で直さない。台本が正で、scenes.json の say は台本の写しだからだ。ずれていれば check.mjs がエラーを出し、本番の書き出しが止まる。文章が同じ段落の絵はそのまま残るので、1 段落直しても他の 37 場面は変わらない。

台本もエージェントに書かせれば速い。そう思っていた。

回ごとのファイルは episodes/ep001_<ネタ名>/ に全部まとめてある。台本、絵コンテ、素材、録音、下書き、完成の動画。ブラウザで開く一覧に、回ごとの状態と「次にやること」が出る。

回の一覧の画面。ep001 から ep005 が 1 行ずつ並び、絵コンテのサムネイル、題と状態の札、公開予定、次にやることが出ている

動画は時刻 t の関数として撮る

絵コンテからアニメーション、そして mp4 までが図 2 だ。

図 2。scenes.json と timing.json と写真が player.html に入り、Chrome の中で HTML と CSS と SVG のスライドになる。Playwright が時刻を 1/30 秒ずつ進めてスクリーンショットを撮り、ffmpeg が声と BGM と実写の音を混ぜて ep001.mp4 と ep001.srt を出す

アニメーションを作っているのは、ただの Web ページだ。player.html を Chrome で開くと、scenes.json を読んで 1920x1080 の動くスライドになる。写真は <img>、図解は SVG、文字は CSS。動画のフレームを描くコードはどこにもない。

肝は 1 つだけ。見た目はすべて時刻 t の純粋な関数にしてある。window.__seek(t) に秒を渡すと、その瞬間の絵が出る。乱数も現在時刻も使わない。

書き出しは Playwright がやる。t を 0 から 1/30 秒ずつ進めてスクリーンショットを撮り、ffmpeg に流し込む。10 分の動画で 18,000 コマ。20〜30 分かかる。 声と BGM と実写の音は ffmpeg で混ぜる。字幕は焼き込みと同じ文面を SRT にも出す。

依存は Playwright の 1 つだけ。あとは ffmpeg と Chrome、それに文字起こしの mlx_whisper。JS は 3,107 行だ。

1 つ落とし穴があった。ブラウザのスクリーンショット (JPEG) をそのまま ffmpeg に渡すと、出力が yuvj420p (フルレンジ) になる。-pix_fmt yuv420p を指定しても通らない。QuickTime で表示が止まることがあり、YouTube の推奨からも外れる。 scale=in_range=pc:out_range=tv,format=yuv420p と BT.709 のタグを足して直った。同じことをする人は、ここで 1 回つまずくと思う。

絵コンテを毎回同じ絵にするためにやったこと

ここに一番手をかけた。

最初の版は、場面の型は固定だったが、図解は回ごとに手描きの SVG で、動きの数値は JS の中に散っていた。seg(lt, .3, .6) のような直書きが 53 か所。これでは 52 本の間で必ずブレる。

やったことを層で描くと図 3 になる。

図 3。3 層の図。上は台本に書けるもの (文章・写真の ID・部品の並び・出る時刻・強調) と書けないもの (色コード・px・style・font)。中はキット (tokens.css、tokens.js、motion.js、diagram-kit.js、player.js)。下は検査と基準 (check.mjs、deco-check.mjs、kit-baseline.mjs と imgdiff.mjs) と、キットを変えたら見本と比べて基準を撮り直すループ

  1. 見た目の値は 1 か所にだけ置く。 色は tokens.css の 10 色とその派生だけ。動きの長さ・距離・文字数の上限は tokens.js。player.js から色コードと px の直書きを消した。grep で 0 件。53 か所の seg も全部 MOTION の値を通すようにした
  2. 動きの語彙を 6 つに絞る。 fadeUp / pop / draw / slideIn / type / count。これ以外の出方は作らない
  3. 図解は部品を JSON で並べる。 株、苗、鉢、土、月の軸、山、矢印、札など 18 種。手描き SVG 7 枚は全部この部品で組み直して削除した。組み直したら、8 / 10 / 12 px とばらついていた線の太さが揃った
  4. 台本に見た目を書けなくする。 style color font は書けない。色コードも書けない。文字数の上限を超えたら、警告ではなくエラー。既存の台本で超えていた 18 か所は文言を短くした
  5. 葉の飾りの乱数は場面番号を種にする。 書き出すたびに同じ位置に出る。同じ場面を 2 回撮って差分 0.000%
  6. 見本と基準画像を持つ。 型 16 種と部品 18 種を全部使う見本 ep000-sample (23 場面) を用意し、480x270 に縮めた基準画像を git に入れた。キットを変えたら kit-baseline.mjs --compare で画素を比べる。違う画素が 0.5% を超えた場面だけ、基準と並べた画像が出る
項目 前 後
動きの数値の直書き (seg) 53 か所 0
色コード・px の直書き あり 0 件
手描きの SVG 7 枚 0
上限を超えた文言 18 か所 0
基準画像 なし 23 枚

図解の部品がどんなものかは、ep001 の 1 場面を見るのが早い。台本に書くのはこれだけだ (say は省略)。

{ "type": "diagram", "para": 6, "title": "黄色くなる場所で、原因の見当がつく",
  "parts": [
    { "kind": "plant", "x": 270, "leaves": [["y","y"],["g","g"],["g","g"]], "label": "① 窒素不足", "sub": "古い葉 (下) から黄色", "at": "8%" },
    { "kind": "arrow", "from": [470, 470], "to": [470, 190], "curve": 0.18, "label": "窒素を回す", "at": "20%" },
    { "kind": "plant", "x": 820, "leaves": [["g","g"],["g","g"],["v","v"]], "label": "② 鉄が吸えない", "sub": "新しい葉 (上) から黄色", "at": "42%" },
    { "kind": "tag", "x": 940, "y": 110, "text": "葉脈だけ緑", "at": "88%" },
    { "kind": "tag", "x": 1000, "y": 430, "text": "クロロシス", "at": "55%" },
    { "kind": "plant", "x": 1370, "leaves": [["y","y"],["y","y"],["y","y"]], "roots": "dead", "label": "③ 根が傷んでいる", "sub": "株全体が黄色", "at": "0%" }
  ] }

葉の色は g (緑) / y (黄) / v (葉脈だけ緑) の 1 文字。at は場面の何 % で出るか。色コードも線の太さも書けない。これが画面ではこうなる。

部品で組んだ図解の場面。クリーム色の地に「黄色くなる場所で、原因の見当がつく」の見出し。株が 3 本並び、左は下の葉だけ黄色、中央は上の葉が黄色で葉脈だけ緑、右は全部の葉が黄色で根が黒い。ピンクの矢印と札が付いている

これで見た目の変更は全部拾える。

ついでに書いておくと、YouTube の「AI の使用」の開示は、リアルな AI 画像や AI の BGM を使った回だけ要る。台本と図解とアニメーションは対象外で、声は自分だ。量産型の判定に効くのも、本人の声と実際の記録と、1 本に 1 か所は入れる実写の場面だと調べてある。

レコーディングツールにて声と絵を合わせるのは、Space キー

最後に収録だ。声とアニメーションをどう合わせるか。

最初の方式は、録音を先に済ませ、Whisper で文字起こしし、台本と 1 文字ずつ突き合わせて場面の切れ目を推定するものだった。仮音声での一致率は 97%。悪くない。ただ推定なので、言い間違いや言い換えが増えるとずれる。

いまの方式が図 4 だ。

図 4。上段は収録の時間軸。録音は止めずに、Space を押した時刻で場面が進み、戻るで言い直した区間は捨てる。中段は最後のテイクだけを切ってつないだ narration.wav。下段は場面ごとに mlx_whisper の文字起こしと台本の段落を突き合わせて字幕の文面を決め、timing.json に書く流れ

ブラウザで台本を見ながら話す。今の場面の台詞が大きく、次の場面の台詞が薄く出る。読み終えたら Space。押した瞬間の時刻がそのまま場面の切れ目になる。録音も操作も MediaRecorder の開始時刻を基準にしているので、声と絵がずれようがない。これがなかなか素晴らしい仕組みだ!

言い間違えたら R でその場面を最初から、← で 1 つ前から。録音は止めない。仕上げの finalize-take.mjs が最後のテイクだけを残して切り、つなぐ。フィラーや冗長な言い回しを削るのも、ここに足す予定だ。

字幕は台本ではなく、実際に話したことに合わせる。場面ごとに文字起こしと台本を突き合わせ、一致した文字は台本の表記に、録音にしかない文字は文字起こしのままにする。アドリブも字幕に出る。

最初は「一致しない部分は文字起こしのまま」にしていた。すると「酸性」が「賛成」に、「鉢」が「蜂」に、「数珠つなぎ」が「呪術なぎ」になって字幕に出た。一致した文字に挟まれて長さが近い区間は誤認識とみなし、台本の表記に寄せる規則を足した。

確かめたのは Chrome の偽マイクだ。見本の読み上げ音声を流し込み、Playwright で Space を送った。場面の頭から最初の一言までのずれは平均 0.43 秒、最大 0.82 秒。

つまり、自分の声ではまだ 1 本も録っていない。

次は ep001「ブルーベリーの苗は誰から買うかで全然違う」を自分の声で録る。38 場面、見積もり 11 分 53 秒。目標の 10 分より 2 分近く長い。

台本を削るところからだ。

使ったものと参考にしたもの

道具

参考

関連記事

Gadenin 栽培記録アプリ・無料アプリを入れる