Channel: Głęboki Odczyt | Request episode: Create issue with whitepaper link
Generate YouTube videos from NotebookLM podcasts about computer science milestone papers - covering LLMs, distributed systems, networking, operating systems, and security.
youtube-whitepapers/
├── notebooklm-automator/ # Browser automation for NotebookLM
│ ├── src/ # Python source code
│ ├── mise.toml # Task runner configuration
│ └── README.md # Full automation docs
├── scripts/ # Python automation scripts
│ ├── compress_images.py # Batch PNG compression (>threshold)
│ ├── generate_status.py # Generate status report for tracking
│ ├── generate_video.py # Generate video from concat.txt + audio
│ ├── prepare_slides.py # Extract/normalize slides from PDF
│ ├── rename_thumbnails.py # Rename thumbnails to match whitepapers
│ ├── transcribe.py # Batch transcription with Whisper
│ └── verify_video.py # Verify video quality
├── tests/ # Pytest test suite
├── mise.toml # Task runner configuration
├── whitepapers/
│ ├── distributed-computing/ # Distributed systems papers
│ ├── llm/ # LLM research papers
│ ├── networking/ # Networking protocols & systems
│ ├── operating-systems/ # OS research papers
│ └── security/ # Security research papers
└── youtube/
├── pl/ # Polish language assets
│ ├── audio/ # NotebookLM podcast audio (.m4a)
│ ├── slides/ # Presentation PDFs + extracted PNGs
│ └── transcripts/ # Whisper transcriptions (.json)
├── output/ # Final videos (.mp4) + metadata (.txt)
├── thumbnails/ # Video thumbnails (.png)
└── prompts/ # Claude Code prompt templates
Automate NotebookLM interactions via Playwright browser automation.
Setup:
cd notebooklm-automator
mise install # Install Python 3.13 + uv
mise run install # Install deps + Playwright
mise run init # Initialize Chrome profile
mise run login # Login to Google (one-time)Key features:
-
Create notebook: Auto-create from episode number (finds PDF from status.json)
mise run notebook -- 66
-
Generate audio: Start podcast generation
uv run notebooklm-automator audio generate \ --notebook-url "https://notebooklm.google.com/notebook/xxx" \ --language Polish -
Download audio: Download when ready (waits if still generating)
uv run notebooklm-automator audio download \ --notebook-url "https://notebooklm.google.com/notebook/xxx" \ --output ./audio/15-glam.m4a -
Batch download: Download all ready audio from status.json
uv run notebooklm-automator audio batch-download
-
Generate slides: Create slides PDF from prompt
uv run notebooklm-automator slides generate \ --notebook-url "https://notebooklm.google.com/notebook/xxx" \ --prompt-file ./prompts/slides.md \ --output ./slides/15-glam.pdf
See notebooklm-automator/README.md for full documentation.
Batch download papers from curated lists:
# See template for input format
cat future/future-template.md
# Dry run to preview
mise run download -- future/my-papers.md --dry-run
# Download papers with auto-indexing
mise run download -- future/my-papers.mdFeatures:
- Auto-assigns episode numbers (fills gaps in status.json)
- Checks for duplicates by name/URL
- Tries multiple sources (arXiv, OpenReview, direct URL)
- Verifies PDF validity
- Updates status.json automatically
- Reports failed downloads for manual intervention
Export podcast from NotebookLM and save to audio/:
audio/XX-paper-name.m4a
Naming convention: {number}-{paper-name}.m4a (e.g., 02-gpt.m4a)
Run batch transcription (uses Whisper, Polish language):
mise run transcribe
# Or with custom parallelization:
mise run transcribe -- 4Output: youtube/pl/transcripts/XX-paper-name.json
# Read transcript and create slide prompt
cat prompts/generate-slides-command.mdRun in fresh Claude Code session:
Read the transcript file at youtube/pl/transcripts/XX-paper-name.json
and create a detailed NotebookLM prompt for generating 10 presentation slides.
Then paste the generated prompt into NotebookLM to create slides PDF.
Save slides to: slides/XX-paper-name.pdf
Generate thumbnail in NotebookLM or externally.
Save to: youtube/thumbnails/XX-paper-name.png
Rename numeric thumbnails to match whitepaper names and compress:
mise run rename-thumbnails # Rename + compress to <1.9MB
mise run rename-thumbnails --dry-run # Preview changesmise run prepare -- 28 # Extracts PDF, normalizes imagesUse the template script which automatically handles intro/outro:
# Provide content slide durations (thumbnail/outro added automatically)
mise run generate-concat -- 28 --durations slide-01:180,slide-02:150,slide-03:120
# Preview without writing:
mise run generate-concat -- 28 --durations ... --dry-run
# From JSON file:
mise run generate-concat -- 28 --json timings.jsonThe script automatically adds:
- 5s thumbnail intro (duplicated to avoid ffmpeg drop bug)
- Your content slides with specified durations
- 5s silent outro with last-slide.png
mise run verify-concat -- 28 --check-dimsmise run video -- 28
# Or skip verification:
mise run video -- 28 --skip-verify/generate-video 28This runs the full video generation workflow with proper timings.
Final files in output/:
XX-paper-name.mp4- Video fileXX-paper-name-metadata.txt- Title, description, tags for YouTube
All files must follow: {XX}-{paper-name} where:
XX= episode number with leading zeros (01, 02, ... 99, 100+)paper-name= lowercase, hyphenated paper identifier
Examples:
01-attention-is-all-you-need73-raft130-trusting-trust200-pastry
- whisper - Audio transcription (
pip install openai-whisper) - ffmpeg - Video generation (
brew install ffmpeg) - poppler - PDF to PNG (
brew install poppler) - imagemagick - Image processing (
brew install imagemagick) - jq - JSON processing (
brew install jq)
Install Python dependencies:
mise run installAutomated upload requires Google Cloud OAuth credentials.
- Go to Google Cloud Console
- Create new project or select existing
- Enable YouTube Data API v3:
- APIs & Services → Library → search "YouTube Data API v3" → Enable
- APIs & Services → Credentials → Create Credentials → OAuth client ID
- Application type: Desktop app
- Download JSON → rename to
client_secret.json - Move to
.youtube-credentials/client_secret.json
Set playlist IDs via environment variables:
export YOUTUBE_PLAYLIST_LLM="PLxxxxxxxxxx"
export YOUTUBE_PLAYLIST_DISTRIBUTED_COMPUTING="PLyyyyyyyyyy"
export YOUTUBE_PLAYLIST_SECURITY="PLzzzzzzzzzz"
export YOUTUBE_PLAYLIST_NETWORKING="PLaaaaaaaaa"
export YOUTUBE_PLAYLIST_OPERATING_SYSTEMS="PLbbbbbbbbb"Or create youtube/config.json:
{
"playlists": {
"llm": "PLxxxxxxxxxx",
"distributed-computing": "PLyyyyyyyyyy",
"security": "PLzzzzzzzzzz",
"networking": "PLaaaaaaaaa",
"operating-systems": "PLbbbbbbbbb"
}
}mise run upload -- 28 # Upload episode 28 (private)
mise run upload -- 28 --dry-run # Validate without uploading
mise run upload -- 28 --privacy unlistedFirst run opens browser for Google OAuth. Token saved to .youtube-credentials/token.json.
# 1. Transcribe new audio
mise run transcribe
# 2. Prepare slides for episode
mise run prepare -- 28
# 3. Run Claude Code slash command (or generate video manually)
# /generate-video 28
mise run video -- 28
# 5. Upload to YouTube:
# - Video: youtube/output/28-paper-name.mp4
# - Metadata: youtube/output/28-paper-name-metadata.txt
# - Thumbnail: youtube/thumbnails/28-paper-name.png