ブログ一覧

AI で動画を置くだけで YouTube に上がる仕組みを作る

撮った動画の編集と YouTube への投稿を AI エージェントに任せたい人に向けて、動画をフォルダに置くだけで YouTube に上がる仕組みの作り方を書きます。私は自宅で定点で撮った筋トレ動画を YouTube に上げるために、手元の Mac のフォルダで Claude Code(Anthropic のコーディング用 AI エージェント。フォルダの中でコマンドを実行し、ファイルを書き、画像を読める)を動かしています。

依頼は Claude のアプリから Remote Control(アプリから、自分の端末のフォルダで動く Claude Code のセッションに依頼できる機能)で出します。

どの場面を使うかといった判断だけを設定ファイル 1つに書かせ、残りをスクリプトにした構成は Claude Code に YouTube 動画編集を任せた に書きました。この記事は、その仕組みを作った順番と、投稿の所で当たった Google 側の制約を扱います。API で上げた動画は審査を通すまで非公開に固定され、ログインは 7日で切れる、といった制約です。コードは全部 Claude Code が書きました。

題材は筋トレ動画ですが、筋トレに固有なのは、設定ファイルに書く判断の中身(どこがセットか、画面に何を出すか)と、タイトルの文面のルールだけです。手順の骨組みは、撮った素材から使う所を選んで仕上げる動画なら、題材を問わず同じ形で作れます。

できあがった流れは、動画を置いて私が OK を返すと非公開で YouTube に上がる形

1本の動画は、次の順に進みます。

  1. 動画をフォルダに置く
  2. 下調べのコマンドで、動き・音量の解析と、2秒ごとのコマを並べた一覧画像の書き出し
  3. Claude Code がコマ一覧を見て使う区間を決め、動画ごとの設定ファイル(JSON)に書く
  4. 検証のコマンドで、設定の誤りと出来上がりのタイムラインの表示
  5. 一括作成のコマンドで、動画・サムネイル・BGM・文字の重ね表示(私の場合は、セット中に扱っている重量)の作成
  6. Claude Code がタイトル・概要欄を同じ設定ファイルに書く
  7. 投稿のコマンドの確認モードで、送る内容の表示
  8. 私の OK のあと、非公開で投稿

コマンドは wv という 1つのコマンドの下にまとめてあり、フォルダ作成、下調べ、検証、一括作成、チャプター出力、ログイン、投稿の 7つがあります。

1本目の記事の動画に続き、この流れで仕上げた 2本目は、69分22秒の元動画から全セットを入れた 12分23秒の動画になり、私の OK のあと非公開のまま YouTube に上がりました。

フォルダの形は次のとおりです。投稿の部分は youtube.py、youtube_api.py、publish.py と、動画ごとの upload.json です。

<動画フォルダ>/
├── CLAUDE.md                 方針と判断の基準(Claude Code が最初に読む)
├── .claude/settings.json     サンドボックスの設定
├── docs/youtube-metadata.md  タイトル・概要欄の書き方(公式ヘルプが根拠)
├── src/workout_video/        道具(どの動画にも使う物)
│   ├── analysis.py           下調べ(動き・音量の解析とコマ一覧)
│   ├── project.py            設定ファイルの読み込みと検証
│   ├── pipeline.py           完成物を一括で作る
│   ├── youtube.py            概要欄へのチャプターの差し込み、送る内容、書き方の検査
│   ├── youtube_api.py        YouTube Data API(ログイン・再開可能アップロード・サムネイル)
│   ├── publish.py            1本分の投稿と、投稿の記録
│   └── cli.py                wv コマンド
├── .secrets/                 ログイン情報(Git に入れない)
└── videos/<日付>/            動画 1本ごと
    ├── video.json            この回の判断(使う区間・重量・サムネイル・タイトルと概要欄)
    ├── notes.md              作業記録
    └── upload.json           投稿の記録(Git に入れる)

用意した物は Mac と Claude Code、ffmpeg、Python と YouTube のチャンネル

