Library Re-tag: pull cover art from the configured cover-art sources

User feedback (Sokhi): after changing cover-art sources, re-tag should
re-download fresh covers from THEM. The job took cover_url only from the
matched metadata source's album image, ignoring the user's configured
cover-art order. Now prefer select_preferred_art_url (the same
metadata_enhancement.album_art_order the post-process embed honors), falling
back to the source image when no order is configured (non-breaking).

'replace' cover mode already force-refreshes art on every matched album, and
the embed replaces existing art (no duplicate pictures) — so 'replace' + a
configured art order = fresh covers from those sources. Help text updated.

Tests: prefers configured source URL / falls back to source image when unset.
This commit is contained in:
BoulderBadgeDad 2026-06-04 14:46:09 -07:00
parent 3fe635fcd6
commit 93d2399cf8
2 changed files with 62 additions and 1 deletions

View file

@ -208,7 +208,11 @@ class LibraryRetagJob(RepairJob):
'Turn it off to auto-apply on scan.\n'
'- Mode: "overwrite" rewrites every field the source provides; "fill_missing" '
'only fills blank tags (keeps your existing values).\n'
'- Cover art: replace / fill-missing / skip.\n'
'- Cover art: replace / fill-missing / skip. "replace" force-refreshes '
'art on every matched album (use this after changing your cover-art '
'sources to re-pull fresh covers). When you have configured cover-art '
'sources (Settings > metadata enhancement art order), the art is pulled '
'from those; otherwise it falls back to the matched source\'s album image.\n'
'- Source: which matched source to pull from (default: your source priority).'
)
icon = 'repair-icon-retag'
@ -339,6 +343,23 @@ class LibraryRetagJob(RepairJob):
cover_url = v
break
# Honor the user's configured cover-art sources (the same
# `metadata_enhancement.album_art_order` the post-process embed uses), so
# changing those sources and re-tagging pulls fresh art FROM them rather
# than always using the matched metadata source's album image. Non-
# breaking: select_preferred_art_url returns None when no order is
# configured, so we keep the source image. Skipped when not embedding art.
if cover_mode != 'skip':
try:
from core.metadata.art_lookup import select_preferred_art_url
order = (context.config_manager.get('metadata_enhancement.album_art_order')
if context.config_manager else None)
preferred = select_preferred_art_url(artist_name, album_title, album_meta, order)
if preferred:
cover_url = preferred
except Exception as e:
logger.debug("preferred cover-art lookup failed for album %s: %s", album_id, e)
# Cover action (album-level), independent of tag changes. Decided first
# so cover-only albums (tags fine, art missing) still include their
# tracks for the apply to embed art into.

View file

@ -111,6 +111,46 @@ def test_scan_dry_run_off_auto_applies_no_finding(tmp_path, monkeypatch):
assert writes and writes[0]['title'] == 'Real Title' # actually wrote
def test_scan_prefers_configured_cover_art_source(tmp_path, monkeypatch):
"""Sokhi's request: when cover-art sources are configured, the re-tag pulls
art from them (select_preferred_art_url) instead of the matched source's
album image so changing sources + 'replace' re-downloads fresh covers."""
track = tmp_path / 'track.flac'; track.write_bytes(b'')
conn = _db_with_album(str(tmp_path / 'm.db'), str(track), current_title='Old Title')
ctx = _context(conn, {'mode': 'overwrite', 'cover_art': 'replace', 'source': 'spotify'})
_patch_source(monkeypatch, {
'title': 'Old Title', 'album_artist': 'Real Artist', 'album': 'Real Album',
'year': '2021', 'genre': 'Rock', 'track_number': 1, 'disc_number': 1,
})
monkeypatch.setattr('core.metadata.art_lookup.select_preferred_art_url',
lambda artist, album, meta, order, **k: 'http://itunes/big-cover.jpg')
result = lr.LibraryRetagJob().scan(ctx)
assert result.findings_created == 1
d = ctx.findings[0]['details']
assert d['cover_url'] == 'http://itunes/big-cover.jpg' # configured source won
assert d['cover_action'] == 'replace'
def test_scan_falls_back_to_source_image_when_no_configured_art(tmp_path, monkeypatch):
"""Non-breaking: with no configured cover-art order, keep using the matched
source's album image (select_preferred_art_url returns None)."""
track = tmp_path / 'track.flac'; track.write_bytes(b'')
conn = _db_with_album(str(tmp_path / 'm.db'), str(track), current_title='Old Title')
ctx = _context(conn, {'mode': 'overwrite', 'cover_art': 'replace', 'source': 'spotify'})
_patch_source(monkeypatch, {
'title': 'Old Title', 'album_artist': 'Real Artist', 'album': 'Real Album',
'year': '2021', 'genre': 'Rock', 'track_number': 1, 'disc_number': 1,
})
monkeypatch.setattr('core.metadata.art_lookup.select_preferred_art_url',
lambda *a, **k: None)
result = lr.LibraryRetagJob().scan(ctx)
d = ctx.findings[0]['details']
assert d['cover_url'] == 'http://art/cover.jpg' # _ALBUM_META['image_url']
def test_scan_full_depth_attaches_full_meta_to_finding(tmp_path, monkeypatch):
"""depth=full: each track plan carries a full_meta dict (title/album/artist +
source ids) for the enrichment cascade, and details record the depth."""