Skip to content

Implemented issue 2572, Query to find albums with/without embedded cover art#6451

Open
Wouter2devries wants to merge 5 commits intobeetbox:masterfrom
Wouter2devries:issue2572
Open

Implemented issue 2572, Query to find albums with/without embedded cover art#6451
Wouter2devries wants to merge 5 commits intobeetbox:masterfrom
Wouter2devries:issue2572

Conversation

@Wouter2devries
Copy link

Fixes #2572

Added has_images function to query items by embedded cover art presence.
User can now do beets list has_images:true or beets list has_images:false to find albums with our without cover art.

First time contributing, tried to follow the guidelines as closely as possible. If anything is not up to standard I will edit it as soon as possible.

To Do

The following 3 are included in the pull request:

  • Documentation.
  • Changelog.
  • Tests.

No test cases yet.
Query to find albums with/without embedded cover art beetbox#2572
@Wouter2devries Wouter2devries requested a review from a team as a code owner March 20, 2026 14:20
Copy link
Member

@snejus snejus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love this, thank you! A couple of comments

getters = plugins.item_field_getters()
getters["singleton"] = lambda i: i.album_id is None
getters["filesize"] = Item.try_filesize # In bytes.
getters["has_images"] = Item.has_cover_art
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use has_cover_art as we refer to images as cover_art everywhere

Suggested change
getters["has_images"] = Item.has_cover_art
getters["has_cover_art"] = Item.has_cover_art

If file unreadable or no images, return False.
"""
try:
mediafile = MediaFile(syspath(self.path))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for syspath here, the value should already be standardized upon access. And I think you can simplify this to

Suggested change
mediafile = MediaFile(syspath(self.path))
with suppress(OSError):
return bool(MediaFile(self.path).images)
return False


To find all tracks with embedded cover art:

::
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Best to use

Suggested change
::
.. code-block: shell

Comment on lines +533 to +543
def test_has_images_getter_exists(self):
"""Verify has_images in getters dict."""
getters = Item._getters()
assert "has_images" in getters
assert getters["has_images"] == Item.has_cover_art

def test_has_images_returns_boolean(self):
"""Method always return boolean."""
item = _common.item()
result = item.has_cover_art()
assert isinstance(result, bool) No newline at end of file
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are implementation tests. Instead, test the behaviour:

  1. Create an item without an image
  2. Create an item with an image
  3. Assert that the query lib.query("has_cover_art:false") returns the first item and vice versa.

Per suggestion as this is what used everywhere else
Removed the return boolean test as it already tests for both true and false.
@Wouter2devries
Copy link
Author

Thank you for all the suggestions. Yeah found the tests a bit lacking myself aswell, did not look at the helper.py enough the first time. Hopefully these are better!

Comment on lines +543 to +548
def test_has_cover_art_getter_exists(self):
"""Verify has_cover_art in getters dict."""
getters = Item._getters()
assert "has_cover_art" in getters
assert getters["has_cover_art"] == Item.has_cover_art

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for this - the next test covers this explicitly :)

Comment on lines +549 to +553
def test_query_true(self, lib):
assert {i.title for i in lib.items("has_cover_art:true")} == {"with_art"}

def test_query_false(self, lib):
assert {i.title for i in lib.items("has_cover_art:false")} == {"without_art"} No newline at end of file
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can make use of pytest.mark.parametrize here, something like

@pytest.mark.parametrize("query, expected_titles", [("has_cover_art:true", ("with_art")), ...])
def test_has_cover_art_query(self, query, expected_titles):
    ...

@snejus
Copy link
Member

snejus commented Mar 21, 2026

And note that the changelog has a merge conflict.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Query to find albums with/without embedded cover art

2 participants