diff --git a/beetsplug/lastgenre/__init__.py b/beetsplug/lastgenre/__init__.py index 1c91688a6..e14cd5227 100644 --- a/beetsplug/lastgenre/__init__.py +++ b/beetsplug/lastgenre/__init__.py @@ -106,6 +106,7 @@ class LastGenrePlugin(plugins.BeetsPlugin): "count": 1, "fallback": None, "canonical": False, + "canonicalize_existing": False, "source": "album", "force": False, "keep_existing": False, @@ -399,6 +400,24 @@ class LastGenrePlugin(plugins.BeetsPlugin): genres = self._get_existing_genres(obj) if genres and not self.config["force"]: + # Without force and whitelisting + canonicalize_existing, we attempt + # to canonicalize pre-populated tags before returning them. + # If none are found, we use the fallback. + if ( + self.config["whitelist"] + and self.config["canonical"] + and self.config["canonicalize_existing"] + ): + keep_genres = [g.lower() for g in genres] + if result := _try_resolve_stage("original", keep_genres, []): + return result + elif fallback := self.config["fallback"].get(): + # Return fallback string. + return fallback, "fallback" + else: + # No fallback configured. + return None, "fallback unconfigured" + # Without force pre-populated tags are returned as-is. label = "keep any, no-force" if isinstance(obj, library.Item): diff --git a/docs/changelog.rst b/docs/changelog.rst index 25a0c1365..7eae84ad3 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -30,6 +30,8 @@ been dropped. New features: +- :doc:`plugins/lastgenre`: Added ``canonicalize_existing`` configuration flag + to allow whitelist canonicalization of existing genres. - :doc:`plugins/fetchart`: Added config setting for a fallback cover art image. - :doc:`plugins/ftintitle`: Added argument for custom feat. words in ftintitle. - :doc:`plugins/ftintitle`: Added album template value ``album_artist_no_feat``. diff --git a/docs/plugins/lastgenre.rst b/docs/plugins/lastgenre.rst index ace7caaf0..f552ce3fc 100644 --- a/docs/plugins/lastgenre.rst +++ b/docs/plugins/lastgenre.rst @@ -171,6 +171,11 @@ file. The available options are: - **canonical**: Use a canonicalization tree. Setting this to ``yes`` will use a built-in tree. You can also set it to a path, like the ``whitelist`` config value, to use your own tree. Default: ``no`` (disabled). +- **canonicalize_existing**: This option only takes effect with ``force: no``, + ``canonical: yes`` and ``whitelist: yes``. + Setting this to ``yes`` will result in attempted canonicalization of existing + genres. If this fails, the ``fallback`` is used isntead. + Default: ``no`` (disabled). - **count**: Number of genres to fetch. Default: 1 - **fallback**: A string to use as a fallback genre when no genre is found ``or`` the original genre is not desired to be kept (``keep_existing: no``). diff --git a/test/plugins/test_lastgenre.py b/test/plugins/test_lastgenre.py index 3de43d197..65877e550 100644 --- a/test/plugins/test_lastgenre.py +++ b/test/plugins/test_lastgenre.py @@ -302,6 +302,29 @@ class LastGenrePluginTest(PluginTestCase): }, ("Jazzin", "album, any"), ), + # 5.1 - Canonicalize original genre when force is **off** and + # whitelist, canonical and canonicalize_existing are on. + # "Cosmic Disco" is not in the default whitelist, thus gets resolved "up" in the + # tree to "Disco" and "Electronic". + ( + { + "force": False, + "keep_existing": False, + "source": "artist", + "whitelist": True, + "canonical": True, + "canonicalize_existing": True, + "prefer_specific": False, + }, + "Cosmic Disco", + { + "artist": [], + }, + ( + "Disco, Electronic", + "keep + original, whitelist", + ), + ), # 6 - fallback to next stages until found ( {