From 8413de1a85a47b0e75acc162e131a43f8eaf1054 Mon Sep 17 00:00:00 2001 From: wisp3rwind <17089248+wisp3rwind@users.noreply.github.com> Date: Mon, 14 Apr 2025 20:29:46 +0200 Subject: [PATCH 1/2] ftintitle: add typings --- beetsplug/ftintitle.py | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/beetsplug/ftintitle.py b/beetsplug/ftintitle.py index 3f01af593..5dd6fbf4c 100644 --- a/beetsplug/ftintitle.py +++ b/beetsplug/ftintitle.py @@ -14,13 +14,20 @@ """Moves "featured" artists to the title from the artist field.""" +from __future__ import annotations + import re +from typing import TYPE_CHECKING from beets import plugins, ui from beets.util import displayable_path +if TYPE_CHECKING: + from beets.importer import ImportSession, ImportTask + from beets.library import Item -def split_on_feat(artist): + +def split_on_feat(artist: str) -> tuple[str, str | None]: """Given an artist string, split the "main" artist from any artist on the right-hand side of a string like "feat". Return the main artist, which is always a string, and the featuring artist, which @@ -28,14 +35,15 @@ def split_on_feat(artist): """ # split on the first "feat". regex = re.compile(plugins.feat_tokens(), re.IGNORECASE) - parts = [s.strip() for s in regex.split(artist, 1)] + parts = tuple(s.strip() for s in regex.split(artist, 1)) if len(parts) == 1: return parts[0], None else: - return tuple(parts) + assert len(parts) == 2 # help mypy out + return parts -def contains_feat(title): +def contains_feat(title: str) -> bool: """Determine whether the title contains a "featured" marker.""" return bool( re.search( @@ -46,7 +54,7 @@ def contains_feat(title): ) -def find_feat_part(artist, albumartist): +def find_feat_part(artist: str, albumartist: str) -> str | None: """Attempt to find featured artists in the item's artist fields and return the results. Returns None if no featured artist found. """ @@ -75,7 +83,7 @@ def find_feat_part(artist, albumartist): class FtInTitlePlugin(plugins.BeetsPlugin): - def __init__(self): + def __init__(self) -> None: super().__init__() self.config.add( @@ -103,7 +111,7 @@ class FtInTitlePlugin(plugins.BeetsPlugin): if self.config["auto"]: self.import_stages = [self.imported] - def commands(self): + def commands(self) -> list[ui.Subcommand]: def func(lib, opts, args): self.config.set_args(opts) drop_feat = self.config["drop"].get(bool) @@ -119,7 +127,7 @@ class FtInTitlePlugin(plugins.BeetsPlugin): self._command.func = func return [self._command] - def imported(self, session, task): + def imported(self, session: ImportSession, task: ImportTask) -> None: """Import hook for moving featuring artist automatically.""" drop_feat = self.config["drop"].get(bool) keep_in_artist_field = self.config["keep_in_artist"].get(bool) @@ -128,7 +136,13 @@ class FtInTitlePlugin(plugins.BeetsPlugin): if self.ft_in_title(item, drop_feat, keep_in_artist_field): item.store() - def update_metadata(self, item, feat_part, drop_feat, keep_in_artist_field): + def update_metadata( + self, + item: Item, + feat_part: str, + drop_feat: bool, + keep_in_artist_field: bool, + ) -> None: """Choose how to add new artists to the title and set the new metadata. Also, print out messages about any changes that are made. If `drop_feat` is set, then do not add the artist to the title; just @@ -156,7 +170,12 @@ class FtInTitlePlugin(plugins.BeetsPlugin): self._log.info("title: {0} -> {1}", item.title, new_title) item.title = new_title - def ft_in_title(self, item, drop_feat, keep_in_artist_field) -> bool: + def ft_in_title( + self, + item: Item, + drop_feat: bool, + keep_in_artist_field: bool, + ) -> bool: """Look for featured artists in the item's artist fields and move them to the title. From b49528612742dc1637236979a58e370eb036019b Mon Sep 17 00:00:00 2001 From: wisp3rwind <17089248+wisp3rwind@users.noreply.github.com> Date: Mon, 14 Apr 2025 20:34:19 +0200 Subject: [PATCH 2/2] ftintitle: flatten code linear code with early exits instead of more complicated nested conditionals --- beetsplug/ftintitle.py | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/beetsplug/ftintitle.py b/beetsplug/ftintitle.py index 5dd6fbf4c..a85aa9719 100644 --- a/beetsplug/ftintitle.py +++ b/beetsplug/ftintitle.py @@ -188,21 +188,22 @@ class FtInTitlePlugin(plugins.BeetsPlugin): # Check whether there is a featured artist on this track and the # artist field does not exactly match the album artist field. In # that case, we attempt to move the featured artist to the title. + if not albumartist or albumartist == artist: + return False + _, featured = split_on_feat(artist) - if featured and albumartist != artist and albumartist: - self._log.info("{}", displayable_path(item.path)) + if not featured: + return False - feat_part = None + self._log.info("{}", displayable_path(item.path)) - # Attempt to find the featured artist. - feat_part = find_feat_part(artist, albumartist) + # Attempt to find the featured artist. + feat_part = find_feat_part(artist, albumartist) - # If we have a featuring artist, move it to the title. - if feat_part: - self.update_metadata( - item, feat_part, drop_feat, keep_in_artist_field - ) - return True - else: - self._log.info("no featuring artists found") - return False + if not feat_part: + self._log.info("no featuring artists found") + return False + + # If we have a featuring artist, move it to the title. + self.update_metadata(item, feat_part, drop_feat, keep_in_artist_field) + return True