diff --git a/beetsplug/ftintitle.py b/beetsplug/ftintitle.py index ab841a12c2..825bac033c 100644 --- a/beetsplug/ftintitle.py +++ b/beetsplug/ftintitle.py @@ -36,11 +36,23 @@ def split_on_feat( artist, which is always a string, and the featuring artist, which may be a string or None if none is present. """ - # split on the first "feat". - regex = re.compile( - plugins.feat_tokens(for_artist, custom_words), re.IGNORECASE + # Try explicit featuring tokens first (ft, feat, featuring, etc.) + # to avoid splitting on generic separators like "&" when both are present + regex_explicit = re.compile( + plugins.feat_tokens(for_artist=False, custom_words=custom_words), + re.IGNORECASE, ) - parts = tuple(s.strip() for s in regex.split(artist, 1)) + parts = tuple(s.strip() for s in regex_explicit.split(artist, 1)) + if len(parts) == 2: + return parts + + # Fall back to all tokens including generic separators if no explicit match + if for_artist: + regex = re.compile( + plugins.feat_tokens(for_artist, custom_words), re.IGNORECASE + ) + parts = tuple(s.strip() for s in regex.split(artist, 1)) + if len(parts) == 1: return parts[0], None else: diff --git a/docs/changelog.rst b/docs/changelog.rst index 475e566349..8080fab03e 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -62,6 +62,9 @@ Bug fixes: "albumartist" instead of a list of unique album artists. - Sanitize log messages by removing control characters preventing terminal rendering issues. +- :doc:`/plugins/ftintitle`: Fixed artist name splitting to prioritize explicit + featuring tokens (feat, ft, featuring) over generic separators (&, and), + preventing incorrect splits when both are present. For plugin developers: diff --git a/test/plugins/test_ftintitle.py b/test/plugins/test_ftintitle.py index 6f01601e02..fb9f4eb398 100644 --- a/test/plugins/test_ftintitle.py +++ b/test/plugins/test_ftintitle.py @@ -303,6 +303,10 @@ def test_find_feat_part( ("Alice and Bob", ("Alice", "Bob")), ("Alice With Bob", ("Alice", "Bob")), ("Alice defeat Bob", ("Alice defeat Bob", None)), + ("Alice & Bob feat Charlie", ("Alice & Bob", "Charlie")), + ("Alice & Bob ft. Charlie", ("Alice & Bob", "Charlie")), + ("Alice & Bob featuring Charlie", ("Alice & Bob", "Charlie")), + ("Alice and Bob feat Charlie", ("Alice and Bob", "Charlie")), ], ) def test_split_on_feat(