Skip to content

Repository files navigation

YTAutomation — Autonomous Stoic Shorts Pipeline

Production-grade Text-to-Video system that transforms Stoic philosophy into viral YouTube Shorts (1080×1920, 30-60s) with professional effects, karaoke subtitles, background music, and automatic YouTube upload.


��� User Guidelines — Start Here

What This System Does

Fully automated pipeline: Topic → Script → Audio → Images → Video → YouTube Upload

  1. Generates trending Stoic topics (scroll-stopping hooks)
  2. Writes viral scripts with SEO metadata (title, description, hashtags, tags)
  3. Produces cinematic voiceover with dramatic pauses (hook → quote → elaboration → CTA)
  4. Creates 5-7 AI-generated vertical images
  5. Assembles professional video with:
    • Karaoke word-by-word subtitles (2 rows, amber highlight)
    • 6 visual effects (Ken Burns, grain, glow, flakes, rays)
    • Pinned background music + thunderstorm overlay
    • Fade-to-black ending
  6. Uploads privately to YouTube with SEO metadata

Quick Start (First-Time Users)

```bash

1. Install dependencies

pip install -r requirements.txt

2. Configure Gemini chat URLs in config.py

Create two separate Gemini chat threads:

3. Configure Chrome profile path in config.py

CHROME_PROFILE_DIR = r"D:\SeleniumChromeProfile"

4. Set up YouTube OAuth (for upload)

- Enable "YouTube Data API v3"

- Create OAuth 2.0 credentials (Desktop app)

- Download JSON → save as client_secret.json in project root

5. Validate setup

python validate_setup.py

6. Generate your first video (inc. upload)

python main.py --new ```

First upload: Browser opens once for Google consent → tokens cached → never asks again.

Daily Usage

```bash

Generate one complete video (topic → upload)

python main.py --new

Upload all rendered videos (status RENDER)

python upload.py

Upload specific video

python upload.py --id 12

Check what would be uploaded (dry run)

python upload.py --dry-run

View all projects

python main.py --status

Resume any interrupted project

python main.py ```

Expected Output

Per video directory: `output/video_N/` ``` voiceover.wav # 4-part narration with pauses voiceover.timestamps.json # Word-level timing for karaoke images/ ├── scene_1.png # 5-7 AI images (count varies) ├── scene_2.png └── ... short_N.mp4 # Final 1080×1920 video ```

YouTube: Private video uploaded with:

  • Title (from LLM)
  • Description with hashtags (from LLM)
  • 8-12 SEO tags (from LLM)

Customization Quick Reference

What to Change Where Examples
Voice speed/pitch config.py `VOICE_SPEED = 0.86`, `VOICE_PITCH_SEMITONES = -1.0`
Pause durations config.py `HOOK_PAUSE_SEC = 0.50`, `QUOTE_PAUSE_SEC = 0.85`
Background music volume config.py `BG_MUSIC_VOLUME = 0.15`
Caption styling config.py `CAPTION_FONTSIZE = 52`, `CAPTION_COLOR = "white"`
Video effects engines/hands.py `_build_scene_with_effects()`
Script prompts prompts.py `SCRIPT_SYSTEM`, `IMAGE_PROMPT_TEMPLATE`
Image count (5-7) prompts.py LLM decides count in script

Troubleshooting Common Issues

Problem Solution
Chrome crashes / locks Close all Chrome windows, delete profile lock files
Gemini input not found Verify logged into Gemini, check chat URLs are correct
YouTube consent every time First upload only; tokens cached in `yt_token.json`
TTS voice quality Adjust `VOICE_SPEED`, `VOICE_PITCH_SEMITONES` in config.py
Images too dark Lower `DARKEN_FACTOR` in config.py (default 0.85)
No background music Place `_pinned_bgm.m4a` in `assets/music/`
Upload fails Check `client_secret.json` exists, API enabled in Google Cloud

���️ System Architecture

Project Structure

