Moved plugin docs into folder.

This commit is contained in:
Sebastian Mohr 2025-08-24 11:12:56 +02:00 committed by Šarūnas Nejus
parent c60f0ceb3c
commit 07549ed896
No known key found for this signature in database
GPG key ID: DD28F6704DBE3435
4 changed files with 264 additions and 263 deletions

View file

@ -0,0 +1,50 @@
.. _add_subcommands:
Add Commands to the CLI
~~~~~~~~~~~~~~~~~~~~~~~
Plugins can add new subcommands to the ``beet`` command-line interface. Define
the plugin class' ``commands()`` method to return a list of ``Subcommand``
objects. (The ``Subcommand`` class is defined in the ``beets.ui`` module.)
Here's an example plugin that adds a simple command:
::
from beets.plugins import BeetsPlugin
from beets.ui import Subcommand
my_super_command = Subcommand('super', help='do something super')
def say_hi(lib, opts, args):
print("Hello everybody! I'm a plugin!")
my_super_command.func = say_hi
class SuperPlug(BeetsPlugin):
def commands(self):
return [my_super_command]
To make a subcommand, invoke the constructor like so: ``Subcommand(name, parser,
help, aliases)``. The ``name`` parameter is the only required one and should
just be the name of your command. ``parser`` can be an `OptionParser instance`_,
but it defaults to an empty parser (you can extend it later). ``help`` is a
description of your command, and ``aliases`` is a list of shorthand versions of
your command name.
.. _optionparser instance: https://docs.python.org/library/optparse.html
You'll need to add a function to your command by saying ``mycommand.func =
myfunction``. This function should take the following parameters: ``lib`` (a
beets ``Library`` object) and ``opts`` and ``args`` (command-line options and
arguments as returned by OptionParser.parse_args_).
.. _optionparser.parse_args: https://docs.python.org/library/optparse.html#parsing-arguments
The function should use any of the utility functions defined in ``beets.ui``.
Try running ``pydoc beets.ui`` to see what's available.
You can add command-line options to your new command using the ``parser`` member
of the ``Subcommand`` class, which is a ``CommonOptionsParser`` instance. Just
use it like you would a normal ``OptionParser`` in an independent script. Note
that it offers several methods to add common options: ``--album``, ``--path``
and ``--format``. This feature is versatile and extensively documented, try
``pydoc beets.ui.CommonOptionsParser`` for more information.

142
docs/dev/plugins/events.rst Normal file
View file