用意した物は次のとおりです。

  • macOS の Mac
  • Claude Code(Claude のアプリから Remote Control で依頼)
  • Homebrew で入れた FFmpeg(8 以上。映像と音の切り出し、つなぎ、書き出し)
  • Python 3.11 以上と、ライブラリの NumPy(解析の数値計算と BGM の合成)、Pillow(サムネイルと重ねる文字の画像)、pytest(テスト)
  • YouTube のチャンネルがある Google アカウント

Claude Code は、読み書きできる場所と通信先を制限するサンドボックスの中で動かしています。フォルダの設定ファイルでサンドボックスを有効にし、コマンドが読めるのはこのフォルダの中だけ、キーチェーンを読むコマンドは禁止です。設定の実物は次のとおりです。

{
  "sandbox": {
    "enabled": true,
    "allowUnsandboxedCommands": false,
    "filesystem": {
      "denyRead": ["~/"],
      "allowRead": ["."]
    }
  },
  "permissions": {
    "deny": ["Bash(security:*)"]
  }
}

このため、brew install のようにフォルダの外が要る準備は私が行います。

完全にクラウドで動かす形も考えましたが、手元の Mac にしました。映像の解析と書き出しに要るマシンスペックと、自宅で撮って素材がそのまま手元にある今の撮影の仕方を考えた結果です。元動画は 1本で 60〜70分、2本目は約 5.6GB ありました。

手順 1: 最初の 1本は Claude Code にその場で編集させる

動画を置いたフォルダで Claude Code を動かし、「YouTube 用に編集して」と頼みました。最初の 1本は、Claude Code がその場で Python のスクリプト(解析、切り貼り、BGM、サムネイル)を書いて仕上げました。私がしたのは、注文と OK を返すことだけです。

詰まった所は、Claude Code が動画を再生して見られないことでした。解析のスクリプトが 2秒ごとのコマを並べた一覧画像を書き出し、Claude Code はその静止画を読んで、どこがセットかを決めました。音も聴けないので、BGM の良し悪しは私が聴いて判断しました。解析のしきい値や BGM の作りなどの細部は、1本目の記事に書きました。

手順 2: 判断の出口を設定ファイル 1つに絞り、残りを道具に固める

OK のあと、ばらばらのスクリプトを 1つの Python パッケージに整えさせました。そのとき、道具(どの動画にも使う物)と、動画ごとのフォルダ(元動画、設定ファイル、作業記録、出力)を分けました。最初の 1本の流れのままだと、特定の日付のファイルが道具のフォルダに混ざっていました。

分け方の要は、Claude Code の判断の出口を動画ごとの設定ファイル(JSON)1つにしたことです。判断とは、どこを使うか、サムネイルのコマ、タイトル・概要欄の文面です。それ以外の解析、切り貼り、BGM、音量、書き出し、重量表示、サムネイルの画像、チャプターの計算は、道具が毎回同じ結果を出します。道具で前回の動画を作り直すと、完成動画・BGM・重量表示・サムネイルがバイト単位で一致しました。同じ設定ファイルからは、LLM の出来に左右されず同じ物が出ます。

道具の Python のソースは全部で 1,462行です。実行時の依存ライブラリは NumPy と Pillow の 2つで、映像と音の処理は FFmpeg、テストは pytest です。

設定ファイルの形は 2本目で広げました。1本に種目が 2つ以上ある回のために、区間ごとに種目と重量表示を持てるようにしました。片手ずつの種目(右と左で 1セットと数える)のために、区間ごとにチャプターを作らない指定を足しました。回ごとの判断は設定ファイルに残し、広げたのは道具の側です。

手順 3: CLAUDE.md には、どの回にも当てはまる基準だけを書く

CLAUDE.md は、Claude Code がそのフォルダで作業を始めるときに自動で読むファイルです。ここに方針と判断の基準を書きました。

  • 場面選びは毎回映像を見て判断
  • 使う区間の定義(私の場合は、同じ動作を繰り返しているセット)
  • 分からないことは推測で進め、確認はまとめて聞く
  • 迷った場面は外して報告
  • 投稿は必ず非公開

場面選びはルールで機械化しませんでした。私の場合は種目も器具も毎回違うからです。題材が変わっても、書き換えるのは使う区間の定義のような判断の基準で、道具は同じです。

