Skip to content

Commit

Permalink
Merge pull request beetbox#4582 from jpluscplusm/jcm_fix_albumtypes
Browse files Browse the repository at this point in the history
Store `albumtypes` multi-value field consistently in-DB & in-tag, preventing continual file re-tagging
  • Loading branch information
JOJ0 authored and daniele-athome committed Aug 14, 2024
1 parent e76e4a2 commit 2bf3807
Show file tree
Hide file tree
Showing 8 changed files with 233 additions and 5 deletions.
2 changes: 1 addition & 1 deletion beets/autotag/mb.py
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,7 @@ def album_info(release):
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 @@ -494,7 +494,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 @@ -1045,7 +1045,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
is_va = item.mb_albumartistid == VARIOUS_ARTISTS_ID
for type in types:
if type[0] in albumtypes and type[1]:
Expand Down
176 changes: 176 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,182 @@
Changelog
=========

1.6.1 (in development)
----------------------

Changelog goes here! Please add your entry to the bottom of one of the lists below!

With this release, beets now requires Python 3.7 or later (it removes support
for Python 3.6).

New features:

* Added additional error handling for `spotify` plugin.
:bug:`4686`
* We now import the remixer field from Musicbrainz into the library.
:bug:`4428`
* :doc:`/plugins/mbsubmit`: Added a new `mbsubmit` command to print track information to be submitted to MusicBrainz after initial import.
:bug:`4455`
* Added `spotify_updated` field to track when the information was last updated.
* We now import and tag the `album` information when importing singletons using Spotify source.
:bug:`4398`
* :doc:`/plugins/spotify`: The plugin now provides an additional command
`spotifysync` that allows getting track popularity and audio features
information from Spotify.
:bug:`4094`
* :doc:`/plugins/spotify`: The plugin now records Spotify-specific IDs in the
`spotify_album_id`, `spotify_artist_id`, and `spotify_track_id` fields.
:bug:`4348`
* Create the parental directories for database if they do not exist.
:bug:`3808` :bug:`4327`
* :ref:`musicbrainz-config`: a new :ref:`musicbrainz.enabled` option allows disabling
the MusicBrainz metadata source during the autotagging process
* :doc:`/plugins/kodiupdate`: Now supports multiple kodi instances
:bug:`4101`
* Add the item fields ``bitrate_mode``, ``encoder_info`` and ``encoder_settings``.
* Add query prefixes ``=`` and ``~``.
* A new configuration option, :ref:`duplicate_keys`, lets you change which
fields the beets importer uses to identify duplicates.
:bug:`1133` :bug:`4199`
* Add :ref:`exact match <exact-match>` queries, using the prefixes ``=`` and
``=~``.
:bug:`4251`
* :doc:`/plugins/discogs`: Permit appending style to genre.
* :doc:`plugins/discogs`: Implement item_candidates for matching singletons.
* :doc:`/plugins/convert`: Add a new `auto_keep` option that automatically
converts files but keeps the *originals* in the library.
:bug:`1840` :bug:`4302`
* Added a ``-P`` (or ``--disable-plugins``) flag to specify one/multiple plugin(s) to be
disabled at startup.
* :ref:`import-options`: Add support for re-running the importer on paths in
log files that were created with the ``-l`` (or ``--logfile``) argument.
:bug:`4379` :bug:`4387`
* Add :ref:`%sunique{} <sunique>` template to disambiguate between singletons.
:bug:`4438`
* Add a new ``import.ignored_alias_types`` config option to allow for
specific alias types to be skipped over when importing items/albums.
* :doc:`/plugins/smartplaylist`: A new ``--pretend`` option lets the user see
what a new or changed smart playlist saved in the config is actually
returning.
:bug:`4573`
* :doc:`/plugins/fromfilename`: Add debug log messages that inform when the
plugin replaced bad (missing) artist, title or tracknumber metadata.
:bug:`4561` :bug:`4600`

Bug fixes:

