--- phase: 06-ai-sync plan: 01 subsystem: audio-input tags: [youtube, yt-dlp, sse, streaming, frontend, transport-bar] dependency_graph: requires: [] provides: [youtube-audio-download, sse-progress-stream, yt-dlp-dependency] affects: [lightsync/api/audio.py, lightsync/frontend] tech_stack: added: [yt-dlp>=2025.0.0, anthropic>=0.40.0] patterns: [asyncio subprocess, SSE ReadableStream, terminal progress bar] key_files: created: [] modified: - pyproject.toml - docker-compose.prod.yml - lightsync/api/audio.py - lightsync/frontend/index.html - lightsync/frontend/app.js - lightsync/frontend/style.css decisions: - "sys.executable -m yt_dlp used to avoid PATH issues in Docker" - "--newline flag required for line-by-line progress parsing from yt-dlp" - "yt_%(id)s.%(ext)s output template ensures predictable filenames" - "ANTHROPIC_API_KEY wired with empty default so container starts without it" - "Beat analysis runs post-download (same as upload path) for identical audio pipeline entry" metrics: duration: "~8 min" completed: "2026-04-07" tasks_completed: 3 files_changed: 6 requirements_satisfied: [AUD-02] --- # Phase 06 Plan 01: YouTube Audio Loading via yt-dlp Summary **One-liner:** YouTube URL download via yt-dlp subprocess with SSE progress streaming, terminal-style block progress bar, and automatic audio pipeline entry after download. ## What Was Built Added full YouTube audio loading capability to the LightSync transport bar: 1. **Dependencies (Task 1):** Added `yt-dlp>=2025.0.0` and `anthropic>=0.40.0` to `pyproject.toml`. Wired `ANTHROPIC_API_KEY` env var (empty default) in `docker-compose.prod.yml`. Both packages are importable inside the running container. 2. **Backend endpoint (Task 2):** New `POST /api/audio/fetch` endpoint in `lightsync/api/audio.py` that: - Accepts `{url: string}` JSON body - Spawns yt-dlp subprocess via `sys.executable -m yt_dlp` (avoids PATH issues in Docker) - Parses progress with `PROGRESS_RE = re.compile(r'\[download\]\s+([\d.]+)%')` using `--newline` flag - Streams SSE progress events with `media_type="text/event-stream"` - Runs beat analysis on completion and caches in `app.state.beats` - Returns `{type: "done", path, filename}` event on success 3. **Frontend UI (Task 3):** Added to transport bar in `index.html`: - Text input `#yt-url` with placeholder "YOUTUBE URL" - `#btn-fetch` button - `#yt-progress` span for terminal-style progress display In `app.js`: - `fetchWithProgress()` function using fetch ReadableStream reader for SSE - `renderProgressBar()` rendering `DOWNLOADING ████████░░░░░░░░░░░░ 42%` style - FETCH button handler with D-02 disable-during-download - Auto-load into `#player` after download (identical to upload pipeline) In `style.css`: `.yt-url-input` and `.yt-progress` styles using existing CSS variables, flat/monospace/no border-radius. ## Commits | Task | Commit | Description | |------|--------|-------------| | 1 | `165cdb6` | feat(06-01): add yt-dlp and anthropic dependencies | | 2 | `d58ea8c` | feat(06-01): add YouTube download endpoint with SSE progress streaming | | 3 | `dca1cb8` | feat(06-01): add YouTube URL input with terminal progress bar | ## Deviations from Plan None - plan executed exactly as written. ## Known Stubs None - YouTube download is fully wired end-to-end. ## Self-Check: PASSED - `pyproject.toml` contains `yt-dlp>=2025.0.0` and `anthropic>=0.40.0` - `docker-compose.prod.yml` contains `ANTHROPIC_API_KEY` - `lightsync/api/audio.py` contains `async def fetch_youtube`, `PROGRESS_RE`, `media_type="text/event-stream"`, `class YouTubeFetchRequest`, `analyze_async(filename)` - `lightsync/frontend/index.html` contains `id="yt-url"`, `id="btn-fetch"`, `id="yt-progress"` - `lightsync/frontend/app.js` contains `fetchWithProgress`, `renderProgressBar`, `/api/audio/fetch`, `urlInput.disabled = true`, `audio.src` - `lightsync/frontend/style.css` contains `.yt-url-input`, `.yt-progress` - All 3 task commits exist: `165cdb6`, `d58ea8c`, `dca1cb8`