Commit Graph

29 Commits

Author SHA1 Message Date
Mistral Vibe
cd1d098ca4 fix: avatar stale state, nginx intercept, and dev tooling
Frontend (SettingsPage):
- Sync avatarUrl state via useEffect when me.avatar_url changes after
  background refetch, so profile section never shows stale avatar
- Invalidate ["comments"] after upload/generate/remove so SongPage
  comment avatars update immediately instead of waiting for staleTime
- Fix Remove button: was sending avatar_url: undefined which JSON.stringify
  drops entirely, so the server never cleared it; now sends ""

nginx:
- Change /api/ and /ws/ locations to use ^~ prefix so the static-asset
  regex rule (~* \.(png|svg|ico)$) cannot intercept API paths; PNG/SVG
  avatar uploads were returning 404 from nginx in production
- Merge nc-scan 300s timeout into ^~ /api/v1/bands/ block
- Add client_max_body_size 10m (default 1MB was silently rejecting
  uploads before they reached FastAPI)

Dev tooling:
- Add docker-compose.dev.yml for hot-reload development workflow
- Add Taskfile.yml with dev, test, lint, migrate, and shell tasks

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 20:41:32 +02:00
Mistral Vibe
cd6fabb31c fix: correct avatar upload and DiceBear URL version
- Add api.upload() to client.ts that passes FormData without setting
  Content-Type, letting the browser set multipart/form-data with the
  correct boundary (was causing 422 on the upload endpoint)
- Use api.upload() instead of api.post() for avatar file upload
- Update DiceBear URLs from v6 to 9.x in both frontend and backend

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 20:17:21 +02:00
Mistral Vibe
da051be673 fix: proper TypeScript type assertion for error object
- Use type assertion to define error object structure
- Use optional chaining for safe property access
- Maintain all error handling functionality

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-03-30 19:58:29 +02:00
Mistral Vibe
e4beebc57e fix: add type guard for detail property in error data
- Check for 'detail' property before accessing it
- Maintain all error handling functionality
- Ensure TypeScript type safety

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-03-30 19:57:24 +02:00
Mistral Vibe
b66660ee30 fix: TypeScript type safety for error object properties
- Add proper type guards for error object properties
- Check for 'status' and 'data' properties before accessing
- Maintain all debugging functionality

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-03-30 19:56:41 +02:00
Mistral Vibe
a62297f2c4 fix: add comprehensive debugging and validation for avatar uploads
- Add detailed error extraction from API responses
- Validate file content is not empty before saving
- Verify file was actually saved to disk
- Check saved file size matches expectations
- Add extensive logging for debugging upload issues
- Improve error messages with specific details

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-03-30 19:55:20 +02:00
Mistral Vibe
b20b98a17a fix: improve error handling for avatar uploads
- Change invalid file type error from 400 to 422 for better frontend handling
- Add specific error message for 422 responses in frontend
- Improve error message clarity
- Better error classification and user guidance

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-03-30 19:49:39 +02:00
Mistral Vibe
48969e110a fix: remove incorrect headers parameter from API call
- Remove extra parameter that was causing TypeScript error
- Keep all other file size handling improvements

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-03-30 19:44:00 +02:00
Mistral Vibe
26f45009d4 fix: improve file size handling and error messages
- Reduce server-side limit to 5MB for upload endpoint
- Increase client-side resizing threshold to 4MB
- Add specific error handling for 413 responses
- Add more detailed logging for file sizes
- Improve user error messages

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-03-30 19:43:22 +02:00
Mistral Vibe
a63a1571ba feat: implement client-side image resizing for avatar uploads
- Add resizeImage function to SettingsPage
- Resize images larger than 2MB to max 800x800 pixels
- Convert to JPEG with 80% quality to reduce file size
- Add server-side validation for 10MB file size limit
- Maintain aspect ratio during resizing
- Log resizing details for debugging

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-03-30 19:40:33 +02:00
Mistral Vibe
675399836c fix: add comprehensive logging to debug avatar issues
- Add console.log statements to frontend avatar functions
- Add print statements to backend avatar endpoints
- Log file uploads, settings updates, and errors
- Track the complete flow from UI to backend

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-03-30 19:34:28 +02:00
Mistral Vibe
a17b4a7ec0 fix: TypeScript errors in avatar implementation
- Remove unnecessary headers parameter from API call
- Fix unused error parameter in onError handler
- Use undefined instead of null for avatar_url removal

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-03-30 19:28:41 +02:00
Mistral Vibe
b59eb584a6 fix: implement proper avatar upload and display
- Add file upload endpoint to auth router
- Mount static files for avatar serving
- Implement real file upload in frontend
- Add error handling and fallback for broken images
- Fix avatar persistence and state management
- Add loading states and proper error messages

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-03-30 19:27:35 +02:00
Mistral Vibe
184a288b7f fix: add avatar_url to updateSettings type definition
Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-03-30 19:17:19 +02:00
Mistral Vibe
ccafcd38af feat: implement user avatars with DiceBear integration
- Add avatar_url field to MemberSettingsUpdate schema
- Create AvatarService for generating default avatars using DiceBear
- Update auth service to generate avatars on user registration
- Add avatar upload UI to settings page
- Update settings endpoint to handle avatar URL updates
- Display current avatar in settings with upload/generate options

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-03-30 19:15:24 +02:00
Mistral Vibe
3b8c4a0cb8 fix: comment waveform integration with timestamps and avatars
- Add author_avatar_url to API schema and frontend interface
- Capture current playhead timestamp when creating comments
- Display user avatars in waveform markers instead of placeholders
- Improve marker visibility with better styling (size, borders, shadows)
- Fix TypeScript type errors for nullable timestamps
- Add debug logging for troubleshooting