``` YTAutomation/ ├── config.py # Central settings (URLs, audio, video, effects) ├── database.py # SQLAlchemy ORM + pipeline state machine ├── prompts.py # All Gemini prompt templates ├── main.py # CLI orchestrator with state machine ├── upload.py # Batch YouTube upload script ├── batch.py # Generate multiple videos sequentially ├── validate_setup.py # Pre-flight environment checks ├── requirements.txt # All Python dependencies ├── client_secret.json # YouTube OAuth credentials (user-provided) ├── yt_token.json # Cached YouTube tokens (auto-created) ├── videos.db # SQLite state database (auto-created) └── engines/ ├── browser.py # Shared Selenium helpers (Chrome automation) ├── brain.py # Topic + script generation (Gemini) ├── voice.py # 4-part TTS with pauses (Kokoro + faster-whisper) ├── eyes.py # Image generation + download (Gemini) ├── hands.py # Video assembly + effects (MoviePy) └── uploader.py # YouTube upload (Data API v3) ```

The 6 Engines

1. Brain (Topic + Script Generation)

  • Connects to your Gemini script chat URL
  • Step A: Generates trending Stoic topic with scroll-stopping hook
  • Step B: Writes complete script JSON: ```json { "title": "YouTube title with emoji", "hook": "Scroll-stopping opening line", "quote": "The main Stoic quote", "elaboration": "3-4 sentences expanding the quote", "cta": "Closing call-to-action", "image_prompts": ["5-7 vertical image descriptions"], "description": "SEO description with #hashtags", "tags": ["8-12", "seo", "keywords"] } ```
  • Output: Status `SCRIPT`, full JSON stored in DB

2. Voice (TTS + Cinematic Audio)

  • Uses local Kokoro ONNX (am_adam voice, deep narrator)
  • 4-part structure with pauses:
    1. Hook → `HOOK_PAUSE_SEC` (0.50s) →
    2. Quote → `QUOTE_PAUSE_SEC` (0.85s) →
    3. Elaboration → `ELAB_PAUSE_SEC` (0.60s) →
    4. CTA
  • Applies deliberate slowdown: `VOICE_SPEED = 0.86` + pitch drop `-1.0 semitones`
  • Generates word-level timestamps via faster-whisper for karaoke sync
  • Output: `voiceover.wav` + `voiceover.timestamps.json` + status `AUDIO`

3. Eyes (Image Generation + Download)

  • Connects to your Gemini image chat URL
  • Processes 5-7 image prompts (count decided by LLM in script)
  • For each prompt:
    • Sends to Gemini image generator
    • Polls DOM for new `` tag (baseline snapshot technique)
    • Upgrades URL to max resolution (`=s1920`)
    • Validates file size > 50 KB
    • Downloads as PNG
  • Output: 5-7 PNG files in `images/` folder + status `VISUALS`

4. Hands (Video Assembly + Effects)

Uses MoviePy for professional video production:

Core Assembly:

  • Resizes images to 1080×1920 (center crop)
  • Divides audio by image count for timing
  • Applies 1-second crossfade transitions

Visual Effects (randomized per scene):

  1. Ken Burns — 7 zoom/pan presets (organic movement)
  2. Flakes — particle overlay (snow/dust effect)
  3. Rays — light beam overlay (divine/epic feel)
  4. Glow — radial brightness (soft focus)
  5. Grain — film texture (vintage authenticity)

Global Effects:

  • Vignette (darkened edges)
  • Color grading (darken by 0.85× for text contrast)

