diff --git a/beets/importer.py b/beets/importer.py index aac21d77f..4e4084eec 100644 --- a/beets/importer.py +++ b/beets/importer.py @@ -313,6 +313,8 @@ class ImportSession(object): stages += [import_asis(self)] # Plugin stages. + for stage_func in plugins.early_import_stages(): + stages.append(plugin_stage(self, stage_func)) for stage_func in plugins.import_stages(): stages.append(plugin_stage(self, stage_func)) diff --git a/beets/plugins.py b/beets/plugins.py index d62f3c011..4a2475f50 100644 --- a/beets/plugins.py +++ b/beets/plugins.py @@ -81,6 +81,7 @@ class BeetsPlugin(object): self.template_fields = {} if not self.album_template_fields: self.album_template_fields = {} + self.early_import_stages = [] self.import_stages = [] self._log = log.getChild(self.name) @@ -94,6 +95,22 @@ class BeetsPlugin(object): """ return () + def _set_stage_log_level(self, stages): + """Adjust all the stages in `stages` to WARNING logging level. + """ + return [self._set_log_level_and_params(logging.WARNING, stage) + for stage in stages] + + def get_early_import_stages(self): + """Return a list of functions that should be called as importer + pipelines stages early in the pipeline. + + The callables are wrapped versions of the functions in + `self.early_import_stages`. Wrapping provides some bookkeeping for the + plugin: specifically, the logging level is adjusted to WARNING. + """ + return self._set_stage_log_level(self.early_import_stages) + def get_import_stages(self): """Return a list of functions that should be called as importer pipelines stages. @@ -102,8 +119,7 @@ class BeetsPlugin(object): `self.import_stages`. Wrapping provides some bookkeeping for the plugin: specifically, the logging level is adjusted to WARNING. """ - return [self._set_log_level_and_params(logging.WARNING, import_stage) - for import_stage in self.import_stages] + return self._set_stage_log_level(self.import_stages) def _set_log_level_and_params(self, base_log_level, func): """Wrap `func` to temporarily set this plugin's logger level to @@ -393,6 +409,14 @@ def template_funcs(): return funcs +def early_import_stages(): + """Get a list of early import stage functions defined by plugins.""" + stages = [] + for plugin in find_plugins(): + stages += plugin.get_early_import_stages() + return stages + + def import_stages(): """Get a list of import stage functions defined by plugins.""" stages = [] diff --git a/beetsplug/convert.py b/beetsplug/convert.py index 380061248..d1223596f 100644 --- a/beetsplug/convert.py +++ b/beetsplug/convert.py @@ -146,7 +146,7 @@ class ConvertPlugin(BeetsPlugin): u'copy_album_art': False, u'album_art_maxwidth': 0, }) - self.import_stages = [self.auto_convert] + self.early_import_stages = [self.auto_convert] self.register_listener('import_task_files', self._cleanup) diff --git a/docs/changelog.rst b/docs/changelog.rst index 4f00b123b..effa6d687 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -51,6 +51,11 @@ Fixes: querying for dates within a certain month would fail to match dates within hours of the end of that month. :bug:`2652` +* Convert plugin now runs before all others in the pipeline to solve an issue + with generating ReplayGain data incompatible between the source and target + file formats. This option to request (part of) your plugin to run early in the + pipeline has been exposed in the plugin API as well (```early_import_stages```). + Thanks to :user:`autrimpo`. 1.4.6 (December 21, 2017) diff --git a/docs/dev/plugins.rst b/docs/dev/plugins.rst index 4d41c8971..5b0e4d08b 100644 --- a/docs/dev/plugins.rst +++ b/docs/dev/plugins.rst @@ -432,6 +432,11 @@ to register it:: def stage(self, session, task): print('Importing something!') +It is also possible to request your function to run early in the pipeline by +adding the function to the plugin's ``early_import_stages`` field instead.:: + + self.early_import_stages = [self.stage] + .. _extend-query: Extend the Query Syntax