- 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>
- 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>
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>
- 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>
Two filter bugs caused every activity to be silently dropped:
1. Path format: Nextcloud activity API returns paths as
/username/files/bands/... but is_band_audio_path() was checking
parts[0] == "bands" after strip("/"), getting "username" instead.
Fix: normalize_nc_path() strips the /username/files/ and DAV prefixes
before any path checks are applied.
2. Subject names: filter was checking for "file_created"/"file_changed"
but Nextcloud uses "created_by"/"changed_by"/"created_self"/etc.
Fix: expanded _UPLOAD_SUBJECTS to cover all known NC activity subjects.
3. Expose NextcloudWatcherClient.username so normalize_nc_path() can
construct the correct prefix for the configured user.
4. Set watcher log level to DEBUG with per-activity skip reasons logged,
so the next filter edge case is immediately diagnosable. httpx/httpcore
kept at INFO/WARNING to avoid flooding the output.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>