# ASCILINE YouTube Streamer Plays any YouTube video (live or VOD) as a real-time ASCII art stream in a web browser. Designed for the Tesla in-car browser, works in any Chromium browser. Runs directly on a Mac mini — no Docker. ## Requirements - macOS (Apple Silicon recommended) - Python 3.11+ - ffmpeg (Homebrew) ## Setup ```bash brew install ffmpeg python3 -m venv .venv source .venv/bin/activate pip install -r requirements.txt ``` Or run `./run.sh` once and it does the same thing idempotently, prompts for a PIN, and starts the server. ## Running ```bash source .venv/bin/activate python server.py --pin 1234 ``` The server listens on `http://0.0.0.0:8000` by default. Open it, enter the PIN, then type a YouTube URL, video ID, or search query and press Play. | Flag | Default | Description | |------|---------|-------------| | `--pin` | required | 4–8 digit PIN (or `ASCIILINE_PIN` env var) | | `--port` | 8000 | TCP port to listen on | | `--bind` | 0.0.0.0 | Bind address | | `--max-source-height` | 720 | Maximum source video resolution | ## Quality presets | Preset | Grid | FPS | Notes | |--------|------|-----|-------| | **Low** (default) | 120×50 | 24 | Tesla / target | | **Med** | 160×68 | 30 | Desktop | | **High** | 200×84 | 30 | Desktop | LOW is the only preset that's been verified clean on the M4 reference (FPS 24/24, JIT ~42 ms). MED/HIGH are paint-bound on the per-cell `fillText` loop on the M4 — they work, but with visible jitter. The Tesla in-car browser runs LOW. Preset changes take effect on the next Play (server restarts ffmpeg with the new dimensions). ## Architecture ``` Browser Mac mini ───────── ──────────────────────────────────────── GET / ──▶ static/index.html (or pin.html if no cookie) POST /api/auth ──▶ HMAC cookie issued (Secure only on https) POST /api/play ──▶ resolver.py (yt-dlp) → ResolvedMedia pipeline.py spawns ffmpeg ×2 (video + audio) WS /ws/video ◀── INIT message + binary ASCILINE frames GET /audio ◀── AAC/ADTS chunked stream ``` Wire format and rendering are vendored from [ASCILINE](https://github.com/YusufB5/ASCILINE). See `vendor/asciline/PROTOCOL-NOTES.md`. ### Pipeline pacing ffmpeg is invoked with `-re -fps_mode cfr -r ` so frames arrive evenly paced at the target rate. The consumer is then a trivial drain — read from the queue, encode, send. Earlier iterations tried consumer-side pacing (relative sleep, absolute targets, skip-ahead, re-anchor) and all failed the same way: a bursty producer cannot be smoothed by any consumer-side scheme. Source-side pacing fixed it in one flag. ## Cloudflare Tunnel The service is designed to run behind a Cloudflare Tunnel for internet access from the Tesla: ```bash brew install cloudflare/cloudflare/cloudflared cloudflared tunnel login cloudflared tunnel create asciline cloudflared tunnel route dns asciline yourdomain.com cloudflared tunnel run asciline ``` The PIN brute-force lockout uses `CF-Connecting-IP`, so each car/device is locked out independently — one device's 5 bad attempts does not lock out the others. The auth cookie is marked `Secure` only when the auth request itself came in over https. Over plain `http://localhost` (local dev) the cookie is set without `Secure` so the browser will actually send it on the `/audio` GET; otherwise audio would silently 403 while video kept working. ## Tesla browser notes - **Audio** is AAC/ADTS via `