詰まった所は、1本目でたまたまそうだったことがルールになっていたことです。1本目の後、CLAUDE.md に「約 5分に仕上げる」「前回のタイトルの形を踏襲する」が書かれていました。2本目は背中の日で、ウォームアップのセットが無く全部が本番のセットでした。Claude Code は約 5分に収めるために各種目 3セットに絞った 4分59秒の版を作り、私の指摘で全セットを入れた 12分23秒の版に作り直しました。

直した設計では、CLAUDE.md には「どの回にも当てはまること」だけを書き、ある回でたまたまそうだったこと(長さ、セット数、種目、構図)はその回の作業記録に書きます。長さに目標は置かず、本番のセットは全部入れます。

手順 4: 投稿は非公開でのアップロードまでをつなぐ

投稿には YouTube Data API を使います。YouTube への動画のアップロードなどをプログラムから行うための、Google の公式 API です。

Google Cloud の初期設定は私が 1回だけ行う

OAuth の同意画面は、Google のアカウントでログインしてアプリに権限を渡すときに出る画面で、Google Cloud のコンソールで設定します。初期設定は次の順に私が行いました。

  1. プロジェクトの作成
  2. YouTube Data API v3 の有効化
  3. OAuth の同意画面を「外部」で作成し、公開ステータスは「テスト中」のまま、テストユーザーに自分を追加
  4. クライアントを種類「デスクトップアプリ」で作成し、JSON をダウンロードして Git に入れないフォルダに配置

ログインと投稿の 2つのコマンドを Claude Code に作らせる

Claude Code に作らせたのは、ログインのコマンド(wv auth)と投稿のコマンド(wv upload)です。投稿のコマンドは次の物を持ちます。

  • 非公開でのアップロード
  • サムネイルの設定
  • 投稿の記録(upload.json)
  • 確認モード(--dry-run)
  • サムネイルだけの再設定(--thumbnail-only)

タイトル・概要欄・タグ・公開設定は、編集と同じ設定ファイルの youtube という項目に書きます。概要欄のチャプター(各セットの開始時刻)は手で書かず、概要欄に {chapters} と書いておくと、投稿のときに設定ファイルの区間から時刻を計算して差し込みます。編集をやり直しても時刻はずれません。

Google に求める権限(スコープ)は youtube.upload だけに絞りました。これで動画のアップロード(`videos.insert`)とサムネイルの設定(thumbnails.set)の両方を呼べますが、チャンネルの動画を見る・編集する・公開する・消すことはできません。テストで上げた動画の削除は、私が YouTube Studio で行いました。YouTube Studio は、チャンネルの運営者が動画の公開設定などを操作する公式の管理画面です。ログイン情報は Git に入れないフォルダに、本人だけが読める権限(600)で置きます。

投稿は必ず非公開です。公開は取り消せないからです。

投稿の部分は、依存を増やさず Python の標準ライブラリだけで書かせました(HTTP は urllib.request、ログインの受け口は http.server)。サンドボックスの中で pip を使うと、証明書の確認でエラーになったためです。

アップロードは YouTube Data API の再開可能アップロードです。8MiB ずつ送り、切断やサーバーの 5xx の後は、サーバーが受け取った位置を問い合わせて続きから送ります。同じコマンドをもう一度実行しても続きから再開し、投稿の記録があれば二重に投稿しません。

ログインは OAuth のループバック方式と PKCE です。ブラウザでの許可の後、手元の 127.0.0.1 の一時的な受け口に戻します。手でコードをコピーして貼る方式(OOB)は Google が廃止しています。

詰まった所はログインの受け口、7日の期限、非公開の固定

1つ目は、サンドボックスの中ではローカルのポートで待ち受けできないことでした。ログインのコマンドが PermissionError: Operation not permitted で止まります。ログインのコマンドだけは、私が普通のターミナルで実行します。

2つ目は、同意画面が「テスト中」のままだと、リフレッシュトークンが 7日で切れることです(Google の OAuth 2.0 のドキュメント)。「本番」にするにはプライバシーポリシーの URL などが要ります。私は当面テスト中のまま、7日ごとにログインし直すと決めました。

