Generate a 4K narrated knowledge video from a topic with research, script, TTS, and Remotion rendering.
Design & media
Podcast Video Publisher
Try itRender podcast episodes as 1080p art-slideshow MP4s and publish them to a YouTube Podcasts playlist: covers/thumbnails generated from your art, resumable bat...
What it does
Render podcast episodes as 1080p art-slideshow MP4s and publish them to a YouTube Podcasts playlist: covers/thumbnails generated from your art, resumable batch upload, playlist ordering, retiring old versions. Use when releasing podcast episodes to YouTube, batch-upgrading existing episode videos, rebuilding a podcast playlist, or debugging YouTube API upload/thumbnail/playlist failures.
The skill document
Podcast Video Publisher
Turn a folder of podcast audio + a folder of artwork into a complete, correctly-ordered YouTube Podcasts section. Battle-tested by shipping a 12-episode season in one day (Ghost Signals with Kannaka — canonical home: https://github.com/NickFlach/kannaka-radio).
What This Skill Does
- Render —
scripts/podcast-slideshow.pybuilds a 1920×1080 MP4 per episode: art slides (~45 s each, blur-filled background, sharp center panel, dip-to-black fades) plus a generated cover card (hero art + episode title) that doubles as the thumbnail. Video is padded then muxed-shortest, so A/V drift is 0.00 s by construction. - Publish —
scripts/podcast-upload-batch.jsruns resumable phases:upload → thumbs → playlist → order → retire → verify, with all progress in a state file so any failure (quota, rate limit, network) resumes exactly where it stopped. - Self-heal —
scripts/podcast-finisher.jsis a long-running watcher that retries rate-limited thumbnails (2 h cadence) and applies playlist positions the moment position updates become possible.
All six scripts in scripts/ are self-contained (Node stdlib + ffmpeg +
Pillow only — no npm install).
Setup
- Drop the
scripts/folder into your project root (/scripts/). The scripts treat the parent ofscripts/as the project root. - Install prerequisites: ffmpeg + ffprobe on PATH, Python 3 with Pillow, Node 18+.
- Create YouTube OAuth credentials:
node scripts/youtube-grant.js(prompts for an OAuth client id/secret from Google Cloud Console with the fullyoutubescope). Writes/.youtube.json— never commit this file. - Set
PODCAST_PLAYLISTinscripts/podcast-upload-batch.jsandscripts/podcast-finisher.jsto your podcast playlist ID. - Fonts: cover cards default to Segoe UI (
C:/Windows/Fonts/) — editFONT_BOLD/FONT_REGinpodcast-slideshow.pyon Linux/macOS.
Project Layout
/
├── .youtube.json # OAuth creds (gitignore!)
├── scripts/ # this skill's scripts
└── workspace/podcasts/
├── episodes.json # [{num, title, audio}, ...]
├── metadata.json # [{num, title, description, tags,
│ # video, cover, oldVideoId?}, ...]
├── art/ # square images ≥1024px
│ └── manifest.json # [{file, title}, ...]
├── renders/ # output MP4s + cover PNGs
└── upload-state.json # created by the pipeline (gitignore)
Quick Start
python scripts/podcast-slideshow.py --all # render (idempotent)
node scripts/podcast-upload-batch.js # publish, re-run to resume
node scripts/podcast-finisher.js # only if thumbs/order pending
Phase Reference (podcast-upload-batch.js)
| Phase | What it does | Quota |
|---|---|---|
upload | videos.insert per episode, missing-episodes first | 1600/video |
thumbs | thumbnails.set with the rendered cover | ~50 |
playlist | remove stale items, insert episodes into the podcast playlist | 50/item |
order | playlistItems.update explicit positions 1→N | 50/item |
retire | unlist each replaced video + prepend an "upgraded edition" pointer to its description (old links keep working) | 50/video |
verify | read back playlist order + processing status of every upload | ~1 |
Run one phase with --phase . Delete an entry from
upload-state.json to force a redo of just that item.
YouTube Gotchas (all hit in production — trust these)
- Uploads can silently vanish.
videos.insertmay return 200 + a video ID and the video is simply gone minutes later — no error, norejectionReason. Always re-verify IDs withvideos.listbefore playlist adds (which otherwise fail "Video not found"), and keep uploads resumable per-item. Re-uploading the identical file works. - Thumbnails: 2 MB hard limit + long rate-limit. 1080p PNGs blow the
2,097,152-byte cap (
413) — save covers as JPEG quality ~88. After ~10 rapid sets you get429 "too many thumbnails recently"and the cooldown outlasts 40+ minutes — hence the finisher's 2 h retry cadence. - Podcast playlists force publish-date ordering. Display order is
exactly
publishedAt DESC; insert order is irrelevant, delete + re-insert changes nothing, andplaylistItems.updatewith a position fails with "Playlist sort type need to be MANUAL to support position".publishedAtis immutable (a private→public round-trip does NOT reset it) and the sort setting is not in the Data API. Fix: flip the playlist to Manual ordering in YouTube Studio (one click, owner only), then run--phase order. Or upload in episode order in the first place. - Daily quota is not always 10k. A 13-upload day (~21k nominal units) cleared fine. Don't pre-abort on quota math, but assume any phase can die mid-run — that's what the state file is for.
Design Notes
- Cover cards are drawn with Pillow (hero art right, blurred/darkened art as backdrop, kicker + wrapped title + footer left) — consistent branding and readable at thumbnail size.
- Slides render as independent per-slide MP4 segments (parallel, resumable) joined with the concat demuxer; audio is muxed once at the end, so chapter timestamps in descriptions stay valid.
- Descriptions: keep every factual claim traceable to the episode audio (transcribe with faster-whisper if needed), and scrub anything private before publishing — no server names, IPs, file paths, or credentials.
Related skills
Turn an article, notes, or a finished script into a listener-ready solo podcast episode with a consistent host voice. This AI podcast voice generator and AI podcast narration service adapts supplied material into a speakable podcast script, sets names and specialist terms for clear pronunciation, and creates MP3 podcast audio with natural pacing. Use this podcast voiceover AI and text-to-speech podcast service for article-to-podcast audio, news briefings, expert commentary, and knowledge shows, then carry the host direction into the next episode.
Create YouTube thumbnails from a video topic, title, script, key frame, portrait, product photo, or channel reference. This AI thumbnail maker compares three directions in text first, then renders the one you pick as a 16:9 image with clear visual hooks, readable hierarchy, and a headline-safe composition for explainers, reviews, tutorials, vlogs, games, podcasts, and long-form creative videos. It pairs the image with title-matching advice and refines an accepted direction into a consistent channel look. Rendering requires a Beatra account at beatra.ai.
For transcript or subtitle requests involving podcast URLs, public audio URLs/files, or raw transcript cleanup. Generates audio + SRT + TXT artifacts and can...
Turn local audio into Spotify episodes with a chapter timeline, images, links, and Spotify entity cards.
One CLI that turns a prompt into images, video, voice, music, sound effects, and full podcast episodes.