Commit Graph

96 Commits

Author SHA1 Message Date
Claude
0963ad4a70 fix(02-03): fix upload file selection — pass abs path to refreshFileList
- Options use /app/shows/filename as value
- Upload response returns /app/shows/filename as path
- Pass path directly to refreshFileList so pre-selection matches
2026-04-06 15:39:21 +00:00
Claude
f1941a90db feat(02-03): redesign frontend — browser audio element, beat markers, animation selector
- index.html: add <audio id=player> (hidden), beat-indicator dot in header
- index.html: animation selector panel with Beat Pulse/Strobe/Color Wave/Rainbow options
- index.html: remove seek slider + time display (browser audio element has native controls)
- index.html: upload button + file selector remain in transport bar
- app.js: wire <audio> element — play/pause/seeked/timeupdate events send WS ticks
- app.js: 10Hz setInterval tick loop (supplements native timeupdate ~4Hz)
- app.js: WS beat message -> flashBeatIndicator() animation
- app.js: loadWaveformAndBeats() draws waveform peaks + beat marker lines on canvas
- app.js: loadSelectedFile() sets audio.src=/shows/filename, fetches beats+waveform
- app.js: upload response caches beat data returned from server immediately
- style.css: .beat-indicator with .beat-flash (yellow glow animation)
- style.css: .animation-select-input for the animations panel dropdown
2026-04-06 15:38:30 +00:00
Claude
1f1ff92446 chore(02-03): delete lightsync/audio/engine.py (MPV engine removed)
- Browser plays audio now; server-side MPV engine is no longer used
- Beat detection via librosa replaces position-polling engine
2026-04-06 15:37:10 +00:00
Claude
6366d8468f feat(02-03): remove MPVEngine from main.py, add beats cache and shows static route
- Remove MPVEngine import, engine module-level var, and lifespan start/stop logic
- Remove broadcast_loop task (no server-side position polling)
- Add app.state.beats = {} dict (filepath -> beat data cache) in lifespan startup
- Mount /shows as StaticFiles so browser <audio src=/shows/file.mp3> works
- Simplified lifespan: registry + show_store + beats init only
2026-04-06 15:37:06 +00:00
Claude
2c9ca0d6b4 feat(02-03): redesign WebSocket protocol — browser tick-driven beat detection
- Remove server-side broadcast_loop (MPV position polling eliminated)
- Browser sends {type: tick, position: N} at ~10Hz
- Server looks up nearest beat within 50ms window, sends {type: beat, position, tempo}
- Browser sends play/pause/seek events (logged, no UDP yet)
- Browser sends {type: load, path} to switch beat cache to specific file
- _find_nearest_beat() uses bisect for O(log n) lookup
- Per-connection beat state: current_file, beats list, tempo, last_beat_reported
2026-04-06 15:36:50 +00:00
Claude
636160e9a1 feat(02-03): redesign audio.py — browser-audio architecture, beat endpoint
- Remove engine dependency entirely (no MPVEngine, no app.state.engine)
- POST /api/audio/upload: saves file, runs librosa beat analysis, caches in app.state.beats
- GET /api/audio/beats?path=...: returns cached or on-demand beat analysis
- GET /api/audio/waveform?path=...: now takes path query param (no engine state lookup)
- GET /api/audio/files: unchanged
- Removed POST /api/audio/load and GET /api/audio/state (engine-only endpoints)
2026-04-06 15:36:21 +00:00
Claude
92cc09e908 feat(02-03): add librosa beat detection module (lightsync/audio/beats.py)
- analyze(path) -> {tempo: float, beats: [float, ...]} using librosa.beat.beat_track
- Returns beat timestamps in seconds converted from librosa frame indices
- analyze_async() wraps in asyncio.to_thread for non-blocking use from FastAPI
- Supports MP3/WAV/FLAC/OGG via librosa's ffmpeg/soundfile fallback chain
2026-04-06 15:35:58 +00:00
Claude
cce76a2b1c chore(02-03): remove mpv-libs from Dockerfile, keep ffmpeg and libsndfile
- Remove mpv-libs and libmpv.so symlink (browser plays audio now)
- Keep ffmpeg/ffmpeg-libs (used by librosa and waveform extraction)
- Keep libsndfile and its symlink (used by soundfile package)
2026-04-06 15:35:43 +00:00
Claude
5d93294b97 chore(02-03): replace python-mpv with librosa>=0.10.0 in pyproject.toml
- Remove python-mpv>=1.0.5 (server-side playback replaced by browser audio)
- Add librosa>=0.10.0 for beat detection and tempo analysis
- Keep soundfile, numpy, python-multipart (still needed)
2026-04-06 15:35:35 +00:00
Claude
9239988d38 fix(02): Alpine libmpv/libsndfile symlinks, upload endpoint, browser file picker
- Dockerfile: add libmpv.so + libsndfile.so symlinks (Alpine/musl has no ldconfig)
- engine.py: monkey-patch ctypes.util.find_library as fallback for Alpine
- audio.py: add /upload (multipart) and /files endpoints
- pyproject.toml: add python-multipart
- frontend: replace path input with upload button + file selector dropdown
2026-04-06 15:34:11 +00:00
Claude
7f9efee47a docs(02-03): complete waveform display and transport UI plan — AUD-05 fulfilled
- SUMMARY.md for 02-03 (waveform extractor + transport UI)
- STATE.md: checkpoint at human-verify task 3, decisions recorded
- ROADMAP.md: phase 2 progress updated (3/3 plans)
- REQUIREMENTS.md: AUD-05 marked complete
2026-04-06 13:15:17 +00:00
Claude
148f16ba5e feat(02-03): build transport UI and waveform canvas
- Replace timeline placeholder with waveform canvas + playback cursor
- Replace transport placeholder with full transport bar controls
- Add play/pause/stop buttons, seek slider, time display, audio path input
- Add drawWaveform(), loadWaveform(), updatePosition(), formatTime() to app.js
- Dispatch msg.type=tick to updatePosition in handleMessage
- Wire transport buttons via WebSocket commands
- Add transport/waveform styles to style.css using existing CSS variables
- Deploy via ~/bin/deploy.sh
2026-04-06 13:14:05 +00:00
Claude
da474d978d feat(02-03): add waveform extraction module and HTTP endpoint
- Create lightsync/audio/waveform.py with extract_peaks() (AUD-05)
- MP3 via ffmpeg pipe, WAV/FLAC/OGG via soundfile+numpy
- Add GET /api/audio/waveform endpoint with asyncio.to_thread
- peaks param clamped 100-5000, returns {peaks, count, file}
2026-04-06 13:11:38 +00:00
Claude
3de7f43b3f docs(02-02): complete audio engine plan 02 — WebSocket broadcast + REST audio endpoints
- SUMMARY: WebSocket hub wired to broadcast MPV position at 10Hz, command dispatch
- STATE: plan advanced to 3/3, progress 86%, decisions added
- ROADMAP: phase 2 progress updated (2/3 summaries)
- REQUIREMENTS: AUD-02, AUD-04 marked complete
2026-04-06 13:08:54 +00:00
Claude
de455400d2 feat(02-02): add REST audio load endpoint with file validation
- Create lightsync/api/audio.py with POST /load and GET /state endpoints
- Validate file existence and extension (.mp3/.wav/.flac/.ogg)
- Use asyncio.to_thread for engine.load to avoid blocking event loop
- Return 404/400/503 on error conditions
- Register audio router at /api/audio prefix in main.py
- AUD-02 (YouTube) noted as Phase 6 deferral
2026-04-06 13:07:45 +00:00
Claude
5e400a6834 feat(02-02): wire WebSocket broadcast loop and command dispatch
- Add broadcast_loop sending position ticks at 10Hz to all clients
- Replace echo stub with play/pause/seek/load command dispatch to MPVEngine
- Start broadcast_loop as asyncio task in lifespan, cancel on shutdown
- Use get_engine callable to avoid circular import at module level
2026-04-06 13:07:18 +00:00
Claude
3173fa2228 docs(02-01): complete audio engine plan 01 — MPVEngine, SUMMARY, STATE, ROADMAP updated
- Create 02-01-SUMMARY.md with task commits, decisions, self-check
- Advance plan counter to plan 2 of 3
- Update progress to 71% (5/7 plans complete)
- Add 3 key decisions to STATE.md
- Mark AUD-01, AUD-03, INF-03 complete in REQUIREMENTS.md
- Update ROADMAP.md phase 2 progress
2026-04-06 13:05:02 +00:00
Claude
5f75a19868 feat(02-01): implement MPVEngine with position polling and lifespan integration
- Create lightsync/audio/__init__.py package marker
- Create lightsync/audio/engine.py with MPVEngine class
  - Headless mpv wrapper (vo=null, ao from MPV_AO env var default null)
  - 10Hz polling loop in daemon thread (time.sleep(0.1), not observe_property)
  - Thread-safe get_state() returning position, paused, duration, loaded, file
  - load/play/pause/seek/stop methods (AUD-01, AUD-03)
  - apply_timer_fix() with sys.platform == win32 guard (INF-03)
