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>
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>
Race condition (worker "Job not found in DB"):
- RedisJobQueue.enqueue() was pushing job IDs to Redis immediately after
flush() but before the API transaction committed, so the worker would
read an ID that didn't exist yet in the DB from its own session.
- Fix: defer the Redis rpush until after session.commit() via a pending-
push list drained by get_session() after each successful commit.
- Worker: drain stale Redis queue entries on startup to clear any IDs
left over from previously uncommitted transactions.
- Worker: add 3-attempt retry with 200ms sleep when a job is not found,
as a safety net for any remaining propagation edge cases.
NC scan folder structure (YYMMDD rehearsal subfolders):
- Previously used dir_name as song title for all files in a subdirectory,
meaning every file got the folder name (e.g. "231015") as its title.
- Fix: derive song title from Path(sub_rel).stem so each audio file gets
its own name; use the file's parent path as nc_folder for version grouping.
- Rehearsal folder name stored in song.notes as "Rehearsal: YYMMDD".
- Added structured logging throughout the scan: entries found, per-folder
file counts, skip/create/import decisions, and final summary count.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>