diff --git a/beets/autotag/mb.py b/beets/autotag/mb.py index e6a2e277fc..48241f3ac5 100644 --- a/beets/autotag/mb.py +++ b/beets/autotag/mb.py @@ -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) diff --git a/beets/dbcore/types.py b/beets/dbcore/types.py index 40f6a0805b..8c8bfa3f6e 100644 --- a/beets/dbcore/types.py +++ b/beets/dbcore/types.py @@ -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. """ @@ -231,3 +252,4 @@ def parse(self, string): NULL_FLOAT = NullFloat() STRING = String() BOOLEAN = Boolean() +SEMICOLON_SPACE_DSV = DelimitedString(delimiter='; ') diff --git a/beets/library.py b/beets/library.py index 888836cd99..43f8ded668 100644 --- a/beets/library.py +++ b/beets/library.py @@ -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, @@ -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, diff --git a/beetsplug/albumtypes.py b/beetsplug/albumtypes.py index 47f8dc64ec..b54e802e63 100644 --- a/beetsplug/albumtypes.py +++ b/beetsplug/albumtypes.py @@ -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]: diff --git a/docs/changelog.rst b/docs/changelog.rst index 37f5756fa7..282a542181 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -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 ` 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{} ` 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 + 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) ------------------------- diff --git a/docs/plugins/albumtypes.rst b/docs/plugins/albumtypes.rst index 8ae7cb6335..b361cb9231 100644 --- a/docs/plugins/albumtypes.rst +++ b/docs/plugins/albumtypes.rst @@ -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 ------------- diff --git a/test/test_albumtypes.py b/test/test_albumtypes.py index 91808553d2..3d329dd7b8 100644 --- a/test/test_albumtypes.py +++ b/test/test_albumtypes.py @@ -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 ) diff --git a/test/test_ui.py b/test/test_ui.py index 9804b0a125..22e20916a8 100644 --- a/test/test_ui.py +++ b/test/test_ui.py @@ -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):