Merge branch 'develop' into feature/reshuffle-config
Conflicts: mopidy/ext.py
This commit is contained in:
commit
a2b29151a5
11
docs/api/ext.rst
Normal file
11
docs/api/ext.rst
Normal file
@ -0,0 +1,11 @@
|
||||
.. _ext-api:
|
||||
|
||||
*************
|
||||
Extension API
|
||||
*************
|
||||
|
||||
If you want to learn how to make Mopidy extensions, read :ref:`extensiondev`.
|
||||
|
||||
.. automodule:: mopidy.ext
|
||||
:synopsis: Extension API for extending Mopidy
|
||||
:members:
|
||||
@ -1,3 +1,5 @@
|
||||
.. _api-ref:
|
||||
|
||||
*************
|
||||
API reference
|
||||
*************
|
||||
@ -11,4 +13,5 @@ API reference
|
||||
core
|
||||
audio
|
||||
frontends
|
||||
ext
|
||||
http
|
||||
|
||||
@ -1493,8 +1493,7 @@ In the last two months, Mopidy's MPD frontend has gotten lots of stability
|
||||
fixes and error handling improvements, proper support for having the same track
|
||||
multiple times in a playlist, and support for IPv6. We have also fixed the
|
||||
choppy playback on the libspotify backend. For the road ahead of us, we got an
|
||||
updated :doc:`release roadmap <development>` with our goals for the 0.1 to 0.3
|
||||
releases.
|
||||
updated release roadmap with our goals for the 0.1 to 0.3 releases.
|
||||
|
||||
Enjoy the best alpha relase of Mopidy ever :-)
|
||||
|
||||
|
||||
@ -27,8 +27,8 @@ Making changes
|
||||
|
||||
2. Install dependencies as described in the :ref:`installation` section.
|
||||
|
||||
3. Checkout a new branch (usually based on develop) and name it accordingly to
|
||||
what you intend to do.
|
||||
3. Checkout a new branch (usually based on ``develop``) and name it accordingly
|
||||
to what you intend to do.
|
||||
|
||||
- Features get the prefix ``feature/``
|
||||
|
||||
@ -95,10 +95,22 @@ Submitting changes
|
||||
|
||||
- One branch per feature or fix. Keep branches small and on topic.
|
||||
|
||||
- Follow the :ref:`style guide <codestyle>`_, especially make sure ``flake8``
|
||||
- Follow the :ref:`code style <codestyle>`, especially make sure ``flake8``
|
||||
does not complain about anything.
|
||||
|
||||
- Send a pull request to the ``develop`` branch.
|
||||
- Write good commit messages. Here's three blog posts on how to do it right:
|
||||
|
||||
- `Writing Git commit messages
|
||||
<http://365git.tumblr.com/post/3308646748/writing-git-commit-messages>`_
|
||||
|
||||
- `A Note About Git Commit Messages
|
||||
<http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html>`_
|
||||
|
||||
- `On commit messages
|
||||
<http://who-t.blogspot.ch/2009/12/on-commit-messages.html>`_
|
||||
|
||||
- Send a pull request to the ``develop`` branch. See the `GitHub pull request
|
||||
docs <https://help.github.com/articles/using-pull-requests>`_ for help.
|
||||
|
||||
|
||||
Additional resources
|
||||
@ -110,7 +122,4 @@ Additional resources
|
||||
|
||||
- `Mailing List <https://groups.google.com/forum/?fromgroups=#!forum/mopidy>`_
|
||||
|
||||
- `General GitHub documentation <https://help.github.com/>`_
|
||||
|
||||
- `GitHub pull request documentation
|
||||
<https://help.github.com/articles/using-pull-requests>`_
|
||||
- `GitHub documentation <https://help.github.com/>`_
|
||||
|
||||
@ -4,16 +4,9 @@
|
||||
Extension development
|
||||
*********************
|
||||
|
||||
.. warning:: Draft
|
||||
|
||||
This document is a draft open for discussion. It shows how we imagine that
|
||||
development of Mopidy extensions should become in the future, not how to
|
||||
currently develop an extension for Mopidy.
|
||||
|
||||
|
||||
Mopidy started as simply an MPD server that could play music from Spotify.
|
||||
Early on Mopidy got multiple "frontends" to expose Mopidy to more than just MPD
|
||||
clients: for example the Last.fm frontend what scrobbles what you've listened
|
||||
clients: for example the scrobbler frontend what scrobbles what you've listened
|
||||
to to your Last.fm account, the MPRIS frontend that integrates Mopidy into the
|
||||
Ubuntu Sound Menu, and the HTTP server and JavaScript player API making web
|
||||
based Mopidy clients possible. In Mopidy 0.9 we added support for multiple
|
||||
@ -120,9 +113,9 @@ register themselves as available Mopidy extensions when they are installed on
|
||||
your system.
|
||||
|
||||
The example below also includes a couple of convenient tricks for reading the
|
||||
package version from the source code so that it it's just defined in a single
|
||||
place, and to reuse the README file as the long description of the package for
|
||||
the PyPI registration.
|
||||
package version from the source code so that it is defined in a single place,
|
||||
and to reuse the README file as the long description of the package for the
|
||||
PyPI registration.
|
||||
|
||||
The package must have ``install_requires`` on ``setuptools`` and ``Mopidy``, in
|
||||
addition to any other dependencies required by your extension. The
|
||||
@ -189,17 +182,18 @@ Python package.
|
||||
|
||||
The root of your Python package should have an ``__version__`` attribute with a
|
||||
:pep:`386` compliant version number, for example "0.1". Next, it should have a
|
||||
class named ``Extension`` which inherits from Mopidy's extension base class.
|
||||
This is the class referred to in the ``entry_points`` part of ``setup.py``. Any
|
||||
imports of other files in your extension should be kept inside methods. This
|
||||
ensures that this file can be imported without raising :exc:`ImportError`
|
||||
exceptions for missing dependencies, etc.
|
||||
class named ``Extension`` which inherits from Mopidy's extension base class,
|
||||
:class:`mopidy.ext.Extension`. This is the class referred to in the
|
||||
``entry_points`` part of ``setup.py``. Any imports of other files in your
|
||||
extension should be kept inside methods. This ensures that this file can be
|
||||
imported without raising :exc:`ImportError` exceptions for missing
|
||||
dependencies, etc.
|
||||
|
||||
The default configuration for the extension is defined by the
|
||||
``get_default_config()`` method in the ``Extension`` class which returns a
|
||||
:mod:`ConfigParser` compatible config section. The config section's name should
|
||||
:mod:`ConfigParser` compatible config section. The config section's name must
|
||||
be the same as the extension's short name, as defined in the ``entry_points``
|
||||
part of ``setup.py``, for example ``soundspot``. All extensions should include
|
||||
part of ``setup.py``, for example ``soundspot``. All extensions must include
|
||||
an ``enabled`` config which should default to ``true``. Provide good defaults
|
||||
for all config values so that as few users as possible will need to change
|
||||
them. The exception is if the config value has security implications; in that
|
||||
@ -211,8 +205,6 @@ and ``password``.
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import os
|
||||
|
||||
import pygst
|
||||
pygst.require('0.10')
|
||||
import gst
|
||||
@ -332,67 +324,48 @@ If you want to extend Mopidy's GStreamer pipeline with new custom GStreamer
|
||||
elements, you'll need to register them in GStreamer before they can be used.
|
||||
|
||||
Basically, you just implement your GStreamer element in Python and then make
|
||||
your :meth:`Extension.register_gstreamer_elements` method register all your
|
||||
custom GStreamer elements.
|
||||
your :meth:`~mopidy.ext.Extension.register_gstreamer_elements` method register
|
||||
all your custom GStreamer elements.
|
||||
|
||||
For examples of custom GStreamer elements implemented in Python, see
|
||||
:mod:`mopidy.audio.mixers`.
|
||||
|
||||
|
||||
Implementation steps
|
||||
====================
|
||||
Use of Mopidy APIs
|
||||
==================
|
||||
|
||||
A rough plan of how to make the above document the reality of how Mopidy
|
||||
extensions work.
|
||||
When writing an extension, you should only use APIs documented at
|
||||
:ref:`api-ref`. Other parts of Mopidy, like :mod:`mopidy.utils`, may change at
|
||||
any time, and is not something extensions should rely on being stable.
|
||||
|
||||
1. Implement :class:`mopidy.utils.ext.Extension` base class and the
|
||||
:exc:`mopidy.exceptions.ExtensionError` exception class.
|
||||
|
||||
2. Switch from using distutils to setuptools to package and install Mopidy so
|
||||
that we can register entry points for the bundled extensions and get
|
||||
information about all extensions available on the system from
|
||||
:mod:`pkg_resources`.
|
||||
Logging in extensions
|
||||
=====================
|
||||
|
||||
3. Add :class:`Extension` classes for all existing frontends and backends. Skip
|
||||
any default config and config validation for now.
|
||||
When making servers like Mopidy, logging is essential for understanding what's
|
||||
going on. We use the :mod:`logging` module from Python's standard library. When
|
||||
creating a logger, always namespace the logger using your Python package name
|
||||
as this will be visible in Mopidy's debug log::
|
||||
|
||||
4. Add entry points for the existing extensions in the ``setup.py`` file.
|
||||
import logging
|
||||
|
||||
5. Rewrite the startup procedure to find extensions and thus frontends and
|
||||
backends via :mod:`pkg_resouces` instead of the ``FRONTENDS`` and
|
||||
``BACKENDS`` settings.
|
||||
logger = logging.getLogger('mopidy_soundspot')
|
||||
|
||||
6. Remove the ``FRONTENDS`` and ``BACKENDS`` settings.
|
||||
When logging at logging level ``info`` or higher (i.e. ``warning``, ``error``,
|
||||
and ``critical``, but not ``debug``) the log message will be displayed to all
|
||||
Mopidy users. Thus, the log messages at those levels should be well written and
|
||||
easy to understand.
|
||||
|
||||
7. Add default config files and config validation to all existing extensions.
|
||||
As the logger name is not included in Mopidy's default logging format, you
|
||||
should make it obvious from the log message who is the source of the log
|
||||
message. For example::
|
||||
|
||||
8. Switch to ini file based configuration, using :mod:`ConfigParser`. The
|
||||
default config is the combination of a core config file plus the config from
|
||||
each installed extension. To find the effective config for the system, the
|
||||
following config sources are added together, with the later ones overriding
|
||||
the earlier ones:
|
||||
Loaded 17 Soundspot playlists
|
||||
|
||||
- the default config built from Mopidy core and all installed extensions,
|
||||
Is much better than::
|
||||
|
||||
- ``/etc/mopidy/mopidy.conf``,
|
||||
Loaded 17 playlists
|
||||
|
||||
- ``~/.config/mopidy/mopidy.conf``,
|
||||
|
||||
- any config file provided via command line arguments, and
|
||||
|
||||
- any config values provided via command line arguments.
|
||||
|
||||
9. Replace all use of ``mopidy.settings`` with the new config object.
|
||||
|
||||
10. Add command line options for:
|
||||
|
||||
- loading an additional config file for this execution of Mopidy,
|
||||
|
||||
- setting a config value for this execution of Mopidy,
|
||||
|
||||
- printing the effective config and exit, and
|
||||
|
||||
- write a config value permanently to ``~/.config/mopidy/mopidy.conf``, or
|
||||
``/etc/mopidy/mopidy.conf`` if root, and exit.
|
||||
|
||||
11. Reimplement ``--list-deps`` based upon information provided by extensions.
|
||||
If you want to turn on debug logging for your own extension, but not for
|
||||
everything else due to the amount of noise, see the docs for the
|
||||
:confval:`logging.levels/*` config section.
|
||||
|
||||
@ -18,3 +18,19 @@ using ``kill``::
|
||||
kill `ps ax | grep mopidy | grep -v grep | cut -d' ' -f1`
|
||||
|
||||
This can be useful e.g. if you create init script for managing Mopidy.
|
||||
|
||||
|
||||
mopidy command
|
||||
==============
|
||||
|
||||
.. program:: mopidy
|
||||
|
||||
TODO: Document all command line options
|
||||
|
||||
|
||||
mopidy-scan command
|
||||
===================
|
||||
|
||||
.. program:: mopidy-scan
|
||||
|
||||
TODO: Document all command line options
|
||||
|
||||
@ -11,28 +11,35 @@ logger = logging.getLogger('mopidy.ext')
|
||||
|
||||
|
||||
class Extension(object):
|
||||
"""Base class for Mopidy extensions"""
|
||||
|
||||
dist_name = None
|
||||
ext_name = None
|
||||
version = None
|
||||
|
||||
def get_default_config(self):
|
||||
"""TODO"""
|
||||
raise NotImplementedError(
|
||||
'Add at least a config section with "enabled = true"')
|
||||
|
||||
def get_config_schema(self):
|
||||
"""TODO"""
|
||||
return config_lib.ExtensionConfigSchema()
|
||||
|
||||
def validate_environment(self):
|
||||
"""TODO"""
|
||||
pass
|
||||
|
||||
def get_frontend_classes(self):
|
||||
"""TODO"""
|
||||
return []
|
||||
|
||||
def get_backend_classes(self):
|
||||
"""TODO"""
|
||||
return []
|
||||
|
||||
def register_gstreamer_elements(self):
|
||||
"""TODO"""
|
||||
pass
|
||||
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user