fix(quality-scanner): resolve relative library paths before probing
Diagnostics revealed the real cause: the tracks table stores file_path
RELATIVE to the library root (e.g. "Asketa/Another Side/01-01 - Another
Side.flac"), so probing the raw path failed for the entire library — every
track came back unprobeable and was left unflagged ("20/20 could not be
probed").
The scanner now resolves each path via _resolve_library_file_path (checks
transfer/download/library dirs, same helper the rest of the app uses) before
probing, falling back to docker_resolve_path. Injected via deps for testability.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
9af31c3706
commit
ff061324ba
3 changed files with 45 additions and 8 deletions
|
|
@ -77,6 +77,10 @@ class QualityScannerDeps:
|
|||
# so the scan checks against the SAME ranked-target core as the download
|
||||
# quality guard — not just the file extension. Injected for testability.
|
||||
probe_audio_quality: Callable = None
|
||||
# Resolve a DB-stored (often RELATIVE, e.g. "Artist/Album/Track.flac") path
|
||||
# to an absolute on-disk file by checking the transfer/download/library
|
||||
# dirs. Without this the probe opens a relative path and fails.
|
||||
resolve_library_file_path: Callable = None
|
||||
|
||||
|
||||
def _extract_lookup_value(value: Any, *names: str, default: Any = None) -> Any:
|
||||
|
|
@ -438,14 +442,21 @@ def run_quality_scanner(scope='watchlist', profile_id=1, deps: QualityScannerDep
|
|||
|
||||
# Probe the REAL audio quality (bit depth / sample rate / bitrate)
|
||||
# and check it against the ranked targets — same core as the
|
||||
# download guard. The library path may be a Windows/host path that
|
||||
# needs Docker resolution before mutagen can open it.
|
||||
resolved_path = file_path
|
||||
try:
|
||||
from core.imports.paths import docker_resolve_path
|
||||
resolved_path = docker_resolve_path(file_path) if file_path else file_path
|
||||
except Exception:
|
||||
resolved_path = file_path
|
||||
# download guard. The DB stores paths RELATIVE to the library root
|
||||
# (e.g. "Artist/Album/Track.flac"), so resolve to an absolute
|
||||
# on-disk path first or mutagen can't open the file.
|
||||
resolved_path = None
|
||||
if deps.resolve_library_file_path:
|
||||
try:
|
||||
resolved_path = deps.resolve_library_file_path(file_path)
|
||||
except Exception:
|
||||
resolved_path = None
|
||||
if not resolved_path:
|
||||
try:
|
||||
from core.imports.paths import docker_resolve_path
|
||||
resolved_path = docker_resolve_path(file_path) if file_path else file_path
|
||||
except Exception:
|
||||
resolved_path = file_path
|
||||
|
||||
aq = deps.probe_audio_quality(resolved_path) if deps.probe_audio_quality else None
|
||||
if aq is not None:
|
||||
|
|
|
|||
|
|
@ -175,6 +175,7 @@ def _build_deps(
|
|||
get_quality_tier_from_extension=lambda fp: quality_tier_result,
|
||||
add_activity_item=lambda *a, **kw: None,
|
||||
probe_audio_quality=probe_fn or _default_probe,
|
||||
resolve_library_file_path=lambda fp: fp, # identity — tests use direct paths
|
||||
)
|
||||
return deps
|
||||
|
||||
|
|
@ -263,6 +264,30 @@ def test_high_quality_tracks_skipped(mock_db_and_wishlist):
|
|||
assert state['low_quality'] == 0
|
||||
|
||||
|
||||
def test_scanner_resolves_relative_path_before_probing(mock_db_and_wishlist):
|
||||
"""DB stores RELATIVE paths (Artist/Album/Track.flac); the scanner must
|
||||
resolve them to absolute via resolve_library_file_path before probing,
|
||||
otherwise mutagen can't open the file and the whole library passes."""
|
||||
from core.quality.model import AudioQuality
|
||||
|
||||
db, _ = mock_db_and_wishlist
|
||||
db._watchlist_artists = [_WatchlistArtist('A')]
|
||||
db._tracks = [_track_row(file_path='Artist/Album/Track.flac')]
|
||||
state = {}
|
||||
probed = []
|
||||
|
||||
def _probe(fp):
|
||||
probed.append(fp)
|
||||
return AudioQuality('flac', bit_depth=16, sample_rate=44100)
|
||||
|
||||
deps = _build_deps(state=state, probe_fn=_probe)
|
||||
deps.resolve_library_file_path = lambda fp: '/music/' + fp
|
||||
|
||||
qs.run_quality_scanner('watchlist', 1, deps)
|
||||
|
||||
assert probed == ['/music/Artist/Album/Track.flac']
|
||||
|
||||
|
||||
def test_low_quality_tracks_attempted(mock_db_and_wishlist):
|
||||
"""Low-quality tracks (tier_num > min) trigger a metadata search."""
|
||||
db, _ = mock_db_and_wishlist
|
||||
|
|
|
|||
|
|
@ -17662,6 +17662,7 @@ def _build_quality_scanner_deps():
|
|||
get_quality_tier_from_extension=_get_quality_tier_from_extension,
|
||||
add_activity_item=add_activity_item,
|
||||
probe_audio_quality=_probe_audio_quality,
|
||||
resolve_library_file_path=_resolve_library_file_path,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue