Cover Art Filler: always write the cover.jpg sidecar (Sokhi)

Sokhi: filler works but doesn't write cover.jpg. Cause: the sidecar write
(download_cover_art) respects the import-time "Download cover.jpg to album
folder" toggle, while embedding ignores it — so with that toggle off, art
embeds but no sidecar is written.

A job literally called Cover Art Filler should produce the complete art when
you explicitly run it. download_cover_art gains force=True (bypasses the toggle)
and the filler's apply path passes it. The import pipeline still calls without
force, so it keeps honoring the user's setting.

Tests: filler passes force=True; existing cover-write mocks updated for the new
kwarg. 1732 art/cover/repair/import tests pass.
This commit is contained in:
BoulderBadgeDad 2026-06-08 11:07:00 -07:00
parent 7d481ae02f
commit cdee7b3550
3 changed files with 32 additions and 5 deletions

View file

@ -176,7 +176,10 @@ def apply_art_to_album_files(
# the read-only flag it sets back.
cover_ctx = context if isinstance(context, dict) else {}
try:
download_cover_art(album_info, target_dir, cover_ctx)
# force=True: the Cover Art Filler always writes the cover.jpg sidecar,
# regardless of the import-time "Download cover.jpg" toggle — running
# the art filler is an explicit request for the complete art (Sokhi).
download_cover_art(album_info, target_dir, cover_ctx, force=True)
result["cover_written"] = folder_has_cover_sidecar(target_dir)
except Exception as exc:
if getattr(exc, "errno", None) == errno.EROFS:

View file

@ -470,9 +470,17 @@ def embed_album_art_metadata(audio_file, metadata: dict):
return False
def download_cover_art(album_info: dict, target_dir: str, context: dict = None):
def download_cover_art(album_info: dict, target_dir: str, context: dict = None, force: bool = False):
"""Write cover.jpg into ``target_dir``.
``force`` bypasses the import-time "Download cover.jpg to album folder"
toggle used by the Cover Art Filler, whose whole job is to add cover art
(if you explicitly run the filler you want the sidecar regardless of the
auto-import preference). The import pipeline calls this WITHOUT force, so it
still honors the user's setting.
"""
cfg = get_config_manager()
if cfg.get("metadata_enhancement.cover_art_download", True) is False:
if not force and cfg.get("metadata_enhancement.cover_art_download", True) is False:
return
try:

View file

@ -109,7 +109,7 @@ def test_apply_embeds_into_each_file_and_writes_cover(tmp_path, monkeypatch):
monkeypatch.setattr(aa, 'embed_album_art_metadata', lambda a, m: embed_calls.append(m) or True)
# download_cover_art is the standard cover.jpg writer — stub it to drop one.
monkeypatch.setattr(aa, 'download_cover_art',
lambda album_info, folder, ctx=None: open(f"{folder}/cover.jpg", 'wb').close())
lambda album_info, folder, ctx=None, **k: open(f"{folder}/cover.jpg", 'wb').close())
meta = {'artist': 'A', 'album': 'B', 'album_art_url': 'http://x/y.jpg'}
res = aa.apply_art_to_album_files([str(f1), str(f2)], meta, {'album_name': 'B'}, folder=str(tmp_path))
@ -203,7 +203,7 @@ def test_cover_only_read_only_is_detected(tmp_path, monkeypatch):
audio = SimpleNamespace(pictures=['pic'], tags={'ok': 1}) # already has art → embed skipped
monkeypatch.setattr(aa, 'get_mutagen_symbols', lambda: _fake_symbols(audio))
def _fake_download(album_info, target_dir, ctx):
def _fake_download(album_info, target_dir, ctx, **k):
# mimic download_cover_art's EROFS handling: record on context, swallow
if isinstance(ctx, dict):
ctx['_cover_read_only'] = True
@ -231,3 +231,19 @@ def test_apply_normal_failure_not_flagged_read_only(tmp_path, monkeypatch):
assert res['failed'] == 1
def test_filler_forces_cover_sidecar_write(tmp_path, monkeypatch):
# The Cover Art Filler must write cover.jpg regardless of the import-time
# "Download cover.jpg" toggle — so apply passes force=True (Sokhi).
f = tmp_path / 'a.mp3'; f.write_bytes(b'')
audio = SimpleNamespace(pictures=[], tags={'ok': 1}, save=lambda: None)
monkeypatch.setattr(aa, 'get_mutagen_symbols', lambda: _fake_symbols(audio))
monkeypatch.setattr(aa, 'embed_album_art_metadata', lambda *a, **k: True)
captured = {}
monkeypatch.setattr(aa, 'download_cover_art',
lambda album_info, target_dir, ctx, **k: captured.update(k))
aa.apply_art_to_album_files([str(f)], {}, {}, folder=str(tmp_path))
assert captured.get('force') is True