# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## What This Is

Automated YouTube Shorts bot that generates "What If?" speculative scenario videos. A single Python script (`bot/generate_short.py`, ~1300 lines) handles the entire pipeline: concept selection → TTS voiceover → stock video/image → FFmpeg assembly → captioned output. Runs via cron every 12 hours.

## Commands

```bash
# Initialize environment (Xvfb, Chrome, verify dependencies)
bash init.sh

# Production run (picks random unused concept, outputs to output/)
export DISPLAY=:50 && /bin/python3.12 bot/generate_short.py

# Dry run (builds video in /tmp but doesn't save to output/ or update DB)
export DISPLAY=:50 && /bin/python3.12 bot/generate_short.py --dry-run

# Force a specific concept
export DISPLAY=:50 && /bin/python3.12 bot/generate_short.py --concept ocean_transparent

# Via cron wrapper (sets DISPLAY and Python path automatically)
bash bot/run.sh

# Syntax check after edits
/bin/python3.12 -m py_compile bot/generate_short.py

# Check latest execution log
tail -50 logs/run.log

# Check production database
sqlite3 bot/state.db "SELECT * FROM produced;"
sqlite3 bot/state.db "SELECT * FROM runs ORDER BY run_id DESC LIMIT 10;"
```

## Architecture

**Single-file pipeline** in `bot/generate_short.py`. No pip dependencies — uses only stdlib plus Selenium (system-installed).

### Pipeline Flow (produce_short)
1. **Voiceover**: ElevenLabs TTS via Selenium browser automation + Firebase auth token API download. Falls back to silent audio.
2. **Scene clips** (4 per short): Try stock video first (Pexels → Pixabay, portrait then landscape), fall back to AI image (Pollinations → ImageMagick placeholder) + Ken Burns zoom.
3. **Assembly**: FFmpeg concatenates hook (3s) + scene clips + endcard (3s), burns ASS subtitles, muxes voiceover audio.
4. **Output**: `output/short_NNNN.mp4` + `_thumb.jpg` + `.txt` metadata.

### Video Source Design
Stock video clips are fetched via HTTP APIs (Pexels and Pixabay) — no browser automation needed. Each concept scene has a `video_keywords` list of 2-3 search phrases tried in order. The orchestrator tries portrait/vertical orientation first, then falls back to landscape + center-crop. Used video IDs are tracked per-run to avoid duplicates across scenes.

### Browser Automation
Selenium is only used for ElevenLabs TTS. Chrome connects via remote debugging port 9222, running headless on Xvfb display `:50` with profile at `/home/melon/.chrome_whatif_profile`.

### Fallback Chain Design
Both image and video generation use `_failed_sources` / `_failed_video_sources` sets to skip sources that already failed during the same run.

## Video Specs
- **Resolution**: 1080×1920 (WIDTH/HEIGHT constants), 9:16 portrait
- **FPS**: 30
- **Duration**: ~30s (3s hook + variable scenes + 3s endcard)
- **Fonts**: Oswald-Bold (titles), Inter-Bold (body) at `/root/.fonts/chess_shorts/`

## API Keys
- **Pexels**: Free tier, 200 req/hr. Key stored as `PEXELS_API_KEY` constant.
- **Pixabay**: Free tier, 100 req/min. Key stored as `PIXABAY_API_KEY` constant.

## Database
SQLite at `bot/state.db` with two tables:
- **produced**: tracks completed shorts (id, title, produced_at, output_file, duration_sec)
- **runs**: execution log (run_id, run_at, concept_id, status, note)

Concepts are picked randomly from 12 hardcoded entries. Once all are produced, the selection resets.

## Key Gotchas
- Chrome must be running with `--remote-debugging-port=9222` before the script starts. `init.sh` or `run.sh` handle this.
- Pollinations.ai is the fallback image source (free, no auth, HTTP GET).
- Stock video from Pexels/Pixabay is center-cropped to 1080×1920 via `prepare_video_clip()`.
- The `crop_watermark` parameter on `prepare_video_clip()` defaults to False (stock video has no watermarks).
- Log output goes to `logs/run.log` via the `log()` function, not stdout.
- Cron is currently PAUSED. Re-enable after verifying output quality.