Karaoke Subtitles:

  • 2 rows × 3 words (6 words visible)
  • Active word: amber (#ff8c00) highlight
  • Other words: white text with black shadow
  • Word-synced using faster-whisper timestamps
  • All words always visible (no appear/disappear)

Audio:

  • Pinned BGM: `assets/music/_pinned_bgm.m4a` (starts 12s in, volume 15%)
  • Thunder overlay: `assets/music/_thunder.m4a` (volume 2.2%)

Ending:

  • Fade-to-black: Last 1.2 seconds

Output: `short_N.mp4` (1080×1920, 30fps, H.264, ~10Mbps) + status `RENDER`

5. Uploader (YouTube Upload)

  • Uses YouTube Data API v3 with OAuth 2.0
  • Extracts metadata from script JSON:
    • Title
    • Description (with hashtags)
    • Tags (8-12 keywords)
  • Uploads in private mode (change `privacy="private"` in code if needed)
  • First run: Opens browser for Google consent (tokens cached to `yt_token.json`)
  • Subsequent runs: Auto-refreshes tokens silently (no browser)
  • Output: YouTube video ID + status `UPLOAD`

6. Manager (Orchestrator)

  • CLI entry point (main.py)
  • State machine: `TOPIC → SCRIPT → AUDIO → VISUALS → RENDER → UPLOAD`
  • Crash-proof: resumes from last completed stage
  • Rich console output with colored progress tables
  • Timestamped logging to `logs/` directory

���️ Database Schema

SQLite table: `videos`

Column Type Description
`id` INTEGER Primary key (auto-increment)
`topic` STRING The Stoic topic chosen
`title` STRING YouTube video title
`script_json` TEXT Full JSON from Brain engine
`quote` TEXT Main Stoic quote
`elaboration` TEXT 3-4 sentence expansion
`image_prompts` TEXT JSON array of image descriptions
`audio_path` STRING Path to `voiceover.wav`
`image_paths` TEXT JSON array of PNG paths
`video_path` STRING Path to final MP4
`youtube_id` STRING YouTube video ID (after upload)
`status` ENUM `TOPIC`, `SCRIPT`, `AUDIO`, `VISUALS`, `RENDER`, `UPLOAD`, `FAILED`
`error_msg` TEXT Human-readable error if `FAILED`
`created_at` DATETIME Project creation timestamp
`updated_at` DATETIME Last modification timestamp

Key Functions:

  • `get_next_pending()` — Returns oldest unfinished project
  • `get_last_n_titles(n)` — For topic deduplication

��� Filesystem Layout

Auto-created on first run:

``` YTAutomation/ ├── output/ │ ├── video_1/ │ │ ├── voiceover.wav │ │ ├── voiceover.timestamps.json │ │ ├── images/ │ │ │ ├── scene_1.png │ │ │ ├── scene_2.png │ │ │ └── ... (5-7 total) │ │ └── short_1.mp4 ← FINAL VIDEO │ └── video_2/ ... ├── assets/ │ ├── fonts/ (reserved for custom fonts) │ ├── music/ │ │ ├── _pinned_bgm.m4a ← YOUR background music │ │ └── _thunder.m4a ← Thunderstorm overlay │ └── kokoro/ │ └── kokoro-v1.0.onnx (TTS model, auto-downloaded) ├── logs/ │ └── run_20260221_143055.log ← Timestamped logs ├── temp/ (temporary processing files) └── videos.db ← SQLite database ```


⚙️ Configuration Deep-Dive

Audio Settings (config.py)

```python VOICE_MODEL = "am_adam" # Deep male narrator VOICE_SPEED = 0.86 # Slower, deliberate pacing VOICE_PITCH_SEMITONES = -1.0 # Deeper tone SAMPLE_RATE = 24000 # Kokoro default

Dramatic pauses

HOOK_PAUSE_SEC = 0.50 # After scroll-stopping hook QUOTE_PAUSE_SEC = 0.85 # After main quote (most dramatic) ELAB_PAUSE_SEC = 0.60 # Before closing CTA

Background audio

BG_MUSIC_VOLUME = 0.15 # 15% volume (audible but not overpowering) ```

Video Effects (config.py)

```python CROSSFADE_DURATION = 1.0 # Transition between scenes DARKEN_FACTOR = 0.85 # Color grading (0-1, lower = darker) ZOOM_RANGE = (1.0, 1.12) # Ken Burns zoom range PAN_DRIFT_PX = 60 # Max camera drift pixels FADE_TO_BLACK = 1.2 # End-of-video fade duration ```

Caption Styling (config.py)

```python CAPTION_FONT = "Georgia" # Serif for classic feel CAPTION_FONTSIZE = 52 # Large, readable CAPTION_COLOR = "white" # Main text color CAPTION_STROKE_CLR = "black" # Shadow/outline color CAPTION_STROKE_W = 3 # Shadow thickness CAPTION_FADE_DUR = 0.6 # Fade in/out duration

Karaoke-specific (in engines/hands.py)

WORDS_PER_KARAOKE_LINE = 6 # Total words visible WORDS_PER_ROW = 3 # 2 rows × 3 words KARAOKE_ACTIVE_COLOR = "#ff8c00" # Amber highlight ```

YouTube Settings (config.py)

```python YT_CLIENT_SECRET = BASE_DIR / "client_secret.json" YT_TOKEN_FILE = BASE_DIR / "yt_token.json" YT_CATEGORY_ID = "22" # 22 = People & Blogs ```


��� Visual Effects Explained

Per-Scene Effects (Randomized)

Each scene gets one random effect from this pool (never repeats consecutively):

  1. Flakes — Particle overlay (falling snow/dust)

    • Density: 40-60 particles
    • Opacity: 60-80%
    • Movement: Vertical drift with random horizontal
  2. Rays — Light beam overlay (divine/epic atmosphere)

    • 8-12 radial beams from top
    • Animated rotation
    • Soft blend mode
  3. Glow — Radial soft-focus effect

    • Center brightness boost
    • Gaussian blur overlay
    • Creates dreamy, ethereal look
  4. Grain — Film texture overlay

    • Per-frame noise (not static)
    • Intensity: 10-15 units
    • Adds analog authenticity

Global Effects (All Scenes)

  1. Ken Burns Zoom + Pan

    • 7 randomized presets (zoom in/out + drift directions)
    • Smooth easing curves
    • Creates organic, non-static feel
  2. Vignette

    • Radial darkening from center outward
    • Focuses viewer attention
    • Adds cinematic moodiness
  3. Color Grading

    • Global darken by 0.85× (configurable)
    • Increases white text legibility
    • Professional color correction

Karaoke Subtitle System

Layout: ``` [word1] [word2] [word3] ← Row 1 [word4] [word5] [word6] ← Row 2 ```

Behavior:

  • 6 words always visible (2 rows × 3 words)
  • Active word (currently spoken): Amber (#ff8c00) with glow
  • Inactive words (context): White with black shadow
  • Synced to word-level timestamps from faster-whisper
  • Smooth transitions as speaking progresses

Why this works:

  • Viewer always has context (see upcoming words)
  • Amber highlight guides eye to current word
  • Feels like karaoke, not robotic subtitles

��� Advanced Usage

Batch Upload All Rendered Videos

```bash

Upload all videos at RENDER status

python upload.py

Upload specific video

python upload.py --id 12

Preview what would be uploaded (no action)

python upload.py --dry-run ```

Resume Interrupted Pipeline

```bash

Automatically picks up last incomplete project

python main.py

Or force start a new one

python main.py --new ```

Inspect Database Directly

```bash

Using Python

python -c " from database import get_session, Video s = get_session() for v in s.query(Video).order_by(Video.id.desc()).limit(5): print(f'{v.id}: {v.title} [{v.status.value}]') s.close() "

Or use any SQLite browser (DBeaver, DB Browser for SQLite)

File: videos.db

```

Change Gemini URLs Mid-Project

Edit config.py, save. Existing projects continue with old URLs; new projects use updated URLs.

Replace Background Music

  1. Place your audio file in `assets/music/_pinned_bgm.m4a`
  2. Downloads will use this file automatically
  3. Adjust volume: `BG_MUSIC_VOLUME` in config.py

Add Custom Fonts

  1. Place `.ttf` file in `assets/fonts/`
  2. Update `CAPTION_FONT` in config.py to filename (without extension)
  3. Restart pipeline

��� Troubleshooting

Chrome Issues

Problem: `SessionNotCreatedException` or Chrome crashes

Solutions:

  1. Fully close all Chrome windows (check Task Manager)
  2. Delete lock files in `CHROME_PROFILE_DIR`:
    • `Default/Lockfile`
    • `SingletonLock`
  3. browser.py auto-kills processes, but manual close is safer
  4. Ensure `CHROME_PROFILE_DIR` exists and is not used elsewhere

Gemini Issues

Problem: `RuntimeError: Could not locate Gemini chat input`

Solutions:

  1. Open each Gemini URL in Chrome manually
  2. Verify you're logged into Google/Gemini
  3. Ensure text input box is visible

Problem: Image generation times out

Solutions:

  1. Increase `ELEMENT_WAIT` in config.py (default 45s)
  2. Check Gemini chat manually — is image generation working?

TTS Issues

Problem: `ModuleNotFoundError: kokoro-onnx`

Solution: ```bash pip install kokoro-onnx soundfile scipy faster-whisper ```

Problem: Voice sounds robotic

Solutions:

  1. Increase `VOICE_SPEED` (closer to 1.0 = faster but less robotic)
  2. Adjust `VOICE_PITCH_SEMITONES` (try 0 or -0.5)

Video Rendering Issues

Problem: MoviePy rendering hangs at 0%

Solutions:

  1. Install `ffmpeg` if not already:
    • Windows: `choco install ffmpeg`
    • Mac: `brew install ffmpeg`
    • Linux: `apt install ffmpeg`

Problem: Video has no background music

Solutions:

  1. Ensure `_pinned_bgm.m4a` exists in `assets/music/`
  2. Check volume: `BG_MUSIC_VOLUME` in config.py

YouTube Upload Issues

Problem: Upload always asks for consent

Solution: This should only happen once. Check:

  1. `yt_token.json` was created after first consent
  2. File has valid credentials (JSON format)

Problem: `FileNotFoundError: client_secret.json`

Solution:

  1. Create Google Cloud project at https://console.cloud.google.com
  2. Enable "YouTube Data API v3"
  3. Create OAuth 2.0 credentials (Desktop application type)
  4. Download JSON → save as `client_secret.json` in project root

Problem: Upload fails with `403 Forbidden`

Solutions:

  1. Check YouTube Data API v3 is enabled in Google Cloud Console
  2. Verify OAuth credentials are for correct Google account

��� Performance & Optimization

Expected Timings (Per Video)

Stage Duration Notes
Brain (topic + script) 1-2 min Depends on Gemini response speed
Voice (TTS + timestamps) 30-60s Faster-whisper can be slow on first run
Eyes (5-7 images) 3-5 min ~30s per image from Gemini
Hands (assembly + effects) 2-4 min Depends on CPU; GPU doesn't help
Upload (YouTube) 1-2 min Depends on file size + internet
Total 8-15 min End-to-end automation

��� Dependencies

Core:

  • `selenium` + `undetected-chromedriver` — Gemini automation
  • `kokoro-onnx` — Local TTS
  • `faster-whisper` — Word-level timestamps for karaoke
  • `moviepy` — Video assembly
  • `sqlalchemy` — State management
  • `Pillow` — Caption rendering
  • `rich` — Pretty CLI output

YouTube:

  • `google-api-python-client` — YouTube Data API v3
  • `google-auth-httplib2`, `google-auth-oauthlib` — OAuth 2.0

See requirements.txt for complete list


��� Privacy & Security

  • Local TTS: No audio sent to cloud (Kokoro runs offline)
  • YouTube tokens: Stored locally in `yt_token.json` (gitignored)
  • OAuth credentials: `client_secret.json` is user-provided (not in repo)

Important:

  • Do NOT commit `client_secret.json` or `yt_token.json` to version control
  • Do NOT share your Gemini chat URLs (they contain session tokens)

���️ Development

File-by-File Guide

File Lines Purpose
config.py ~125 Central settings
database.py ~113 ORM + state machine
prompts.py ~120 Gemini prompts
main.py ~273 Pipeline orchestrator
upload.py ~135 Batch YouTube upload
engines/browser.py ~240 Selenium automation
engines/brain.py ~170 Script generation
engines/voice.py ~255 TTS with pauses
engines/eyes.py ~250 Image download
engines/hands.py ~819+ Video assembly + effects
engines/uploader.py ~203 YouTube upload engine

���️ Roadmap

Completed:

  • ✅ Full pipeline automation
  • ✅ 4-part TTS with pauses
  • ✅ Karaoke word-sync subtitles
  • ✅ Dynamic image count (5-7)
  • ✅ Per-scene effects + BGM
  • ✅ YouTube upload with SEO metadata

Future Enhancements:

  • Batch upload scheduling
  • Thumbnail generation
  • Instagram Reels export
  • TikTok upload integration
  • Analytics dashboard

Generated: February 21, 2026
Status: ✅ Production-Ready

For detailed build history, see BUILD_STATUS.md

About

Autonomous content pipeline, built using a multi-agent system to handle scripting, voice synthesis, and video assembly, to content delivery on youtube

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages