Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add maxwidth setting for album art #108

Merged
merged 1 commit into from
Nov 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,12 @@ following settings.

By default no transcoding is done.

* **`albumart_maxwidth`** Downscale the embedded album art to a width
of maximum `albumart_maxwidth` pixels. The aspect ratio of the image
will be preserved. This is comparable to the setting with the same
name of the [convert plugin][convert plugin].


* **`removable`** If this is `true` (the default) and `directory` does
not exist, the `update` command will ask you to confirm the creation
of the external collection. (optional)
Expand Down
10 changes: 9 additions & 1 deletion beetsplug/alternatives.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ def parse_config(self, config: confuse.ConfigView):
self.query, _ = parse_query_string(query, Item)

self.removable = config.get(dict).get("removable", True) # type: ignore
self.album_art_maxwidth = config.get(dict).get("album_art_maxwidth", None) # type: ignore

if "directory" in config:
dir = config["directory"].as_path()
Expand Down Expand Up @@ -342,7 +343,14 @@ def _sync_art(self, item: Item, path: Path):
album = item.get_album()
if album and album.artpath and Path(str(album.artpath, "utf8")).is_file():
self._log.debug(f"Embedding art from {album.artpath} into {path}")
art.embed_item(self._log, item, album.artpath, itempath=bytes(path))

art.embed_item(
self._log,
item,
album.artpath,
maxwidth=self.album_art_maxwidth,
itempath=bytes(path),
)


class ExternalConvert(External):
Expand Down
21 changes: 19 additions & 2 deletions test/cli_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import pytest
from beets.library import Item
from beets.ui import UserError
from beets.util.artresizer import ArtResizer
from confuse import ConfigValueError
from mediafile import MediaFile

Expand Down Expand Up @@ -348,6 +349,8 @@ def test_embed_art(self, tmp_path: Path):

This test comprehensively checks that embedded artwork is up-to-date
with the artwork file, even if no changes to the database happen.

It also tests if the album_art_maxwidth is applied
"""

def touch_art(item: Item, image_path: Path):
Expand All @@ -363,7 +366,8 @@ def touch_art(item: Item, image_path: Path):
item_mtime_alt = Path(str(item.path, "utf8")).stat().st_mtime
os.utime(image_path, (item_mtime_alt + 2, item_mtime_alt + 2))

# Initially add album without artwork.
# Initially add album without artwork. Do not do resizing
self.config["alternatives"]["myexternal"]["album_art_maxwidth"] = None
album = self.add_album(myexternal="true")
album.store()
self.runcli("alt", "update", "myexternal")
Expand All @@ -373,7 +377,7 @@ def touch_art(item: Item, image_path: Path):

# Make a copy of the artwork, so that changing mtime/content won't
# affect the repository.
image_path = tmp_path / "image"
image_path = tmp_path / "image.png"
shutil.copy(self.IMAGE_FIXTURE1, image_path)
touch_art(item, image_path)

Expand All @@ -395,6 +399,19 @@ def touch_art(item: Item, image_path: Path):
item = album.items().get()
assert_has_embedded_artwork(self.get_path(item), self.IMAGE_FIXTURE2)

# now set a maxwidth and verify the final image has the right
# dimensions
touch_art(item, image_path)
self.config["alternatives"]["myexternal"]["album_art_maxwidth"] = 1
self.runcli("alt", "update", "myexternal")
mediafile = MediaFile(self.get_path(item))
tmp_embedded_image = tmp_path / "embedded_image.png"
tmp_embedded_image.write_bytes(mediafile.art) # pyright: ignore
art_resizer = ArtResizer()
(width, height) = art_resizer.get_size(bytes(tmp_embedded_image)) # pyright: ignore
assert width == 1
assert height < 3

def test_update_all(self, tmp_path: Path):
dir_a = tmp_path / "a"
dir_a.mkdir()
Expand Down
Loading