This implements the full comment waveform integration as requested:
- Comments are created with exact playhead timestamps
- Waveform markers show at correct positions with user avatars
- Clicking markers scrolls to corresponding comments
- Backward compatible with existing comments without timestamps
2026-03-30 19:06:40 +02:00
Mistral Vibe
a8aba72b3a WIP: Add timestamp to comments and fix frontend errors 2026-03-29 22:06:36 +02:00
Mistral Vibe
e71c7fc3ad Fix play behaviour in track view
- Version switching now stops playback and resets UI state
- Playback must be started manually after version switch
- Added space key shortcut for play/pause toggle
- UI elements correctly reflect playback state

Fixes: Play/pause controls work correctly after version switching
Improves: User experience with keyboard shortcuts
2026-03-29 20:39:15 +02:00
Mistral Vibe
1179d9f063 WIP: Fix play behaviour in track view
- Added wasPlayingRef to preserve playback state across version changes
- Auto-play new waveform if previous version was playing
- Ensure play/pause buttons work correctly after version switch
- Added WebSocket proxy configuration for real-time features

Fixes: Version switching now preserves playback state
Todo: Test edge cases and finalize implementation
2026-03-29 20:37:25 +02:00
Steffen Schuhmann
ececcb24f6 fix: remove unused NcScanResult interface in BandPage (TS error)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 15:14:18 +02:00
Steffen Schuhmann
7cad3e544a feat: incremental SSE scan, recursive NC traversal, custom folder support
- nc_scan.py: recursive collect_audio_files (fixes depth-1 bug); scan_band_folder
  yields ndjson events (progress/song/session/skipped/done) for streaming
- songs.py: replace old flat scan with scan_band_folder; add GET nc-scan/stream
  endpoint using _member_from_request so ?token= auth works for fetch-based SSE
- BandPage.tsx: scan button now consumes ndjson stream via fetch+ReadableStream;
  sessions/unattributed invalidated as each song/session event arrives
- session.py: add extract_session_folder() for YYMMDD path extraction
- rehearsal_session.py: get_or_create uses begin_nested() savepoint to handle races
- band.py: add get_by_nc_folder_prefix() for custom nc_folder_path band lookup
- internal.py: nc-upload falls back to prefix match when slug lookup fails
- event_loop.py: remove hardcoded bands/ guard; let internal API handle filtering

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 15:09:42 +02:00
Steffen Schuhmann
dc6dd9dcfd fix: scan visibility, NC folder validation, watcher logging
- nc-scan: detailed INFO logging of every path found, subfolder
  contents and skip reasons; 502 now includes the exact folder and
  error so user sees a real message instead of a blank result
- band creation: if nc_base_path is explicitly given, verify the
  folder exists in Nextcloud before saving — returns 422 with a
  clear message to the user; auto-generated paths still do MKCOL
- songs search: add ?unattributed=true to return songs with no
  session_id (files not in a YYMMDD folder)
- BandPage: show "Unattributed Recordings" section below sessions
  so scanned files without a dated folder always appear
