Show HN: E3d-pod2vid – AI pipeline that turns podcasts into YouTube-ready videos A developer released e3d-pod2vid, an open-source AI pipeline that converts diarized podcast audio into YouTube-ready videos with semantically matched B-roll, burned-in subtitles, and optional TTS voice replacement. The tool also handles YouTube uploads and multi-platform social posting, aiming to streamline podcast-to-video production for creators. AI-powered podcast-to-video pipeline. Converts a diarized audio file NotebookLM, podcast, interview into a YouTube-ready MP4 with: - Semantically matched Pexels B-roll per utterance GPT-4o-mini picks the clip - Burned-in subtitles no ffmpeg libass required — pure Pillow - Optional OpenAI TTS voice replacement swap out NotebookLM / AI voices - YouTube upload + description/thumbnail update - One-shot multi-platform social posting Discord, Telegram, X, Moltbook, LinkedIn git clone https://github.com/spacepacket1/e3d-pod2vid.git cd e3d-pod2vid Python deps pip install -r requirements.txt Node deps YouTube + social posting only npm install Copy and fill in your API keys cp .env.example .env $EDITOR .env python3 pod2vid.py episode.m4a output/episode.mp4 This single command: - Uploads audio to AssemblyAI for speaker diarization - Asks GPT-4o-mini for a specific Pexels search query per utterance - Downloads matching B-roll clips cached per query - Renders each segment with burned-in subtitles - Concatenates into a final MP4 + SRT subtitle file Caches diarization and queries as JSON so re-runs are fast. If you want custom voices instead of the original audio e.g. replace NotebookLM voices : Synthesize with OpenAI TTS voices python3 tts replace.py output/episode-diarization.json episode-tts Render video using TTS audio python3 pod2vid.py output/episode-tts.mp3 output/episode-tts.mp4 Default voices: onyx Speaker A and nova Speaker B . Override with VOICE A / VOICE B . Available voices: alloy , echo , fable , onyx , nova , shimmer python3 make thumbnail.py "Predictive GPS for Autonomous AI Agents" thumbnail.png /path/to/logo.png Outputs a 1280×720 PNG with title, accent stripe, and optional logo overlay. Pure Pillow — no browser or design tool required. First time: authorize your account node yt auth.js The script prints a URL. Open it on any device phone, browser — the machine running the script doesn't need a browser . After approving, paste the redirect URL back into the terminal. Tokens are saved to youtube-tokens.json . Upload the video node yt upload.js output/episode-tts.mp4 "My Episode Title" Prints the video URL and ID when done. Update description and thumbnail YT DESCRIPTION="Check out maps.e3d.ai — AI-powered GPS for autonomous vehicles. Follow us: • X: @e3dmaps • Discord: https://discord.gg/your-server" \ node yt update.js VIDEO ID thumbnail.png node announce.js https://www.youtube.com/watch?v=VIDEO ID "New episode: Predictive GPS for Autonomous AI Agents" Posts simultaneously to all configured platforms. Platforms with no credentials are silently skipped. | Platform | Credential s needed | |---|---| | Discord | DISCORD BOT TOKEN + DISCORD CHANNEL ID | | Telegram | TELEGRAM BOT TOKEN + TELEGRAM CHAT ID | | X Twitter | X ACCESS TOKEN | | Moltbook | MOLTBOOK API KEY | linkedin-tokens.json with person urn run node linkedin auth.js | LinkedIn's API requires a few one-time setup steps before announce.js can post there. Step 1 — Create a LinkedIn app Go to linkedin.com/developers/apps https://www.linkedin.com/developers/apps/new and create an app. Under the Auth tab, add this as an authorized redirect URL: https://www.linkedin.com/developers/tools/oauth/redirect Step 2 — Add required products Under the Products tab, request access to both: Share on LinkedIn — grants w member social scope post on behalf of user Sign In with LinkedIn using OpenID Connect — grants openid profile scopes needed to resolve your person URN Both are typically approved instantly for personal apps. Step 3 — Verify company association if prompted LinkedIn may ask you to verify a company page association. Open the verification URL while logged in as a Page Admin and approve it. Step 4 — Authorize and get tokens Add your app credentials to .env : LINKEDIN CLIENT ID=your client id LINKEDIN CLIENT SECRET=your client secret Then run: node linkedin auth.js Open the printed URL on any device. After approving, paste the redirect URL back. Tokens are saved to linkedin-tokens.json . Step 5 — Add your person URN LinkedIn's API requires your encoded person ID not your numeric member ID . To find it: - Go to your LinkedIn profile in a browser - View Page Source Cmd+U / Ctrl+U and search for urn:li:member: - Note the numeric ID e.g. 4435724 - Make a test API call — the error response will reveal your encoded person URN e.g. urn:li:person:2KqUAyg4oY Or run this one-liner after getting a token: js node -e " const https = require 'https' ; const t = JSON.parse require 'fs' .readFileSync 'linkedin-tokens.json' ; // Replace MEMBER ID with your numeric ID from page source const body = JSON.stringify {author:'urn:li:member:MEMBER ID',commentary:'test',visibility:'PUBLIC',distribution:{feedDistribution:'MAIN FEED',targetEntities: ,thirdPartyDistributionChannels: },lifecycleState:'PUBLISHED',isReshareDisabledByAuthor:false} ; const u = require 'url' .parse 'https://api.linkedin.com/rest/posts' ; const r = https.request Object.assign u,{method:'POST',headers:{'Authorization':'Bearer '+t.access token,'Content-Type':'application/json','Content-Length':Buffer.byteLength body ,'LinkedIn-Version':'202506','X-Restli-Protocol-Version':'2.0.0'}} ,res= {let d='';res.on 'data',c= d+=c ;res.on 'end', = console.log d.slice 0,300 ;} ; r.write body ;r.end ; " The error message will contain your encoded URN. Save it: js node -e " const fs = require 'fs' ; const t = JSON.parse fs.readFileSync 'linkedin-tokens.json' ; t.person urn = 'urn:li:person:YOUR ENCODED ID'; fs.writeFileSync 'linkedin-tokens.json', JSON.stringify t, null, 2 ; " Once linkedin-tokens.json contains person urn , announce.js will post to LinkedIn automatically. Copy .env.example to .env and fill in the keys you need. | Variable | Required for | Notes | |---|---|---| ASSEMBLYAI API KEY | pod2vid.py | | OPENAI API KEY pod2vid.py , tts replace.py PEXELS API KEY pod2vid.py pexels.com/api https://www.pexels.com/api/ — free DISCORD BOT TOKEN announce.js DISCORD CHANNEL ID announce.js TELEGRAM BOT TOKEN announce.js TELEGRAM CHAT ID announce.js X ACCESS TOKEN announce.js MOLTBOOK API KEY announce.js MOLTBOOK SUBMOLT announce.js agentfinance LINKEDIN CLIENT ID linkedin auth.js LinkedIn Developer Portal https://www.linkedin.com/developers/apps LINKEDIN CLIENT SECRET linkedin auth.js LINKEDIN TOKEN FILE announce.js linkedin-tokens.json — must contain person urn VOICE A tts replace.py onyx VOICE B tts replace.py nova SPEAKER A NAME pod2vid.py Host SPEAKER B NAME pod2vid.py Guest YT PRIVACY yt upload.js public / unlisted / private YT DESCRIPTION yt update.js Instead of rotating through a fixed clip library, this pipeline asks GPT-4o-mini to generate a specific Pexels search query for each utterance: "EZPass saved us 90 seconds at every toll plaza" → "toll booth highway payment" "the dual-witness problem" → "courtroom judge testimony" "machine learning position predictions" → "machine learning data training loop" Queries are cached so re-runs or TTS voice swaps don't re-spend API credits. ~82 unique clips across a 90-segment episode is typical. Python 3.8+ - Pillow = 10.0 - python-dotenv = 1.0 - ffmpeg any version — subtitle rendering does not require libfreetype/libass Node.js 18+ - dotenv External APIs - AssemblyAI diarization - OpenAI GPT-4o-mini + TTS - Pexels B-roll clips, free tier fine for personal use - YouTube Data API v3 via Google Cloud Console - LinkedIn API via LinkedIn Developer Portal https://www.linkedin.com/developers/apps — optional, for posting output/ episode.mp4 final video episode.srt subtitle file for YouTube CC episode-diarization.json cached AssemblyAI result episode-queries.json cached GPT Pexels queries broll/ cached B-roll clips one per unique query tts-cache/ cached TTS utterances per voice+text hash Built by E3D Maps https://maps.e3d.ai — AI-powered navigation for autonomous vehicles. MIT