mirror of
https://code.eliotberriot.com/funkwhale/funkwhale.git
synced 2025-10-03 19:39:18 +02:00
Plugins infrastructure
This commit is contained in:
parent
9964adfbf6
commit
d4028450a9
32 changed files with 1560 additions and 47 deletions
165
docs/developers/plugins.rst
Normal file
165
docs/developers/plugins.rst
Normal file
|
@ -0,0 +1,165 @@
|
|||
Funkwhale plugins
|
||||
=================
|
||||
|
||||
Starting with Funkwhale 1.0, it is now possible to implement new features
|
||||
via plugins.
|
||||
|
||||
Some plugins are maintained by the Funkwhale team (e.g. this is the case of the ``scrobbler`` plugin),
|
||||
or by third-parties.
|
||||
|
||||
Installing a plugin
|
||||
-------------------
|
||||
|
||||
To install a plugin, ensure its directory is present in the ``FUNKWHALE_PLUGINS_PATH`` directory.
|
||||
|
||||
Then, add its name to the ``FUNKWHALE_PLUGINS`` environment variable, like this::
|
||||
|
||||
FUNKWHALE_PLUGINS=myplugin,anotherplugin
|
||||
|
||||
We provide a command to make it easy to install third-party plugins::
|
||||
|
||||
python manage.py fw plugins install https://pluginurl.zip
|
||||
|
||||
.. note::
|
||||
|
||||
If you use the command, you will still need to append the plugin name to ``FUNKWHALE_PLUGINS``
|
||||
|
||||
|
||||
Types of plugins
|
||||
----------------
|
||||
|
||||
There are two types of plugins:
|
||||
|
||||
1. Plugins that are accessible to end-users, a.k.a. user-level plugins. This is the case of our Scrobbler plugin
|
||||
2. Pod-level plugins that are configured by pod admins and are not tied to a particular user
|
||||
|
||||
Additionally, user-level plugins can be regular plugins or source plugins. A source plugin provides
|
||||
a way to import files from a third-party service, e.g via webdav, FTP or something similar.
|
||||
|
||||
Hooks and filters
|
||||
-----------------
|
||||
|
||||
Funkwhale includes two kind of entrypoints for plugins to use: hooks and filters. B
|
||||
|
||||
Hooks should be used when you want to react to some change. For instance, the ``LISTENING_CREATED`` hook
|
||||
notify each registered callback that a listening was created. Our ``scrobbler`` plugin has a callback
|
||||
registered to this hook, so that it can notify Last.fm properly:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from config import plugins
|
||||
from .funkwhale_startup import PLUGIN
|
||||
|
||||
@plugins.register_hook(plugins.LISTENING_CREATED, PLUGIN)
|
||||
def notify_lastfm(listening, conf, **kwargs):
|
||||
# do something
|
||||
|
||||
Filters work slightly differently, and expect callbacks to return a value that will be used by Funkwhale.
|
||||
|
||||
For instance, the ``PLUGINS_DEPENDENCIES`` filter can be used as a way to install additional dependencies needed by your plugin:
|
||||
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# funkwhale_startup.py
|
||||
# ...
|
||||
from config import plugins
|
||||
|
||||
@plugins.register_filter(plugins.PLUGINS_DEPENDENCIES, PLUGIN)
|
||||
def dependencies(dependencies, **kwargs):
|
||||
return dependencies + ["django_prometheus"]
|
||||
|
||||
To sum it up, hooks are used when you need to react to something, and filters when you need to alter something.
|
||||
|
||||
Writing a plugin
|
||||
----------------
|
||||
|
||||
Regardless of the type of plugin you want to write, lots of concepts are similar.
|
||||
|
||||
First, a plugin need three files:
|
||||
|
||||
- a ``__init__.py`` file, since it's a Python package
|
||||
- a ``funkwhale_startup.py`` file, that is loaded during Funkwhale initialization
|
||||
- a ``funkwhale_ready.py`` file, that is loaded when Funkwhale is configured and ready
|
||||
|
||||
So your plugin directory should look like this::
|
||||
|
||||
myplugin
|
||||
├── funkwhale_ready.py
|
||||
├── funkwhale_startup.py
|
||||
└── __init__.py
|
||||
|
||||
Now, let's write our plugin!
|
||||
|
||||
``funkwhale_startup.py`` is where you declare your plugin and it's configuration options:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# funkwhale_startup.py
|
||||
from config import plugins
|
||||
|
||||
PLUGIN = plugins.get_plugin_config(
|
||||
name="myplugin",
|
||||
label="My Plugin",
|
||||
description="An example plugin that greets you",
|
||||
version="0.1",
|
||||
# here, we write a user-level plugin
|
||||
user=True,
|
||||
conf=[
|
||||
# this configuration options are editable by each user
|
||||
{"name": "greeting", "type": "text", "label": "Greeting", "default": "Hello"},
|
||||
],
|
||||
)
|
||||
|
||||
Now that our plugin is declared and configured, let's implement actual functionality in ``funkwhale_ready.py``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# funkwhale_ready.py
|
||||
from django.urls import path
|
||||
from rest_framework import response
|
||||
from rest_framework import views
|
||||
|
||||
from config import plugins
|
||||
|
||||
from .funkwhale_startup import PLUGIN
|
||||
|
||||
# Our greeting view, where the magic happens
|
||||
class GreetingView(views.APIView):
|
||||
permission_classes = []
|
||||
def get(self, request, *args, **kwargs):
|
||||
# retrieve plugin configuration for the current user
|
||||
conf = plugins.get_conf(PLUGIN["name"], request.user)
|
||||
if not conf["enabled"]:
|
||||
# plugin is disabled for this user
|
||||
return response.Response(status=405)
|
||||
greeting = conf["conf"]["greeting"]
|
||||
data = {
|
||||
"greeting": "{} {}!".format(greeting, request.user.username)
|
||||
}
|
||||
return response.Response(data)
|
||||
|
||||
# Ensure our view is known by Django and available at /greeting
|
||||
@plugins.register_filter(plugins.URLS, PLUGIN)
|
||||
def register_view(urls, **kwargs):
|
||||
return urls + [
|
||||
path('greeting', GreetingView.as_view())
|
||||
]
|
||||
|
||||
And that's pretty much it. Now, login, visit https://yourpod.domain/settings/plugins, set a value in the ``greeting`` field and enable the plugin.
|
||||
|
||||
After that, you should be greeted properly if you go to https://yourpod.domain/greeting.
|
||||
|
||||
Hooks reference
|
||||
---------------
|
||||
|
||||
.. autodata:: config.plugins.LISTENING_CREATED
|
||||
|
||||
Filters reference
|
||||
-----------------
|
||||
|
||||
.. autodata:: config.plugins.PLUGINS_DEPENDENCIES
|
||||
.. autodata:: config.plugins.PLUGINS_APPS
|
||||
.. autodata:: config.plugins.PLUGINSMIDDLEWARES_BEFORE_DEPENDENCIES
|
||||
.. autodata:: config.plugins.MIDDLEWARES_AFTER
|
||||
.. autodata:: config.plugins.URLS
|
Loading…
Add table
Add a link
Reference in a new issue