Cover Art Filler: scan falls back to the raw file path when mapping fails (Sokhi #fix)

Root cause of Sokhi's endless 0-findings: the SCAN resolved each track's path
through the path-mapping layer with NO fallback, while the APPLY
(_fix_missing_cover_art) uses `_resolve_file_path(...) or p` — i.e. it falls
back to the raw DB path when mapping returns nothing. On his Docker setup the
mapping returns None, so the scan set has_local=False and skipped every album
(never looking at the folder), even though the apply WOULD have written the
cover.jpg from the raw path.

Fix: make the scan match the apply — if mapping returns nothing but the raw DB
path is a real file (container path == stored path), use it as-is. Now the scan
actually inspects the folder, sees the missing cover.jpg, and flags it; the
apply then writes it from the embedded art.

Tests: unresolved-path-but-real-file → flagged (sidecar_from_embedded); the
fallback does NOT fire for a non-existent path (media-server-only stays skipped).
Kept the [cover-diag] logging from the prior commit to confirm on Sokhi's run.
This commit is contained in:
BoulderBadgeDad 2026-06-08 17:32:38 -07:00
parent d9a1fdb81f
commit 2f3ade8acb
2 changed files with 47 additions and 0 deletions

View file

@ -185,6 +185,14 @@ class MissingCoverArtJob(RepairJob):
download_folder=download_folder,
config_manager=context.config_manager,
) if rep_path else None
# Match the apply (_fix_missing_cover_art line ~1376: `... or p`): if
# the path-mapping layer returns nothing but the raw DB path is
# already a real file (the common Docker case where the container
# path == the stored path), use it as-is. Without this the scan never
# sees the folder and skips the album, while the apply WOULD have
# written the cover.jpg — the exact gap behind Sokhi's 0-findings.
if not resolved_rep and rep_path and os.path.isfile(rep_path):
resolved_rep = rep_path
has_local = bool(resolved_rep)
# Check embedded art and the cover.jpg sidecar SEPARATELY (not the
# combined album_has_art_on_disk, which returns True if EITHER is

View file

@ -390,3 +390,42 @@ def test_media_server_only_album_empty_thumb_still_flagged(monkeypatch):
result = mca.MissingCoverArtJob().scan(context)
assert result.findings_created == 1 # media-server-only + empty thumb → flagged
def test_unresolved_path_falls_back_to_raw_when_file_exists(tmp_path, monkeypatch):
# Docker case (Sokhi): the path-mapping layer returns None, but the raw DB
# path is already a real file in the container. The scan must use it as-is —
# like the apply does (`_resolve_file_path(...) or p`) — so the album's
# folder is actually checked instead of the album being skipped.
track = tmp_path / 'Album' / '01.flac'
track.parent.mkdir()
track.write_bytes(b'')
conn = _make_db((1, 'Album', 1, 'https://has/thumb', None, None, None, None, None))
_add_track(conn, str(track))
context = _make_context(conn)
monkeypatch.setattr(mca, 'resolve_library_file_path', lambda raw, **k: None) # mapping fails
monkeypatch.setattr(mca, 'file_has_embedded_art', lambda p: True) # files have art
# real folder has no cover.jpg → should flag for the sidecar
monkeypatch.setattr(mca, 'get_primary_source', lambda: 'spotify')
monkeypatch.setattr(mca, 'get_client_for_source', lambda s: _FakeClient()) # API finds nothing
result = mca.MissingCoverArtJob().scan(context)
assert result.findings_created == 1 # raw path used → folder checked → flagged
assert context.findings[0]['details']['sidecar_from_embedded'] is True
def test_unresolved_path_with_no_real_file_still_skips(tmp_path, monkeypatch):
# Guard: the raw-path fallback must NOT fire for a path that isn't a real
# file (no false "local" on a genuinely media-server-only album).
conn = _make_db((1, 'Album', 1, 'https://has/thumb', None, None, None, None, None))
_add_track(conn, '/does/not/exist/01.flac')
context = _make_context(conn)
monkeypatch.setattr(mca, 'resolve_library_file_path', lambda raw, **k: None)
called = []
monkeypatch.setattr(mca, 'file_has_embedded_art', lambda p: called.append(p) or True)
result = mca.MissingCoverArtJob().scan(context)
assert result.findings_created == 0 # no real file, db has thumb → skipped
assert called == [] # never treated as local