8.8 KiB
8.8 KiB
Phase 6: AI Sync - Context
Gathered: 2026-04-07 Status: Ready for planning
## Phase BoundaryYouTube URL loading (yt-dlp → local file → existing audio stack), AI-assisted timeline auto-fill from multi-feature audio analysis (beats/onsets/chroma/RMS heuristics + optional LLM generation), and structural segmentation overlaid on the waveform as colored labeled bands. No microcontroller firmware (Phase 7).
## Implementation DecisionsYouTube Loading (06-01)
- D-01: yt-dlp downloads audio to
/app/shows/— same directory as uploaded files. After download completes, load the file identically to a local upload (beat analysis, waveform, show saving all reuse existing wiring unchanged). No MPV backend change. - D-02: During download, show a terminal-style progress bar with download percentage. Parse yt-dlp
--progressoutput and stream updates to the browser. Disable the URL input during download; re-enable and auto-load when complete. - D-03: URL input lives in the transport bar — inline with the existing file selector controls. Text input +
FETCHbutton, same row, terminal aesthetic (flat, monospace, no rounded corners).
AI Sync Engine (06-02)
- D-04: Two-layer architecture — heuristic base always runs; LLM enhancement is optional (off by default, user can trigger it explicitly).
- D-05: Heuristic features to extract and use:
- Beats — from existing
beats.pybeat_track - Onsets —
librosa.onset.onset_detect()for note attacks and transients - Chroma —
librosa.feature.chroma_cqt()for tonal/harmonic content → drives color palette selection - RMS energy —
librosa.feature.rms()for loudness envelope → drives animation intensity
- Beats — from existing
- D-06: LLM path: generate from scratch. The LLM receives raw audio features (beat list, segment boundaries, onset times, energy profile, chroma summary) and generates a complete block list — timestamps, duration, animation type, params per device. The heuristic result is NOT passed to the LLM; it generates independently.
- D-07: Conflict handling: prompt before replacing. If the timeline has existing blocks when AI Sync is triggered, show a confirmation dialog: "Replace all existing blocks?" Yes clears and re-fills, No cancels. Undo/redo (Phase 4) can recover from replacements.
- D-08: AI Sync trigger lives in a dedicated panel or context menu — not in the main toolbar or transport bar. Keeps the primary UI uncluttered.
Segmentation Overlay (06-03)
- D-09: Translucent colored band overlays drawn on the waveform canvas — one band per detected section, spanning the full waveform height. Alternating colors (e.g. cyan/purple variants from the existing palette).
- D-10: Section labels are generic numbered:
SEC 1,SEC 2,SEC 3, ... — no attempt to infer VERSE/CHORUS. Simple, always correct, terminal-readable. - D-11: Bands are interactive: clicking a section band selects it (highlight border). When AI Sync is triggered with a section selected, it fills only that section's time range. This allows per-section AI fill rather than whole-show fill.
Claude's Discretion
- librosa segmentation algorithm: Choose the best-fit algorithm from librosa's segment module (e.g.
librosa.segment.agglomerativewith recurrence matrix, or MSAF-style structural analysis). Target 4–12 sections for a typical 3–5 minute song. Planner decides implementation details. - LLM prompt design: Structure the feature payload as a compact JSON (beat timestamps, RMS samples at 1s resolution, chroma average per section, segment boundaries). Planner designs the prompt. Use existing Anthropic SDK patterns from the codebase if available.
- yt-dlp progress parsing: Parse
--progressoutput lines via subprocess pipe. Planner decides exact regex/parsing approach. - Section color palette: Pick 2–3 alternating colors from existing CSS variables (
--accent, complementary hues). No new colors outside the existing terminal palette.
<canonical_refs>
Canonical References
Downstream agents MUST read these before planning or implementing.
Requirements
.planning/REQUIREMENTS.md— SYNC-03 (AI-assisted show generation), AUD-02 (YouTube URL loading).planning/ROADMAP.md§Phase 6 — Success criteria (3 items), plan breakdown (06-01 through 06-03)
Existing audio analysis
lightsync/audio/beats.py—analyze(path)→{tempo, beats}usinglibrosa.beat.beat_track. Phase 6 extends this module (or addssegments.py) for onset/chroma/RMS/segmentation.lightsync/api/audio.py—/api/audio/upload,/api/audio/beats,/api/audio/waveformendpoints. YouTube download adds a new/api/audio/fetch(or similar) endpoint here.
Existing frontend wiring
lightsync/frontend/app.js— Audio load flow: file selected →audio.src = /shows/filename→loadedmetadatafires →timeline.loadWaveform(path)+timeline.loadBeats(path). YouTube download reuses the same post-load path.lightsync/frontend/timeline/timeline.js—loadWaveform()andloadBeats()methods; segmentation overlay drawn on the same waveform canvas.lightsync/frontend/index.html— Transport bar layout (file selector, upload, LOAD button); URL input + FETCH button are added to this row.lightsync/frontend/style.css— CSS variables; all new UI elements must use--accent,--bg,--border,--text-dim.
Data models
lightsync/models/show.py—source_type: Literal["file", "youtube"]already present; set this when a YouTube URL is used.
Prior context
.planning/phases/01-foundation/01-CONTEXT.md— Visual aesthetic (flat, sharp, cyan/teal, no rounded corners, no drop shadows, monospace uppercase labels).planning/phases/05-live-show-execution/05-CONTEXT.md— WebSocket protocol patterns, transport bar layout, CSS variable usage
</canonical_refs>
<code_context>
Existing Code Insights
Reusable Assets
beats.pyanalyze_async()— thread-pool wrapper for librosa analysis; new segmentation/feature extraction follows the sameasyncio.to_threadpatternaudio.pyupload endpoint — POST handler pattern; YouTube fetch endpoint mirrors this (async subprocess + cache beat data after download)app.state.beatscache — keyed by filepath; addapp.state.segmentsfor segmentation cache in the same lifespan setuptimeline.jswaveform canvas — already drawing waveform peaks; segmentation bands drawn as canvasfillRectcalls on the same canvas layer, below the waveform
Established Patterns
- Librosa analysis runs in
asyncio.to_thread— never blocks the event loop - New API endpoints go into
lightsync/api/audio.py(or a newlightsync/api/sync.pyrouter) - Frontend ES modules — new JS files must use
export/import - WebSocket message protocol:
{"type": "...", ...}— AI sync progress can use async_progressmessage type - CSS panel strip pattern from Phase 4/5: compact horizontal strips,
1px solid var(--border)borders
Integration Points
- Transport bar (
index.html) — add URL text input + FETCH button next to existing file controls audio.pyrouter — add/api/audio/fetch(POST, takes{url: string}) for yt-dlp download;/api/audio/segmentsfor structural analysistimeline.jsloadWaveform()/loadBeats()— addloadSegments()method to render colored bandsmain.pylifespan — addapp.state.segments = {}alongside existingapp.state.beats- AI Sync panel — new dedicated panel or popover; triggers
/api/sync/generate(POST) with{path, use_llm: bool, section_range?: [start, end]}
</code_context>
## Specific Ideas- Progress bar during YouTube download should feel terminal:
DOWNLOADING ██████░░░░ 62%— monospace, fills left to right, uses--accentcolor. Not a smooth CSS animation, a stepped block fill. - Section bands: translucent (opacity ~0.15-0.20), drawn below the waveform fill so the waveform is still readable. Labels in small monospace uppercase at the top-left of each band.
- The AI Sync panel could look like a terminal dialog — dark background, border,
AI SYNCheader, feature toggles (use beats / onsets / chroma / RMS all checked by default), aGENERATEbutton, and an optionalUSE LLMtoggle. - When a section band is selected, draw a 1px bright border around it (using
--accentat full opacity). Deselect when clicking elsewhere.
- Per-section manual label renaming (e.g. rename SEC 2 to CHORUS) — out of Phase 6 scope
- LLM-named sections — deferred; generic numbering is sufficient for v1
- Full MPV backend (streaming YouTube without download) — out of scope, download approach is cleaner
- AI Sync with partial fill (gap-only mode) — deferred; prompt+replace is sufficient for Phase 6
Phase: 06-ai-sync Context gathered: 2026-04-07