fix(fetchart): prevent deletion of configured fallback cover art

When `import.delete` or `import.move` is enabled, the `assign_art` method calls `task.prune(candidate.path)` unconditionally.
This incorrectly deletes the configured `fetchart.fallback` file.
Add explicit check to skip pruning when the candidate path matches the configured fallback.
This commit is contained in:
Danny Trunk 2026-01-12 17:00:23 +01:00
parent cdfb813910
commit 8f56d33ca4
3 changed files with 23 additions and 1 deletions

View file

@ -1445,6 +1445,16 @@ class FetchArtPlugin(plugins.BeetsPlugin, RequestMixin):
"move"
].get(bool)
def _is_candidate_fallback(self, candidate: Candidate) -> bool:
try:
return (
candidate.path is not None
and self.fallback is not None
and os.path.samefile(candidate.path, self.fallback)
)
except OSError:
return False
# Asynchronous; after music is added to the library.
def fetch_art(self, session: ImportSession, task: ImportTask) -> None:
"""Find art for the album being imported."""
@ -1493,7 +1503,7 @@ class FetchArtPlugin(plugins.BeetsPlugin, RequestMixin):
self._set_art(task.album, candidate, not removal_enabled)
if removal_enabled:
if removal_enabled and not self._is_candidate_fallback(candidate):
task.prune(candidate.path)
# Manual album art fetching.

View file

@ -11,6 +11,8 @@ New features:
Bug fixes:
- :doc:`plugins/fetchart`: Prevent deletion of configured fallback cover art
For packagers:
Other changes:

View file

@ -310,6 +310,16 @@ class FSArtTest(UseThePlugin):
]
assert candidates == paths
@patch("os.path.samefile")
def test_is_candidate_fallback_os_error(self, mock_samefile):
mock_samefile.side_effect = OSError("os error")
fallback = os.path.join(self.temp_dir, b"a.jpg")
self.plugin.fallback = fallback
candidate = fetchart.Candidate(logger, self.source.ID, fallback)
result = self.plugin._is_candidate_fallback(candidate)
mock_samefile.assert_called_once()
assert not result
class CombinedTest(FetchImageTestCase, CAAHelper):
ASIN = "xxxx"