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.
85 lines
3.5 KiB
HTML
85 lines
3.5 KiB
HTML
<!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>
|