@ -0,0 +1,142 @@
.. _plugin_events:
Listen for Events
~~~~~~~~~~~~~~~~~
Event handlers allow plugins to run code whenever something happens in beets'
operation. For instance, a plugin could write a log message every time an album
is successfully autotagged or update MPD's index whenever the database is
changed.
You can "listen" for events using ``BeetsPlugin.register_listener``. Here's an
example:
::
from beets.plugins import BeetsPlugin
def loaded():
print 'Plugin loaded!'
class SomePlugin(BeetsPlugin):
def __init__(self):
super().__init__()
self.register_listener('pluginload', loaded)
Note that if you want to access an attribute of your plugin (e.g. ``config`` or
``log``) you'll have to define a method and not a function. Here is the usual
registration process in this case:
::
from beets.plugins import BeetsPlugin
class SomePlugin(BeetsPlugin):
def __init__(self):
super().__init__()
self.register_listener('pluginload', self.loaded)
def loaded(self):
self._log.info('Plugin loaded!')
The events currently available are:
- ``pluginload``: called after all the plugins have been loaded after the
``beet`` command starts
- ``import``: called after a ``beet import`` command finishes (the ``lib``
keyword argument is a Library object; ``paths`` is a list of paths (strings)
that were imported)
- ``album_imported``: called with an ``Album`` object every time the ``import``
command finishes adding an album to the library. Parameters: ``lib``,
``album``
- ``album_removed``: called with an ``Album`` object every time an album is
removed from the library (even when its file is not deleted from disk).
- ``item_copied``: called with an ``Item`` object whenever its file is copied.
Parameters: ``item``, ``source`` path, ``destination`` path
- ``item_imported``: called with an ``Item`` object every time the importer adds
a singleton to the library (not called for full-album imports). Parameters:
``lib``, ``item``
- ``before_item_moved``: called with an ``Item`` object immediately before its
file is moved. Parameters: ``item``, ``source`` path, ``destination`` path
- ``item_moved``: called with an ``Item`` object whenever its file is moved.
Parameters: ``item``, ``source`` path, ``destination`` path
- ``item_linked``: called with an ``Item`` object whenever a symlink is created
for a file. Parameters: ``item``, ``source`` path, ``destination`` path
- ``item_hardlinked``: called with an ``Item`` object whenever a hardlink is
created for a file. Parameters: ``item``, ``source`` path, ``destination``
path
- ``item_reflinked``: called with an ``Item`` object whenever a reflink is
created for a file. Parameters: ``item``, ``source`` path, ``destination``
path
- ``item_removed``: called with an ``Item`` object every time an item (singleton
or album's part) is removed from the library (even when its file is not
deleted from disk).
- ``write``: called with an ``Item`` object, a ``path``, and a ``tags``
dictionary just before a file's metadata is written to disk (i.e., just before
the file on disk is opened). Event handlers may change the ``tags`` dictionary
to customize the tags that are written to the media file. Event handlers may
also raise a ``library.FileOperationError`` exception to abort the write
operation. Beets will catch that exception, print an error message and
continue.
- ``after_write``: called with an ``Item`` object after a file's metadata is
written to disk (i.e., just after the file on disk is closed).
- ``import_task_created``: called immediately after an import task is
initialized. Plugins can use this to, for example, change imported files of a
task before anything else happens. It's also possible to replace the task with
another task by returning a list of tasks. This list can contain zero or more
``ImportTask``. Returning an empty list will stop the task. Parameters:
``task`` (an ``ImportTask``) and ``session`` (an ``ImportSession``).
- ``import_task_start``: called when before an import task begins processing.
Parameters: ``task`` and ``session``.
- ``import_task_apply``: called after metadata changes have been applied in an
import task. This is called on the same thread as the UI, so use this
sparingly and only for tasks that can be done quickly. For most plugins, an
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 taken.
Parameters: ``task`` and ``session``.
- ``import_task_files``: called after an import task finishes manipulating the
filesystem (copying and moving files, writing metadata tags). Parameters:
``task`` and ``session``.
- ``library_opened``: called after beets starts up and initializes the main
Library object. Parameter: ``lib``.
- ``database_change``: a modification has been made to the library database. The
change might not be committed yet. Parameters: ``lib`` and ``model``.
- ``cli_exit``: called just before the ``beet`` command-line program exits.
Parameter: ``lib``.
- ``import_begin``: called just before a ``beet import`` session starts up.
Parameter: ``session``.
- ``trackinfo_received``: called after metadata for a track item has been
fetched from a data source, such as MusicBrainz. You can modify the tags that
the rest of the pipeline sees on a ``beet import`` operation or during later
adjustments, such as ``mbsync``. Slow handlers of the event can impact the
operation, since the event is fired for any fetched possible match ``before``
the user (or the autotagger machinery) gets to see the match. Parameter:
``info``.
- ``albuminfo_received``: like ``trackinfo_received``, the event indicates new
metadata for album items. The parameter is an ``AlbumInfo`` object instead of
a ``TrackInfo``. Parameter: ``info``.
- ``before_choose_candidate``: called before the user is prompted for a decision
during a ``beet import`` interactive session. Plugins can use this event for
:ref:`appending choices to the prompt <append_prompt_choices>` by returning a
list of ``PromptChoices``. Parameters: ``task`` and ``session``.
- ``mb_track_extract``: called after the metadata is obtained from MusicBrainz.
The parameter is a ``dict`` containing the tags retrieved from MusicBrainz for
a track. Plugins must return a new (potentially empty) ``dict`` with
additional ``field: value`` pairs, which the autotagger will apply to the
item, as flexible attributes if ``field`` is not a hardcoded field. Fields
already present on the track are overwritten. Parameter: ``data``
- ``mb_album_extract``: Like ``mb_track_extract``, but for album tags.
Overwrites tags set at the track level, if they have the same ``field``.
Parameter: ``data``
The included ``mpdupdate`` plugin provides an example use case for event
listeners.