3つ目は、監査(YouTube による API クライアントの審査)を通っていない API プロジェクト(2020年7月28日以降に作成)から videos.insert で上げた動画は、非公開に固定されることです(YouTube ヘルプ「非公開としてロックされた動画」)。私は監査を申請していません。そのため、公開と予約は YouTube Studio で私が手で行います。投稿のコマンドは非公開でのアップロードまでで、動画の URL と Studio の編集画面の URL を報告して終わります。

この説明どおりなら、API で上げた動画は公開できないはずでした。ところが 2本目の動画は、API で非公開に上げたあと Studio で公開予約を設定すると、予約した時刻を過ぎてからログインなしで見られる状態になりました。予約の前は、YouTube の埋め込み情報を返す口(oEmbed)が非公開の動画として 403 を返し、予約の後は 200 と動画のタイトルを返しています。私の API プロジェクトで、この 1本で確かめた結果です。

カスタムサムネイルは、チャンネルが電話番号の確認を済ませていないと設定できません(YouTube ヘルプ「YouTube アカウントの確認」)。設定が失敗しても動画のアップロードは止まらず、理由が記録に残ります。後からサムネイルだけ設定し直せます。

手順 5: タイトル・概要欄のルールを投稿の前の検査にする

タイトル・概要欄の書き方を、YouTube 公式のヘルプ(タイトルとサムネイルのヒント、概要欄、ハッシュタグ、タグ)を根拠にして文書にまとめさせました。根拠が公式か、自分で決めた目安かを分けて書いてあります。

  • 公式を根拠にしたもの: 中身どおりに書く、大事な言葉を先頭に、ブランドは最後に、主要語をタイトルと概要欄の両方に、概要欄の最初の数行で動画を説明、ハッシュタグは最初の 3個がタイトルの上に表示、タグの効果は小
  • 目安(公式ではない): タイトル 40字以内などの文字数

そのうち機械で確かめられるものはコードで検査します。検索される主題の語が私の場合は種目名で、一番の数字が重量です。

  • 「記録」という語を入れない
  • 【】で始めない
  • 40字以内
  • 種目名が先頭 20字以内
  • 重量を入れる
  • 概要欄の冒頭 100字に種目名と重量
  • ハッシュタグ 1〜5個

検証のコマンドと確認モードは違反を表示し、投稿のコマンドは違反があると止まります。言い回しの良し悪しは Claude Code が判断します。検査は一括作成の時点では警告だけにし、投稿の時点で止めます。タイトルは編集の最後に書くからです。

詰まった所は、手順 3 と同じ根でした。タイトルも 1本目の形(【自宅ジム】で始まり「〜記録」で終わる)をまねていて、検索やクリックの観点で選んだものではありませんでした。新しい検査にかけると、その案は違反 4件、書き直した案は 0件でした。

種目名の言い方は YouTube の検索候補で確かめます。2本目では「ワンハンドロー」より「ワンハンドローイング」が候補に多く出ました。もう一方の言い方は概要欄に入れます。

手順 6: 道具と回ごとの判断を Git に残させる

道具・CLAUDE.md・書き方の文書・動画ごとの設定と作業記録を GitHub の非公開リポジトリに置き、作業ごとの記録(目的/分かったこと/決めたこと/やったこと/持ち越し)を残させています。元動画と完成物は Git に入れません。Claude Code は会話をまたいで記憶を持たないので、次の依頼でも前回までを引き継げるようにするためです。

人に残るのは、仕上がりへの OK と公開、7日ごとのログイン

仕組みができた時点で、私の手に残っているのは次の物です。

  • 仕上がりとタイトル・概要欄への OK
  • 公開と予約の操作(YouTube Studio で手で操作)
  • 7日ごとのログインのやり直し
  • Google Cloud の初期設定(1回だけ)

公開を Studio の手作業に残しても、予約を使えば公開の時刻に私が画面の前にいる必要はありません。

監査を通せば、API で上げた動画の非公開の固定が外れ、投稿のコマンドに公開の時刻まで持たせられます。そうすれば、素材を置くだけで YouTube の動画が仕上がり、公開まで進むパイプラインにできます。私は監査をまだ申請しておらず、公開の操作だけを手に残しています。

参考リンク

この記事をシェア