Initial commit: ASCILINE YouTube Streamer

ASCII-art YouTube streaming for the Tesla in-car browser.

- FastAPI server on a Mac mini, no Docker.
- yt-dlp resolver: ID/URL/search.
- ffmpeg with -re -fps_mode cfr for source-paced video; trivial drain
  consumer.  Separate ffmpeg for AAC/ADTS audio.
- Vendored ASCILINE renderer (MIT) for the binary wire protocol; pure
  fillText color path, on-demand selection flush.
- HMAC PIN-gated cookie; Secure flag scheme-aware so /audio works on
  plain http during local dev.
- LOW preset (120x50 24fps) verified clean on M4: FPS 24/24, JIT ~42ms.
This commit is contained in:
Erhan Keseli
2026-06-13 18:05:19 +02:00
commit 74f49c7712
21 changed files with 4127 additions and 0 deletions

84
static/index.html Normal file
View File

@@ -0,0 +1,84 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>ASCILINE</title>
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Barlow+Condensed:wght@400;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/static/style.css">
</head>
<body>
<!-- ── TOP BAR ─────────────────────────────────────────────────── -->
<header id="topbar">
<div id="brand">ASCILINE</div>
<input
id="query-input"
type="text"
autocomplete="off"
autocorrect="off"
autocapitalize="off"
spellcheck="false"
placeholder="Video ID, URL or search…"
aria-label="Video query"
>
<button id="btn-play" class="btn btn-primary" aria-label="Play">
<span class="btn-icon"></span>
<span class="btn-label">PLAY</span>
</button>
<button id="btn-stop" class="btn btn-secondary" aria-label="Stop" disabled>
<span class="btn-icon"></span>
<span class="btn-label">STOP</span>
</button>
</header>
<!-- ── SUB BAR ─────────────────────────────────────────────────── -->
<div id="subbar">
<div id="preset-group" role="group" aria-label="Quality preset">
<span class="subbar-label">PRESET</span>
<button class="preset-btn active" data-preset="low">LOW</button>
<button class="preset-btn" data-preset="med">MED</button>
<button class="preset-btn" data-preset="high">HIGH</button>
</div>
<div id="preset-hint" class="subbar-hint hidden">
Preset change takes effect on next Play
</div>
<div id="volume-group">
<span class="subbar-label">VOL</span>
<button id="btn-mute" class="icon-btn" aria-label="Toggle mute">🔊</button>
<input type="range" id="volume-slider" min="0" max="1" step="0.05" value="1"
aria-label="Volume">
</div>
</div>
<!-- ── CANVAS PLAYER ───────────────────────────────────────────── -->
<main id="player-container">
<pre id="ascii-player"></pre>
<canvas id="ascii-canvas"></canvas>
<div id="idle-overlay">
<div id="idle-content">
<div id="idle-logo">ASCILINE</div>
<div id="idle-sub">ASCII video streaming</div>
<div id="idle-arrow">↑ type a video above and hit PLAY</div>
</div>
</div>
</main>
<!-- ── STATUS BAR ──────────────────────────────────────────────── -->
<footer id="statusbar">
<span id="status-title" class="status-segment"></span>
<span id="status-live" class="live-badge hidden">● LIVE</span>
<span id="status-state" class="status-segment status-state">IDLE</span>
<span id="status-fps" class="status-segment status-fps"></span>
<span id="status-perf-hint" class="status-segment status-perf hidden">↓ try a lower preset</span>
</footer>
<!-- hidden audio element; no src until Play -->
<audio id="ascii-audio" preload="auto"></audio>
<script src="/static/app.js"></script>
</body>
</html>