* :doc:`/plugins/fetchart`: Fix fetching from Cover Art Archive when the
`maxwidth` option is set to one of the supported Cover Art Archive widths.
* :doc:`/plugins/discogs`: Fix "Discogs plugin replacing Feat. or Ft. with
a comma" by fixing an oversight that removed a functionality from the code
base when the MetadataSourcePlugin abstract class was introduced in PR's
#3335 and #3371.
:bug:`4401`
* :doc:`/plugins/convert`: Set default ``max_bitrate`` value to ``None`` to
avoid transcoding when this parameter is not set. :bug:`4472`
* :doc:`/plugins/replaygain`: Avoid a crash when errors occur in the analysis
backend.
:bug:`4506`
* We now use Python's defaults for command-line argument encoding, which
should reduce the chance for errors and "file not found" failures when
invoking other command-line tools, especially on Windows.
:bug:`4507`
* We now respect the Spotify API's rate limiting, which avoids crashing when the API reports code 429 (too many requests).
:bug:`4370`
* Fix implicit paths OR queries (e.g. ``beet list /path/ , /other-path/``)
which have previously been returning the entire library.
:bug:`1865`
* The Discogs release ID is now populated correctly to the discogs_albumid
field again (it was no longer working after Discogs changed their release URL
format).
:bug:`4225`
* The autotagger no longer considers all matches without a MusicBrainz ID as
duplicates of each other.
:bug:`4299`
* :doc:`/plugins/convert`: Resize album art when embedding
:bug:`2116`
* :doc:`/plugins/deezer`: Fix auto tagger pagination issues (fetch beyond the
first 25 tracks of a release).
* :doc:`/plugins/spotify`: Fix auto tagger pagination issues (fetch beyond the
first 50 tracks of a release).
* :doc:`/plugins/lyrics`: Fix Genius search by using query params instead of body.
* :doc:`/plugins/unimported`: The new ``ignore_subdirectories`` configuration
option added in 1.6.0 now has a default value if it hasn't been set.
* :doc:`/plugins/deezer`: Tolerate missing fields when searching for singleton
tracks.
:bug:`4116`
* :doc:`/plugins/replaygain`: The type of the internal ``r128_track_gain`` and
``r128_album_gain`` fields was changed from integer to float to fix loss of
precision due to truncation.
:bug:`4169`
* Fix a regression in the previous release that caused a `TypeError` when
moving files across filesystems.
:bug:`4168`
* :doc:`/plugins/convert`: Deleting the original files during conversion no
longer logs output when the ``quiet`` flag is enabled.
* :doc:`plugins/web`: Fix handling of "query" requests. Previously queries
consisting of more than one token (separated by a slash) always returned an
empty result.
* :doc:`/plugins/discogs`: Skip Discogs query on insufficiently tagged files
(artist and album tags missing) to prevent arbitrary candidate results.
:bug:`4227`
* :doc:`plugins/lyrics`: Fixed issues with the Tekstowo.pl and Genius
backends where some non-lyrics content got included in the lyrics
* :doc:`plugins/limit`: Better header formatting to improve index
* :doc:`plugins/replaygain`: Correctly handle the ``overwrite`` config option,
which forces recomputing ReplayGain values on import even for tracks
that already have the tags.
* :doc:`plugins/embedart`: Fix a crash when using recent versions of
ImageMagick and the ``compare_threshold`` option.
:bug:`4272`
* :doc:`plugins/lyrics`: Fixed issue with Genius header being included in lyrics,
added test case of up-to-date Genius html
* :doc:`plugins/importadded`: Fix a bug with recently added reflink import option
that casues a crash when ImportAdded plugin enabled.
:bug:`4389`
* :doc:`plugins/convert`: Fix a bug with the `wma` format alias.
* :doc:`/plugins/web`: Fix get file from item.
* :doc:`/plugins/lastgenre`: Fix a duplicated entry for trip hop in the
default genre list.
:bug:`4510`
* :doc:`plugins/lyrics`: Fixed issue with Tekstowo backend not actually checking
if the found song matches.
:bug:`4406`
* :doc:`/plugins/fromfilename`: Fix failed detection of <track> <title>
filename patterns.
: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:

* As noted above, the minimum Python version is now 3.7.
* We fixed a version for the dependency on the `Confuse`_ library.
:bug:`4167`
* The minimum required version of :pypi:`mediafile` is now 0.9.0.

Other changes:

* :doc:`/plugins/absubmit`: Deprecate the ``absubmit`` plugin since
AcousticBrainz has stopped accepting new submissions.
:bug:`4627`
* :doc:`/plugins/acousticbrainz`: Deprecate the ``acousticbrainz`` plugin
since the AcousticBrainz project has shut down.
:bug:`4627`
* :doc:`/plugins/limit`: Limit query results to head or tail (``lslimit``
command only)
* :doc:`/plugins/fish`: Add ``--output`` option.
* :doc:`/plugins/lyrics`: Remove Musixmatch from default enabled sources as
they are currently blocking requests from the beets user agent.
:bug:`4585`

1.6.0 (November 27, 2021)
-------------------------

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,
mb_albumartistid=artist_id
)
25 changes: 25 additions & 0 deletions test/test_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -682,6 +682,31 @@ def test_mtime_match_skips_update(self):
item = self.lib.items().get()
self.assertEqual(item.title, 'full')

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
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()
self.assertEqual(album.albumtype, correct_albumtype)
self.assertEqual(album.albumtypes, correct_albumtypes)

self._update()

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


class PrintTest(_common.TestCase):
def setUp(self):
Expand Down

0 comments on commit 2bf3807

Please sign in to comment.