はじめに
追っている技術の新着記事を、毎朝 NotebookLM の音声ラジオにして届ける仕組みを、自分の GitHub に作ります。作業は約 20 分。最初の音声ができるまでさらに 20 分ほどです。
PC には Python 3.10 以上、uv、GitHub CLI(入れたら一度 gh auth login)が要りますが、足りなければステップ 4 のウィザードが教えてくれます。
1
234- 何を追う? — おすすめセットかチップを押す。カタログに無いもの(Zenn のトピック、Qiita のタグ、任意の URL)は同じ画面の下から。「確認」で本当にフィードかを見に行く。
- 番組に分ける — 番組 = NotebookLM のノートブック 1 冊 = 1 日 1 本のラジオ。混ぜて違和感がある話題は分ける。名前は自由、雰囲気は「じっくり / 手短に」。
- いつ聴く? — 朝の時刻、2 回目の有無、NotebookLM のプラン。「番組数 × 実行回数 = 1 日の本数」が上限を超えると赤くなる。
- できあがり —
config.yamlをダウンロードし、cron の 2 行をコピーしておく。
56GitHub に自分のコピーを作る
notebooklm-radio はテンプレートリポジトリです。「Use this template」で、あなたのアカウントの下に同じ中身の新しいリポジトリができます。fork ではなく、作者との繋がりの無い完全に自分のものです。
1
23
4state.json)をあなたのリポジトリにコミットし続けます。購読の履歴なので、公開しない方がよいものです。設定をコピーに入れる
リポジトリを PC に clone し、ステップ 1 のファイルで置き換えて push します。まだ何も動きません(ログイン情報が無いので、動いても止まります)。
$ gh repo clone you/my-radio $ cd my-radio # 1. ダウンロードした config.yaml で丸ごと置き換える(feeds: の下に貼るのではない) $ cp ~/Downloads/config.yaml ./config.yaml # 2. .github/workflows/rss-radio.yml の "- cron:" 2 行を、ビルダー「できあがり」の cron 2 行に差し替える # 3. 検査(何も実行しない。設定ファイルを読んで問題を列挙するだけ) $ python radio_batch.py --check-config config.yaml OK: 8 feed(s), topics: AI, インフラ, 言語 $ git add -A && git commit -m "my feeds" && git push
- 直すのは 2 か所だけ: config.yaml を丸ごと置き換える(ダウンロードしたファイルで上書き。
feeds:の下に貼るのではない)と、.github/workflows/rss-radio.ymlの- cron:2 行をビルダーの 2 行に差し替える。 - 検査が
NGと言ったら、列挙された箇所を直します。知らないキーはわざとエラーにしています(typo が黙って既定値に落ちるのが一番怖いため)。 - 手元に
yamlなどのパッケージが無くて検査が動かない場合は、pip install -r requirements.txtで入ります。CI でも毎回同じ検査が走るので、飛ばしても壊れたまま動くことはありません。 - ファイルを GitHub の画面で直接編集しても構いません。clone するのはウィザードのためです。
ウィザードを一度だけ走らせる
NotebookLM に入るには、あなたの Google ログインが要ります。ブラウザでしか取れないので、PC で 1 回だけログインし、その結果をリポジトリの secret(GitHub Actions だけが読める秘密の置き場)に渡します。それを全部やるのが setup.py です。
$ python scripts/setup.py notebooklm-radio セットアップ 1/6 環境の確認 ✓ Python 3.12.4 ✓ リポジトリ you/my-radio ✓ 固定バージョン notebooklm-py 0.8.4(requirements-notebooklm.txt) ✓ uv uv 0.9.24 ✓ GitHub CLI インストール済み・ログイン済み ✗ notebooklm CLI インストールされていない → uv tool install "notebooklm-py[browser]==0.8.4"(このウィザードでもできる) ✗ ログイン(ci プロファイル) 認証ファイルがまだ無い → notebooklm -p ci login --fresh(このウィザードでもできる) ✗ secret NOTEBOOKLM_AUTH_JSON 未設定 → 上のログインからウィザードが設定する ! secret NOTIFY_WEBHOOK_URL 未設定 → 無いとエラーも含めて通知が一切届かない ✓ config.yaml config.yaml OK: 8 feed(s), topics: AI, インフラ, 言語 2/6 notebooklm CLI notebooklm CLI(notebooklm-py[browser]==0.8.4)を uv でインストールしますか? [Y/n] y $ uv tool install --force "notebooklm-py[browser]==0.8.4" 3/6 Google ログイン(CI 用プロファイル) $ notebooklm -p ci login --fresh ブラウザが開きます。NotebookLM を使える Google アカウントでログインしてください。 ブラウザの古いセッションは消してから開くので、Google へのログインを毎回やり直します。 ci プロファイルは CI 専用で、普段使いの default プロファイルとは分けておきます。 ローカルで notebooklm を使うたびに Cookie が更新され、CI に渡したコピーの寿命が縮むためです。 (ブラウザで普通にログインする。終わるとターミナルに戻る) $ notebooklm -p ci auth check --test --passive ログインできていることを確認しました。 4/6 リポジトリの secret NOTEBOOKLM_AUTH_JSON $ gh secret set NOTEBOOKLM_AUTH_JSON -R you/my-radio(/Users/you/.notebooklm/profiles/ci/storage_state.json から) NOTEBOOKLM_AUTH_UPDATED=2026-09-27 をリポジトリ変数に記録しました。 5/6 リポジトリの secret NOTIFY_WEBHOOK_URL Slack の Incoming Webhook か Discord の Webhook の URL を貼り付けてください(入力は表示されません)。 Slack: api.slack.com/apps → 自分のアプリ → Incoming Webhooks → Add New Webhook to Workspace Discord: チャンネルの編集 → 連携サービス → ウェブフック → 新しいウェブフック → ウェブフック URL をコピー 空のまま Enter を押すと飛ばせます(その場合、エラーも含めて通知は届きません)。 Webhook URL: (貼り付けても表示されない) $ gh secret set NOTIFY_WEBHOOK_URL -R you/my-radio 6/6 初回の実行 ワークフローを今すぐ実行しますか?(今日のノートブックを作り、音声の生成を始めます) [Y/n] y $ gh workflow run rss-radio.yml -R you/my-radio 開始しました: https://github.com/you/my-radio/actions/runs/1234567890 終わるまでここで見届けますか?(3〜5 分ほど) [Y/n] y $ gh run watch 1234567890 -R you/my-radio --exit-status --compact 成功しました。結果は Webhook に届きます。音声は NotebookLM の中で 20 分ほどかけて作られます。 完了しました。この後の流れ: - 数分以内に、今日の記事一覧とノートブックへのリンクが Webhook に届きます。 - 音声は NotebookLM の中で 20 分ほどかけて作られます(通知は「開始した」で、「完了した」ではありません)。 - Google のセッションは約 3.5 週で切れます。エラー通知が来たら python scripts/setup.py renew を実行してください。 - フィードや話し方は config.yaml で変えられます(https://inoueuj.github.io/tech-feed-catalog/ で作り直すこともできます)。
webhook URL の取り方
- Slack: api.slack.com/apps → Create New App → From scratch → 左メニュー Incoming Webhooks → On → Add New Webhook to Workspace → チャンネルを選ぶ →
https://hooks.slack.com/services/…をコピー。 - Discord: 通知したいチャンネルの設定 → 連携サービス → ウェブフック → 新しいウェブフック → ウェブフック URL をコピー。
ci プロファイルに保存されます。分けるのは、混ぜると認証の寿命が 3.5 週から 3 日に縮むためです。認証ファイルの中身は画面にもシェルの履歴にも出ません。Windows(PowerShell)でも同じ手順です。- 「workflows are disabled」のような失敗 → テンプレートから作った直後は Actions が無効なことがあります。リポジトリの Actions タブで有効化して、
gh workflow run rss-radio.ymlかウィザードをもう一度。 notebooklmが見つからない → 新しいターミナルを開く(インストール後に PATH が変わる)。python scripts/setup.py doctorはいつでも実行でき、何も変えません。
初回実行を確かめる
3 か所で確かめます。GitHub の Actions、通知、NotebookLM。初回は既読状態の初期化として各フィードの最新 1 件だけを処理し、過去分は読んだことにします(洪水は起きません)。
12届く通知(Slack の例)
34
5毎朝の見方
ここからは何もしなくても、予約した時刻に GitHub が動きます。
- 通知のリンクからノートブックを開き、NotebookLM アプリで聴く。アプリの通知を有効にしておくと、音声の完了が分かる。
- 新着が無い回は「😪 今回は新着なしだったよ。ラジオはおやすみ〜」が来る。何も来ないのは異常(ステップ 9)。
- 2 回目の実行では、その回に新しく入った記事だけで別の音声ができる。朝の記事は再放送されない。
- 見出しに「(音声は未生成)」と付いていたら、記事は入ったが音声を始められなかった。理由は別の通知に出る(多くは生成の上限)。アプリから手動で「音声解説」を作れる。
- 1 週間ほどで「🧹 [ドライラン] 7日より古いラジオ 2件が削除対象だよ(まだ消してない)」が来る。挙がったのが古いラジオだけなら、
config.yamlのcleanup.dry_runをfalseに。削除対象は「このバッチの命名に完全一致するノートブック」だけで、手で作ったものには触らない。
設定を変える
すべて config.yaml です。変えて push すれば次の実行から反映されます(push しても即実行はされません)。迷ったらビルダーで作り直して丸ごと置き換えるのが確実です。
| やりたいこと | どこを変えるか |
|---|---|
| フィードを足す・外す | feeds: の項目。ビルダーの共有リンクから作り直すと楽 |
| フィードを別の番組へ | そのフィードの topic: |
| 番組の形式・長さ・指示文 | topics: → その番組 → audio: |
| 実行時刻 | .github/workflows/rss-radio.yml の - cron: 2 行(UTC)。ビルダーの「できあがり」が UTC に変換した 2 行を出す |
| 1 回に処理する記事数 | settings.limits.per_feed(既定 3)と total(既定 15)。超えた分は次回へ |
| 流量の多いフィード | そのフィードに mode: latest(最新 N 件だけ拾い、残りは読んだことにする) |
| 音声の言語 | settings.language |
| 記事ページがボット対策で取れないサイト | そのフィードに source_mode: text |
変えたあとは python radio_batch.py --check-config で検査できます。使えるキーは config.schema.json に全部書いてあります。
約 3.5 週ごと: ログインを更新する
手元の Google セッションは生き続けますが、secret に凍結したコピーは約 3.5 週で切れます。コードでは直せません。切れると、原因と手順つきのエラー通知が届きます。
NotebookLM command failed.
Command: notebooklm list --json
Exit code: 2
UNEXPECTED_ERROR: Unexpected error: Authentication expired or invalid. Run 'notebooklm login' to re-authenticate.
認証切れ。CI 専用プロファイルで `notebooklm -p ci login` をやり直し、`gh secret set NOTEBOOKLM_AUTH_JSON < ~/.notebooklm/profiles/ci/storage_state.json` でシークレットを更新すること。環境変数モードは書き戻し先が無く自動更新されないため、放置しても直らない。…
実行ログ: GitHub Actions Run$ python scripts/setup.py renew notebooklm-radio 認証の更新 1/4 notebooklm CLI 2/4 Google ログイン(CI 用プロファイル) $ notebooklm -p ci login --fresh ブラウザが開きます。NotebookLM を使える Google アカウントでログインしてください。 (ブラウザが開く。ログインし直す) $ notebooklm -p ci auth check --test --passive ログインできていることを確認しました。 3/4 リポジトリの secret NOTEBOOKLM_AUTH_JSON $ gh secret set NOTEBOOKLM_AUTH_JSON -R you/my-radio(/Users/you/.notebooklm/profiles/ci/storage_state.json から) NOTEBOOKLM_AUTH_UPDATED=2026-10-21 をリポジトリ変数に記録しました。 4/4 再実行 ワークフローを今すぐ実行しますか?(今日のノートブックを作り、音声の生成を始めます) [Y/n] y $ gh workflow run rss-radio.yml -R you/my-radio 開始しました: https://github.com/you/my-radio/actions/runs/1234567891 終わるまでここで見届けますか?(3〜5 分ほど) [Y/n] y $ gh run watch 1234567891 -R you/my-radio --exit-status --compact 成功しました。結果は Webhook に届きます。音声は NotebookLM の中で 20 分ほどかけて作られます。
- 2 分ほどで終わります。
python scripts/setup.py doctorでログインの古さがいつでも見られ、21 日を超えると先に警告します。 - 普段
notebooklmを手元で使うときは-p ciを付けないでください。CI 用と混ぜると寿命が 3 日に縮みます。 state.jsonは bot のものです。手で編集もコミットもしないでください。
困ったとき
| 見えたもの | 意味 | やること |
|---|---|---|
| 何も届かない日が続く | 通知の仕組みごと止まっている | Actions タブで最後の実行を見る。NOTIFY_WEBHOOK_URL が入っているか doctor で確認 |
| 🚨 テスト/lint が失敗した… | テスト・lint・config.yaml の検査で止まった。ラジオは作られない | リンク先の実行ログを見る。config.yaml の誤りなら config.yaml NG と原因が出ている |
| 実行エラー + Authentication expired | 認証切れ | python scripts/setup.py renew |
| (音声は未生成)+ rate_limited | 音声生成の上限に当たった | 番組を減らす、2 回目を切る、プランを上げる。記事は入っているので手動生成もできる |
| ⚠️ RSSフィードの取得に問題があるよ ・X: HTTP 403 | そのフィードが取れない | 一時的なら放置。続くなら URL を見直す。403 が続くならボット拒否 |
| ⚠️ 以下はサイトのボット対策で本文を取り込めなかったから… | 記事ページがボット対策ページだった | そのフィードに source_mode: text |
| 同じ記事が 2 つ入る | 2 つのフィードが同じ記事を別 URL で配信している(同一 URL なら自動で 1 つにまとまる) | 片方を外す。ビルダーは選んだ時点で警告する |
| 古い記事ばかり流れる | 流量が上限を超えていて古い順に消化している | mode: latest、または limits.per_feed を上げる |
| 夕方の回が翌日のノートブックに入った | GitHub の実行が数時間遅れて日付をまたいだ | cron を早める。:00 と :30 は特に混む |
用語
- フィード
- サイトが配っている「新着一覧」。RSS / Atom が普通の形式で、無いサイトは sitemap.xml で代用する。
- config.yaml
- 設定ファイル。フィード、番組、音声の設定、掃除の設定がすべて入っている。
- state.json
- 「どこまで読んだか」の記録。bot が毎回コミットする。
- 番組 / トピック / ノートブック
- フィードのまとまり。1 日 × 1 番組で NotebookLM に 1 冊でき、1 冊につき実行ごとに 1 本の音声ができる。
- 音声解説(音声概要)
- NotebookLM が生成する二人の対談形式のラジオ。生成には上限がある。
- cron
- GitHub Actions に「この時刻に実行」と予約する行。UTC で書く。
- secret
- GitHub Actions だけが読める秘密の値。認証(
NOTEBOOKLM_AUTH_JSON)と通知先(NOTIFY_WEBHOOK_URL)の 2 つ。 - webhook
- Slack や Discord に外から投稿するための URL。
- プロファイル(ci / default)
- notebooklm CLI のログイン保存先。CI 用と普段用を分ける。
- 最新だけ / mode: latest
- 流量の多いフィードで、最新 N 件だけ拾って残りを読んだことにする設定。