Introduce Album.art_filepath to simplify existence checks

This commit is contained in:
Šarūnas Nejus 2025-06-01 08:17:24 +01:00
parent d017270196
commit e40c7fd71c
No known key found for this signature in database
GPG key ID: DD28F6704DBE3435
6 changed files with 49 additions and 42 deletions

View file

@ -45,6 +45,11 @@ class LibModel(dbcore.Model["Library"]):
def writable_media_fields(cls) -> set[str]:
return set(MediaFile.fields()) & cls._fields.keys()
@property
def filepath(self) -> Path:
"""The path to the entity as pathlib.Path."""
return Path(os.fsdecode(self.path))
def _template_funcs(self):
funcs = DefaultTemplateFunctions(self, self._db).functions()
funcs.update(plugins.template_funcs())
@ -207,6 +212,8 @@ class Album(LibModel):
Reflects the library's "albums" table, including album art.
"""
artpath: bytes
_table = "albums"
_flex_table = "album_attributes"
_always_dirty = True
@ -331,6 +338,11 @@ class Album(LibModel):
f"ON {cls._table}.id = {cls._relation._table}.album_id"
)
@property
def art_filepath(self) -> Path | None:
"""The path to album's cover picture as pathlib.Path."""
return Path(os.fsdecode(self.artpath)) if self.artpath else None
@classmethod
def _getters(cls):
# In addition to plugin-provided computed fields, also expose
@ -748,11 +760,6 @@ class Item(LibModel):
f"ON {cls._table}.album_id = {cls._relation._table}.id"
)
@property
def filepath(self) -> Path:
"""The path to the item's file as pathlib.Path."""
return Path(os.fsdecode(self.path))
@property
def _cached_album(self):
"""The Album object that this item belongs to, if any, or

View file

@ -19,6 +19,7 @@ from __future__ import annotations
import os
import shutil
import unittest
from pathlib import Path
from typing import TYPE_CHECKING
from unittest.mock import patch
@ -804,12 +805,10 @@ class ArtImporterTest(UseThePlugin):
self.plugin.fetch_art(self.session, self.task)
self.plugin.assign_art(self.session, self.task)
artpath = self.lib.albums()[0].artpath
artpath = self.lib.albums()[0].art_filepath
if should_exist:
assert artpath == os.path.join(
os.path.dirname(self.i.path), b"cover.jpg"
)
self.assertExists(artpath)
assert artpath == self.i.filepath.parent / "cover.jpg"
assert artpath.exists()
else:
assert artpath is None
return artpath
@ -861,7 +860,7 @@ class ArtImporterTest(UseThePlugin):
self.plugin.batch_fetch_art(
self.lib, self.lib.albums(), force=False, quiet=False
)
self.assertExists(self.album.artpath)
assert self.album.art_filepath.exists()
class ArtForAlbumTest(UseThePlugin):

View file

@ -203,23 +203,21 @@ class EmbedartCliTest(IOMixin, PluginMixin, FetchImageHelper, BeetsTestCase):
resource_path = os.path.join(_common.RSRC, b"image.mp3")
album = self.add_album_fixture()
trackpath = album.items()[0].path
albumpath = album.path
shutil.copy(syspath(resource_path), syspath(trackpath))
self.run_command("extractart", "-n", "extracted")
self.assertExists(os.path.join(albumpath, b"extracted.png"))
self.assertExists(album.filepath / "extracted.png")
def test_extracted_extension(self):
resource_path = os.path.join(_common.RSRC, b"image-jpeg.mp3")
album = self.add_album_fixture()
trackpath = album.items()[0].path
albumpath = album.path
shutil.copy(syspath(resource_path), syspath(trackpath))
self.run_command("extractart", "-n", "extracted")
self.assertExists(os.path.join(albumpath, b"extracted.jpg"))
self.assertExists(album.filepath / "extracted.jpg")
def test_clear_art_with_yes_input(self):
self._setup_data()

View file

@ -19,6 +19,7 @@ import shutil
import stat
import unittest
from os.path import join
from pathlib import Path
import pytest
@ -314,9 +315,10 @@ class ArtFileTest(BeetsTestCase):
# Make an album.
self.ai = self.lib.add_album((self.i,))
# Make an art file too.
self.art = self.lib.get_album(self.i).art_destination("something.jpg")
touch(self.art)
self.ai.artpath = self.art
art_bytes = self.lib.get_album(self.i).art_destination("something.jpg")
self.art = Path(os.fsdecode(art_bytes))
self.art.touch()
self.ai.artpath = art_bytes
self.ai.store()
# Alternate destination dir.
self.otherdir = os.path.join(self.temp_dir, b"testotherdir")
@ -345,10 +347,10 @@ class ArtFileTest(BeetsTestCase):
self.i.load()
# Art should be in new directory.
self.assertNotExists(self.art)
newart = self.lib.get_album(self.i).artpath
self.assertExists(newart)
assert b"testotherdir" in newart
assert not self.art.exists()
newart = self.lib.get_album(self.i).art_filepath
assert newart.exists()
assert "testotherdir" in str(newart)
def test_setart_copies_image(self):
util.remove(self.art)
@ -363,7 +365,7 @@ class ArtFileTest(BeetsTestCase):
assert ai.artpath is None
ai.set_art(newart)
self.assertExists(ai.artpath)
assert ai.art_filepath.exists()
def test_setart_to_existing_art_works(self):
util.remove(self.art)
@ -380,7 +382,7 @@ class ArtFileTest(BeetsTestCase):
# Set the art again.
ai.set_art(ai.artpath)
self.assertExists(ai.artpath)
assert ai.art_filepath.exists()
def test_setart_to_existing_but_unset_art_works(self):
newart = os.path.join(self.libdir, b"newart.jpg")
@ -397,7 +399,7 @@ class ArtFileTest(BeetsTestCase):
# Set the art again.
ai.set_art(artdest)
self.assertExists(ai.artpath)
assert ai.art_filepath.exists()
def test_setart_to_conflicting_file_gets_new_path(self):
newart = os.path.join(self.libdir, b"newart.jpg")
@ -442,34 +444,34 @@ class ArtFileTest(BeetsTestCase):
os.chmod(syspath(ai.artpath), 0o777)
def test_move_last_file_moves_albumart(self):
oldartpath = self.lib.albums()[0].artpath
self.assertExists(oldartpath)
oldartpath = self.lib.albums()[0].art_filepath
assert oldartpath.exists()
self.ai.album = "different_album"
self.ai.store()
self.ai.items()[0].move()
artpath = self.lib.albums()[0].artpath
assert b"different_album" in artpath
self.assertExists(artpath)
self.assertNotExists(oldartpath)
artpath = self.lib.albums()[0].art_filepath
assert "different_album" in str(artpath)
assert artpath.exists()
assert not oldartpath.exists()
def test_move_not_last_file_does_not_move_albumart(self):
i2 = item()
i2.albumid = self.ai.id
self.lib.add(i2)
oldartpath = self.lib.albums()[0].artpath
self.assertExists(oldartpath)
oldartpath = self.lib.albums()[0].art_filepath
assert oldartpath.exists()
self.i.album = "different_album"
self.i.album_id = None # detach from album
self.i.move()
artpath = self.lib.albums()[0].artpath
assert b"different_album" not in artpath
artpath = self.lib.albums()[0].art_filepath
assert "different_album" not in str(artpath)
assert artpath == oldartpath
self.assertExists(oldartpath)
assert oldartpath.exists()
class RemoveTest(BeetsTestCase):

View file

@ -23,6 +23,7 @@ import sys
import unicodedata
import unittest
from io import StringIO
from pathlib import Path
from tarfile import TarFile
from tempfile import mkstemp
from unittest.mock import Mock, patch
@ -1566,14 +1567,14 @@ class ReimportTest(AutotagImportTestCase):
replaced_album = self._album()
replaced_album.set_art(art_source)
replaced_album.store()
old_artpath = replaced_album.artpath
old_artpath = replaced_album.art_filepath
self.importer.run()
new_album = self._album()
new_artpath = new_album.art_destination(art_source)
assert new_album.artpath == new_artpath
self.assertExists(new_artpath)
assert new_album.art_filepath.exists()
if new_artpath != old_artpath:
self.assertNotExists(old_artpath)
assert not old_artpath.exists()
def test_reimported_album_has_new_flexattr(self):
self._setup_session()

View file

@ -601,12 +601,12 @@ class UpdateTest(IOMixin, BeetsTestCase):
assert not self.lib.albums()
def test_delete_removes_album_art(self):
artpath = self.album.artpath
self.assertExists(artpath)
art_filepath = self.album.art_filepath
assert art_filepath.exists()
util.remove(self.i.path)
util.remove(self.i2.path)
self._update()
self.assertNotExists(artpath)
assert not art_filepath.exists()
def test_modified_metadata_detected(self):
mf = MediaFile(syspath(self.i.path))