diff --git a/beets/autotag/hooks.py b/beets/autotag/hooks.py index 363bcaab4f..695d5edd61 100644 --- a/beets/autotag/hooks.py +++ b/beets/autotag/hooks.py @@ -109,6 +109,7 @@ def __init__( country: Optional[str] = None, style: Optional[str] = None, genre: Optional[str] = None, + genres: Optional[str] = None, albumstatus: Optional[str] = None, media: Optional[str] = None, albumdisambig: Optional[str] = None, @@ -152,6 +153,7 @@ def __init__( self.country = country self.style = style self.genre = genre + self.genres = genres self.albumstatus = albumstatus self.media = media self.albumdisambig = albumdisambig @@ -221,6 +223,7 @@ def __init__( bpm: Optional[str] = None, initial_key: Optional[str] = None, genre: Optional[str] = None, + genres: Optional[str] = None, album: Optional[str] = None, **kwargs, ): @@ -255,6 +258,7 @@ def __init__( self.bpm = bpm self.initial_key = initial_key self.genre = genre + self.genres = genres self.album = album self.update(kwargs) diff --git a/beets/library.py b/beets/library.py index b97a80b13d..fe9bf6ae39 100644 --- a/beets/library.py +++ b/beets/library.py @@ -531,6 +531,7 @@ class Item(LibModel): "albumartist_credit": types.STRING, "albumartists_credit": types.MULTI_VALUE_DSV, "genre": types.STRING, + "genres": types.MULTI_VALUE_DSV, "style": types.STRING, "discogs_albumid": types.INTEGER, "discogs_artistid": types.INTEGER, @@ -1173,6 +1174,7 @@ class Album(LibModel): "albumartists_credit": types.MULTI_VALUE_DSV, "album": types.STRING, "genre": types.STRING, + "genres": types.MULTI_VALUE_DSV, "style": types.STRING, "discogs_albumid": types.INTEGER, "discogs_artistid": types.INTEGER, @@ -1229,6 +1231,7 @@ class Album(LibModel): "albumartists_credit", "album", "genre", + "genres", "style", "discogs_albumid", "discogs_artistid", diff --git a/docs/changelog.rst b/docs/changelog.rst index 9474604b42..4997f17dd9 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -20,6 +20,7 @@ New features: * :doc:`plugins/autobpm`: Add new configuration option ``beat_track_kwargs`` which enables adjusting keyword arguments supplied to librosa's ``beat_track`` function call. +* New multi-value `genres` tag Bug fixes: diff --git a/test/test_library.py b/test/test_library.py index 9b29505a3d..67c024c510 100644 --- a/test/test_library.py +++ b/test/test_library.py @@ -708,13 +708,13 @@ def test_if_def_false_complete(self): self._assert_dest(b"/base/not_played") def test_first(self): - self.i.genres = "Pop; Rock; Classical Crossover" - self._setf("%first{$genres}") + self.i.semicolon_delimited_field = "Pop; Rock; Classical Crossover" + self._setf("%first{$semicolon_delimited_field}") self._assert_dest(b"/base/Pop") def test_first_skip(self): - self.i.genres = "Pop; Rock; Classical Crossover" - self._setf("%first{$genres,1,2}") + self.i.semicolon_delimited_field = "Pop; Rock; Classical Crossover" + self._setf("%first{$semicolon_delimited_field,1,2}") self._assert_dest(b"/base/Classical Crossover") def test_first_different_sep(self): @@ -1306,6 +1306,28 @@ def test_write_date_field(self): item.write() assert MediaFile(syspath(item.path)).year == clean_year + def test_write_multi_genres(self): + item = self.add_item_fixture(genre="old genre") + item.write( + tags={"genres": ["g1", "g2"]}, + ) + + # Ensure it reads all genres + assert MediaFile(syspath(item.path)).genres == ["g1", "g2"] + + # Ensure reading single genre outputs the first of the genres + assert MediaFile(syspath(item.path)).genre == "g1" + + def test_write_multi_genres_both_single_and_multi(self): + item = self.add_item_fixture(genre="old genre 1") + item.write( + tags={"genre": "single genre", "genres": ["multi genre"]}, + ) + + # Ensure the multi takes precedence + assert MediaFile(syspath(item.path)).genre == "multi genre" + assert MediaFile(syspath(item.path)).genres == ["multi genre"] + class ItemReadTest(unittest.TestCase): def test_unreadable_raise_read_error(self):