View file

@ -0,0 +1,72 @@
Plugin Development Guide
========================
Beets plugins are Python modules or packages that extend the core functionality
of beets. The plugin system is designed to be flexible, allowing developers to
add virtually any type of features.
.. _writing-plugins:
Writing Plugins
---------------
A beets plugin is just a Python module or package inside the ``beetsplug``
namespace package. (Check out `this article`_ and `this Stack Overflow
question`_ if you haven't heard about namespace packages.) So, to make one,
create a directory called ``beetsplug`` and add either your plugin module:
::
beetsplug/
myawesomeplugin.py
or your plugin subpackage:
::
beetsplug/
myawesomeplugin/
__init__.py
myawesomeplugin.py
.. attention::
You do not anymore need to add a ``__init__.py`` file to the ``beetsplug``
directory. Python treats your plugin as a namespace package automatically,
thus we do not depend on ``pkgutil``-based setup in the ``__init__.py`` file
anymore.
.. _this article: https://realpython.com/python-namespace-package/#setting-up-some-namespace-packages
.. _this stack overflow question: https://stackoverflow.com/a/27586272/9582674
The meat of your plugin goes in ``myawesomeplugin.py``. There, you'll have to
import ``BeetsPlugin`` from ``beets.plugins`` and subclass it, for example
.. code-block:: python
from beets.plugins import BeetsPlugin
class MyAwesomePlugin(BeetsPlugin):
pass
Once you have your ``BeetsPlugin`` subclass, there's a variety of things your
plugin can do. (Read on!)
To use your new plugin, package your plugin (see how to do this with poetry_ or
setuptools_, for example) and install it into your ``beets`` virtual
environment. Then, add your plugin to beets configuration
.. _poetry: https://python-poetry.org/docs/pyproject/#packages
.. _setuptools: https://setuptools.pypa.io/en/latest/userguide/package_discovery.html#finding-simple-packages
.. code-block:: yaml
# config.yaml
plugins:
- myawesomeplugin
and you're good to go!

View file

@ -1,267 +1,4 @@
Plugin Development Guide
========================
Beets plugins are Python modules or packages that extend the core functionality
of beets. The plugin system is designed to be flexible, allowing developers to
add virtually any type of features.
.. _writing-plugins:
Writing Plugins
---------------
A beets plugin is just a Python module or package inside the ``beetsplug``
namespace package. (Check out `this article`_ and `this Stack Overflow
question`_ if you haven't heard about namespace packages.) So, to make one,
create a directory called ``beetsplug`` and add either your plugin module:
::
beetsplug/
myawesomeplugin.py
or your plugin subpackage:
::
beetsplug/
myawesomeplugin/
__init__.py
myawesomeplugin.py
.. attention::
You do not anymore need to add a ``__init__.py`` file to the ``beetsplug``
directory. Python treats your plugin as a namespace package automatically,
thus we do not depend on ``pkgutil``-based setup in the ``__init__.py`` file
anymore.
.. _this article: https://realpython.com/python-namespace-package/#setting-up-some-namespace-packages
.. _this stack overflow question: https://stackoverflow.com/a/27586272/9582674
The meat of your plugin goes in ``myawesomeplugin.py``. There, you'll have to
import ``BeetsPlugin`` from ``beets.plugins`` and subclass it, for example
.. code-block:: python
from beets.plugins import BeetsPlugin
class MyAwesomePlugin(BeetsPlugin):
pass
Once you have your ``BeetsPlugin`` subclass, there's a variety of things your
plugin can do. (Read on!)
To use your new plugin, package your plugin (see how to do this with poetry_ or
setuptools_, for example) and install it into your ``beets`` virtual
environment. Then, add your plugin to beets configuration
.. _poetry: https://python-poetry.org/docs/pyproject/#packages
.. _setuptools: https://setuptools.pypa.io/en/latest/userguide/package_discovery.html#finding-simple-packages
.. code-block:: yaml
# config.yaml
plugins:
- myawesomeplugin
and you're good to go!
.. _add_subcommands:
Add Commands to the CLI
~~~~~~~~~~~~~~~~~~~~~~~
Plugins can add new subcommands to the ``beet`` command-line interface. Define
the plugin class' ``commands()`` method to return a list of ``Subcommand``
objects. (The ``Subcommand`` class is defined in the ``beets.ui`` module.)
Here's an example plugin that adds a simple command:
::
from beets.plugins import BeetsPlugin
from beets.ui import Subcommand
my_super_command = Subcommand('super', help='do something super')
def say_hi(lib, opts, args):
print("Hello everybody! I'm a plugin!")
my_super_command.func = say_hi
class SuperPlug(BeetsPlugin):
def commands(self):
return [my_super_command]
To make a subcommand, invoke the constructor like so: ``Subcommand(name, parser,
help, aliases)``. The ``name`` parameter is the only required one and should
just be the name of your command. ``parser`` can be an `OptionParser instance`_,
but it defaults to an empty parser (you can extend it later). ``help`` is a
description of your command, and ``aliases`` is a list of shorthand versions of
your command name.
.. _optionparser instance: https://docs.python.org/library/optparse.html
You'll need to add a function to your command by saying ``mycommand.func =
myfunction``. This function should take the following parameters: ``lib`` (a
beets ``Library`` object) and ``opts`` and ``args`` (command-line options and
arguments as returned by OptionParser.parse_args_).
.. _optionparser.parse_args: https://docs.python.org/library/optparse.html#parsing-arguments
The function should use any of the utility functions defined in ``beets.ui``.
Try running ``pydoc beets.ui`` to see what's available.
You can add command-line options to your new command using the ``parser`` member
of the ``Subcommand`` class, which is a ``CommonOptionsParser`` instance. Just
use it like you would a normal ``OptionParser`` in an independent script. Note
that it offers several methods to add common options: ``--album``, ``--path``
and ``--format``. This feature is versatile and extensively documented, try
``pydoc beets.ui.CommonOptionsParser`` for more information.
.. _plugin_events:
Listen for Events
~~~~~~~~~~~~~~~~~
Event handlers allow plugins to run code whenever something happens in beets'
operation. For instance, a plugin could write a log message every time an album
is successfully autotagged or update MPD's index whenever the database is
changed.
You can "listen" for events using ``BeetsPlugin.register_listener``. Here's an
example:
::
from beets.plugins import BeetsPlugin
def loaded():
print 'Plugin loaded!'
class SomePlugin(BeetsPlugin):
def __init__(self):
super().__init__()
self.register_listener('pluginload', loaded)
Note that if you want to access an attribute of your plugin (e.g. ``config`` or
``log``) you'll have to define a method and not a function. Here is the usual
registration process in this case:
::
from beets.plugins import BeetsPlugin
class SomePlugin(BeetsPlugin):
def __init__(self):
super().__init__()
self.register_listener('pluginload', self.loaded)
def loaded(self):
self._log.info('Plugin loaded!')
The events currently available are:
- ``pluginload``: called after all the plugins have been loaded after the
``beet`` command starts
- ``import``: called after a ``beet import`` command finishes (the ``lib``
keyword argument is a Library object; ``paths`` is a list of paths (strings)
that were imported)
- ``album_imported``: called with an ``Album`` object every time the ``import``
command finishes adding an album to the library. Parameters: ``lib``,
``album``
- ``album_removed``: called with an ``Album`` object every time an album is
removed from the library (even when its file is not deleted from disk).
- ``item_copied``: called with an ``Item`` object whenever its file is copied.
Parameters: ``item``, ``source`` path, ``destination`` path
- ``item_imported``: called with an ``Item`` object every time the importer adds
a singleton to the library (not called for full-album imports). Parameters:
``lib``, ``item``
- ``before_item_moved``: called with an ``Item`` object immediately before its
file is moved. Parameters: ``item``, ``source`` path, ``destination`` path
- ``item_moved``: called with an ``Item`` object whenever its file is moved.
Parameters: ``item``, ``source`` path, ``destination`` path
- ``item_linked``: called with an ``Item`` object whenever a symlink is created
for a file. Parameters: ``item``, ``source`` path, ``destination`` path
- ``item_hardlinked``: called with an ``Item`` object whenever a hardlink is
created for a file. Parameters: ``item``, ``source`` path, ``destination``
path
- ``item_reflinked``: called with an ``Item`` object whenever a reflink is
created for a file. Parameters: ``item``, ``source`` path, ``destination``
path
- ``item_removed``: called with an ``Item`` object every time an item (singleton
or album's part) is removed from the library (even when its file is not
deleted from disk).
- ``write``: called with an ``Item`` object, a ``path``, and a ``tags``
dictionary just before a file's metadata is written to disk (i.e., just before
the file on disk is opened). Event handlers may change the ``tags`` dictionary
to customize the tags that are written to the media file. Event handlers may
also raise a ``library.FileOperationError`` exception to abort the write
operation. Beets will catch that exception, print an error message and
continue.
- ``after_write``: called with an ``Item`` object after a file's metadata is
written to disk (i.e., just after the file on disk is closed).
- ``import_task_created``: called immediately after an import task is
initialized. Plugins can use this to, for example, change imported files of a
task before anything else happens. It's also possible to replace the task with
another task by returning a list of tasks. This list can contain zero or more
``ImportTask``. Returning an empty list will stop the task. Parameters:
``task`` (an ``ImportTask``) and ``session`` (an ``ImportSession``).
- ``import_task_start``: called when before an import task begins processing.
Parameters: ``task`` and ``session``.
- ``import_task_apply``: called after metadata changes have been applied in an
import task. This is called on the same thread as the UI, so use this
sparingly and only for tasks that can be done quickly. For most plugins, an
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 taken.
Parameters: ``task`` and ``session``.
- ``import_task_files``: called after an import task finishes manipulating the
filesystem (copying and moving files, writing metadata tags). Parameters:
``task`` and ``session``.
- ``library_opened``: called after beets starts up and initializes the main
Library object. Parameter: ``lib``.
- ``database_change``: a modification has been made to the library database. The
change might not be committed yet. Parameters: ``lib`` and ``model``.
- ``cli_exit``: called just before the ``beet`` command-line program exits.
Parameter: ``lib``.
- ``import_begin``: called just before a ``beet import`` session starts up.
Parameter: ``session``.
- ``trackinfo_received``: called after metadata for a track item has been
fetched from a data source, such as MusicBrainz. You can modify the tags that
the rest of the pipeline sees on a ``beet import`` operation or during later
adjustments, such as ``mbsync``. Slow handlers of the event can impact the
operation, since the event is fired for any fetched possible match ``before``
the user (or the autotagger machinery) gets to see the match. Parameter:
``info``.
- ``albuminfo_received``: like ``trackinfo_received``, the event indicates new
metadata for album items. The parameter is an ``AlbumInfo`` object instead of
a ``TrackInfo``. Parameter: ``info``.
- ``before_choose_candidate``: called before the user is prompted for a decision
during a ``beet import`` interactive session. Plugins can use this event for
:ref:`appending choices to the prompt <append_prompt_choices>` by returning a
list of ``PromptChoices``. Parameters: ``task`` and ``session``.
- ``mb_track_extract``: called after the metadata is obtained from MusicBrainz.
The parameter is a ``dict`` containing the tags retrieved from MusicBrainz for
a track. Plugins must return a new (potentially empty) ``dict`` with
additional ``field: value`` pairs, which the autotagger will apply to the
item, as flexible attributes if ``field`` is not a hardcoded field. Fields
already present on the track are overwritten. Parameter: ``data``
- ``mb_album_extract``: Like ``mb_track_extract``, but for album tags.
Overwrites tags set at the track level, if they have the same ``field``.
Parameter: ``data``
The included ``mpdupdate`` plugin provides an example use case for event
listeners.
Extend the Autotagger
~~~~~~~~~~~~~~~~~~~~~