- watcher event_loop: promote all per-activity log lines from DEBUG
  to INFO so they're visible in default Docker Compose log output;
  log normalized path and skip reason for every activity

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 14:11:07 +02:00
Steffen Schuhmann
25502458d0 feat(web): BandPage — By Date and Search tabs
Replaces flat song list with two tabs:
- By Date: session list (newest first) with weekday, date, label,
  recording count — each row links to SessionPage
- Search: title/key/BPM range/tag filters, results as flat song list

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 13:45:12 +02:00
Steffen Schuhmann
5be1a9808f feat(web): SessionPage — rehearsal date detail view
Shows date, optional label/notes (admin-editable), and a flat list
of all recordings from that session. Each recording links to SongPage
and shows tags, key, BPM chips inline.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 13:42:22 +02:00
Steffen Schuhmann
53da085f28 fix: proxy audio stream through API with query-param token auth
WaveSurfer makes plain browser fetches without Authorization headers,
causing 401s on the stream endpoint. The stream endpoint now accepts
a ?token= query param in addition to the Authorization header, and
proxies audio bytes directly through FastAPI instead of redirecting to
raw WebDAV (which would require a second Nextcloud auth challenge).
Falls back to nc_file_path if HLS transcoding hasn't run yet.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 13:23:51 +02:00
Steffen Schuhmann
47bc802775 fix: robust NC activity filter, title extraction, scan result detail
Watcher:
- Accept both NC 22+ (type="file_created") and older NC (subject="created_self")
  so the upload filter works across all Nextcloud versions
- Add .opus to audio_extensions
- Fix tests: set nc.username on mocks, use realistic activity dicts with type field
- Add tests for old NC style, non-band path filter, normalize_nc_path, cursor advance

API:
- Fix internal.py title extraction: always use filename stem (was using
  parts[-2] for >3-part paths, which gave folder name instead of song title)
- nc-scan now returns NcScanResult with folder, files_found, imported, skipped counts
  instead of bare song list — gives the UI actionable feedback

Web:
- Show rich scan result message: folder scanned, count imported, count already registered

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 12:26:58 +02:00
Steffen Schuhmann
fbac62a0ea feat: band NC folder config, fix watcher event filter, add light/dark theme
- Add PATCH /bands/{id} endpoint for admins to update nc_folder_path
- Add band NC scan folder UI panel with inline edit
- Fix watcher: use activity type field (not human-readable subject) for upload detection
- Reorder watcher filters: audio extension check first, then band path, then type
- Add dark/light theme toggle using GitHub Primer-inspired CSS custom properties
- All inline styles migrated to CSS variables for theme-awareness

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 00:29:58 +01:00
Steffen Schuhmann
a6a64032ec fix: auto-logout on 401, raise nginx timeout for nc-scan
401 Unauthorized loop:
- client.ts had no 401 handler, leaving stale expired tokens in
  localStorage. The PrivateRoute guard only checked token existence,
  so the app would render but every API call would fail silently.
- Fix: on any 401 response, clear the token and redirect to /login.

504 Gateway Timeout on nc-scan:
- nginx default proxy_read_timeout is 60s. The scan endpoint makes
  one Nextcloud request per audio file (list + metadata), which easily
  exceeds that on larger libraries.
- Fix: add a dedicated location block for nc-scan with 300s timeouts.
  General /api/ block gets explicit 60s timeouts for clarity.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 22:03:16 +01:00
Steffen Schuhmann
f7be1b994d Initial commit: RehearsalHub POC
Full-stack self-hosted band rehearsal platform:

Backend (FastAPI + SQLAlchemy 2.0 async):
- Auth with JWT (register, login, /me, settings)
- Band management with Nextcloud folder integration
- Song management with audio version tracking
- Nextcloud scan to auto-import audio files
- Band membership with link-based invite system
- Song comments
- Audio analysis worker (BPM, key, loudness, waveform)
- Nextcloud activity watcher for auto-import
- WebSocket support for real-time annotation updates
- Alembic migrations (0001–0003)
- Repository pattern, Ruff + mypy configured

Frontend (React 18 + Vite + TypeScript strict):
- Login/register page with post-login redirect
- Home page with band list and creation form
- Band page with member panel, invite link, song list, NC scan
- Song page with waveform player, annotations, comment thread
- Settings page for per-user Nextcloud credentials
- Invite acceptance page (/invite/:token)
- ESLint v9 flat config + TypeScript strict mode

Infrastructure:
- Docker Compose: PostgreSQL, Redis, API, worker, watcher, nginx
- nginx reverse proxy for static files + /api/ proxy
- make check runs all linters before docker compose build

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 21:53:03 +01:00