From 4cb193781051586f5f22b6a10908d31c11887d7d Mon Sep 17 00:00:00 2001 From: dev Date: Mon, 15 Jun 2026 11:40:18 +0200 Subject: [PATCH] fix(path-resolve): try full relative path first in shared resolver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- core/library/path_resolver.py | 19 ++++++++++++++++--- tests/library/test_path_resolver.py | 19 +++++++++++++++++++ 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/core/library/path_resolver.py b/core/library/path_resolver.py index 712ec3c2..0e40e678 100644 --- a/core/library/path_resolver.py +++ b/core/library/path_resolver.py @@ -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 diff --git a/tests/library/test_path_resolver.py b/tests/library/test_path_resolver.py index 713fc8ae..de77b738 100644 --- a/tests/library/test_path_resolver.py +++ b/tests/library/test_path_resolver.py @@ -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()