- Update main.py lifespan to create/start/stop MPVEngine
  - MPV_AO env var controls audio output (default null for server mode)
  - engine exposed via app.state.engine for API routes
2026-04-06 13:03:43 +00:00
Claude
474b4a71d5 chore(02-01): add audio dependencies to Dockerfile and pyproject.toml
- Add mpv-libs, ffmpeg, ffmpeg-libs, libsndfile to Alpine Dockerfile
- Add python-mpv>=1.0.5, soundfile>=0.12.0, numpy>=1.26.0 to pyproject.toml
2026-04-06 13:02:41 +00:00
Claude
4af7f4de5f docs(02): research and plans for audio engine phase 2026-04-06 12:45:09 +00:00
Claude
f8d60cc0e0 docs(02): create phase 02 audio engine plans (3 plans, 3 waves) 2026-04-06 12:44:41 +00:00
Claude
d61c32a75b docs(02): research audio engine phase — MPV, soundfile, waveform, WebSocket broadcast 2026-04-06 12:36:51 +00:00
Claude
a8db2a4ce7 docs(01): mark verification passed — user confirmed device CRUD works 2026-04-06 12:24:26 +00:00
Claude
7590fc309d fix(ws): use wss:// on HTTPS to prevent mixed content SecurityError blocking module execution 2026-04-06 12:23:05 +00:00
Claude
dc69cf2bc7 fix(server): serve static files with no-cache headers to prevent stale JS/CSS 2026-04-06 10:46:49 +00:00
Claude
e3dd78bee2 fix(ui): replace JS toggle with native <details> for device manage section 2026-04-05 21:38:48 +00:00
Claude
06845d862d fix(ui): compact devices panel (flex auto), fix manage button type=button 2026-04-05 21:23:44 +00:00
Claude
82995de95a feat(ui): hide device management behind ⚙ MANAGE toggle — form and remove buttons only shown when active 2026-04-05 20:59:35 +00:00
Claude
a024e762c0 test(01): persist human verification items as UAT 2026-04-05 20:17:47 +00:00
Claude
57e40b2be2 docs(01-04): complete device CRUD UI and CSS fix plan — SUMMARY, STATE, ROADMAP, REQUIREMENTS updated
- Close DEV-03: add-device form, POST/DELETE wired, remove buttons on device rows
- Close UI-02: --accent canonical CSS variable, panel IDs all present
- Phase 01 foundation: 4/4 plans complete
2026-04-05 20:15:04 +00:00
Claude
2ff35f9628 feat(01-04): fix CSS variable naming — add --accent as canonical source of truth
- Add --accent: #00ffff as canonical variable in :root
- Make --border-bright and --text-accent aliases of var(--accent)
- Add .device-form styles: flex column, gap, border-top separator
- Add .device-remove styles: borderless, hover red
- Update .device-row to flex row with justify-content: space-between for remove button
- Add .device-row-info for name/metadata stack within each row
2026-04-05 20:13:19 +00:00
Claude
ccc40c8721 feat(01-04): add device CRUD UI — form, POST, DELETE, remove buttons
- Add <form id='add-device-form'> to DEVICES panel with 5 inputs (name, strip_type, led_count, ip, port) and submit button
- Add apiPost() and apiDelete() helper functions to app.js
- Update loadDevices() to render device rows with remove button (.device-remove)
- Add removeDevice(id) function with DELETE /api/devices/{id} + window export for onclick
- Add form submit handler: POST /api/devices, form reset, refresh device list
- Add panel IDs: devices-panel, animations-panel, timeline-panel, transport-panel to index.html
2026-04-05 20:12:44 +00:00
Claude
e95e4748fc docs(01-04): add gap-closure plan for device CRUD UI and CSS fixes
Closes 2 gaps from VERIFICATION.md:
- DEV-03: add-device-form + POST/DELETE handlers in app.js
- UI-02: --accent CSS variable rename + panel IDs in index.html

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 20:02:11 +00:00
Claude
ab162fb244 docs(01): create gap closure plan 01-04 — device CRUD UI + CSS variable fix 2026-04-05 20:01:48 +00:00
Claude
af1100a850 docs(01-03): complete integration and deployment plan — SUMMARY, STATE, ROADMAP, REQUIREMENTS updated 2026-04-05 19:00:58 +00:00
Claude
a21962cbcf feat(01-03): integration wiring, Docker deployment, lightsync.groll.cloud
- Fix API routing: redirect_slashes=False + strip trailing slashes from route paths
- Fix StaticFiles catch-all intercepting /api/* before FastAPI redirect
- Add [tool.hatch.build.targets.wheel] to pyproject.toml for Docker builds
- Install docker-compose plugin (v2.34.0) as CLI plugin
- Create Dockerfile (python:3.11-alpine, CMD python -m lightsync)
- Create docker-compose.prod.yml (Traefik + Authelia, edge network)
- Deploy lightsync container to lightsync.groll.cloud
- Add LightSync entry to start/projects.json
- Update frontend app.js to use /api/devices (no trailing slash)
2026-04-05 18:59:34 +00:00
Claude
9f3a2fc7a1 docs(01-02): add frontend shell summary — built as part of 01-01 2026-04-05 18:50:32 +00:00
Claude
dd5a1b22fd docs(01-01): complete backend scaffold plan — SUMMARY, STATE, ROADMAP, REQUIREMENTS updated
- Created 01-01-SUMMARY.md with full accomplishment record
- STATE.md: advanced to plan 2/3, progress 33%, added 3 key decisions
- ROADMAP.md: Phase 1 updated to 1/3 summaries (In Progress)
- REQUIREMENTS.md: marked INF-01, INF-02, DEV-01, DEV-02, SHW-01, SHW-02 complete
2026-04-05 18:49:37 +00:00
Claude
5e10272c69 feat(01-01): create device registry, show store, FastAPI app, API routes, and frontend shell
- DeviceRegistry with load/save/add/remove/instantiate and _DEVICE_CLASSES dict
- ShowStore with save/load/list_ids using aiofiles for async I/O
- FastAPI app with lifespan hook (startup: load registry/store, shutdown: save registry)
- API routes: GET/POST /api/devices/, DELETE /api/devices/{id}, GET/POST /api/shows/, GET /api/shows/{id}
- WebSocket stub at /ws with ConnectionManager and ack echo
- Frontend shell: DAW layout with header, sidebar (DEVICES + ANIMATIONS), TIMELINE, TRANSPORT
- Terminal aesthetic: dark theme, cyan accents, monospace fonts, no drop shadows
- .gitignore for runtime data (devices.json, shows/, __pycache__, .venv)
2026-04-05 18:47:25 +00:00
Claude
061edfcf6a feat(01-01): create package structure, Pydantic models, and device abstractions
- pyproject.toml with FastAPI, uvicorn, Pydantic v2, aiofiles, structlog deps
- DeviceConfig Pydantic model with StripType, UUID, validation constraints
- ShowModel with AudioRef, TrackModel, CueModel, AnalysisBlock, schema_version=1
- BaseDevice ABC with encode_frame, encode_animation_cmd, bytes_per_pixel
- SK6812Device (4 bytes/pixel) and WS2801Device (3 bytes/pixel) implementations
2026-04-05 18:45:19 +00:00
Claude
5721bef791 docs(01-foundation): create phase plan — 3 plans in 2 waves 2026-04-05 18:40:20 +00:00
Claude
519849e855 docs(01): research phase 1 foundation — FastAPI, Pydantic v2, device ABC, terminal CSS 2026-04-05 18:34:54 +00:00
Claude
15c8d3e544 docs(01): add phase 1 context — project structure and UI shell decisions 2026-04-05 18:23:10 +00:00
Claude
8b7c0b87ad docs: add requirements, roadmap, and initial state
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 18:03:01 +00:00
Claude
6e05abe7a8 docs: add domain research — stack, features, architecture, pitfalls 2026-04-05 17:59:51 +00:00
Claude
3d756129fa chore: initialize project — LightSync config and PROJECT.md 2026-04-05 17:51:10 +00:00