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>
This commit is contained in:
@@ -60,6 +60,7 @@ async def search_songs(
|
||||
bpm_min: float | None = Query(None, ge=0),
|
||||
bpm_max: float | None = Query(None, ge=0),
|
||||
session_id: uuid.UUID | None = Query(None),
|
||||
unattributed: bool = Query(False, description="Only songs with no rehearsal session"),
|
||||
session: AsyncSession = Depends(get_session),
|
||||
current_member: Member = Depends(get_current_member),
|
||||
):
|
||||
@@ -78,6 +79,7 @@ async def search_songs(
|
||||
bpm_min=bpm_min,
|
||||
bpm_max=bpm_max,
|
||||
session_id=session_id,
|
||||
unattributed=unattributed,
|
||||
)
|
||||
return [
|
||||
SongRead.model_validate(s, update={"version_count": len(s.versions)})
|
||||
@@ -173,14 +175,20 @@ async def scan_nextcloud(
|
||||
skipped_count = 0
|
||||
band_folder = band.nc_folder_path or f"bands/{band.slug}/"
|
||||
|
||||
log.info("Starting NC scan for band '%s' in folder '%s'", band.slug, band_folder)
|
||||
log.info("NC scan START — band='%s' folder='%s' nc_user='%s'", band.slug, band_folder, nc._auth[0])
|
||||
|
||||
try:
|
||||
items = await nc.list_folder(band_folder)
|
||||
except Exception as exc:
|
||||
raise HTTPException(status_code=status.HTTP_502_BAD_GATEWAY, detail=f"Nextcloud unreachable: {exc}")
|
||||
log.error("NC scan FAILED — could not list '%s': %s", band_folder, exc)
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_502_BAD_GATEWAY,
|
||||
detail=f"Cannot read Nextcloud folder '{band_folder}': {exc}",
|
||||
)
|
||||
|
||||
log.info("Found %d top-level entries in '%s'", len(items), band_folder)
|
||||
log.info("NC scan — found %d top-level entries in '%s'", len(items), band_folder)
|
||||
for item in items:
|
||||
log.info(" entry href=%s → rel=%s", item.path, relative(item.path))
|
||||
|
||||
# Collect (nc_file_path, nc_folder, song_title, rehearsal_label) tuples.
|
||||
# nc_folder is the directory that groups versions of the same song.
|
||||
@@ -195,27 +203,39 @@ async def scan_nextcloud(
|
||||
try:
|
||||
sub_items = await nc.list_folder(rel)
|
||||
except Exception as exc:
|
||||
log.warning("Could not list subfolder '%s': %s", rel, exc)
|
||||
log.warning("NC scan — could not list subfolder '%s': %s", rel, exc)
|
||||
continue
|
||||
|
||||
all_sub = [relative(s.path) for s in sub_items]
|
||||
audio_files = [s for s in sub_items if Path(relative(s.path)).suffix.lower() in AUDIO_EXTENSIONS]
|
||||
log.info("Subfolder '%s': %d audio files found", dir_name, len(audio_files))
|
||||
log.info(
|
||||
"NC scan — subfolder '%s': %d entries total, %d audio files",
|
||||
dir_name, len(all_sub), len(audio_files),
|
||||
)
|
||||
for s in sub_items:
|
||||
sr = relative(s.path)
|
||||
ext = Path(sr).suffix.lower()
|
||||
if ext and ext not in AUDIO_EXTENSIONS:
|
||||
log.info(" skip (not audio ext=%s): %s", ext, sr)
|
||||
|
||||
for sub in audio_files:
|
||||
sub_rel = relative(sub.path)
|
||||
song_title = Path(sub_rel).stem
|
||||
# Each file in a rehearsal folder is its own song,
|
||||
# grouped under its own sub-subfolder path for version tracking.
|
||||
song_folder = str(Path(sub_rel).parent) + "/"
|
||||
rehearsal_label = dir_name # e.g. "231015" or "2023-10-15"
|
||||
rehearsal_label = dir_name
|
||||
log.info(" queue for import: %s → title='%s' folder='%s'", sub_rel, song_title, song_folder)
|
||||
to_import.append((sub_rel, song_folder, song_title, rehearsal_label))
|
||||
else:
|
||||
if Path(rel).suffix.lower() in AUDIO_EXTENSIONS:
|
||||
ext = Path(rel).suffix.lower()
|
||||
if ext in AUDIO_EXTENSIONS:
|
||||
folder = str(Path(rel).parent) + "/"
|
||||
title = Path(rel).stem
|
||||
log.info(" queue for import (root-level): %s → title='%s'", rel, title)
|
||||
to_import.append((rel, folder, title, None))
|
||||
elif ext:
|
||||
log.info(" skip root-level (not audio ext=%s): %s", ext, rel)
|
||||
|
||||
log.info("NC scan: %d audio files to evaluate for import", len(to_import))
|
||||
log.info("NC scan — %d audio files queued for import", len(to_import))
|
||||
|
||||
song_repo = SongRepository(session)
|
||||
from rehearsalhub.schemas.audio_version import AudioVersionCreate # noqa: PLC0415
|
||||
|
||||
Reference in New Issue
Block a user