diff --git a/mopidy/backends/local/__init__.py b/mopidy/backends/local/__init__.py
index 8ee58d3b..99c50e1f 100644
--- a/mopidy/backends/local/__init__.py
+++ b/mopidy/backends/local/__init__.py
@@ -1,4 +1,10 @@
-"""A backend for playing music from a local music archive.
+from __future__ import unicode_literals
+
+import mopidy
+from mopidy import ext
+
+
+__doc__ = """A backend for playing music from a local music archive.
This backend handles URIs starting with ``file:``.
@@ -20,7 +26,24 @@ https://github.com/mopidy/mopidy/issues?labels=Local+backend
- :attr:`mopidy.settings.LOCAL_TAG_CACHE_FILE`
"""
-from __future__ import unicode_literals
-# flake8: noqa
+# TODO Move import into method when BACKENDS setting is removed
from .actor import LocalBackend
+
+
+class Extension(ext.Extension):
+
+ name = 'Mopidy-Local'
+ version = mopidy.__version__
+
+ def get_default_config(self):
+ return '[ext.local]'
+
+ def validate_config(self, config):
+ pass
+
+ def validate_environment(self):
+ pass
+
+ def get_backend_classes(self):
+ return [LocalBackend]
diff --git a/mopidy/backends/spotify/__init__.py b/mopidy/backends/spotify/__init__.py
index 507511f4..efa5338b 100644
--- a/mopidy/backends/spotify/__init__.py
+++ b/mopidy/backends/spotify/__init__.py
@@ -1,4 +1,39 @@
-"""A backend for playing music from Spotify
+from __future__ import unicode_literals
+
+import mopidy
+from mopidy import ext
+from mopidy.exceptions import ExtensionError
+from mopidy.utils.formatting import indent
+
+
+config = """
+[ext.spotify]
+
+# If the Spotify extension should be enabled or not
+enabled = true
+
+# Your Spotify Premium username
+username =
+
+# Your Spotify Premium password
+password =
+
+# The preferred audio bitrate. Valid values are 96, 160, 320
+bitrate = 160
+
+# Max number of seconds to wait for Spotify operations to complete
+timeout = 10
+
+# Path to the Spotify data cache. Cannot be shared with other Spotify apps
+cache_path = $XDG_CACHE_DIR/mopidy/spotify
+
+# Connect to Spotify through a proxy
+proxy_host =
+proxy_username =
+proxy_password =
+"""
+
+__doc__ = """A backend for playing music from Spotify
`Spotify `_ is a music streaming service. The backend
uses the official `libspotify
@@ -22,14 +57,39 @@ https://github.com/mopidy/mopidy/issues?labels=Spotify+backend
.. literalinclude:: ../../../requirements/spotify.txt
-**Settings:**
+**Default config:**
-- :attr:`mopidy.settings.SPOTIFY_CACHE_PATH`
-- :attr:`mopidy.settings.SPOTIFY_USERNAME`
-- :attr:`mopidy.settings.SPOTIFY_PASSWORD`
-"""
+.. code-block:: ini
-from __future__ import unicode_literals
+%(config)s
+""" % {'config': indent(config)}
-# flake8: noqa
+
+# TODO Move import into method when BACKENDS setting is removed
from .actor import SpotifyBackend
+
+
+class Extension(ext.Extension):
+
+ name = 'Mopidy-Spotify'
+ version = mopidy.__version__
+
+ def get_default_config(self):
+ return config
+
+ def validate_config(self, config):
+ if not config.getboolean('spotify', 'enabled'):
+ return
+ if not config.get('spotify', 'username'):
+ raise ExtensionError('Config spotify.username not set')
+ if not config.get('spotify', 'password'):
+ raise ExtensionError('Config spotify.password not set')
+
+ def validate_environment(self):
+ try:
+ import spotify # noqa
+ except ImportError as e:
+ raise ExtensionError('pyspotify library not found', e)
+
+ def get_backend_classes(self):
+ return [SpotifyBackend]
diff --git a/mopidy/backends/stream/__init__.py b/mopidy/backends/stream/__init__.py
index 82755540..dbf3e6d5 100644
--- a/mopidy/backends/stream/__init__.py
+++ b/mopidy/backends/stream/__init__.py
@@ -1,4 +1,10 @@
-"""A backend for playing music for streaming music.
+from __future__ import unicode_literals
+
+import mopidy
+from mopidy import ext
+
+
+__doc__ = """A backend for playing music for streaming music.
This backend will handle streaming of URIs in
:attr:`mopidy.settings.STREAM_PROTOCOLS` assuming the right plugins are
@@ -17,7 +23,24 @@ https://github.com/mopidy/mopidy/issues?labels=Stream+backend
- :attr:`mopidy.settings.STREAM_PROTOCOLS`
"""
-from __future__ import unicode_literals
-# flake8: noqa
+# TODO Move import into method when BACKENDS setting is removed
from .actor import StreamBackend
+
+
+class Extension(ext.Extension):
+
+ name = 'Mopidy-Stream'
+ version = mopidy.__version__
+
+ def get_default_config(self):
+ return '[ext.stream]'
+
+ def validate_config(self, config):
+ pass
+
+ def validate_environment(self):
+ pass
+
+ def get_backend_classes(self):
+ return [StreamBackend]
diff --git a/mopidy/frontends/http/__init__.py b/mopidy/frontends/http/__init__.py
index e81ddf3f..25fe788f 100644
--- a/mopidy/frontends/http/__init__.py
+++ b/mopidy/frontends/http/__init__.py
@@ -1,4 +1,11 @@
-"""
+from __future__ import unicode_literals
+
+import mopidy
+from mopidy import ext
+from mopidy.exceptions import ExtensionError
+
+
+__doc__ = """
The HTTP frontends lets you control Mopidy through HTTP and WebSockets, e.g.
from a web based client.
@@ -477,5 +484,32 @@ Example to get started with
and all events that are emitted.
"""
-# flake8: noqa
+
+# TODO Move import into method when FRONTENDS setting is removed
from .actor import HttpFrontend
+
+
+class Extension(ext.Extension):
+
+ name = 'Mopidy-HTTP'
+ version = mopidy.__version__
+
+ def get_default_config(self):
+ return '[ext.http]'
+
+ def validate_config(self, config):
+ pass
+
+ def validate_environment(self):
+ try:
+ import cherrypy # noqa
+ except ImportError as e:
+ raise ExtensionError('Library cherrypy not found', e)
+
+ try:
+ import ws4py # noqa
+ except ImportError as e:
+ raise ExtensionError('Library ws4py not found', e)
+
+ def get_frontend_classes(self):
+ return [HttpFrontend]
diff --git a/mopidy/frontends/lastfm/__init__.py b/mopidy/frontends/lastfm/__init__.py
new file mode 100644
index 00000000..e3f1f5c6
--- /dev/null
+++ b/mopidy/frontends/lastfm/__init__.py
@@ -0,0 +1,55 @@
+from __future__ import unicode_literals
+
+import mopidy
+from mopidy import ext
+from mopidy.exceptions import ExtensionError
+
+
+__doc__ = """
+Frontend which scrobbles the music you play to your `Last.fm
+`_ profile.
+
+.. note::
+
+ This frontend requires a free user account at Last.fm.
+
+**Dependencies:**
+
+.. literalinclude:: ../../../requirements/lastfm.txt
+
+**Settings:**
+
+- :attr:`mopidy.settings.LASTFM_USERNAME`
+- :attr:`mopidy.settings.LASTFM_PASSWORD`
+
+**Usage:**
+
+Make sure :attr:`mopidy.settings.FRONTENDS` includes
+``mopidy.frontends.lastfm.LastfmFrontend``. By default, the setting includes
+the Last.fm frontend.
+"""
+
+
+# TODO Move import into method when FRONTENDS setting is removed
+from .actor import LastfmFrontend
+
+
+class Extension(ext.Extension):
+
+ name = 'Mopidy-Lastfm'
+ version = mopidy.__version__
+
+ def get_default_config(self):
+ return '[ext.lastfm]'
+
+ def validate_config(self, config):
+ pass
+
+ def validate_environment(self):
+ try:
+ import pylast # noqa
+ except ImportError as e:
+ raise ExtensionError('pylast library not found', e)
+
+ def get_frontend_classes(self):
+ return [LastfmFrontend]
diff --git a/mopidy/frontends/lastfm.py b/mopidy/frontends/lastfm/actor.py
similarity index 87%
rename from mopidy/frontends/lastfm.py
rename to mopidy/frontends/lastfm/actor.py
index 61dc306c..60a909e0 100644
--- a/mopidy/frontends/lastfm.py
+++ b/mopidy/frontends/lastfm/actor.py
@@ -1,27 +1,3 @@
-"""
-Frontend which scrobbles the music you play to your `Last.fm
-`_ profile.
-
-.. note::
-
- This frontend requires a free user account at Last.fm.
-
-**Dependencies:**
-
-.. literalinclude:: ../../../requirements/lastfm.txt
-
-**Settings:**
-
-- :attr:`mopidy.settings.LASTFM_USERNAME`
-- :attr:`mopidy.settings.LASTFM_PASSWORD`
-
-**Usage:**
-
-Make sure :attr:`mopidy.settings.FRONTENDS` includes
-``mopidy.frontends.lastfm.LastfmFrontend``. By default, the setting includes
-the Last.fm frontend.
-"""
-
from __future__ import unicode_literals
import logging
diff --git a/mopidy/frontends/mpd/__init__.py b/mopidy/frontends/mpd/__init__.py
index 6b4eacc8..8d9d13e0 100644
--- a/mopidy/frontends/mpd/__init__.py
+++ b/mopidy/frontends/mpd/__init__.py
@@ -1,4 +1,10 @@
-"""The MPD server frontend.
+from __future__ import unicode_literals
+
+import mopidy
+from mopidy import ext
+
+
+__doc__ = """The MPD server frontend.
MPD stands for Music Player Daemon. MPD is an independent project and server.
Mopidy implements the MPD protocol, and is thus compatible with clients for the
@@ -44,7 +50,24 @@ near future:
- Live update of the music database is not supported
"""
-from __future__ import unicode_literals
-# flake8: noqa
+# TODO Move import into method when FRONTENDS setting is removed
from .actor import MpdFrontend
+
+
+class Extension(ext.Extension):
+
+ name = 'Mopidy-MPD'
+ version = mopidy.__version__
+
+ def get_default_config(self):
+ return '[ext.mpd]'
+
+ def validate_config(self, config):
+ pass
+
+ def validate_environment(self):
+ pass
+
+ def get_frontend_classes(self):
+ return [MpdFrontend]
diff --git a/mopidy/frontends/mpris/__init__.py b/mopidy/frontends/mpris/__init__.py
index 2be6efea..b21dafff 100644
--- a/mopidy/frontends/mpris/__init__.py
+++ b/mopidy/frontends/mpris/__init__.py
@@ -1,4 +1,11 @@
-"""
+from __future__ import unicode_literals
+
+import mopidy
+from mopidy import ext
+from mopidy.exceptions import ExtensionError
+
+
+__doc__ = """
Frontend which lets you control Mopidy through the Media Player Remote
Interfacing Specification (`MPRIS `_) D-Bus
interface.
@@ -50,7 +57,27 @@ Now you can control Mopidy through the player object. Examples:
player.Quit(dbus_interface='org.mpris.MediaPlayer2')
"""
-from __future__ import unicode_literals
-# flake8: noqa
+# TODO Move import into method when FRONTENDS setting is removed
from .actor import MprisFrontend
+
+
+class Extension(ext.Extension):
+
+ name = 'Mopidy-MPRIS'
+ version = mopidy.__version__
+
+ def get_default_config(self):
+ return '[ext.mpris]'
+
+ def validate_config(self, config):
+ pass
+
+ def validate_environment(self):
+ try:
+ import dbus # noqa
+ except ImportError as e:
+ raise ExtensionError('Library dbus not found', e)
+
+ def get_frontend_classes(self):
+ return [MprisFrontend]
diff --git a/setup.py b/setup.py
index eeab24bd..cff6ce23 100644
--- a/setup.py
+++ b/setup.py
@@ -44,7 +44,15 @@ setup(
'mopidy = mopidy.__main__:main',
'mopidy-scan = mopidy.scanner:main',
],
- b'mopidy.extension': [],
+ b'mopidy.extension': [
+ 'http = mopidy.frontends.http:Extension',
+ 'lastfm = mopidy.frontends.lastfm:Extension',
+ 'local = mopidy.backends.local:Extension',
+ 'mpd = mopidy.frontends.mpd:Extension',
+ 'mpris = mopidy.frontends.mpris:Extension',
+ 'spotify = mopidy.backends.spotify:Extension',
+ 'stream = mopidy.backends.stream:Extension',
+ ],
},
classifiers=[
'Development Status :: 4 - Beta',