Skip to content
2 changes: 1 addition & 1 deletion beets/autotag/mb.py
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,7 @@ def album_info(release: Dict) -> beets.autotag.hooks.AlbumInfo:
if release['release-group']['secondary-type-list']:
for sec_type in release['release-group']['secondary-type-list']:
albumtypes.append(sec_type.lower())
info.albumtypes = '; '.join(albumtypes)
info.albumtypes = albumtypes

# Release events.
info.country, release_date = _preferred_release_event(release)
Expand Down
22 changes: 22 additions & 0 deletions beets/dbcore/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,27 @@ def normalize(self, value):
return self.model_type(value)


class DelimitedString(String):
"""A list of Unicode strings, represented in-database by a single string
containing delimiter-separated values.
"""
model_type = list

def __init__(self, delimiter):
self.delimiter = delimiter

def format(self, value):
return self.delimiter.join(value)

def parse(self, string):
if not string:
return []
return string.split(self.delimiter)

def to_sql(self, model_value):
return self.delimiter.join(model_value)


class Boolean(Type):
"""A boolean type.
"""
Expand All @@ -231,3 +252,4 @@ def parse(self, string):
NULL_FLOAT = NullFloat()
STRING = String()
BOOLEAN = Boolean()
SEMICOLON_SPACE_DSV = DelimitedString(delimiter='; ')
4 changes: 2 additions & 2 deletions beets/library.py
Original file line number Diff line number Diff line change
Expand Up @@ -504,7 +504,7 @@ class Item(LibModel):
'mb_releasetrackid': types.STRING,
'trackdisambig': types.STRING,
'albumtype': types.STRING,
'albumtypes': types.STRING,
'albumtypes': types.SEMICOLON_SPACE_DSV,
'label': types.STRING,
'acoustid_fingerprint': types.STRING,
'acoustid_id': types.STRING,
Expand Down Expand Up @@ -1064,7 +1064,7 @@ class Album(LibModel):
'mb_albumid': types.STRING,
'mb_albumartistid': types.STRING,
'albumtype': types.STRING,
'albumtypes': types.STRING,
'albumtypes': types.SEMICOLON_SPACE_DSV,
'label': types.STRING,
'mb_releasegroupid': types.STRING,
'asin': types.STRING,
Expand Down
2 changes: 1 addition & 1 deletion beetsplug/albumtypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def _atypes(self, item: Album):
bracket_r = ''

res = ''
albumtypes = item.albumtypes.split('; ')
albumtypes = item.albumtypes

@JOJ0 JOJ0 Feb 25, 2023

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

item.albumtypes will always return a list already,

is_va = item.mb_albumartistid == VARIOUS_ARTISTS_ID
for type in types:
if type[0] in albumtypes and type[1]:
Expand Down
7 changes: 7 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,13 @@ Bug fixes:
:bug:`4561` :bug:`4600`
* Fix issue where deletion of flexible fields on an album doesn't cascade to items
:bug:`4662`
* Fix issue where ``beet write`` continuosly retags the ``albumtypes`` metadata
field in files. Additionally broken data could have been added to the library
when the tag was read from file back into the library using ``beet update``.
It is required for all users to **check if such broken data is present in the
library**. Following the instructions `described here
<https://github.com/beetbox/beets/pull/4582#issuecomment-1445023493>`_, a
sanity check and potential fix is easily possible. :bug:`4528`

For packagers:

Expand Down
5 changes: 5 additions & 0 deletions docs/plugins/albumtypes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ you can use in your path formats or elsewhere.

.. _MusicBrainz documentation: https://musicbrainz.org/doc/Release_Group/Type

A bug introduced in beets 1.6.0 could have possibly imported broken data into
the ``albumtypes`` library field. Please follow the instructions `described
here <https://github.com/beetbox/beets/pull/4582#issuecomment-1445023493>`_ for
a sanity check and potential fix. :bug:`4528`

Configuration
-------------

Expand Down
2 changes: 1 addition & 1 deletion test/test_albumtypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,6 @@ def _set_config(self, types: [(str, str)], ignore_va: [str], bracket: str):

def _create_album(self, album_types: [str], artist_id: str = 0):
return self.add_album(
albumtypes='; '.join(album_types),
albumtypes=album_types,

@JOJ0 JOJ0 Feb 25, 2023

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

and also when creating the test-album we can rely on the DelimitedString type to translate to a ;-delimited string for us.

mb_albumartistid=artist_id
)
21 changes: 12 additions & 9 deletions test/test_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -701,27 +701,30 @@ def test_mtime_match_skips_update(self):
item = self.lib.items().get()
self.assertEqual(item.title, 'full')

@unittest.expectedFailure
def test_multivalued_albumtype_roundtrip(self):
# https://github.com/beetbox/beets/issues/4528

# albumtypes is empty for our test fixtures, so populate it first
album = self.album
# setting albumtypes does not set albumtype currently...
# FIXME: When actually fixing the issue 4528, consider whether this
# should be set to "album" or ["album"]
album.albumtype = "album"
album.albumtypes = "album"
correct_albumtypes = ["album", "live"]

# Setting albumtypes does not set albumtype, currently.
# Using x[0] mirrors https://github.com/beetbox/mediafile/blob/057432ad53b3b84385e5582f69f44dc00d0a725d/mediafile.py#L1928 # noqa: E501
correct_albumtype = correct_albumtypes[0]

album.albumtype = correct_albumtype
album.albumtypes = correct_albumtypes
album.try_sync(write=True, move=False)

album.load()
albumtype_before = album.albumtype
self.assertEqual(albumtype_before, "album")
self.assertEqual(album.albumtype, correct_albumtype)
self.assertEqual(album.albumtypes, correct_albumtypes)

self._update()

album.load()
self.assertEqual(albumtype_before, album.albumtype)
self.assertEqual(album.albumtype, correct_albumtype)
self.assertEqual(album.albumtypes, correct_albumtypes)


class PrintTest(_common.TestCase):
Expand Down