fix(path-resolve): CWD-independent base dirs + quality-scan resolve diagnostic
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 <noreply@anthropic.com>
This commit is contained in:
parent
5b3061ee2e
commit
973d28f61d
2 changed files with 49 additions and 2 deletions
|
|
@ -138,10 +138,25 @@ def _collect_base_dirs(
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.debug("music paths read failed: %s", 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.
|
# De-duplicate while preserving order, drop empties / non-existent dirs.
|
||||||
seen: set[str] = set()
|
seen: set[str] = set()
|
||||||
out: list[str] = []
|
out: list[str] = []
|
||||||
for c in candidates:
|
for c in expanded:
|
||||||
if not c or c in seen:
|
if not c or c in seen:
|
||||||
continue
|
continue
|
||||||
seen.add(c)
|
seen.add(c)
|
||||||
|
|
|
||||||
|
|
@ -85,14 +85,22 @@ class QualityUpgradeScannerJob(RepairJob):
|
||||||
context.update_progress(0, total)
|
context.update_progress(0, total)
|
||||||
|
|
||||||
probe_failed = 0
|
probe_failed = 0
|
||||||
|
_diag_logged = False
|
||||||
for i, (track_id, info) in enumerate(track_list):
|
for i, (track_id, info) in enumerate(track_list):
|
||||||
if context.check_stop():
|
if context.check_stop():
|
||||||
return result
|
return result
|
||||||
if i % 20 == 0 and context.wait_if_paused():
|
if i % 20 == 0 and context.wait_if_paused():
|
||||||
return result
|
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:
|
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
|
result.skipped += 1
|
||||||
probe_failed += 1
|
probe_failed += 1
|
||||||
continue
|
continue
|
||||||
|
|
@ -235,6 +243,30 @@ class QualityUpgradeScannerJob(RepairJob):
|
||||||
config_manager=context.config_manager,
|
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:
|
def estimate_scope(self, context: JobContext) -> int:
|
||||||
conn = None
|
conn = None
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue