fix(path-resolve): try full relative path first in shared resolver

ROOT CAUSE of the quality scanner's "18/18 could not be probed". The shared
resolver (core/library/path_resolver.py) suffix-walked starting at index 1,
which is correct for absolute media-server paths (/music/Artist/... — index 0
is the empty leading segment) but WRONG for SoulSync's own library, which
stores RELATIVE paths like "Asketa/Another Side/track.flac". Index 0 there is
the artist folder; dropping it meant the resolver joined base/Another Side/...
(no artist) and nothing ever matched — so every library track came back
unresolved and the probe opened a relative path that didn't exist from CWD.

Start the suffix walk at index 0 so the FULL relative path is tried first.
Safe for absolute paths (i=0 yields base//Artist/... which harmlessly misses
and falls through to i=1) and Windows drive parts (E: fails on POSIX, falls
through). Other tools (orphan/fake-lossless detectors) were unaffected because
they os.walk the transfer folder directly and never used this resolver.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
dev 2026-06-15 11:40:18 +02:00
parent 59858b033b
commit 4cb1937810
2 changed files with 35 additions and 3 deletions

View file

@ -224,10 +224,23 @@ def resolve_library_file_path_with_diagnostic(
if not base_dirs:
return None, attempt
# Skip index 0 to avoid drive-letter / leading-slash artifacts
# (e.g. "E:" or "" from a leading "/").
# Try progressively shorter path suffixes against each base dir.
#
# Start at index 0 so a clean RELATIVE library path is tried in FULL first.
# SoulSync's own library scanner stores paths like
# "Asketa/Another Side/01 - Track.flac" (no leading slash) — index 0 is the
# artist folder and dropping it (the old range(1, ...)) meant the artist
# segment was never joined, so nothing under transfer/ ever resolved and
# every track looked unreadable to the quality scanner.
#
# For ABSOLUTE media-server paths ("/music/Artist/Album/track.flac") index 0
# is the empty leading segment and i=0 yields os.path.join(base, "", ...) ==
# base/Artist/... which simply won't exist and harmlessly falls through to
# i=1 ("music/...") etc. A Windows drive part ("E:") at i=0 likewise just
# fails on POSIX and falls through. So starting at 0 is safe for every form
# and only ADDS the relative-full-path match that was missing.
for base in base_dirs:
for i in range(1, len(path_parts)):
for i in range(0, len(path_parts)):
candidate = os.path.join(base, *path_parts[i:])
if os.path.exists(candidate):
return candidate, attempt

View file

@ -74,6 +74,25 @@ def test_finds_file_via_transfer_folder_suffix_walk(tmp_path: Path) -> None:
assert result == str(actual)
def test_finds_file_via_relative_db_path_full_suffix(tmp_path: Path) -> None:
"""SoulSync's own library scanner stores RELATIVE paths with NO leading
slash, e.g. "Artist/Album/track.flac". Index 0 is the artist folder, so the
resolver must try the FULL path first the old range(1, ...) dropped the
artist segment and every such track came back unresolved (quality scanner:
"could not be probed"). Regression guard for that fix."""
transfer = tmp_path / "Transfer"
(transfer / "Asketa" / "Another Side").mkdir(parents=True)
actual = transfer / "Asketa" / "Another Side" / "01 - Another Side.flac"
actual.write_bytes(b"audio")
result = resolve_library_file_path(
"Asketa/Another Side/01 - Another Side.flac",
transfer_folder=str(transfer),
)
assert result == str(actual)
def test_finds_file_via_download_folder_when_transfer_misses(tmp_path: Path) -> None:
transfer = tmp_path / "Transfer"
transfer.mkdir()