name: podguy-youtube-publisher description: Use when the user wants to upload, schedule, or manage podcast episode videos on YouTube. Compose upload metadata from existing podguy analysis artifacts and publish with scripts/youtube_publish.py. compatibility: Requires a Google Cloud OAuth client with the YouTube Data API v3 enabled, plus the youtube uv dependency group. Works from repo root.
podguy YouTube Publisher
Use this skill when the user asks to upload an episode to YouTube, schedule a premiere/publish time, set thumbnails or captions, add uploads to a playlist, or check upload status.
This skill complements podguy-post-production:
- pi does editorial judgment: drafting the title, description, tags, and chapter list
scripts/youtube_publish.pydoes deterministic YouTube Data API calls- uploads default to private so publishing stays a deliberate, reviewable step
One-time setup
If the user has never authenticated, walk them through it:
- Create a Google Cloud project and enable YouTube Data API v3.
- Create an OAuth client of type Desktop app (APIs & Services > Credentials).
- Save the downloaded JSON to
~/.config/podguy/youtube/client_secret.json. - Run the auth flow (opens a browser):
uv run --group youtube python scripts/youtube_publish.py auth
The token is cached at ~/.config/podguy/youtube/token.json. Both paths can be overridden with --client-secrets / --token or PODGUY_YT_CLIENT_SECRETS / PODGUY_YT_TOKEN.
Personal-channel projects can stay in OAuth "testing" mode with the user's account added as a test user. Two caveats to surface:
- Videos uploaded through unverified API projects may be locked private until the project passes a YouTube API audit, so verify scheduling behavior before relying on it.
- While the OAuth app is in Testing mode, Google expires refresh tokens after about 7 days; expect to re-run the
authcommand periodically (or move the consent screen to "In production" to stop the expiry).
Inputs to gather
- Final episode video path (a real export, not a review cut).
- Episode slug, e.g.
ep006, to find existing analysis artifacts. - Title — draft from show notes if not provided; max 100 characters.
- Description — prefer composing a
dist/analysis/<slug>/youtube-description.mdfrom show notes. - Chapters — reuse
dist/analysis/<slug>/chapters.mdwhen it exists (YouTube parses00:00 Titlelines; the first chapter must start at00:00). - Privacy and timing — private (default), unlisted, public, or scheduled via
--publish-at. - Optional: thumbnail image, SRT captions from the transcript run, playlist.
Reuse existing analysis outputs when they match the episode. If chapters or show notes are missing and the user wants them in the description, run the normal post-production workflow first.
Workflow
- Draft the title, description, and tags from the episode's analysis artifacts and the
[youtube]section ofpodguy.toml. - Write the description body to
dist/analysis/<slug>/youtube-description.md. - Show the user the metadata and confirm before uploading.
- Preview the exact request first:
uv run --group youtube python scripts/youtube_publish.py upload \
<episode-video> \
--title "<title>" \
--description-file dist/analysis/<slug>/youtube-description.md \
--chapters-file dist/analysis/<slug>/chapters.md \
--dry-run
- Upload for real by dropping
--dry-run. Add--thumbnail,--caption,--playlist-id, or--publish-atas requested. - Report the YouTube Studio link and remind the user the video is private until they (or the schedule) publish it.
Commands
Scheduled publish (YouTube flips it public at the given time):
uv run --group youtube python scripts/youtube_publish.py upload \
<episode-video> \
--title "<title>" \
--description-file dist/analysis/<slug>/youtube-description.md \
--privacy private \
--publish-at 2026-06-20T16:00:00Z
Manage an existing upload:
uv run --group youtube python scripts/youtube_publish.py status <video-id>
uv run --group youtube python scripts/youtube_publish.py set-thumbnail <video-id> thumb.jpg
uv run --group youtube python scripts/youtube_publish.py set-caption <video-id> dist/analysis/<slug>/transcript.srt
uv run --group youtube python scripts/youtube_publish.py add-to-playlist <video-id> <playlist-id>
uv run --group youtube python scripts/youtube_publish.py update <video-id> --title "<new title>"
uv run --group youtube python scripts/youtube_publish.py list-uploads --limit 10
Profile defaults
The [youtube] section of podguy.toml supplies defaults (see podguy.example.toml):
default_privacy— used when--privacyis omitted (falls back toprivate)default_category— YouTube category id (falls back to22, People & Blogs)default_tags— used when--tagsis omittedplaylist_id— uploads are added here automaticallydescription_footer— appended to every composed descriptionmade_for_kids— self-declared made-for-kids flag
Safety guidance
- Never upload without explicit user confirmation of the final metadata; always offer a
--dry-runpreview first. - Default to
private; only usepublicwhen the user explicitly says to publish now. - Treat
--publish-atas the preferred way to "go live at - Each upload costs 1600 of the default 10000 daily quota units — surface quota errors plainly and suggest retrying after the Pacific-midnight reset.
- Do not commit OAuth secrets or tokens; they live outside the repo by default.
References
- Publisher CLI: ../../scripts/youtube_publish.py
- Publish prompt: ../../prompts/publish-youtube.md
- Chapters prompt: ../../prompts/chapters.md
- Show notes prompt: ../../prompts/show-notes.md