--- name: youtube description: Manage your YouTube channel — upload, list, edit metadata, schedule/unschedule videos, set thumbnails, download your own private videos, get transcripts, generate AI chapter summaries, and post comments (with a Chrome-automation playbook for pinning). Use when asked to upload to YouTube, schedule a video, edit video metadata, download a private YouTube video, get a transcript, generate chapters, or post/pin a comment. --- # YouTube Channel Management Use the `youtube` CLI tool to manage your YouTube channel: upload videos, list uploads, edit metadata, schedule/unschedule publication, set thumbnails, download your own videos (including private ones), get transcripts, and generate AI chapter summaries. ## Architecture Three separate systems: 1. **YouTube Data API v3 + OAuth 2.0** — upload, list, update, schedule, thumbnails 2. **yt-dlp + browser cookies** — download your own videos (including private) 3. **youtube_transcript_api + Gemini** — transcripts (fetches existing captions) and AI chapter generation The YouTube Data API does **not** have a download endpoint for uploaded video files. Download is handled separately via yt-dlp with authenticated cookies. Transcripts are fetched from YouTube's existing captions (no AI needed). Chapter generation uses Google Gemini. ## Setup ### 1. OAuth Credentials (Required for API operations) Create OAuth 2.0 credentials in the [Google Cloud Console](https://console.cloud.google.com/apis/credentials): 1. Create a project (or use an existing one) 2. Enable the **YouTube Data API v3** 3. Create **OAuth 2.0 Client ID** (type: Desktop app) 4. Download the client secret JSON Provide credentials via one of: ```bash # Option A: Place the downloaded JSON file at the default path cp ~/Downloads/client_secret_*.json ~/.youtube_client_secret.json # Option B: Set env vars export YOUTUBE_CLIENT_ID="your_client_id" export YOUTUBE_CLIENT_SECRET="your_client_secret" # Option C: Point to your JSON file export YOUTUBE_CLIENT_SECRETS_FILE="/path/to/client_secret.json" ``` Then authenticate: ```bash youtube auth ``` This opens a browser for Google login and saves the token to `~/.youtube_oauth_token.json`. ### 2. yt-dlp (Required for download only) ```bash pip install yt-dlp # or brew install yt-dlp ``` ## Commands | Command | Description | |---------|-------------| | `youtube auth` | Authenticate via OAuth 2.0 (opens browser) | | `youtube upload` | Upload a video with metadata | | `youtube list` | List your uploaded videos | | `youtube update` | Edit metadata of an existing video | | `youtube schedule` | Schedule a private video for future publication | | `youtube unschedule` | Revert a scheduled video to private draft | | `youtube reschedule` | Change the scheduled publish time | | `youtube set-thumbnail` | Set or replace a video thumbnail | | `youtube download` | Download a video using yt-dlp (supports private videos) | | `youtube transcribe` | Get transcript of a YouTube video (fetches existing captions) | | `youtube chapters` | Generate AI chapter summaries using Gemini | | `youtube comment` | Post a top-level comment on a video (API cannot pin — see Chrome playbook below) | **Not supported:** `youtube delete` — deletion is explicitly excluded. **Pinning comments** is also not supported by the YouTube Data API; it must be done in Studio UI (see the Chrome automation playbook below). > **Note:** `transcribe` and `chapters` do NOT require YouTube OAuth. `transcribe` works with any public video. `chapters` requires `GEMINI_API_KEY`. ## Usage ### Upload a Video ```bash # Upload as private (default) youtube upload --file video.mp4 --title "My Video" --description "Description here" # Upload with tags and schedule youtube upload --file video.mp4 --title "My Video" --tags "tag1, tag2" --publish-at 2026-04-10T16:00:00Z # Upload as public youtube upload --file video.mp4 --title "My Video" --privacy public -v ``` ### List Uploads ```bash # List recent uploads as a table youtube list # List more videos youtube list -n 50 # Output as JSON youtube list --json # Verbose mode (shows tags, category) youtube list -v ``` ### Update Metadata ```bash # Update title only youtube update --id VIDEO_ID --title "New Title" # Update multiple fields youtube update --id VIDEO_ID --title "New Title" --description "New desc" --tags "a, b, c" # Change privacy youtube update --id VIDEO_ID --privacy unlisted # Change category youtube update --id VIDEO_ID --category 28 ``` > **Safe updates:** The tool always fetches the current video first, merges your changes, and sends a complete payload — preserving required fields like `title` and `categoryId`. ### Schedule / Unschedule / Reschedule ```bash # Schedule a private video youtube schedule --id VIDEO_ID --publish-at 2026-04-10T16:00:00Z # Unschedule back to private draft youtube unschedule --id VIDEO_ID # Reschedule to a new time youtube reschedule --id VIDEO_ID --publish-at 2026-04-11T16:00:00Z ``` > **Scheduling rules:** `publishAt` only works on private videos that have never been published. You cannot reschedule a previously-public video. ### Set Thumbnail ```bash youtube set-thumbnail --id VIDEO_ID --file thumbnail.jpg ``` ### Download Videos By default, `youtube download` automatically extracts cookies from Chrome for authentication. This means you just need to be logged into YouTube in Chrome — no manual cookie export needed. ```bash # Download a video (auto-extracts Chrome cookies by default) youtube download --id VIDEO_ID # Download by URL youtube download --url "https://youtu.be/VIDEO_ID" # Save to specific path youtube download --id VIDEO_ID -o ~/Downloads/video.mp4 # Use Firefox instead of Chrome youtube download --id VIDEO_ID --cookies-from-browser firefox # Specify a custom Chrome profile path youtube download --id VIDEO_ID --cookies-from-browser "chrome:/path/to/profile" # Use a cookies.txt file instead youtube download --id VIDEO_ID --cookies ~/cookies.txt # Skip cookie extraction (public videos only) youtube download --id VIDEO_ID --no-cookies # Choose format youtube download --id VIDEO_ID -f "bestvideo+bestaudio" ``` > **How it works:** yt-dlp reads cookies directly from Chrome's cookie database on disk. > You must be logged into YouTube in Chrome. No browser window is opened — it reads the stored cookies programmatically. > For private videos, the Chrome session must be logged into the account that owns the video. ### Transcribe Videos Fetches existing YouTube captions/subtitles — no AI or OAuth needed. For private/unplayable videos, automatically downloads via yt-dlp (with Chrome cookies) and transcribes locally with Whisper. ```bash # Get transcript by URL youtube transcribe "https://youtu.be/VIDEO_ID" # Get transcript by video ID youtube transcribe VIDEO_ID # Timestamps in seconds format youtube transcribe "https://youtu.be/VIDEO_ID" --seconds # Transcribe a local video file (uses Whisper — supports mp4, webm, mkv, etc.) youtube transcribe video.mp4 # Private videos work too (auto-downloads with Chrome cookies, then transcribes locally) youtube transcribe "https://youtu.be/PRIVATE_VIDEO_ID" # Save to file youtube transcribe "https://youtu.be/VIDEO_ID" > transcript.txt ``` ### Post a Comment Posts a top-level comment on a video as the authenticated channel. Uses `commentThreads.insert` with the `youtube.force-ssl` scope (already included in the OAuth token). ```bash youtube comment --id VIDEO_ID --text "Your comment text here" ``` Prints the Thread ID, Comment ID, and a Studio deep link for pinning. > **Pinning is NOT available via the API** — it's a Studio-UI-only action. Use the Chrome automation playbook below after posting. ### Pin a Comment via Chrome Automation The YouTube Data API exposes no pin endpoint, so pinning requires driving Studio's UI. This playbook is verified end-to-end — follow it to avoid fishing for selectors. **Prerequisites:** claude-in-chrome MCP connected and logged into the target YouTube channel in Chrome. **Steps:** 1. **Navigate** to `https://studio.youtube.com/video//comments` in a fresh tab (create one with `tabs_create_mcp` — never clobber an existing tab). 2. **Clear the "Response status: Unresponded" filter chip.** Studio hides the *channel owner's own comments* under this filter by default, so your just-posted comment will NOT appear until you clear it. - `find` query: `"Response status filter chip with X to clear"` — click the returned `ref` for the **Remove** (X) sub-element, not the chip itself. 3. **Screenshot** to confirm your comment from `@` is now visible (labeled e.g. "1 minute ago"). 4. **Open the kebab (three-dot) menu on YOUR comment row.** - `find` query: `"three-dot kebab action menu button on the comment row"`. 5. **Click "Pin"** from the dropdown. - `find` query: `"Pin menu item in the action menu dropdown"`. - ⚠️ **Known quirk:** the `find`-based click on the Pin menu item sometimes re-opens the dropdown instead of activating the item. If the confirmation dialog doesn't appear, fall back to a **coordinate click** on the visible "Pin" text — take a screenshot, read the coords, and `computer.left_click` there. This has been the consistent failure mode across multiple runs. 6. **Click "Pin"** in the "Pin this comment?" confirmation dialog. - `find` query: `"Pin button in the \"Pin this comment?\" confirmation dialog"`. 7. **Verify** via screenshot — the comment should move to the top and show "📌 Pinned by @<your-handle>". **Parallelization:** multiple subagents can pin comments on different videos in parallel. Each agent must create its own fresh tab (`tabs_create_mcp`) and only operate on that tabId — agents sharing a tab will thrash. Each video has its own independent pinned slot, so there's no cross-video contention. ### Generate Chapters Uses Google Gemini to generate chapter summaries with timestamps. ```bash # Generate chapters for a YouTube video youtube chapters "https://youtu.be/VIDEO_ID" # Generate chapters for a local MP4 youtube chapters video.mp4 ``` > **Requires:** `GEMINI_API_KEY` environment variable. Get one at https://aistudio.google.com/apikey ## Options Reference ### Global Options | Option | Short | Description | |--------|-------|-------------| | `--verbose` | `-v` | Show progress and debug info | ### Upload Options | Option | Short | Description | |--------|-------|-------------| | `--file` | `-f` | Path to video file (required) | | `--title` | `-t` | Video title (required) | | `--description` | `-d` | Video description | | `--tags` | | Comma-separated tags | | `--category` | | YouTube category ID (default: 22) | | `--privacy` | `-p` | public, private, unlisted (default: private) | | `--publish-at` | | ISO 8601 timestamp for scheduling | ### Update Options | Option | Short | Description | |--------|-------|-------------| | `--id` | | Video ID (required) | | `--title` | `-t` | New title | | `--description` | `-d` | New description | | `--tags` | | New comma-separated tags (replaces existing) | | `--category` | | New category ID | | `--privacy` | `-p` | New privacy status | ### Comment Options | Option | Short | Description | |--------|-------|-------------| | `--id` | | Video ID (required) | | `--text` | `-t` | Comment text (required) | ### Download Options | Option | Short | Description | |--------|-------|-------------| | `--id` | | YouTube video ID | | `--url` | `-u` | YouTube video URL | | `--output` | `-o` | Output file path | | `--cookies` | | Path to cookies.txt file | | `--cookies-from-browser` | | Browser name or path (default: `chrome`). Examples: `chrome`, `firefox`, `chrome:/path/to/profile` | | `--format` | `-f` | yt-dlp format string | | `--no-cookies` | | Skip automatic cookie extraction | ## Environment Variables | Variable | Description | |----------|-------------| | `YOUTUBE_CLIENT_ID` | OAuth 2.0 client ID | | `YOUTUBE_CLIENT_SECRET` | OAuth 2.0 client secret | | `YOUTUBE_CLIENT_SECRETS_FILE` | Path to client secret JSON (default: `~/.youtube_client_secret.json`) | | `YOUTUBE_TOKEN_FILE` | Path to stored OAuth token (default: `~/.youtube_oauth_token.json`) | | `GEMINI_API_KEY` | Google Gemini API key (required only for `youtube chapters`) | ## Requirements - The `hamel` package must be installed: `pip install hamel` - For download: `yt-dlp` must be installed separately - For chapters: `GEMINI_API_KEY` environment variable must be set - For local MP4 transcription: `openai-whisper` and `ffmpeg` are needed ## Troubleshooting **"No OAuth credentials found"**: Set `YOUTUBE_CLIENT_ID` and `YOUTUBE_CLIENT_SECRET` env vars (or place a client_secret JSON file at `~/.youtube_client_secret.json`), then run `youtube auth`. **"Token refresh failed"**: Your OAuth token has expired. Run `youtube auth` again to re-authenticate. **"yt-dlp not found"**: Install with `pip install yt-dlp` or `brew install yt-dlp`. **"Video not found or not accessible"**: Check the video ID is correct and that your authenticated account has access. **"Could not find browser cookies database"**: Chrome is not at the default location. Specify the profile path: `--cookies-from-browser 'chrome:/path/to/profile'`, or use Firefox: `--cookies-from-browser firefox`. **"Authentication required for this video"**: You need to be logged into YouTube in Chrome. Open Chrome, go to youtube.com, sign in, then retry the download. **Scheduling fails**: Only private videos that have never been published can be scheduled. If the video was previously public, scheduling will not work. ## Complete Setup Walkthrough If you're starting from scratch on a new machine: 1. **Install the package:** `pip install hamel` 2. **Install yt-dlp (for downloads):** `pip install yt-dlp` 3. **Create OAuth credentials:** - Go to https://console.cloud.google.com/apis/credentials - Create a project (or use an existing one) - Enable the YouTube Data API v3 at https://console.cloud.google.com/apis/library/youtube.googleapis.com - Go to OAuth consent screen → add your email as a test user - Create an OAuth 2.0 Client ID (type: Desktop app) - Copy the Client ID and Client Secret 4. **Set environment variables:** ```bash export YOUTUBE_CLIENT_ID='your-client-id.apps.googleusercontent.com' export YOUTUBE_CLIENT_SECRET='your-client-secret' ``` 5. **Authenticate:** `youtube auth` (opens browser for one-time Google login) 6. **Verify:** `youtube list` (should show your uploads) 7. **For downloads:** Make sure you're logged into YouTube in Chrome, then `youtube download --id VIDEO_ID` 8. **For transcripts:** `youtube transcribe "https://youtu.be/VIDEO_ID"` (no extra setup needed) 9. **For chapters:** Set `GEMINI_API_KEY` env var, then `youtube chapters "https://youtu.be/VIDEO_ID"`