mirror of
https://github.com/beetbox/beets.git
synced 2026-01-30 03:54:21 +01:00
Merge pull request #3900 from Lanny/master
badfiles: Optionally run checkers during import
This commit is contained in:
commit
eab4372a58
5 changed files with 93 additions and 10 deletions
|
|
@ -698,6 +698,19 @@ class TerminalImportSession(importer.ImportSession):
|
|||
print_(displayable_path(task.paths, u'\n') +
|
||||
u' ({0} items)'.format(len(task.items)))
|
||||
|
||||
# Let plugins display info or prompt the user before we go through the
|
||||
# process of selecting candidate.
|
||||
results = plugins.send('import_task_before_choice',
|
||||
session=self, task=task)
|
||||
actions = [action for action in results if action]
|
||||
|
||||
if len(actions) == 1:
|
||||
return actions[0]
|
||||
elif len(actions) > 1:
|
||||
raise plugins.PluginConflictException(
|
||||
u'Only one handler for `import_task_before_choice` may return '
|
||||
u'an action.')
|
||||
|
||||
# Take immediate action if appropriate.
|
||||
action = _summary_judgment(task.rec)
|
||||
if action == importer.action.APPLY:
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
@ -54,6 +55,11 @@ class BadFiles(BeetsPlugin):
|
|||
super(BadFiles, self).__init__()
|
||||
self.verbose = False
|
||||
|
||||
self.register_listener('import_task_start',
|
||||
self.on_import_task_start)
|
||||
self.register_listener('import_task_before_choice',
|
||||
self.on_import_task_before_choice)
|
||||
|
||||
def run_command(self, cmd):
|
||||
self._log.debug(u"running command: {}",
|
||||
displayable_path(list2cmdline(cmd)))
|
||||
|
|
@ -115,7 +121,7 @@ class BadFiles(BeetsPlugin):
|
|||
if not checker:
|
||||
self._log.error(u"no checker specified in the config for {}",
|
||||
ext)
|
||||
return
|
||||
return []
|
||||
path = item.path
|
||||
if not isinstance(path, six.text_type):
|
||||
path = item.path.decode(sys.getfilesystemencoding())
|
||||
|
|
@ -130,25 +136,75 @@ class BadFiles(BeetsPlugin):
|
|||
)
|
||||
else:
|
||||
self._log.error(u"error invoking {}: {}", e.checker, e.msg)
|
||||
return
|
||||
return []
|
||||
|
||||
error_lines = []
|
||||
|
||||
if status > 0:
|
||||
ui.print_(u"{}: checker exited with status {}"
|
||||
.format(ui.colorize('text_error', dpath), status))
|
||||
error_lines.append(
|
||||
u"{}: checker exited with status {}"
|
||||
.format(ui.colorize('text_error', dpath), status))
|
||||
for line in output:
|
||||
ui.print_(u" {}".format(line))
|
||||
error_lines.append(u" {}".format(line))
|
||||
|
||||
elif errors > 0:
|
||||
ui.print_(u"{}: checker found {} errors or warnings"
|
||||
.format(ui.colorize('text_warning', dpath), errors))
|
||||
error_lines.append(
|
||||
u"{}: checker found {} errors or warnings"
|
||||
.format(ui.colorize('text_warning', dpath), errors))
|
||||
for line in output:
|
||||
ui.print_(u" {}".format(line))
|
||||
error_lines.append(u" {}".format(line))
|
||||
elif self.verbose:
|
||||
ui.print_(u"{}: ok".format(ui.colorize('text_success', dpath)))
|
||||
error_lines.append(
|
||||
u"{}: ok".format(ui.colorize('text_success', dpath)))
|
||||
|
||||
return error_lines
|
||||
|
||||
def on_import_task_start(self, task, session):
|
||||
if not self.config['check_on_import'].get(False):
|
||||
return
|
||||
|
||||
checks_failed = []
|
||||
|
||||
for item in task.items:
|
||||
error_lines = self.check_item(item)
|
||||
if error_lines:
|
||||
checks_failed.append(error_lines)
|
||||
|
||||
if checks_failed:
|
||||
task._badfiles_checks_failed = checks_failed
|
||||
|
||||
def on_import_task_before_choice(self, task, session):
|
||||
if hasattr(task, '_badfiles_checks_failed'):
|
||||
ui.print_('{} one or more files failed checks:'
|
||||
.format(ui.colorize('text_warning', 'BAD')))
|
||||
for error in task._badfiles_checks_failed:
|
||||
for error_line in error:
|
||||
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
|
||||
items = lib.items(ui.decargs(args))
|
||||
self.verbose = opts.verbose
|
||||
par_map(self.check_item, items)
|
||||
|
||||
def check_and_print(item):
|
||||
for error_line in self.check_item(item):
|
||||
ui.print_(error_line)
|
||||
|
||||
par_map(check_and_print, items)
|
||||
|
||||
def commands(self):
|
||||
bad_command = Subcommand('bad',
|
||||
|
|
|
|||
|
|
@ -210,6 +210,8 @@ Other new things:
|
|||
* The :doc:`/plugins/aura` has arrived!
|
||||
* :doc:`plugins/fetchart`: The new ``max_filesize`` option for fetchart can be
|
||||
used to target a maximum image filesize.
|
||||
* :doc:`/plugins/badfiles`: Checkers can now be run during import with the
|
||||
``check_on_import`` config option.
|
||||
|
||||
Fixes:
|
||||
|
||||
|
|
|
|||
|
|
@ -200,6 +200,13 @@ The events currently available are:
|
|||
import pipeline stage is a better choice (see :ref:`plugin-stage`).
|
||||
Parameters: ``task`` and ``session``.
|
||||
|
||||
* `import_task_before_choice`: called after candidate search for an import task
|
||||
before any decision is made about how/if to import or tag. Can be used to
|
||||
present information about the task or initiate interaction with the user
|
||||
before importing occurs. Return an importer action to take a specific action.
|
||||
Only one handler may return a non-None result.
|
||||
Parameters: ``task`` and ``session``
|
||||
|
||||
* `import_task_choice`: called after a decision has been made about an import
|
||||
task. This event can be used to initiate further interaction with the user.
|
||||
Use ``task.choice_flag`` to determine or change the action to be
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ install yourself:
|
|||
You can also add custom commands for a specific extension, like this::
|
||||
|
||||
badfiles:
|
||||
check_on_import: yes
|
||||
commands:
|
||||
ogg: myoggchecker --opt1 --opt2
|
||||
flac: flac --test --warnings-as-errors --silent
|
||||
|
|
@ -25,6 +26,10 @@ Custom commands will be run once for each file of the specified type, with the
|
|||
path to the file as the last argument. Commands must return a status code
|
||||
greater than zero for a file to be considered corrupt.
|
||||
|
||||
You can run the checkers when importing files by using the `check_on_import`
|
||||
option. When on, checkers will be run against every imported file and warnings
|
||||
and errors will be presented when selecting a tagging option.
|
||||
|
||||
.. _mp3val: http://mp3val.sourceforge.net/
|
||||
.. _flac: https://xiph.org/flac/
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue