From 91dbea3664886eb16345a5eaa97fa9877c39b0ae Mon Sep 17 00:00:00 2001 From: Arne Beer Date: Sat, 24 Jan 2026 22:57:32 +0100 Subject: [PATCH] feat(lastgenre): Add fallback_original Adds the `fallback_original` flag. When no genres can be found or no genres match the canonical whitelist, setting this to `yes` will result in existing genres to be preserved, instead of deleting them or setting them to `fallback`. This is for people that would rather have a non-canonicalized genre info than fully loose the existing genre data. --- beetsplug/lastgenre/__init__.py | 16 +++++++++++++-- docs/changelog.rst | 3 +++ docs/plugins/lastgenre.rst | 7 +++++-- test/plugins/test_lastgenre.py | 35 ++++++++++++++++++++++++++++++++- 4 files changed, 56 insertions(+), 5 deletions(-) diff --git a/beetsplug/lastgenre/__init__.py b/beetsplug/lastgenre/__init__.py index a8ba8ee181..8821197257 100644 --- a/beetsplug/lastgenre/__init__.py +++ b/beetsplug/lastgenre/__init__.py @@ -106,6 +106,7 @@ def __init__(self) -> None: "min_weight": 10, "count": 1, "fallback": None, + "fallback_original": False, "canonical": False, "cleanup_existing": False, "source": "album", @@ -404,8 +405,14 @@ def _try_resolve_stage( if result := _try_resolve_stage("cleanup", keep_genres, []): return result - # Return fallback string (None if not set). - return self.config["fallback"].get(), "fallback" + # If configured, keep the original genre information if the genres + # could not be resolved/cleaned up or nothing survived the whitelist. + # + # Otherwise return the fallback string (None if not set). + if self.config["fallback_original"]: + return genres, "fallback + original, no-force" + else: + return self.config["fallback"].get(), "fallback + no-force" # If cleanup_existing is not set, the pre-populated tags are # returned as-is. @@ -495,6 +502,11 @@ def _try_resolve_stage( ): return result + # If configured, keep the original genre information if the genres + # could not be resolved/cleaned up or nothing survived the whitelist. + if genres and self.config["force"] and self.config["fallback_original"]: + return genres, "fallback + original, force" + # Return fallback as a list. if fallback := self.config["fallback"].get(): return [fallback], "fallback" diff --git a/docs/changelog.rst b/docs/changelog.rst index 7318057b36..abd62162b1 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -12,6 +12,9 @@ Unreleased New features ~~~~~~~~~~~~ +- :doc:`plugins/lastgenre`: Added ``fallback_original`` configuration flag. This + allows users to keep the original genre information, if no genres remain after + canonicalization and whitelisting. - :doc:`plugins/lastgenre`: Added ``cleanup_existing`` configuration flag to allow whitelist canonicalization of existing genres. - Add native support for multiple genres per album/track. The ``genres`` field diff --git a/docs/plugins/lastgenre.rst b/docs/plugins/lastgenre.rst index cbf63e4544..51ca318622 100644 --- a/docs/plugins/lastgenre.rst +++ b/docs/plugins/lastgenre.rst @@ -173,12 +173,15 @@ file. The available options are: - **cleanup_existing**: This option only takes effect with ``force: no``, Setting this to ``yes`` will result in cleanup of existing genres. That includes canonicalization and whitelisting, if enabled. If no matching genre - can be determined, the ``fallback`` is used instead. Default: ``no`` - (disabled). + can be determined, the ``fallback`` is used instead, or ``fallback_original``, + if set. 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``). You can use the empty string ``''`` to reset the genre. Default: None. +- **fallback_original**: Setting this to ``yes`` will preserve existing genres + instead of deleting them or setting them to ``fallback`` when no genres are + found or when none match the canonical whitelist. Default: ``no``. - **force**: By default, lastgenre will fetch new genres for empty tags only, enable this option to always try to fetch new last.fm genres. Enable the ``keep_existing`` option to combine existing and new genres. (see `Handling diff --git a/test/plugins/test_lastgenre.py b/test/plugins/test_lastgenre.py index 6ba94913c2..ff6bf56a42 100644 --- a/test/plugins/test_lastgenre.py +++ b/test/plugins/test_lastgenre.py @@ -419,7 +419,40 @@ def config(config): }, (["fallback genre"], "fallback"), ), - # fallback to fallback if no original + # Keep the original genre when force is on, whitelist is enabled, + # but no valid genre can be found and fallback_original is on. + ( + { + "force": True, + "keep_existing": False, + "source": "track", + "whitelist": True, + "canonical": True, + "fallback_original": True, + }, + ["Jazzers-invalid"], + { + "track": None, + "album": None, + "artist": None, + }, + (["Jazzers-invalid"], "fallback + original, force"), + ), + # Keep the original genre when force is off, whitelist and cleanup_existing is + # enabled, but no valid genre can be found and fallback_original is on. + ( + { + "force": False, + "cleanup_existing": True, + "whitelist": True, + "fallback_original": True, + "canonical": True, + }, + ["Jazzers-invalid"], + {}, + (["Jazzers-invalid"], "fallback + original, no-force"), + ), + # Fallback to fallback if no original ( { "force": True,