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>