From 1d4a2c3e2ae348d6d3884bf18823cdbe0e268662 Mon Sep 17 00:00:00 2001 From: Ryan Lanny Jenkins Date: Sat, 27 Mar 2021 11:37:26 -0500 Subject: [PATCH] Add a new hook for badfiles pre-import user interaction, instead of flagging the task. --- beets/importer.py | 1 - beets/plugins.py | 16 ++++++++++++++++ beets/ui/commands.py | 28 +++++++++++++++------------- beetsplug/badfiles.py | 20 ++++++++++++++++---- 4 files changed, 47 insertions(+), 18 deletions(-) diff --git a/beets/importer.py b/beets/importer.py index 491383622..b7bfdb156 100644 --- a/beets/importer.py +++ b/beets/importer.py @@ -472,7 +472,6 @@ class ImportTask(BaseImportTask): def __init__(self, toppath, paths, items): super(ImportTask, self).__init__(toppath, paths, items) self.choice_flag = None - self.skip_summary_judgement = False self.cur_album = None self.cur_artist = None self.candidates = [] diff --git a/beets/plugins.py b/beets/plugins.py index 23f938169..402a6db17 100644 --- a/beets/plugins.py +++ b/beets/plugins.py @@ -501,6 +501,22 @@ def send(event, **arguments): return results +def send_seq(event, **arguments): + """Like `send` but passes the result of the previous event handler to the + next, and returns only the result of the last non-None event handler. + + `event` is the name of the event to send, all other named arguments + are passed along to the handlers. + """ + log.debug(u'Sequentially sending event: {0}', event) + previous = None + for handler in event_handlers()[event]: + result = handler(previous=previous, **arguments) + previous = result or previous + + return previous + + def feat_tokens(for_artist=True): """Return a regular expression that matches phrases like "featuring" that separate a main artist or a song title from secondary artists. diff --git a/beets/ui/commands.py b/beets/ui/commands.py index 8d169129a..c8f85a999 100755 --- a/beets/ui/commands.py +++ b/beets/ui/commands.py @@ -698,18 +698,21 @@ class TerminalImportSession(importer.ImportSession): print_(displayable_path(task.paths, u'\n') + u' ({0} items)'.format(len(task.items))) - # Call here so plugins have the option to skip `_summary_judgement` - choices = self._get_choices(task) + # Let plugins display info or prompt the user before we go through the + # process of selecting candidate. + action = plugins.send_seq('import_task_before_choice', + session=self, task=task) + if action is not None: + return action - if not task.skip_summary_judgement: - # Take immediate action if appropriate. - action = _summary_judgment(task.rec) - if action == importer.action.APPLY: - match = task.candidates[0] - show_change(task.cur_artist, task.cur_album, match) - return match - elif action is not None: - return action + # Take immediate action if appropriate. + action = _summary_judgment(task.rec) + if action == importer.action.APPLY: + match = task.candidates[0] + show_change(task.cur_artist, task.cur_album, match) + return match + elif action is not None: + return action # Loop until we have a choice. while True: @@ -717,6 +720,7 @@ class TerminalImportSession(importer.ImportSession): # `choose_candidate` may be an `importer.action`, an # `AlbumMatch` object for a specific selection, or a # `PromptChoice`. + choices = self._get_choices(task) choice = choose_candidate( task.candidates, False, task.rec, task.cur_artist, task.cur_album, itemcount=len(task.items), choices=choices @@ -745,8 +749,6 @@ class TerminalImportSession(importer.ImportSession): assert isinstance(choice, autotag.AlbumMatch) return choice - choices = self._get_choices(task) - def choose_item(self, task): """Ask the user for a choice about tagging a single item. Returns either an action constant or a TrackMatch object. diff --git a/beetsplug/badfiles.py b/beetsplug/badfiles.py index 44c1ce812..0fede8f4b 100644 --- a/beetsplug/badfiles.py +++ b/beetsplug/badfiles.py @@ -30,6 +30,7 @@ from beets.plugins import BeetsPlugin from beets.ui import Subcommand from beets.util import displayable_path, par_map from beets import ui +from beets import importer class CheckerCommandException(Exception): @@ -56,8 +57,8 @@ class BadFiles(BeetsPlugin): self.register_listener('import_task_start', self.on_import_task_start) - self.register_listener('before_choose_candidate', - self.on_before_choose_candidate) + self.register_listener('import_task_before_choice', + self.on_import_task_before_choice) def run_command(self, cmd): self._log.debug(u"running command: {}", @@ -171,9 +172,8 @@ class BadFiles(BeetsPlugin): if checks_failed: task._badfiles_checks_failed = checks_failed - task.skip_summary_judgement = True - def on_before_choose_candidate(self, task, session): + def on_import_task_before_choice(self, task, session, previous): if hasattr(task, '_badfiles_checks_failed'): ui.print_('{} one or more files failed checks:' .format(ui.colorize('text_warning', 'BAD'))) @@ -182,6 +182,18 @@ class BadFiles(BeetsPlugin): ui.print_(error_line) ui.print_() + ui.print_('What would you like to do?') + + sel = ui.input_options(['aBort', 'skip', 'continue']) + + if sel == 's': + return importer.action.SKIP + elif sel == 'c': + return None + elif sel == 'b': + raise importer.ImportAbort() + else: + raise Exception('Unexpected selection: {}'.format(sel)) def command(self, lib, opts, args): # Get items from arguments