Backend:
- POST /api/admin/clear-waveforms — deletes all cached waveform SVGs
- POST /api/admin/delete-all-clips — nuclear option: deletes all clips,
MP3s, artwork, and waveforms. Returns deleted count.
- database.py: delete_all_clips() for bulk DB wipe
- Clip deletion cascade-removes waveform SVGs
Frontend:
- SettingsModal.tsx: center-stage modal with backdrop blur (bg-black/60
backdrop-blur-sm). Danger zone with red-styled border and header.
- Two-step confirmation for Delete All (click once to arm, click again
to confirm). Shows clip count dynamically.
- Gear icon trigger in header bar
- App.tsx: handleClearWaveforms + handleDeleteAll handlers
Docs:
- Architecture KB updated with album art, waveform, settings modal sections
- Tasks KB updated with T-021 (waveform) and TASK-001 (album art) marked done
- T-026 (settings modal) added to backlog
Replaces 64-band FFT frequency spectrum with 200-bar amplitude envelope:
- RMS amplitude per time slice — shows actual track dynamics
- Quiet intros, loud drops, silence gaps all visible
- Per-track normalization — each track uses its full dynamic range
- Gentle compression (power 0.7) so quiet parts remain visible
- 3-point weighted smoothing preserves dynamics while looking polished
- Time-progressive HSL gradient: deep amber (20°) → bright orange (40°)
- Single centered bars with rounded caps — classic waveform look
- 160px height, 800px width, tight 1px gap for dense visualization
- Cleared cached waveforms so all clips regenerate with new algorithm
Backend:
- Install ffmpeg + numpy in Docker image
- New waveform.py: FFT-based SVG generator with 64 frequency bands
- GET /api/clips/{id}/waveform — generates on demand, caches to /data/waveforms/
- DELETE /api/clips/{id} now cascade-removes waveform SVG
- Warm amber-to-orange HSL gradient (25° to 35° hue)
- Mirrored bars: top full opacity, bottom 60% opacity
Frontend:
- DetailPanel shows waveform image beneath album art (80px height)
- Only shown for clips with status='ready'
Storage: /data/waveforms/ persisted via existing ./data volume mount
The Lyria API returns a candidates[].content.parts[] array where:
- parts[0] = text metadata (e.g. '<instrumental>')
- parts[1] = inlineData with the actual audio payload
Previous code only checked parts[0] for inlineData, which always failed
with 'No audio data in response'. Fixed by iterating all parts to find
the inlineData entry.
Successfully generated first real audio clip (lo-fi hip hop, 30s, 728KB MP3).
Also includes: .env.example, requirements updates, docker-compose fixes,
frontend vite config, logger setup.