Replaces per-member Nextcloud credentials with a BandStorage model that
supports multiple providers. Credentials are Fernet-encrypted at rest;
worker receives audio via an internal streaming endpoint instead of
direct storage access.
- Add BandStorage DB model with partial unique index (one active per band)
- Add migrations 0007 (create band_storage) and 0008 (drop old nc columns)
- Add StorageFactory that builds the correct StorageClient from BandStorage
- Add storage router: connect/nextcloud, OAuth2 authorize/callback, list, disconnect
- Add Fernet encryption helpers in security/encryption.py
- Rewrite watcher for per-band polling via internal API config endpoint
- Update worker to stream audio from API instead of accessing storage directly
- Update frontend: new storage API in bands.ts, rewritten StorageSection,
simplified band creation modal (no storage step)
- Add STORAGE_ENCRYPTION_KEY to all docker-compose files
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Store waveform peaks inline in audio_versions (JSONB columns) so WaveSurfer
can render the waveform immediately on page load without waiting for audio
decode. Adds a 100-point mini-waveform for version selector thumbnails.
Backend:
- Migration 0006: adds waveform_peaks and waveform_peaks_mini JSONB columns
- Worker generates both resolutions (500-pt full, 100-pt mini) during transcode
and stores them directly in DB — replaces file-based waveform_url approach
- AudioVersionRead schema exposes both fields inline (no extra HTTP round-trip)
- GET /versions/{id}/waveform reads from DB; adds ?resolution=mini support
Frontend:
- audioService.initialize() accepts peaks and calls ws.load(url, Float32Array)
so waveform renders instantly without audio decode
- useWaveform hook threads peaks option through to audioService
- PlayerPanel passes waveform_peaks from the active version to the hook
- New MiniWaveform SVG component (no WaveSurfer) renders mini peaks in the
version selector buttons
Fix: docker-compose.dev.yml now runs alembic upgrade head before starting
the API server, so a fresh volume gets the full schema automatically.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- New split layout: waveform/transport/queue left, comment panel right
- Avatar pins above waveform positioned by timestamp with hover tooltips
- Transport bar: speed selector, ±30s skip, 46px amber play/pause, volume
- Comment compose: live timestamp pill, suggestion/issue/keeper tag buttons
- Comment list: per-author colour avatars, amber timestamp seek chips,
playhead-proximity highlight, delete only shown on own comments
- Queue panel showing other songs in the same session
- Waveform colours updated to amber/dim palette (104px height)
- Add GET /songs/{song_id} endpoint for song metadata
- Add tag field to SongComment (model, schema, router, migration 0005)
- Fix migration 0005 down_revision to use short ID "0004"
- Fix ESLint no-unused-expressions in keyboard shortcut handler
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Introduces the rehearsal session concept: each YYMMDD folder in
Nextcloud maps to one RehearsalSession row (band_id + date unique).
Songs gain a nullable session_id FK and a tags TEXT[] column for
manual labeling (searchable by name, tag, BPM, key).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>