From 973d28f61d5d96095dffd566341795134cb5a541 Mon Sep 17 00:00:00 2001 From: dev Date: Mon, 15 Jun 2026 14:39:18 +0200 Subject: [PATCH] fix(path-resolve): CWD-independent base dirs + quality-scan resolve diagnostic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The quality job still resolved 0/18 because the shared resolver kept relative config paths ("./Transfer") as-is and gated them behind os.path.isdir("./Transfer"), which only holds when the calling thread's CWD is the app root. The repair worker thread's CWD isn't guaranteed to be /app, so base_dirs came back empty and every track was "unresolved". - _collect_base_dirs now also adds os.path.abspath() of every relative candidate, so "./Transfer" → "/app/Transfer" regardless of CWD. - quality_upgrade_scanner logs a one-shot [QualityResolve] diagnostic on the first unresolved track (cwd, transfer_folder + abspath + isdir, base dirs tried, abs-join existence) so any remaining mount mismatch is pinpointable instead of a silent "all skipped". Co-Authored-By: Claude Sonnet 4.6 --- core/library/path_resolver.py | 17 ++++++++++- core/repair_jobs/quality_upgrade_scanner.py | 34 ++++++++++++++++++++- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/core/library/path_resolver.py b/core/library/path_resolver.py index 0e40e678..6ff3e6b5 100644 --- a/core/library/path_resolver.py +++ b/core/library/path_resolver.py @@ -138,10 +138,25 @@ def _collect_base_dirs( except Exception as e: logger.debug("music paths read failed: %s", e) + # Normalize to absolute forms so resolution does NOT depend on the calling + # thread's CWD. A relative config like "./Transfer" otherwise only resolves + # when os.path.isdir("./Transfer") happens to be true from the current CWD — + # which fails in background workers whose CWD isn't the app root, leaving + # base_dirs empty and every track "unresolved". For each candidate we try + # the raw form first (cheap, preserves an already-absolute path), then its + # os.path.abspath() form so "./Transfer" → "/app/Transfer". + expanded: list[str] = [] + for c in candidates: + if not c: + continue + expanded.append(c) + if not os.path.isabs(c): + expanded.append(os.path.abspath(c)) + # De-duplicate while preserving order, drop empties / non-existent dirs. seen: set[str] = set() out: list[str] = [] - for c in candidates: + for c in expanded: if not c or c in seen: continue seen.add(c) diff --git a/core/repair_jobs/quality_upgrade_scanner.py b/core/repair_jobs/quality_upgrade_scanner.py index 9919ae1c..5a0c5da8 100644 --- a/core/repair_jobs/quality_upgrade_scanner.py +++ b/core/repair_jobs/quality_upgrade_scanner.py @@ -85,14 +85,22 @@ class QualityUpgradeScannerJob(RepairJob): context.update_progress(0, total) probe_failed = 0 + _diag_logged = False for i, (track_id, info) in enumerate(track_list): if context.check_stop(): return result if i % 20 == 0 and context.wait_if_paused(): return result - resolved = self._resolve_path(info.get('file_path', ''), context) + raw_fp = info.get('file_path', '') + resolved = self._resolve_path(raw_fp, context) if not resolved: + # One-shot diagnostic on the first unresolved track — logs EXACTLY + # what the resolver tried (base dirs, cwd) so a path/mount mismatch + # is diagnosable instead of a silent "all skipped". + if not _diag_logged: + _diag_logged = True + self._log_resolve_diag(raw_fp, context) result.skipped += 1 probe_failed += 1 continue @@ -235,6 +243,30 @@ class QualityUpgradeScannerJob(RepairJob): config_manager=context.config_manager, ) + def _log_resolve_diag(self, file_path, context): + """Log a detailed diagnostic for the first track whose path can't be + resolved — the only reliable way to tell apart a CWD problem, a wrong + transfer mount, or genuinely-missing files in this container.""" + from core.library.path_resolver import resolve_library_file_path_with_diagnostic + try: + _, attempt = resolve_library_file_path_with_diagnostic( + file_path, + transfer_folder=context.transfer_folder, + config_manager=context.config_manager, + ) + tf = context.transfer_folder + abs_tf = os.path.abspath(tf) if tf else None + logger.warning( + "[QualityResolve] unresolved db_path=%r | cwd=%r | transfer_folder=%r " + "(abspath=%r, isdir=%s) | base_dirs_tried=%r | abs_join_exists=%s", + file_path, os.getcwd(), tf, abs_tf, + os.path.isdir(abs_tf) if abs_tf else None, + attempt.base_dirs_tried, + os.path.exists(os.path.join(abs_tf, file_path)) if abs_tf else None, + ) + except Exception as e: + logger.warning("[QualityResolve] diagnostic failed: %s", e) + def estimate_scope(self, context: JobContext) -> int: conn = None try: