A completely free ($0), cloud-hosted system that generates and uploads YouTube Shorts twice daily using GitHub Actions. Uses YouTube Data API v3 with OAuth refresh tokens (no fragile cookies, lasts[...]
| Step | Component | Service | Cost |
|---|---|---|---|
| Scripts | You provide them | Any AI (ChatGPT/Gemini) in your browser | Free |
| Voiceover | Microsoft Edge TTS | edge-tts |
Free (no API key) |
| Video clips | Pexels API | requests → Pexels |
Free tier |
| Video composition | MoviePy 1.0.x | ffmpeg + ImageMagick | Free |
| Upload | YouTube Data API v3 | OAuth refresh token (official API) | Free (3,200 of 10,000 daily quota) |
| Scheduling | GitHub Actions | Cron (2× daily) | Free (2000+ min/month) |
.
├── main.py # Orchestrator — runs the 5-stage pipeline
├── auth_setup.py # One-time local script to get OAuth tokens
├── requirements.txt # Python dependencies
├── SETUP-GUIDE.txt # Step-by-step setup instructions
├── README.md # This file
├── scripts/
│ └── scripts.json # Pre-generated scripts (you provide these)
├── src/
│ ├── __init__.py
│ ├── reader.py # Reads scripts.json, picks next unused script
│ ├── audio.py # edge-tts → MP3 voiceover
│ ├── downloader.py # Pexels API → video clips
│ ├── editor.py # MoviePy → 9:16 video, subtitles, composition
│ └── uploader.py # YouTube Data API v3 (OAuth) upload
└── .github/workflows/
└── schedule.yml # GitHub Actions: cron schedule + env config
| Secret | Description |
|---|---|
PEXELS_API_KEY |
Pexels API key (free at pexels.com/api) |
YT_REFRESH_TOKEN |
YouTube OAuth refresh token (get via auth_setup.py) |
YT_CLIENT_ID |
Google Cloud OAuth client ID |
YT_CLIENT_SECRET |
Google Cloud OAuth client secret |
- Google Cloud Console — create project → enable YouTube Data API v3 → create OAuth 2.0 Desktop credentials
- Run
auth_setup.pylocally — paste client ID & secret → sign in to Google → get refresh token - Add 4 GitHub Secrets —
PEXELS_API_KEY,YT_REFRESH_TOKEN,YT_CLIENT_ID,YT_CLIENT_SECRET - Generate scripts — copy the prompt from
SETUP-GUIDE.txt→ paste to ChatGPT/Gemini → save output asscripts/scripts.json - Run workflow — go to Actions → "Run workflow"
Detailed instructions: read SETUP-GUIDE.txt
[
{
"id": 1,
"title": "Catchy title under 100 chars",
"description": "Description with hashtags",
"tags": ["tag1", "tag2"],
"voiceover_script": "Script under 150 words (~50 seconds)",
"visual_keywords": ["kw1", "kw2", "kw3", "kw4", "kw5"],
"used": false
}
]- read —
ScriptReaderpicks the first unused script, marks it used, pushes back - audio —
edge-ttsconverts script to MP3, returns duration - download — Pexels searched per keyword; 4-5 portrait clips streamed
- compose — MoviePy crops to 1080×1920, concatenates, adds subtitles, renders with
ultrafast/threads=2 - upload — Gets fresh access token from refresh token → uploads via YouTube Data API v3
When all scripts are used → prints "All N scripts used. Generate fresh ones." and exits cleanly.
| Cookies (old) | OAuth Refresh Token (new) | |
|---|---|---|
| Lifespan | Days to weeks | Years (until revoked) |
| GitHub setup | Playwright + Chromium (~300MB) | Zero browser deps |
| Upload method | Browser automation (fragile UI) | Official API (never breaks) |
| Maintenance | Re-export cookies every few days | Set up once, forget forever |
| Quota | Unlimited (but unreliable) | 10,000 units/day (3,200 used) |
| Speed | ~15 min total | ~10 min (no browser launch) |
edge-tts>=6.1.0 # Microsoft Edge TTS
moviepy>=1.0.3,<2.0.0 # Video editing
Pillow>=10.0.0 # Image processing
requests>=2.31.0 # HTTP client for Pexels + YouTube API
google-auth>=2.0.0 # OAuth token refresh