Apple Music / YouTube Music / Spotify のプレイリストを素材に、AI が自動進行するラジオ番組を流す macOS アプリです。
AI が台本を作り、2 人のパーソナリティが読み上げ、音楽と重ねて再生します。
デモ動画を見るには画像をクリック
動作環境
- macOS 14 (Sonoma) 以降
- 台本生成に使う AI CLI(いずれか 1 つをインストールしておきます)
claude(Claude Code)geminicodex(ChatGPT Codex)copilot
- Gemini API Key(読み上げに使用。Google AI Studio で無料取得可能です)
クイックスタート
- アプリを起動する(初回は右クリック → 開く)
- 設定 → 生成・TTS接続 を開く
- Gemini TTS の資格情報セットに API Key を追加する(Google AI Studio で無料取得可能です)
- CLI で台本生成に使う AI を選ぶ(例:
claude) - 設定を閉じ、メイン画面でプレイリストを選んで Start
Apple Music はこれだけで動作します。YouTube Music / Spotify は先にログインが必要です(→ 使い方)。
プレイリストからのトラック取得は最大 30 曲までに制限しています
設定ガイド
アプリを起動したら、ツールバーの 設定 ボタンを開いてサイドバーの各項目を設定します。
プロフィール
プロフィールは、番組の体験を再利用できる設定セットとして保存する機能です。プロフィール管理 で作成、複製、リネーム、削除、切り替えができます。アクティブプロフィールはメイン画面のツールバーからも切り替えられますが、番組の再生中は変更できません。
プロフィールに含まれる設定:
- 番組名、周波数・チャンネル名、地域名、ホスト名
- ボイス名、シーン指示、時間帯別プリセット
- オーバーラップモード、音楽/トーク音量、フェード、最大再生秒数
- ベッド BGM、ジングル、選択した音源、BGM/ジングル音量
音楽サービスのログイン、TTS 資格情報、台本生成 CLI、録音出力先はアプリ共通設定のため、プロフィールを切り替えても変わりません。
生成・TTS接続
最初に設定する項目です。API Key と CLI が未設定だと番組を開始できません。
| 項目 | 説明 |
|---|---|
| Gemini TTS 資格情報セット | API Key とモデルの組み合わせ。利用可能なセットを上から順に試します |
| CLI | 台本生成に使う AI CLI を選ぶ(claude / gemini / codex / copilot)(必須) |
| CLI モデル | CLI で使うモデル名(空欄にするとその CLI の既定値を使う) |
サービス
| 項目 | 説明 |
|---|---|
| 既定のサービス | 起動時にデフォルトで選ばれる音楽サービス |
| YouTube Music でログイン | YouTube Music を使う場合にここからログインします |
| Spotify でログイン | Spotify を使う場合にここからログインします |
| User Agent | YouTube Music 用の任意の User Agent 上書き。空欄なら WKWebView の既定値を使います |
番組情報
| 項目 | 説明 |
|---|---|
| 番組名 | 台本に反映される番組名 |
| 周波数・チャンネル名 | 例: 77.5 FM(台本の雰囲気づけに使う) |
| 地域名 | 任意の地域名。設定すると、CLI が確認できる場合だけ現在の天気に軽く触れることがあります |
| 男性ホスト名 | 男性パーソナリティの名前 |
| 女性ホスト名 | 女性パーソナリティの名前 |
声と話し方
| 項目 | 説明 |
|---|---|
| 男性ボイス | 男性パーソナリティの声(例: Charon) |
| 女性ボイス | 女性パーソナリティの声(例: Kore) |
| シーン・セリフの指示 | 台本生成と TTS 読み上げへの追加指示(例: 深夜帯、静かに話す) |
| 時間帯別プリセット | 早朝・朝・昼・夕方・夜・深夜ごとの任意の演技指示。該当する時間帯のプリセットが、台本生成と TTS のシーン指示に追記されます |
台本生成プロンプトには、ローカルの時刻・曜日・月・季節が自動で含まれます。天気は AgentBooth 側では取得せず、地域名が設定されている場合に限り、選択した CLI が確認できれば触れてよいという指示だけを渡します。
楽曲の再生
音楽とトークのバランス調整。既定値のままでも動作します。
| 項目 | 説明 |
|---|---|
| オーバーラップモード | 音楽とトークを重ねるか、分けるか(後述) |
| 通常音量 | 音楽の基準音量(0〜100) |
| トーク時音量 | トーク中に下げる音量(0〜100)。小さいほど音楽が小さくなる |
| フェード秒数 | 音量を滑らかに変える時間(秒) |
| 楽曲先行開始秒数 | トーク終了前に次の曲を重ねて流し始める秒数 |
| 曲終了前のトーク再開秒数 | 曲が終わる何秒前からトークを開始するか |
| 楽曲最大再生秒数 | 1 曲あたりの上限時間(0 で無制限) |
BGM とジングルを設定すると、よりラジオらしい音像を追加できます。ベッド BGM は外部楽曲が鳴っていない単独トーク区間だけでループ再生され、楽曲開始前にフェードアウトします。ジングルは有効にした場合のみ、オープニング前またはクロージング前に再生されます。
| 項目 | 説明 |
|---|---|
| ベッド BGM を有効にする | 単独トーク区間の下に、選択した音声ファイルまたはフォルダ内のランダムな音声ファイルをループ再生する |
| オープニングでジングルを使う | オープニングトークの前に選択したジングルを再生する |
| クロージングでジングルを使う | クロージングトークの前に選択したジングルを再生する |
| ベッド BGM / オープニングジングル / クロージングジングル | 選択 から音声ファイルまたはフォルダを選択する。ダイアログは前回選択位置で開き、フォルダ指定時は再生時にランダム選択されます |
| ベッド音量 | ベッド BGM の音量 |
| ジングル音量 | ジングルの音量 |
| ベッドフェードアウト秒数 | ベッド BGM を止めるときのフェード時間 |
録音
番組を録音したい場合に設定します。
| 項目 | 説明 |
|---|---|
| 録音出力先 | 録音ファイルの保存先フォルダ。空欄なら ~/Music/AgentBooth/ に保存されます |
録音はシステム音声全体を収録します。初回録音時に「画面収録」の権限確認が表示されます。 システム通知や他のアプリの音も録音されるため、録音中は通知をオフにすることをおすすめします。
アップデート
| 項目 | 説明 |
|---|---|
| 現在のバージョン | インストール済みのバージョンとビルド番号 |
| 最終チェック | 最後にアップデートを確認した日時 |
| 今すぐ確認 | 手動でアップデートを確認する |
| 自動的にアップデートを確認する | バックグラウンドでの定期チェックを有効/無効にする(1日1回) |
メニューバーの AgentBooth → アップデートを確認… からも手動確認できます。
使い方
共通
- テキスト読み上げタブで API Key と CLI を設定する
Gemini API Key は Google AI Studio で無料取得できます。APIキーとモデルのセットを複数設定可能で、上から順番に試行されます。無料枠の API制限に達した場合のみ有料枠を使用する、などの用途に便利です。
- 台本生成に使う AI CLI を選ぶ。
Gemini CLIは無料で利用開始することができます。その他、ローカルLLMを利用したい場合など、任意の外部CLIを設定することもできます。
Apple Music
- メイン画面でサービスに Apple Music を選ぶ
- プレイリストを選ぶ
- Start で番組開始
初回起動時に「自動化」の許可を求めるダイアログが表示されます。許可 を選んでください。
YouTube Music
- サービスタブ → YouTube Music でログイン を押す
- 表示された内蔵ブラウザで YouTube Music にログインする
- ログイン成功後、ログイン状態が ログイン済み(緑)に変わる
- ウィンドウを閉じて、メイン画面でサービスに YouTube Music を選ぶ
- プレイリストを選んで Start
Spotify
- サービスタブ → Spotify でログイン を押す
- 表示された内蔵ブラウザで Spotify にログインする
- ログイン状態が ログイン済み(緑)に変わる
- ウィンドウを閉じて、メイン画面でサービスに Spotify を選ぶ
- プレイリストを選んで Start
操作
| ボタン | 動作 |
|---|---|
| Start | 番組開始 |
| Pause | 一時停止(再生中に表示) |
| Resume | 再開(一時停止中に表示) |
| Stop | 停止して最初に戻る |
画面下部の NowPlayingBar に現在のトラック(アートワーク付き)と番組の進行状態が表示されます。
再生モード
番組情報タブの オーバーラップモード で選択できます。
| モード | 動作 |
|---|---|
| トークと曲を重ねる | 曲の終わりや次曲の入りでトークを重ねる |
| トークと曲を分ける | 曲が止まってからトーク、トークが終わってから次の曲を流す |
トラブルシューティング
プレイリストが途中で切れている
プレイリストから取得する曲数の上限は 30 曲に設定しています。多い曲数のプレイリストを選んだ場合、最初の 30 曲のみが使用されます。
Apple Music のプレイリストが取得できない
システム設定 → プライバシーとセキュリティ → 自動化 を開き、AgentBooth の項目に Music の許可が入っているか確認してください。
YouTube Music / Spotify のログイン状態が「未ログイン」のまま
- 内蔵ブラウザでログインを完了してからウィンドウを閉じ、再度設定タブを開いて確認してください
- ログインが途中で詰まる場合は データを削除 を押してサイトデータを消去してから再ログインしてください
Spotify でプレイリストが取得できない・再生が止まる
Spotify Web Player の画面構造が変わると動作しなくなることがあります。
台本生成が始まらない・エラーになる
- テキスト読み上げタブで選んだ CLI(
claudeなど)がインストール済みか確認してください - CLI のインストール先によってはアプリから見つからない場合がある。その場合はフルパス(例:
/usr/local/bin/claude)で CLI モデル欄に入力するか、インストール場所を確認してください
音声(読み上げ)が生成されない
- テキスト読み上げタブで Gemini API Key が正しく設定されているか確認してください
- API Key の残量や有効期限を Google AI Studio で確認してください
開発者向け情報
アーキテクチャ概要
Domain/ プロトコルと全バリュー型(Protocols.swift / Models.swift)
App/ エントリポイント・DI(AppServiceContainer)
Features/ UI 層(ContentView / MainViewModel / SettingsView / NowPlayingBar)
Services/ ビジネスロジック(Radio / Script / TTS / Music / Audio / Context)
Infrastructure/ 外部依存ラッパー(AppleScript / WebView / Settings)
AgentBoothTests/ ユニットテスト + フェイク実装(TestDoubles.swift)
主要コンポーネント
RadioOrchestrator (Services/Radio/) — Swift actor。番組進行の中核。opening → intro → playing → transition/outro → closing のフェーズを駆動し、音楽・TTS・フェードを協調制御する。曲開始/終了、フェード、ナレーション再生も cuesheet に記録する。
MainViewModel (Features/Main/) — @MainActor ObservableObject。RadioOrchestrator を保持し、UI 状態 (RadioState) を SwiftUI ビューへブリッジする。
ProcessScriptGenerationService (Services/Script/) — 外部 CLI サブプロセスを呼び出して JSON 台本を生成する。セッションごとのスクリプト保存フォルダには cuesheet.txt も併せて出力される。
RealtimeContextProvider (Services/Context/) — 台本プロンプトにローカルの時刻・曜日・月・季節・任意の地域名を追加する。AgentBooth 自体は天気を直接取得しない。
GeminiTTSService (Services/TTS/) — Gemini REST API を直接呼び出して WAV を生成する。リトライ・フォールバックモデルあり。各試行のステータスやフォールバック状況も cuesheet に残す。
AppleMusicService (Services/Music/) — AppleScriptExecutor 経由で Music.app を制御する。
YouTubeMusicService (Services/Music/) — @MainActor。YouTubeMusicAPIFetcher(内部 API 呼び出し)と YouTubeMusicPlayerController(再生制御)に委譲する。
SpotifyMusicService (Services/Music/) — @MainActor。open.spotify.com の DOM をスクレイプしてプレイリスト取得・再生制御を行う。
YouTubeMusicWebViewStore / SpotifyWebViewStore — ログイン UI 用 WebView と再生専用 WebView(オフスクリーン NSWindow)の 2 本を管理する。WKWebsiteDataStore.default() を共有して Cookie を自動同期する。
ディレクトリ構成
AgentBooth/
├── AgentBooth/
│ ├── App/ エントリポイント・DI
│ ├── Domain/ Protocols.swift, Models.swift
│ ├── Features/
│ │ ├── Main/ ContentView, MainViewModel, NowPlayingBar
│ │ ├── Settings/ SettingsView
│ │ ├── SpotifyBrowser/ Spotify ログインブラウザ UI
│ │ └── YouTubeMusicBrowser/ YouTube Music ログインブラウザ UI
│ ├── Infrastructure/
│ │ ├── Settings/ AppSettingsStore
│ │ ├── Music/ AppleScriptExecutor, AppleMusicArtworkFetcher
│ │ ├── Spotify/ SpotifyDOMScripts, SpotifyScriptRunner
│ │ └── YouTube/ YouTubeMusicJSScripts, YouTubeMusicScriptRunner
│ └── Services/
│ ├── Radio/ RadioOrchestrator
│ ├── Script/ ProcessScriptGenerationService
│ ├── TTS/ GeminiTTSService
│ ├── Audio/ SystemAudioPlaybackService
│ ├── Context/ RealtimeContextProvider
│ ├── Recording/
│ └── Music/ AppleMusicService, YouTubeMusicService, SpotifyMusicService
├── AgentBoothTests/ ユニットテスト + TestDoubles.swift
├── project.yml XcodeGen 定義
└── handoff.md
台本生成 JSON 形式
CLI は以下の JSON を stdout に出力する必要がある。
{
"dialogues": [
{ "speaker": "male", "text": "発話内容" },
{ "speaker": "female", "text": "発話内容" }
],
"summaryBullets": [
"今回触れた話題の要点",
"次回は避けたい観点"
]
}
summaryBullets: 2〜4 件の短い箇条書き- トランジション用プロンプトの番組内話題台帳として使われ、後半のトークで前半の話題を繰り返さないようにする。同一アーティスト / 同一アルバム時は、さらに局所的な連続性メモも渡す
dialoguesのみの旧形式も後方互換で受理する
ビルド・テスト
xcodegen generate
xcodebuild -project AgentBooth.xcodeproj -scheme AgentBooth \
-destination 'platform=macOS' -derivedDataPath /tmp/AgentBoothDerived test
xcodebuild -project AgentBooth.xcodeproj -scheme AgentBooth \
-destination 'platform=macOS' -derivedDataPath /tmp/AgentBoothDerived test \
-only-testing:AgentBoothTests/RadioOrchestratorTests
制約・注意事項
- App Sandbox 無効(
ENABLE_APP_SANDBOX: NO) — Mac App Store 配布未対応 project.ymlを編集 →xcodegen generateでプロジェクト再生成する(.xcodeprojは直接編集しない)- 外部 CLI はアプリのプロセス環境から解決する(シェルの
$PATHと異なる場合がある) - Spotify 連携は DOM 制御のため、Spotify Web Player UI 変更でセレクターが壊れる可能性あり