Merge branch 'develop' into feature/reshuffle-config

Conflicts:
	mopidy/backends/local/__init__.py
	mopidy/backends/spotify/__init__.py
	mopidy/backends/stream/__init__.py
	mopidy/config.py
	mopidy/frontends/http/__init__.py
	mopidy/frontends/mpd/__init__.py
	mopidy/frontends/mpris/__init__.py
	mopidy/frontends/scrobbler/__init__.py
	mopidy/utils/config.py
	tests/config/types_test.py
This commit is contained in:
Thomas Adamcik 2013-04-13 01:25:38 +02:00
commit 1ebc265235
56 changed files with 1782 additions and 1568 deletions

View File

@ -2,18 +2,16 @@
Mopidy
******
.. image:: https://secure.travis-ci.org/mopidy/mopidy.png?branch=develop
Mopidy is a music server which can play music both from multiple sources, like
your local hard drive, radio streams, and from Spotify and SoundCloud. Searches
combines results from all music sources, and you can mix tracks from all
sources in your play queue. Your playlists from Spotify or SoundCloud are also
available for use.
Mopidy is a music server which can play music both from your local hard drive
and from Spotify. Searches returns results from both your local hard drive and
from Spotify, and you can mix tracks from both sources in your play queue. Your
Spotify playlists are also available for use, though we don't support modifying
them yet.
To control your music server, you can use the Ubuntu Sound Menu on the machine
running Mopidy, any device on the same network which can control UPnP
MediaRenderers, or any MPD client. MPD clients are available for most
platforms, including Windows, Mac OS X, Linux, Android and iOS.
To control your Mopidy music server, you can use one of Mopidy's web clients,
the Ubuntu Sound Menu, any device on the same network which can control UPnP
MediaRenderers, or any MPD client. MPD clients are available for many
platforms, including Windows, OS X, Linux, Android and iOS.
To get started with Mopidy, check out `the docs <http://docs.mopidy.com/>`_.
@ -21,6 +19,10 @@ To get started with Mopidy, check out `the docs <http://docs.mopidy.com/>`_.
- `Source code <https://github.com/mopidy/mopidy>`_
- `Issue tracker <https://github.com/mopidy/mopidy/issues>`_
- `CI server <https://travis-ci.org/mopidy/mopidy>`_
- `Download development snapshot <https://github.com/mopidy/mopidy/tarball/develop#egg=mopidy-dev>`_
- IRC: ``#mopidy`` at `irc.freenode.net <http://freenode.net/>`_
- Mailing list: `mopidy@googlegroups.com <https://groups.google.com/forum/?fromgroups=#!forum/mopidy>`_
- `Download development snapshot <https://github.com/mopidy/mopidy/tarball/develop#egg=mopidy-dev>`_
- Twitter: `@mopidy <https://twitter.com/mopidy/>`_
.. image:: https://secure.travis-ci.org/mopidy/mopidy.png?branch=develop

View File

@ -13,9 +13,12 @@ The following requirements applies to any frontend implementation:
<http://pykka.readthedocs.org/>`_ actor, called the "main actor" from here
on.
- The main actor MUST accept a constructor argument ``core``, which will be an
:class:`ActorProxy <pykka.proxy.ActorProxy>` for the core actor. This object
gives access to the full :ref:`core-api`.
- The main actor MUST accept two constructor arguments:
- ``config``, which is a dict structure with the entire Mopidy configuration.
- ``core``, which will be an :class:`ActorProxy <pykka.proxy.ActorProxy>` for
the core actor. This object gives access to the full :ref:`core-api`.
- It MAY use additional actors to implement whatever it does, and using actors
in frontend implementations is encouraged.

439
docs/api/http.rst Normal file
View File

@ -0,0 +1,439 @@
.. _http-api:
********
HTTP API
********
The :ref:`ext-http` extension makes Mopidy's :ref:`core-api` available over
HTTP using WebSockets. We also provide a JavaScript wrapper, called
:ref:`Mopidy.js <mopidy-js>` around the HTTP API for use both from browsers and
Node.js.
.. warning:: API stability
Since the HTTP API exposes our internal core API directly it is to be
regarded as **experimental**. We cannot promise to keep any form of
backwards compatibility between releases as we will need to change the core
API while working out how to support new use cases. Thus, if you use this
API, you must expect to do small adjustments to your client for every
release of Mopidy.
From Mopidy 1.0 and onwards, we intend to keep the core API far more
stable.
.. _websocket-api:
WebSocket API
=============
The web server exposes a WebSocket at ``/mopidy/ws/``. The WebSocket gives you
access to Mopidy's full API and enables Mopidy to instantly push events to the
client, as they happen.
On the WebSocket we send two different kind of messages: The client can send
JSON-RPC 2.0 requests, and the server will respond with JSON-RPC 2.0 responses.
In addition, the server will send event messages when something happens on the
server. Both message types are encoded as JSON objects.
Event messages
--------------
Event objects will always have a key named ``event`` whose value is the event
type. Depending on the event type, the event may include additional fields for
related data. The events maps directly to the :class:`mopidy.core.CoreListener`
API. Refer to the ``CoreListener`` method names is the available event types.
The ``CoreListener`` method's keyword arguments are all included as extra
fields on the event objects. Example event message::
{"event": "track_playback_started", "track": {...}}
JSON-RPC 2.0 messaging
----------------------
JSON-RPC 2.0 messages can be recognized by checking for the key named
``jsonrpc`` with the string value ``2.0``. For details on the messaging format,
please refer to the `JSON-RPC 2.0 spec
<http://www.jsonrpc.org/specification>`_.
All methods (not attributes) in the :ref:`core-api` is made available through
JSON-RPC calls over the WebSocket. For example,
:meth:`mopidy.core.PlaybackController.play` is available as the JSON-RPC method
``core.playback.play``.
The core API's attributes is made available through setters and getters. For
example, the attribute :attr:`mopidy.core.PlaybackController.current_track` is
available as the JSON-RPC method ``core.playback.get_current_track``.
Example JSON-RPC request::
{"jsonrpc": "2.0", "id": 1, "method": "core.playback.get_current_track"}
Example JSON-RPC response::
{"jsonrpc": "2.0", "id": 1, "result": {"__model__": "Track", "...": "..."}}
The JSON-RPC method ``core.describe`` returns a data structure describing all
available methods. If you're unsure how the core API maps to JSON-RPC, having a
look at the ``core.describe`` response can be helpful.
.. _mopidy-js:
Mopidy.js JavaScript library
============================
We've made a JavaScript library, Mopidy.js, which wraps the WebSocket and gets
you quickly started with working on your client instead of figuring out how to
communicate with Mopidy.
Getting the library for browser use
-----------------------------------
Regular and minified versions of Mopidy.js, ready for use, is installed
together with Mopidy. When the HTTP extension is enabled, the files are
available at:
- http://localhost:6680/mopidy/mopidy.js
- http://localhost:6680/mopidy/mopidy.min.js
You may need to adjust hostname and port for your local setup.
Thus, if you use Mopidy to host your web client, like described above, you can
load the latest version of Mopidy.js by adding the following script tag to your
HTML file:
.. code-block:: html
<script type="text/javascript" src="/mopidy/mopidy.min.js"></script>
If you don't use Mopidy to host your web client, you can find the JS files in
the Git repo at:
- ``mopidy/frontends/http/data/mopidy.js``
- ``mopidy/frontends/http/data/mopidy.min.js``
Getting the library for Node.js use
-----------------------------------
If you want to use Mopidy.js from Node.js instead of a browser, you can install
Mopidy.js using npm::
npm install mopidy
After npm completes, you can import Mopidy.js using ``require()``:
.. code-block:: js
var Mopidy = require("mopidy").Mopidy;
Getting the library for development on the library
--------------------------------------------------
If you want to work on the Mopidy.js library itself, you'll find a complete
development setup in the ``js/`` dir in our repo. The instructions in
``js/README.md`` will guide you on your way.
Creating an instance
--------------------
Once you got Mopidy.js loaded, you need to create an instance of the wrapper:
.. code-block:: js
var mopidy = new Mopidy();
When you instantiate ``Mopidy()`` without arguments, it will connect to
the WebSocket at ``/mopidy/ws/`` on the current host. Thus, if you don't host
your web client using Mopidy's web server, or if you use Mopidy.js from a
Node.js environment, you'll need to pass the URL to the WebSocket end point:
.. code-block:: js
var mopidy = new Mopidy({
webSocketUrl: "ws://localhost:6680/mopidy/ws/"
});
It is also possible to create an instance first and connect to the WebSocket
later:
.. code-block:: js
var mopidy = new Mopidy({autoConnect: false});
// ... do other stuff, like hooking up events ...
mopidy.connect();
Hooking up to events
--------------------
Once you have a Mopidy.js object, you can hook up to the events it emits. To
explore your possibilities, it can be useful to subscribe to all events and log
them:
.. code-block:: js
mopidy.on(console.log.bind(console));
Several types of events are emitted:
- You can get notified about when the Mopidy.js object is connected to the
server and ready for method calls, when it's offline, and when it's trying to
reconnect to the server by looking at the events ``state:online``,
``state:offline``, ``reconnectionPending``, and ``reconnecting``.
- You can get events sent from the Mopidy server by looking at the events with
the name prefix ``event:``, like ``event:trackPlaybackStarted``.
- You can introspect what happens internally on the WebSocket by looking at the
events emitted with the name prefix ``websocket:``.
Mopidy.js uses the event emitter library `BANE
<https://github.com/busterjs/bane>`_, so you should refer to BANE's
short API documentation to see how you can hook up your listeners to the
different events.
Calling core API methods
------------------------
Once your Mopidy.js object has connected to the Mopidy server and emits the
``state:online`` event, it is ready to accept core API method calls:
.. code-block:: js
mopidy.on("state:online", function () {
mopidy.playback.next();
});
Any calls you make before the ``state:online`` event is emitted will fail. If
you've hooked up an errback (more on that a bit later) to the promise returned
from the call, the errback will be called with an error message.
All methods in Mopidy's :ref:`core-api` is available via Mopidy.js. The core
API attributes is *not* available, but that shouldn't be a problem as we've
added (undocumented) getters and setters for all of them, so you can access the
attributes as well from JavaScript.
Both the WebSocket API and the JavaScript API are based on introspection of the
core Python API. Thus, they will always be up to date and immediately reflect
any changes we do to the core API.
The best way to explore the JavaScript API, is probably by opening your
browser's console, and using its tab completion to navigate the API. You'll
find the Mopidy core API exposed under ``mopidy.playback``,
``mopidy.tracklist``, ``mopidy.playlists``, and ``mopidy.library``.
All methods in the JavaScript API have an associated data structure describing
the Python params it expects, and most methods also have the Python API
documentation available. This is available right there in the browser console,
by looking at the method's ``description`` and ``params`` attributes:
.. code-block:: js
console.log(mopidy.playback.next.params);
console.log(mopidy.playback.next.description);
JSON-RPC 2.0 limits method parameters to be sent *either* by-position or
by-name. Combinations of both, like we're used to from Python, isn't supported
by JSON-RPC 2.0. To further limit this, Mopidy.js currently only supports
passing parameters by-position.
Obviously, you'll want to get a return value from many of your method calls.
Since everything is happening across the WebSocket and maybe even across the
network, you'll get the results asynchronously. Instead of having to pass
callbacks and errbacks to every method you call, the methods return "promise"
objects, which you can use to pipe the future result as input to another
method, or to hook up callback and errback functions.
.. code-block:: js
var track = mopidy.playback.getCurrentTrack();
// => ``track`` isn't a track, but a "promise" object
Instead, typical usage will look like this:
.. code-block:: js
var printCurrentTrack = function (track) {
if (track) {
console.log("Currently playing:", track.name, "by",
track.artists[0].name, "from", track.album.name);
} else {
console.log("No current track");
}
};
mopidy.playback.getCurrentTrack().then(
printCurrentTrack, console.error.bind(console));
The first function passed to ``then()``, ``printCurrentTrack``, is the callback
that will be called if the method call succeeds. The second function,
``console.error``, is the errback that will be called if anything goes wrong.
If you don't hook up an errback, debugging will be hard as errors will silently
go missing.
For debugging, you may be interested in errors from function without
interesting return values as well. In that case, you can pass ``null`` as the
callback:
.. code-block:: js
mopidy.playback.next().then(null, console.error.bind(console));
The promise objects returned by Mopidy.js adheres to the `CommonJS Promises/A
<http://wiki.commonjs.org/wiki/Promises/A>`_ standard. We use the
implementation known as `when.js <https://github.com/cujojs/when>`_. Please
refer to when.js' documentation or the standard for further details on how to
work with promise objects.
Cleaning up
-----------
If you for some reason want to clean up after Mopidy.js before the web page is
closed or navigated away from, you can close the WebSocket, unregister all
event listeners, and delete the object like this:
.. code-block:: js
// Close the WebSocket without reconnecting. Letting the object be garbage
// collected will have the same effect, so this isn't strictly necessary.
mopidy.close();
// Unregister all event listeners. If you don't do this, you may have
// lingering references to the object causing the garbage collector to not
// clean up after it.
mopidy.off();
// Delete your reference to the object, so it can be garbage collected.
mopidy = null;
Example to get started with
---------------------------
1. Make sure that you've installed all dependencies required by
:ref:`ext-http`.
2. Create an empty directory for your web client.
3. Change the :confval:`http/static_dir` config value to point to your new
directory.
4. Start/restart Mopidy.
5. Create a file in the directory named ``index.html`` containing e.g. "Hello,
world!".
6. Visit http://localhost:6680/ to confirm that you can view your new HTML file
there.
7. Include Mopidy.js in your web page:
.. code-block:: html
<script type="text/javascript" src="/mopidy/mopidy.min.js"></script>
8. Add one of the following Mopidy.js examples of how to queue and start
playback of your first playlist either to your web page or a JavaScript file
that you include in your web page.
"Imperative" style:
.. code-block:: js
var consoleError = console.error.bind(console);
var trackDesc = function (track) {
return track.name + " by " + track.artists[0].name +
" from " + track.album.name;
};
var queueAndPlayFirstPlaylist = function () {
mopidy.playlists.getPlaylists().then(function (playlists) {
var playlist = playlists[0];
console.log("Loading playlist:", playlist.name);
mopidy.tracklist.add(playlist.tracks).then(function (tlTracks) {
mopidy.playback.play(tlTracks[0]).then(function () {
mopidy.playback.getCurrentTrack().then(function (track) {
console.log("Now playing:", trackDesc(track));
}, consoleError);
}, consoleError);
}, consoleError);
}, consoleError);
};
var mopidy = new Mopidy(); // Connect to server
mopidy.on(console.log.bind(console)); // Log all events
mopidy.on("state:online", queueAndPlayFirstPlaylist);
Approximately the same behavior in a more functional style, using chaining
of promisies.
.. code-block:: js
var consoleError = console.error.bind(console);
var getFirst = function (list) {
return list[0];
};
var extractTracks = function (playlist) {
return playlist.tracks;
};
var printTypeAndName = function (model) {
console.log(model.__model__ + ": " + model.name);
// By returning the playlist, this function can be inserted
// anywhere a model with a name is piped in the chain.
return model;
};
var trackDesc = function (track) {
return track.name + " by " + track.artists[0].name +
" from " + track.album.name;
};
var printNowPlaying = function () {
// By returning any arguments we get, the function can be inserted
// anywhere in the chain.
var args = arguments;
return mopidy.playback.getCurrentTrack().then(function (track) {
console.log("Now playing:", trackDesc(track));
return args;
});
};
var queueAndPlayFirstPlaylist = function () {
mopidy.playlists.getPlaylists()
// => list of Playlists
.then(getFirst, consoleError)
// => Playlist
.then(printTypeAndName, consoleError)
// => Playlist
.then(extractTracks, consoleError)
// => list of Tracks
.then(mopidy.tracklist.add, consoleError)
// => list of TlTracks
.then(getFirst, consoleError)
// => TlTrack
.then(mopidy.playback.play, consoleError)
// => null
.then(printNowPlaying, consoleError);
};
var mopidy = new Mopidy(); // Connect to server
mopidy.on(console.log.bind(console)); // Log all events
mopidy.on("state:online", queueAndPlayFirstPlaylist);
9. The web page should now queue and play your first playlist every time your
load it. See the browser's console for output from the function, any errors,
and all events that are emitted.

View File

@ -11,3 +11,4 @@ API reference
core
audio
frontends
http

View File

@ -6,11 +6,7 @@ Contributors to Mopidy in the order of appearance:
.. include:: ../AUTHORS
Showing your appreciation
=========================
If you already enjoy Mopidy, or don't enjoy it and want to help us making
Mopidy better, the best way to do so is to contribute back to the community.
You can contribute code, documentation, tests, bug reports, or help other
users, spreading the word, etc.
users, spreading the word, etc. See :ref:`contributing` for a head start.

View File

@ -1,8 +1,8 @@
*******
Changes
*******
*********
Changelog
*********
This change log is used to track all major changes to Mopidy.
This changelog is used to track all major changes to Mopidy.
v0.14.0 (UNRELEASED)
====================
@ -333,7 +333,7 @@ We've added an HTTP frontend for those wanting to build web clients for Mopidy!
**HTTP frontend**
- Added new optional HTTP frontend which exposes Mopidy's core API through
JSON-RPC 2.0 messages over a WebSocket. See :ref:`http-frontend` for further
JSON-RPC 2.0 messages over a WebSocket. See :ref:`http-api` for further
details.
- Added a JavaScript library, Mopidy.js, to make it easier to develop web based

View File

@ -4,14 +4,14 @@
HTTP clients
************
Mopidy added an :ref:`HTTP frontend <http-frontend>` in 0.10 which provides the
Mopidy added an :ref:`HTTP frontend <ext-http>` in 0.10 which provides the
building blocks needed for creating web clients for Mopidy with the help of a
WebSocket and a JavaScript library provided by Mopidy.
This page will list any HTTP/web Mopidy clients. If you've created one, please
notify us so we can include your client on this page.
See :ref:`http-frontend` for details on how to build your own web client.
See :ref:`http-api` for details on how to build your own web client.
woutervanwijk/Mopidy-Webclient

View File

@ -8,9 +8,9 @@ MPRIS clients
Specification. It's a spec that describes a standard D-Bus interface for making
media players available to other applications on the same system.
Mopidy's :ref:`MPRIS frontend <mpris-frontend>` currently implements all
required parts of the MPRIS spec, plus the optional playlist interface. It does
not implement the optional tracklist interface.
Mopidy's :ref:`MPRIS frontend <ext-mpris>` currently implements all required
parts of the MPRIS spec, plus the optional playlist interface. It does not
implement the optional tracklist interface.
.. _ubuntu-sound-menu:

View File

@ -37,18 +37,18 @@ How to make Mopidy available as an UPnP MediaRenderer
With the help of `the Rygel project <https://live.gnome.org/Rygel>`_ Mopidy can
be made available as an UPnP MediaRenderer. Rygel will interface with Mopidy's
:ref:`MPRIS frontend <mpris-frontend>`, and make Mopidy available as a
MediaRenderer on the local network. Since this depends on the MPRIS frontend,
which again depends on D-Bus being available, this will only work on Linux, and
not OS X. MPRIS/D-Bus is only available to other applications on the same host,
so Rygel must be running on the same machine as Mopidy.
:ref:`MPRIS frontend <ext-mpris>`, and make Mopidy available as a MediaRenderer
on the local network. Since this depends on the MPRIS frontend, which again
depends on D-Bus being available, this will only work on Linux, and not OS X.
MPRIS/D-Bus is only available to other applications on the same host, so Rygel
must be running on the same machine as Mopidy.
1. Start Mopidy and make sure the :ref:`MPRIS frontend <mpris-frontend>` is
working. It is activated by default, but you may miss dependencies or be
using OS X, in which case it will not work. Check the console output when
Mopidy is started for any errors related to the MPRIS frontend. If you're
unsure it is working, there are instructions for how to test it on the
:ref:`MPRIS frontend <mpris-frontend>` page.
1. Start Mopidy and make sure the :ref:`MPRIS frontend <ext-mpris>` is working.
It is activated by default, but you may miss dependencies or be using OS X,
in which case it will not work. Check the console output when Mopidy is
started for any errors related to the MPRIS frontend. If you're unsure it is
working, there are instructions for how to test it on the :ref:`MPRIS
frontend <ext-mpris>` page.
2. Install Rygel. On Debian/Ubuntu::

65
docs/codestyle.rst Normal file
View File

@ -0,0 +1,65 @@
.. _codestyle:
**********
Code style
**********
- Always import ``unicode_literals`` and use unicode literals for everything
except where you're explicitly working with bytes, which are marked with the
``b`` prefix.
Do this::
from __future__ import unicode_literals
foo = 'I am a unicode string, which is a sane default'
bar = b'I am a bytestring'
Not this::
foo = u'I am a unicode string'
bar = 'I am a bytestring, but was it intentional?'
- Follow :pep:`8` unless otherwise noted. `flake8
<http://pypi.python.org/pypi/flake8>`_ should be used to check your code
against the guidelines.
- Use four spaces for indentation, *never* tabs.
- Use CamelCase with initial caps for class names::
ClassNameWithCamelCase
- Use underscore to split variable, function and method names for
readability. Don't use CamelCase.
::
lower_case_with_underscores
- Use the fact that empty strings, lists and tuples are :class:`False` and
don't compare boolean values using ``==`` and ``!=``.
- Follow whitespace rules as described in :pep:`8`. Good examples::
spam(ham[1], {eggs: 2})
spam(1)
dict['key'] = list[index]
- Limit lines to 80 characters and avoid trailing whitespace. However note that
wrapped lines should be *one* indentation level in from level above, except
for ``if``, ``for``, ``with``, and ``while`` lines which should have two
levels of indentation::
if (foo and bar ...
baz and foobar):
a = 1
from foobar import (foo, bar, ...
baz)
- For consistency, prefer ``'`` over ``"`` for strings, unless the string
contains ``'``.
- Take a look at :pep:`20` for a nice peek into a general mindset useful for
Python coding.

View File

@ -2,28 +2,9 @@
Configuration
*************
Mopidy has quite a few config values to tweak. Luckily, you only need to change
a few, and stay ignorant of the rest. Below you can find guides for typical
configuration changes you may want to do, and a listing of the available config
values.
Changing configuration
======================
Mopidy primarily reads config from the file ``~/.config/mopidy/mopidy.conf``,
where ``~`` means your *home directory*. If your username is ``alice`` and you
are running Linux, the settings file should probably be at
``/home/alice/.config/mopidy/mopidy.conf``.
You can either create the configuration file yourself, or run the ``mopidy``
command, and it will create an empty settings file for you.
When you have created the configuration file, open it in a text editor, and add
settings you want to change. If you want to keep the default value for a
setting, you should *not* redefine it in your own settings file.
A complete ``~/.config/mopidy/mopidy.conf`` may look as simple as this:
Mopidy has a lot of config values you can tweak, but you only need to change a
few to get up and running. A complete ``~/.config/mopidy/mopidy.conf`` may be
as simple as this:
.. code-block:: ini
@ -34,127 +15,117 @@ A complete ``~/.config/mopidy/mopidy.conf`` may look as simple as this:
username = alice
password = mysecret
Mopidy primarily reads config from the file ``~/.config/mopidy/mopidy.conf``,
where ``~`` means your *home directory*. If your username is ``alice`` and you
are running Linux, the settings file should probably be at
``/home/alice/.config/mopidy/mopidy.conf``. You can either create the
configuration file yourself, or run the ``mopidy`` command, and it will create
an empty settings file for you and print what config values must be set
to successfully start Mopidy.
.. _music-from-spotify:
When you have created the configuration file, open it in a text editor, and add
the config values you want to change. If you want to keep the default for a
config value, you **should not** add it to ``~/.config/mopidy/mopidy.conf``.
Music from Spotify
==================
To see what's the effective configuration for your Mopidy installation, you can
run ``mopidy --show-config``. It will print your full effective config with
passwords masked out so that you safely can share the output with others for
debugging.
If you are using the Spotify backend, which is the default, enter your Spotify
Premium account's username and password into the file, like this:
.. code-block:: ini
[spotify]
username = myusername
password = mysecret
This will only work if you have the Spotify Premium subscription. Spotify
Unlimited will not work.
You can find a description of all config values belonging to Mopidy's core
below, together with their default values. In addition, all :ref:`extensions
<ext>` got additional config values. The extension's config values and config
defaults are documented on the :ref:`extension pages <ext>`.
.. _music-from-local-storage:
Default core configuration
==========================
Music from local storage
========================
If you want use Mopidy to play music you have locally at your machine instead
of or in addition to using Spotify, you need to review and maybe change some of
the local backend config values. See :ref:`local-backend`, for a complete list.
Then you need to generate a tag cache for your local music...
.. literalinclude:: ../mopidy/default.conf
:language: ini
.. _generating-a-tag-cache:
Generating a tag cache
----------------------
The program :command:`mopidy-scan` will scan the path set in the
:confval:`local/media_dir` config value for any media files and build a MPD
compatible ``tag_cache``.
To make a ``tag_cache`` of your local music available for Mopidy:
#. Ensure that the :confval:`local/media_dir` config value points to where your
music is located. Check the current setting by running::
mopidy --show-config
#. Scan your media library. The command outputs the ``tag_cache`` to
standard output, which means that you will need to redirect the output to a
file yourself::
mopidy-scan > tag_cache
#. Move the ``tag_cache`` file to the location
set in the :confval:`local/tag_cache_file` config value, or change the
config value to point to where your ``tag_cache`` file is.
#. Start Mopidy, find the music library in a client, and play some local music!
.. _use-mpd-on-a-network:
Connecting from other machines on the network
=============================================
As a secure default, Mopidy only accepts connections from ``localhost``. If you
want to open it for connections from other machines on your network, see
the documentation for the :confval:`mpd/hostname` config value.
If you open up Mopidy for your local network, you should consider turning on
MPD password authentication by setting the :confval:`mpd/password` config value
to the password you want to use. If the password is set, Mopidy will require
MPD clients to provide the password before they can do anything else. Mopidy
only supports a single password, and do not support different permission
schemes like the original MPD server.
Scrobbling tracks to Last.fm
============================
If you want to submit the tracks you are playing to your `Last.fm
<http://www.last.fm/>`_ profile, make sure you've installed the dependencies
found at :mod:`mopidy.frontends.scrobbler` and add the following to your
settings file:
.. code-block:: ini
[scrobbler]
username = myusername
password = mysecret
.. _install-desktop-file:
Controlling Mopidy through the Ubuntu Sound Menu
================================================
If you are running Ubuntu and installed Mopidy using the Debian package from
APT you should be able to control Mopidy through the `Ubuntu Sound Menu
<https://wiki.ubuntu.com/SoundMenu>`_ without any changes.
If you installed Mopidy in any other way and want to control Mopidy through the
Ubuntu Sound Menu, you must install the ``mopidy.desktop`` file which can be
found in the ``data/`` dir of the Mopidy source into the
``/usr/share/applications`` dir by hand::
cd /path/to/mopidy/source
sudo cp data/mopidy.desktop /usr/share/applications/
After you have installed the file, start Mopidy in any way, and Mopidy should
appear in the Ubuntu Sound Menu. When you quit Mopidy, it will still be listed
in the Ubuntu Sound Menu, and may be restarted by selecting it there.
The Ubuntu Sound Menu interacts with Mopidy's MPRIS frontend,
:mod:`mopidy.frontends.mpris`. The MPRIS frontend supports the minimum
requirements of the `MPRIS specification <http://www.mpris.org/>`_. The
``TrackList`` interface of the spec is not supported.
Using a custom audio sink
Core configuration values
=========================
.. confval:: audio/mixer
Audio mixer to use.
Expects a GStreamer mixer to use, typical values are: ``alsamixer``,
``pulsemixer``, ``ossmixer``, and ``oss4mixer``.
Setting this to blank turns off volume control. ``software`` can be used to
force software mixing in the application.
.. confval:: audio/mixer_track
Audio mixer track to use.
Name of the mixer track to use. If this is not set we will try to find the
master output track. As an example, using ``alsamixer`` you would typically
set this to ``Master`` or ``PCM``.
.. confval:: audio/output
Audio output to use.
Expects a GStreamer sink. Typical values are ``autoaudiosink``,
``alsasink``, ``osssink``, ``oss4sink``, ``pulsesink``, and ``shout2send``,
and additional arguments specific to each sink. You can use the command
``gst-inspect-0.10`` to see what output properties can be set on the sink.
For example: ``gst-inspect-0.10 shout2send``
.. confval:: logging/console_format
The log format used for informational logging.
See `the Python logging docs
<http://docs.python.org/2/library/logging.html#formatter-objects>`_ for
details on the format.
.. confval:: logging/debug_format
The log format used for debug logging.
See `the Python logging docs
<http://docs.python.org/2/library/logging.html#formatter-objects>`_ for
details on the format.
.. confval:: logging/debug_file
The file to dump debug log data to when Mopidy is run with the
:option:`--save-debug-log` option.
.. confval:: logging.levels/*
The ``logging.levels`` config section can be used to change the log level
for specific parts of Mopidy during development or debugging. Each key in
the config section should match the name of a logger. The value is the log
level to use for that logger, one of ``debug``, ``info``, ``warning``,
``error``, or ``critical``.
.. confval:: proxy/hostname
Proxy server to use for communication with the Internet.
Currently only used by the Spotify extension.
.. confval:: proxy/username
Username for the proxy server, if required.
.. confval:: proxy/password
Password for the proxy server, if required.
Advanced configurations
=======================
Custom audio sink
-----------------
If you have successfully installed GStreamer, and then run the ``gst-inspect``
or ``gst-inspect-0.10`` command, you should see a long listing of installed
plugins, ending in a summary line::
@ -190,8 +161,8 @@ this work first::
gst-launch-0.10 audiotestsrc ! audioresample ! oss4sink
Streaming audio through a SHOUTcast/Icecast server
==================================================
Streaming through SHOUTcast/Icecast
-----------------------------------
If you want to play the audio on another computer than the one running Mopidy,
you can stream the audio from Mopidy through an SHOUTcast or Icecast audio
@ -219,8 +190,8 @@ can use with the ``gst-launch-0.10`` command can be plugged into
:confval:`audio/output`.
Custom configuration values
===========================
New configuration values
------------------------
Mopidy's settings validator will stop you from defining any config values in
your settings file that Mopidy doesn't know about. This may sound obnoxious,
@ -232,9 +203,3 @@ system, you can add new sections to the config without triggering the config
validator. We recommend that you choose a good and unique name for the config
section so that multiple extensions to Mopidy can be used at the same time
without any danger of naming collisions.
Available settings
==================
.. note:: TODO: Document config values of the new config system

116
docs/contributing.rst Normal file
View File

@ -0,0 +1,116 @@
.. _contributing:
************
Contributing
************
If you are thinking about making Mopidy better, or you just want to hack on it,
thats great. Here are some tips to get you started.
Getting started
===============
1. Make sure you have a `GitHub account <https://github.com/signup/free>`_.
2. `Submit <https://github.com/mopidy/mopidy/issues/new>`_ a ticket for your
issue, assuming one does not already exist. Clearly describe the issue
including steps to reproduce when it is a bug.
3. Fork the repository on GitHub.
Making changes
==============
1. Clone your fork on GitHub to your computer.
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.
- Features get the prefix ``feature/``
- Bug fixes get the prefix ``fix/``
- Improvements to the documentation get the prefix ``docs/``
.. _run-from-git:
Running Mopidy from Git
=======================
If you want to hack on Mopidy, you should run Mopidy directly from the Git
repo.
1. Go to the Git repo root::
cd mopidy/
2. To get a ``mopidy`` executable, run::
python setup.py develop
3. Now you can run the Mopidy command, and it will run using the code
in the Git repo::
mopidy
If you do any changes to the code, you'll just need to restart ``mopidy``
to see the changes take effect.
Testing
=======
Mopidy has quite good test coverage, and we would like all new code going into
Mopidy to come with tests.
1. To run tests, you need a couple of dependencies. They can be installed using
``pip``::
pip install -r requirements/tests.txt
2. Then, to run all tests, go to the project directory and run::
nosetests
To run tests with test coverage statistics, remember to specify the tests
dir::
nosetests --with-coverage tests/
3. Check the code for errors and style issues using flake8::
flake8 .
For more documentation on testing, check out the `nose documentation
<http://nose.readthedocs.org/>`_.
Submitting changes
==================
- One branch per feature or fix. Keep branches small and on topic.
- Follow the :ref:`style guide <codestyle>`_, especially make sure ``flake8``
does not complain about anything.
- Send a pull request to the ``develop`` branch.
Additional resources
====================
- IRC channel: ``#mopidy`` at `irc.freenode.net <http://freenode.net/>`_
- `Issue tracker <https://github.com/mopidy/mopidy/issues>`_
- `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>`_

View File

@ -1,360 +0,0 @@
***********
Development
***********
Development of Mopidy is coordinated through the IRC channel ``#mopidy`` at
``irc.freenode.net`` and through `GitHub <https://github.com/>`_.
Release schedule
================
We intend to have about one timeboxed feature release every month
in periods of active development. The feature releases are numbered 0.x.0. The
features added is a mix of what we feel is most important/requested of the
missing features, and features we develop just because we find them fun to
make, even though they may be useful for very few users or for a limited use
case.
Bugfix releases, numbered 0.x.y, will be released whenever we discover bugs
that are too serious to wait for the next feature release. We will only release
bugfix releases for the last feature release. E.g. when 0.3.0 is released, we
will no longer provide bugfix releases for the 0.2 series. In other words,
there will be just a single supported release at any point in time.
Feature wishlist
================
We maintain our collection of sane or less sane ideas for future Mopidy
features as `issues <https://github.com/mopidy/mopidy/issues>`_ at GitHub
labeled with `the "wishlist" label
<https://github.com/mopidy/mopidy/issues?labels=wishlist>`_. Feel free to vote
up any feature you would love to see in Mopidy, but please refrain from adding
a comment just to say "I want this too!". You are of course free to add
comments if you have suggestions for how the feature should work or be
implemented, and you may add new wishlist issues if your ideas are not already
represented.
.. _run-from-git:
Run Mopidy from Git repo
========================
If you want to contribute to the development of Mopidy, you should run Mopidy
directly from the Git repo.
#. First of all, install Mopidy in the recommended way for your OS and/or
distribution, like described at :ref:`installation`. You can have a
system-wide installation of the last Mopidy release in addition to the Git
repo which you run from when you code on Mopidy.
#. Then install Git, if haven't already. For Ubuntu/Debian::
sudo apt-get install git-core
On OS X using Homebrew::
sudo brew install git
#. Clone the official Mopidy repository::
git clone git://github.com/mopidy/mopidy.git
or your own fork of it::
git clone git@github.com:mygithubuser/mopidy.git
#. You can then run Mopidy directly from the Git repository::
cd mopidy/ # Move into the Git repo dir
python mopidy # Run python on the mopidy source code dir
How you update your clone depends on whether you cloned the official Mopidy
repository or your own fork, whether you have made any changes to the clone
or not, and whether you are currently working on a feature branch or not. In
other words, you'll need to learn Git.
For an introduction to Git, please visit `git-scm.com <http://git-scm.com/>`_.
Also, please read the rest of our developer documentation before you start
contributing.
Code style
==========
- Always import ``unicode_literals`` and use unicode literals for everything
except where you're explicitly working with bytes, which are marked with the
``b`` prefix.
Do this::
from __future__ import unicode_literals
foo = 'I am a unicode string, which is a sane default'
bar = b'I am a bytestring'
Not this::
foo = u'I am a unicode string'
bar = 'I am a bytestring, but was it intentional?'
- Follow :pep:`8` unless otherwise noted. `pep8.py
<http://pypi.python.org/pypi/pep8/>`_ or `flake8
<http://pypi.python.org/pypi/flake8>`_ can be used to check your code
against the guidelines, however remember that matching the style of the
surrounding code is also important.
- Use four spaces for indentation, *never* tabs.
- Use CamelCase with initial caps for class names::
ClassNameWithCamelCase
- Use underscore to split variable, function and method names for
readability. Don't use CamelCase.
::
lower_case_with_underscores
- Use the fact that empty strings, lists and tuples are :class:`False` and
don't compare boolean values using ``==`` and ``!=``.
- Follow whitespace rules as described in :pep:`8`. Good examples::
spam(ham[1], {eggs: 2})
spam(1)
dict['key'] = list[index]
- Limit lines to 80 characters and avoid trailing whitespace. However note that
wrapped lines should be *one* indentation level in from level above, except
for ``if``, ``for``, ``with``, and ``while`` lines which should have two
levels of indentation::
if (foo and bar ...
baz and foobar):
a = 1
from foobar import (foo, bar, ...
baz)
- For consistency, prefer ``'`` over ``"`` for strings, unless the string
contains ``'``.
- Take a look at :pep:`20` for a nice peek into a general mindset useful for
Python coding.
Commit guidelines
=================
- We follow the development process described at
`nvie.com <http://nvie.com/posts/a-successful-git-branching-model/>`_.
- Keep commits small and on topic.
- If a commit looks too big you should be working in a feature branch not a
single commit.
- Merge feature branches with ``--no-ff`` to keep track of the merge.
Running tests
=============
To run tests, you need a couple of dependencies. They can be installed through
Debian/Ubuntu package management::
sudo apt-get install python-coverage python-mock python-nose
Or, they can be installed using ``pip``::
sudo pip install -r requirements/tests.txt
Then, to run all tests, go to the project directory and run::
nosetests
For example::
$ nosetests
.............................................................................
.............................................................................
.............................................................................
.............................................................................
.............................................................................
.............................................................................
.............................................................................
.............................................................................
.............................................................................
.............................................................................
.............................................................................
.............................................................................
.............................................................................
.............................................................
-----------------------------------------------------------------------------
1062 tests run in 7.4 seconds (1062 tests passed)
To run tests with test coverage statistics, remember to specify the tests dir::
nosetests --with-coverage tests/
For more documentation on testing, check out the `nose documentation
<http://nose.readthedocs.org/>`_.
Continuous integration
======================
Mopidy uses the free service `Travis CI <https://travis-ci.org/mopidy/mopidy>`_
for automatically running the test suite when code is pushed to GitHub. This
works both for the main Mopidy repo, but also for any forks. This way, any
contributions to Mopidy through GitHub will automatically be tested by Travis
CI, and the build status will be visible in the GitHub pull request interface,
making it easier to evaluate the quality of pull requests.
In addition, we run a Jenkins CI server at http://ci.mopidy.com/ that runs all
test on multiple platforms (Ubuntu, OS X, x86, arm) for every commit we push to
the ``develop`` branch in the main Mopidy repo on GitHub. Thus, new code isn't
tested by Jenkins before it is merged into the ``develop`` branch, which is a
bit late, but good enough to get broad testing before new code is released.
In addition to running tests, the Jenkins CI server also gathers coverage
statistics and uses pylint to check for errors and possible improvements in our
code. So, if you're out of work, the code coverage and pylint data at the CI
server should give you a place to start.
Protocol debugging
==================
Since the main interface provided to Mopidy is through the MPD protocol, it is
crucial that we try and stay in sync with protocol developments. In an attempt
to make it easier to debug differences Mopidy and MPD protocol handling we have
created ``tools/debug-proxy.py``.
This tool is proxy that sits in front of two MPD protocol aware servers and
sends all requests to both, returning the primary response to the client and
then printing any diff in the two responses.
Note that this tool depends on ``gevent`` unlike the rest of Mopidy at the time
of writing. See ``--help`` for available options. Sample session::
[127.0.0.1]:59714
listallinfo
--- Reference response
+++ Actual response
@@ -1,16 +1,1 @@
-file: uri1
-Time: 4
-Artist: artist1
-Title: track1
-Album: album1
-file: uri2
-Time: 4
-Artist: artist2
-Title: track2
-Album: album2
-file: uri3
-Time: 4
-Artist: artist3
-Title: track3
-Album: album3
-OK
+ACK [2@0] {listallinfo} incorrect arguments
To ensure that Mopidy and MPD have comparable state it is suggested you setup
both to use ``tests/data/advanced_tag_cache`` for their tag cache and
``tests/data/scanner/advanced/`` for the music folder and ``tests/data`` for
playlists.
Setting profiles during development
===================================
While developing Mopidy switching settings back and forth can become an all too
frequent occurrence. As a quick hack to get around this you can structure your
settings file in the following way::
import os
profile = os.environ.get('PROFILE', '').split(',')
if 'shoutcast' in profile:
OUTPUT = u'lame ! shout2send mount="/stream"'
elif 'silent' in profile:
OUTPUT = u'fakesink'
MIXER = None
SPOTIFY_USERNAME = u'xxxxx'
SPOTIFY_PASSWORD = u'xxxxx'
Using this setup you can now run Mopidy with ``PROFILE=silent mopidy``
if you for instance want to test Spotify without any actual audio output.
Debugging deadlocks
===================
Between the numerous Pykka threads and GStreamer interactions there can
sometimes be a potential for deadlocks. In an effort to make these slightly
simpler to debug Mopidy registers a ``SIGUSR1`` signal handler which logs the
traceback of all alive threads.
To trigger the signal handler, you can use the ``pkill`` command to
send the ``SIGUSR1`` signal to any Mopidy processes::
pkill -SIGUSR1 mopidy
If you check the log, you should now find one log record with a full traceback
for each of the currently alive threads in Mopidy.
Writing documentation
=====================
To write documentation, we use `Sphinx <http://sphinx-doc.org/>`_. See their
site for lots of documentation on how to use Sphinx. To generate HTML or LaTeX
from the documentation files, you need some additional dependencies.
You can install them through Debian/Ubuntu package management::
sudo apt-get install python-sphinx python-pygraphviz graphviz
Then, to generate docs::
cd docs/
make # For help on available targets
make html # To generate HTML docs
The documentation at http://docs.mopidy.com/ is automatically updated when a
documentation update is pushed to ``mopidy/mopidy`` at GitHub.
Creating releases
=================
#. Update changelog and commit it.
#. Merge the release branch (``develop`` in the example) into master::
git checkout master
git merge --no-ff -m "Release v0.2.0" develop
#. Tag the release::
git tag -a -m "Release v0.2.0" v0.2.0
#. Push to GitHub::
git push
git push --tags
#. Build package and upload to PyPI::
python setup.py sdist upload
#. Update the Debian package.
#. Spread the word.

120
docs/devtools.rst Normal file
View File

@ -0,0 +1,120 @@
*****************
Development tools
*****************
Here you'll find description of the development tools we use.
Continuous integration
======================
Mopidy uses the free service `Travis CI <https://travis-ci.org/mopidy/mopidy>`_
for automatically running the test suite when code is pushed to GitHub. This
works both for the main Mopidy repo, but also for any forks. This way, any
contributions to Mopidy through GitHub will automatically be tested by Travis
CI, and the build status will be visible in the GitHub pull request interface,
making it easier to evaluate the quality of pull requests.
In addition, we run a Jenkins CI server at http://ci.mopidy.com/ that runs all
test on multiple platforms (Ubuntu, OS X, x86, arm) for every commit we push to
the ``develop`` branch in the main Mopidy repo on GitHub. Thus, new code isn't
tested by Jenkins before it is merged into the ``develop`` branch, which is a
bit late, but good enough to get broad testing before new code is released.
In addition to running tests, the Jenkins CI server also gathers coverage
statistics and uses pylint to check for errors and possible improvements in our
code. So, if you're out of work, the code coverage and pylint data at the CI
server should give you a place to start.
Protocol debugger
=================
Since the main interface provided to Mopidy is through the MPD protocol, it is
crucial that we try and stay in sync with protocol developments. In an attempt
to make it easier to debug differences Mopidy and MPD protocol handling we have
created ``tools/debug-proxy.py``.
This tool is proxy that sits in front of two MPD protocol aware servers and
sends all requests to both, returning the primary response to the client and
then printing any diff in the two responses.
Note that this tool depends on ``gevent`` unlike the rest of Mopidy at the time
of writing. See ``--help`` for available options. Sample session::
[127.0.0.1]:59714
listallinfo
--- Reference response
+++ Actual response
@@ -1,16 +1,1 @@
-file: uri1
-Time: 4
-Artist: artist1
-Title: track1
-Album: album1
-file: uri2
-Time: 4
-Artist: artist2
-Title: track2
-Album: album2
-file: uri3
-Time: 4
-Artist: artist3
-Title: track3
-Album: album3
-OK
+ACK [2@0] {listallinfo} incorrect arguments
To ensure that Mopidy and MPD have comparable state it is suggested you setup
both to use ``tests/data/advanced_tag_cache`` for their tag cache and
``tests/data/scanner/advanced/`` for the music folder and ``tests/data`` for
playlists.
Documentation writing
=====================
To write documentation, we use `Sphinx <http://sphinx-doc.org/>`_. See their
site for lots of documentation on how to use Sphinx. To generate HTML from the
documentation files, you need some additional dependencies.
You can install them through Debian/Ubuntu package management::
sudo apt-get install python-sphinx python-pygraphviz graphviz
Then, to generate docs::
cd docs/
make # For help on available targets
make html # To generate HTML docs
The documentation at http://docs.mopidy.com/ is automatically updated when a
documentation update is pushed to ``mopidy/mopidy`` at GitHub.
Creating releases
=================
#. Update changelog and commit it.
#. Merge the release branch (``develop`` in the example) into master::
git checkout master
git merge --no-ff -m "Release v0.2.0" develop
#. Tag the release::
git tag -a -m "Release v0.2.0" v0.2.0
#. Push to GitHub::
git push
git push --tags
#. Build package and upload to PyPI::
python setup.py sdist upload
#. Update the Debian package.
#. Spread the word.

103
docs/ext/http.rst Normal file
View File

@ -0,0 +1,103 @@
.. _ext-http:
***********
Mopidy-HTTP
***********
The HTTP extension lets you control Mopidy through HTTP and WebSockets, e.g.
from a web based client. See :ref:`http-api` for details on how to integrate
with Mopidy over HTTP.
Known issues
============
https://github.com/mopidy/mopidy/issues?labels=HTTP+frontend
Dependencies
============
.. literalinclude:: ../../requirements/http.txt
Default configuration
=====================
.. literalinclude:: ../../mopidy/frontends/http/ext.conf
:language: ini
Configuration values
====================
.. confval:: http/enabled
If the HTTP extension should be enabled or not.
.. confval:: http/hostname
Which address the HTTP server should bind to.
``127.0.0.1``
Listens only on the IPv4 loopback interface
``::1``
Listens only on the IPv6 loopback interface
``0.0.0.0``
Listens on all IPv4 interfaces
``::``
Listens on all interfaces, both IPv4 and IPv6
.. confval:: http/port
Which TCP port the HTTP server should listen to.
.. confval:: http/static_dir
Which directory the HTTP server should serve at "/"
Change this to have Mopidy serve e.g. files for your JavaScript client.
"/mopidy" will continue to work as usual even if you change this setting.
Usage
=====
The extension is enabled by default if all dependencies are available.
When it is enabled it starts a web server at the port specified by the
:confval:`http/port` config value.
.. warning:: Security
As a simple security measure, the web server is by default only available
from localhost. To make it available from other computers, change the
:confval:`http/hostname` config value. Before you do so, note that the HTTP
extension does not feature any form of user authentication or
authorization. Anyone able to access the web server can use the full core
API of Mopidy. Thus, you probably only want to make the web server
available from your local network or place it behind a web proxy which
takes care or user authentication. You have been warned.
Using a web based Mopidy client
-------------------------------
The web server can also host any static files, for example the HTML, CSS,
JavaScript, and images needed for a web based Mopidy client. To host static
files, change the ``http/static_dir`` to point to the root directory of your
web client, e.g.::
[http]
static_dir = /home/alice/dev/the-client
If the directory includes a file named ``index.html``, it will be served on the
root of Mopidy's web server.
If you're making a web based client and wants to do server side development as
well, you are of course free to run your own web server and just use Mopidy's
web server for the APIs. But, for clients implemented purely in JavaScript,
letting Mopidy host the files is a simpler solution.
If you're looking for a web based client for Mopidy, go check out
:ref:`http-clients`.

44
docs/ext/index.rst Normal file
View File

@ -0,0 +1,44 @@
.. _ext:
**********
Extensions
**********
Here you can find a list of packages that extend Mopidy with additional
functionality. This list is moderated and updated on a regular basis. If you
want your package to show up here, follow the :ref:`guide on creating
extensions <extensiondev>`.
Bundled with Mopidy
===================
These extensions are maintained by Mopidy's core developers. They are installed
together with Mopidy and are enabled by default.
.. toctree::
:maxdepth: 1
:glob:
**
External extensions
===================
These extensions are maintained outside Mopidy's core, often by other
developers.
Mopidy-SoundCloud
-----------------
Provides a backend for playing music from the `SoundCloud
<http://www.soundcloud.com/>`_ service.
Author:
Janez Troha
PyPI:
`Mopidy-SoundCloud <https://pypi.python.org/pypi/Mopidy-SoundCloud>`_
GitHub:
`dz0ny/mopidy-soundcloud <https://github.com/dz0ny/mopidy-soundcloud>`_

86
docs/ext/local.rst Normal file
View File

@ -0,0 +1,86 @@
.. _ext-local:
************
Mopidy-Local
************
Extension for playing music from a local music archive.
This backend handles URIs starting with ``file:``.
Known issues
============
https://github.com/mopidy/mopidy/issues?labels=Local+backend
Dependencies
============
None. The extension just needs Mopidy.
Default configuration
=====================
.. literalinclude:: ../../mopidy/backends/local/ext.conf
:language: ini
Configuration values
====================
.. confval:: local/enabled
If the local extension should be enabled or not.
.. confval:: local/media_dir
Path to directory with local media files.
.. confval:: local/playlists_dir
Path to playlists directory with m3u files for local media.
.. confval:: local/tag_cache_file
Path to tag cache for local media.
Usage
=====
If you want use Mopidy to play music you have locally at your machine, you need
to review and maybe change some of the local extension config values. See above
for a complete list. Then you need to generate a tag cache for your local
music...
.. _generating-a-tag-cache:
Generating a tag cache
----------------------
The program :command:`mopidy-scan` will scan the path set in the
:confval:`local/media_dir` config value for any media files and build a MPD
compatible ``tag_cache``.
To make a ``tag_cache`` of your local music available for Mopidy:
#. Ensure that the :confval:`local/media_dir` config value points to where your
music is located. Check the current setting by running::
mopidy --show-config
#. Scan your media library. The command outputs the ``tag_cache`` to
standard output, which means that you will need to redirect the output to a
file yourself::
mopidy-scan > tag_cache
#. Move the ``tag_cache`` file to the location
set in the :confval:`local/tag_cache_file` config value, or change the
config value to point to where your ``tag_cache`` file is.
#. Start Mopidy, find the music library in a client, and play some local music!

123
docs/ext/mpd.rst Normal file
View File

@ -0,0 +1,123 @@
.. _ext-mpd:
**********
Mopidy-MPD
**********
This extension implements an MPD server to make Mopidy available to :ref:`MPD
clients <mpd-clients>`.
MPD stands for Music Player Daemon, which is also the name of the `original MPD
server project <http://mpd.wikia.com/>`_. Mopidy does not depend on the
original MPD server, but implements the MPD protocol itself, and is thus
compatible with clients for the original MPD server.
For more details on our MPD server implementation, see
:mod:`mopidy.frontends.mpd`.
Known issues
============
https://github.com/mopidy/mopidy/issues?labels=MPD+frontend
Limitations
===========
This is a non exhaustive list of MPD features that Mopidy doesn't support.
Items on this list will probably not be supported in the near future.
- Toggling of audio outputs is not supported
- Channels for client-to-client communication are not supported
- Stickers are not supported
- Crossfade is not supported
- Replay gain is not supported
- ``count`` does not provide any statistics
- ``stats`` does not provide any statistics
- ``list`` does not support listing tracks by genre
- ``decoders`` does not provide information about available decoders
The following items are currently not supported, but should be added in the
near future:
- Modifying stored playlists is not supported
- ``tagtypes`` is not supported
- Browsing the file system is not supported
- Live update of the music database is not supported
Dependencies
============
None. The extension just needs Mopidy.
Default configuration
=====================
.. literalinclude:: ../../mopidy/frontends/mpd/ext.conf
:language: ini
Configuration values
====================
.. confval:: mpd/enabled
If the MPD extension should be enabled or not.
.. confval:: mpd/hostname
Which address the MPD server should bind to.
``127.0.0.1``
Listens only on the IPv4 loopback interface
``::1``
Listens only on the IPv6 loopback interface
``0.0.0.0``
Listens on all IPv4 interfaces
``::``
Listens on all interfaces, both IPv4 and IPv6
.. confval:: mpd/port
Which TCP port the MPD server should listen to.
.. confval:: mpd/password
The password required for connecting to the MPD server. If blank, no
password is required.
.. confval:: mpd/max_connections
The maximum number of concurrent connections the MPD server will accept.
.. confval:: mpd/connection_timeout
Number of seconds an MPD client can stay inactive before the connection is
closed by the server.
Usage
=====
The extension is enabled by default. To connect to the server, use an :ref:`MPD
client <mpd-clients>`.
.. _use-mpd-on-a-network:
Connecting from other machines on the network
---------------------------------------------
As a secure default, Mopidy only accepts connections from ``localhost``. If you
want to open it for connections from other machines on your network, see
the documentation for the :confval:`mpd/hostname` config value.
If you open up Mopidy for your local network, you should consider turning on
MPD password authentication by setting the :confval:`mpd/password` config value
to the password you want to use. If the password is set, Mopidy will require
MPD clients to provide the password before they can do anything else. Mopidy
only supports a single password, and do not support different permission
schemes like the original MPD server.

105
docs/ext/mpris.rst Normal file
View File

@ -0,0 +1,105 @@
.. _ext-mpris:
************
Mopidy-MPRIS
************
This extension lets you control Mopidy through the Media Player Remote
Interfacing Specification (`MPRIS <http://www.mpris.org/>`_) D-Bus interface.
An example of an MPRIS client is the :ref:`ubuntu-sound-menu`.
Dependencies
============
- D-Bus Python bindings. The package is named ``python-dbus`` in
Ubuntu/Debian.
- ``libindicate`` Python bindings is needed to expose Mopidy in e.g. the
Ubuntu Sound Menu. The package is named ``python-indicate`` in
Ubuntu/Debian.
- An ``.desktop`` file for Mopidy installed at the path set in the
:confval:`mpris/desktop_file` config value. See usage section below for
details.
Default configuration
=====================
.. literalinclude:: ../../mopidy/frontends/mpris/ext.conf
:language: ini
Configuration values
====================
.. confval:: mpris/enabled
If the MPRIS extension should be enabled or not.
.. confval:: mpris/desktop_file
Location of the Mopidy ``.desktop`` file.
Usage
=====
The extension is enabled by default if all dependencies are available.
Controlling Mopidy through the Ubuntu Sound Menu
------------------------------------------------
If you are running Ubuntu and installed Mopidy using the Debian package from
APT you should be able to control Mopidy through the :ref:`ubuntu-sound-menu`
without any changes.
If you installed Mopidy in any other way and want to control Mopidy through the
Ubuntu Sound Menu, you must install the ``mopidy.desktop`` file which can be
found in the ``data/`` dir of the Mopidy source repo into the
``/usr/share/applications`` dir by hand::
cd /path/to/mopidy/source
sudo cp data/mopidy.desktop /usr/share/applications/
If the correct path to the installed ``mopidy.desktop`` file on your system
isn't ``/usr/share/applications/mopidy.conf``, you'll need to set the
:confval:`mpris/desktop_file` config value.
After you have installed the file, start Mopidy in any way, and Mopidy should
appear in the Ubuntu Sound Menu. When you quit Mopidy, it will still be listed
in the Ubuntu Sound Menu, and may be restarted by selecting it there.
The Ubuntu Sound Menu interacts with Mopidy's MPRIS frontend. The MPRIS
frontend supports the minimum requirements of the `MPRIS specification
<http://www.mpris.org/>`_. The ``TrackList`` interface of the spec is not
supported.
Testing the MPRIS API directly
------------------------------
To use the MPRIS API directly, start Mopidy, and then run the following in a
Python shell::
import dbus
bus = dbus.SessionBus()
player = bus.get_object('org.mpris.MediaPlayer2.mopidy',
'/org/mpris/MediaPlayer2')
Now you can control Mopidy through the player object. Examples:
- To get some properties from Mopidy, run::
props = player.GetAll('org.mpris.MediaPlayer2',
dbus_interface='org.freedesktop.DBus.Properties')
- To quit Mopidy through D-Bus, run::
player.Quit(dbus_interface='org.mpris.MediaPlayer2')
For details on the API, please refer to the `MPRIS specification
<http://www.mpris.org/>`_.

53
docs/ext/scrobbler.rst Normal file
View File

@ -0,0 +1,53 @@
****************
Mopidy-Scrobbler
****************
This extension scrobbles the music you play to your `Last.fm
<http://www.last.fm>`_ profile.
.. note::
This extension requires a free user account at Last.fm.
Dependencies
============
.. literalinclude:: ../../requirements/scrobbler.txt
Default configuration
=====================
.. literalinclude:: ../../mopidy/frontends/scrobbler/ext.conf
:language: ini
Configuration values
====================
.. confval:: scrobbler/enabled
If the scrobbler extension should be enabled or not.
.. confval:: scrobbler/username
Your Last.fm username.
.. confval:: scrobbler/password
Your Last.fm password.
Usage
=====
The extension is enabled by default if all dependencies are available. You just
need to add your Last.fm username and password to the
``~/.config/mopidy/mopidy.conf`` file:
.. code-block:: ini
[scrobbler]
username = myusername
password = mysecret

83
docs/ext/spotify.rst Normal file
View File

@ -0,0 +1,83 @@
.. _ext-spotify:
**************
Mopidy-Spotify
**************
An extension for playing music from Spotify.
`Spotify <http://www.spotify.com/>`_ is a music streaming service. The backend
uses the official `libspotify
<http://developer.spotify.com/en/libspotify/overview/>`_ library and the
`pyspotify <http://github.com/mopidy/pyspotify/>`_ Python bindings for
libspotify. This backend handles URIs starting with ``spotify:``.
.. note::
This product uses SPOTIFY(R) CORE but is not endorsed, certified or
otherwise approved in any way by Spotify. Spotify is the registered
trade mark of the Spotify Group.
Known issues
============
https://github.com/mopidy/mopidy/issues?labels=Spotify+backend
Dependencies
============
.. literalinclude:: ../../requirements/spotify.txt
Default configuration
=====================
.. literalinclude:: ../../mopidy/backends/spotify/ext.conf
:language: ini
Configuration values
====================
.. confval:: spotify/enabled
If the Spotify extension should be enabled or not.
.. confval:: spotify/username
Your Spotify Premium username.
.. confval:: spotify/password
Your Spotify Premium password.
.. confval:: spotify/bitrate
The preferred audio bitrate. Valid values are 96, 160, 320.
.. confval:: spotify/timeout
Max number of seconds to wait for Spotify operations to complete.
.. confval:: spotify/cache_dir
Path to the Spotify data cache. Cannot be shared with other Spotify apps.
Usage
=====
If you are using the Spotify backend, which is the default, enter your Spotify
Premium account's username and password into ``~/.config/mopidy/mopidy.conf``,
like this:
.. code-block:: ini
[spotify]
username = myusername
password = mysecret
This will only work if you have the Spotify Premium subscription. Spotify
Unlimited will not work.

56
docs/ext/stream.rst Normal file
View File

@ -0,0 +1,56 @@
.. _ext-stream:
*************
Mopidy-Stream
*************
Extension for playing streaming music.
The stream backend will handle streaming of URIs matching the
:confval:`stream/protocols` config value, assuming the needed GStreamer plugins
are installed.
Known issues
============
https://github.com/mopidy/mopidy/issues?labels=Stream+backend
Dependencies
============
None. The extension just needs Mopidy.
Default configuration
=====================
.. literalinclude:: ../../mopidy/backends/stream/ext.conf
:language: ini
Configuration values
====================
.. confval:: stream/enabled
If the stream extension should be enabled or not.
.. confval:: stream/protocols
Whitelist of URI schemas to allow streaming from.
Usage
=====
This backend does not provide a library or similar. It simply takes any URI
added to Mopidy's tracklist that matches any of the protocols in the
:confval:`stream/protocols` setting and tries to play back the URI using
GStreamer. E.g. if you're using an MPD client, you'll just have to find your
clients "add URI" interface, and provide it with the direct URI of the stream.
Currently the stream backend can only work with URIs pointing direcly at
streams, and not intermediate playlists which is often used. See :issue:`303`
to track the development of playlist expansion support.

View File

@ -2,56 +2,71 @@
Mopidy
******
Mopidy is a music server which can play music both from your :ref:`local hard
drive <local-backend>` and from :ref:`Spotify <spotify-backend>`. Searches
returns results from both your local hard drive and from Spotify, and you can
mix tracks from both sources in your play queue. Your Spotify playlists are
also available for use, though we don't support modifying them yet.
Mopidy is a music server which can play music both from multiple sources, like
your :ref:`local hard drive <ext-local>`, :ref:`radio streams <ext-stream>`,
and from :ref:`Spotify <ext-spotify>` and SoundCloud. Searches combines results
from all music sources, and you can mix tracks from all sources in your play
queue. Your playlists from Spotify or SoundCloud are also available for use.
To control your music server, you can use the :ref:`Ubuntu Sound Menu
<ubuntu-sound-menu>` on the machine running Mopidy, any device on the same
network which can control UPnP MediaRenderers (see :ref:`upnp-clients`), or any
:ref:`MPD client <mpd-clients>`. MPD clients are available for most platforms,
including Windows, Mac OS X, Linux, Android, and iOS.
To control your Mopidy music server, you can use one of Mopidy's :ref:`web
clients <http-clients>`, the :ref:`Ubuntu Sound Menu <ubuntu-sound-menu>`, any
device on the same network which can control :ref:`UPnP MediaRenderers
<upnp-clients>`, or any :ref:`MPD client <mpd-clients>`. MPD clients are
available for many platforms, including Windows, OS X, Linux, Android and iOS.
To install Mopidy, start by reading :ref:`installation`.
To get started with Mopidy, start by reading :ref:`installation`.
If you get stuck, we usually hang around at ``#mopidy`` at `irc.freenode.net
<http://freenode.net/>`_ and also got a `mailing list at Google Groups
<http://freenode.net/>`_ and also have a `mailing list at Google Groups
<https://groups.google.com/forum/?fromgroups=#!forum/mopidy>`_. If you stumble
into a bug or got a feature request, please create an issue in the `issue
tracker <https://github.com/mopidy/mopidy/issues>`_.
tracker <https://github.com/mopidy/mopidy/issues>`_. The `source code
<https://github.com/mopidy/mopidy>`_ may also be of help. If you want to stay
up to date on Mopidy developments, you can follow `@mopidy
<https://twitter.com/mopidy/>`_ on Twitter.
Project resources
=================
- `Documentation <http://docs.mopidy.com/>`_
- `Source code <https://github.com/mopidy/mopidy>`_
- `Issue tracker <https://github.com/mopidy/mopidy/issues>`_
- `CI server <https://travis-ci.org/mopidy/mopidy>`_
- IRC: ``#mopidy`` at `irc.freenode.net <http://freenode.net/>`_
- Mailing list: `mopidy@googlegroups.com <https://groups.google.com/forum/?fromgroups=#!forum/mopidy>`_
User documentation
==================
Usage
=====
.. toctree::
:maxdepth: 2
:maxdepth: 3
installation/index
installation/raspberrypi
config
ext/index
running
clients/index
troubleshooting
About
=====
.. toctree::
:maxdepth: 1
authors
licenses
changes
changelog
versioning
Reference documentation
=======================
Development
===========
.. toctree::
:maxdepth: 1
contributing
devtools
codestyle
extensiondev
Reference
=========
.. toctree::
:maxdepth: 2
@ -60,16 +75,6 @@ Reference documentation
modules/index
Development documentation
=========================
.. toctree::
:maxdepth: 2
development
extensiondev
Indices and tables
==================

View File

@ -1,7 +0,0 @@
*********************************************************
:mod:`mopidy.backends.dummy` -- Dummy backend for testing
*********************************************************
.. automodule:: mopidy.backends.dummy
:synopsis: Dummy backend used for testing
:members:

View File

@ -1,8 +0,0 @@
.. _local-backend:
*********************************************
:mod:`mopidy.backends.local` -- Local backend
*********************************************
.. automodule:: mopidy.backends.local
:synopsis: Backend for playing music files on local storage

View File

@ -1,8 +0,0 @@
.. _spotify-backend:
*************************************************
:mod:`mopidy.backends.spotify` -- Spotify backend
*************************************************
.. automodule:: mopidy.backends.spotify
:synopsis: Backend for the Spotify music streaming service

View File

@ -1,7 +0,0 @@
***********************************************
:mod:`mopidy.backends.stream` -- Stream backend
***********************************************
.. automodule:: mopidy.backends.stream
:synopsis: Backend for playing audio streams
:members:

View File

@ -1,8 +0,0 @@
.. _http-frontend:
*********************************************
:mod:`mopidy.frontends.http` -- HTTP frontend
*********************************************
.. automodule:: mopidy.frontends.http
:synopsis: HTTP and WebSockets frontend

View File

@ -2,6 +2,8 @@
:mod:`mopidy.frontends.mpd` -- MPD server
*****************************************
For details on how to use Mopidy's MPD server, see :ref:`ext-mpd`.
.. automodule:: mopidy.frontends.mpd
:synopsis: MPD server frontend

View File

@ -1,8 +0,0 @@
.. _mpris-frontend:
***********************************************
:mod:`mopidy.frontends.mpris` -- MPRIS frontend
***********************************************
.. automodule:: mopidy.frontends.mpris
:synopsis: MPRIS frontend

View File

@ -1,6 +0,0 @@
***********************************************
:mod:`mopidy.frontends.scrobbler` -- Scrobbler
***********************************************
.. automodule:: mopidy.frontends.scrobbler
:synopsis: Music scrobbler frontend

58
docs/troubleshooting.rst Normal file
View File

@ -0,0 +1,58 @@
.. _troubleshooting:
***************
Troubleshooting
***************
If you run into problems with Mopidy, we usually hang around at ``#mopidy`` at
`irc.freenode.net <http://freenode.net/>`_ and also have a `mailing list at
Google Groups <https://groups.google.com/forum/?fromgroups=#!forum/mopidy>`_.
If you stumble into a bug or got a feature request, please create an issue in
the `issue tracker <https://github.com/mopidy/mopidy/issues>`_.
When you're debugging yourself or asking for help, there are some tools built
into Mopidy that you should know about.
Effective configuration
=======================
The command :option:`mopidy --show-config` will print your full effective
configuration the way Mopidy sees it after all defaults and all config files
have been merged into a single config document. Any secret values like
passwords are masked out, so the output of the command should be safe to share
with others for debugging.
Installed dependencies
======================
The command :option:`mopidy --list-deps` will list the paths to and versions of
any dependency Mopidy or the extensions might need to work. This is very useful
data for checking that you're using the right versions, and that you're using
the right installation if you have multiple installations of a dependency on
your system.
Debug logging
=============
If you run :option:`mopidy -v`, Mopidy will output debug log to stdout. If you
run :option:`mopidy --save-debug-log`, it will save the debug log to the file
``mopidy.log`` in the directory you ran the command from.
If you want to turn on more or less logging for some component, see the
docs for the :confval:`logging.levels/*` config section.
Debugging deadlocks
===================
If Mopidy hangs without and obvious explanation, you can send the ``SIGUSR1``
signal to the Mopidy process. If Mopidy's main thread is still responsive, it
will log a traceback for each running thread, showing what the threads are
currently doing. This is a very useful tool for understanding exactly how the
system is deadlocking. If you have the ``pkill`` command installed, you can use
this by simply running::
pkill -SIGUSR1 mopidy

23
docs/versioning.rst Normal file
View File

@ -0,0 +1,23 @@
**********
Versioning
**********
Mopidy uses `Semantic Versioning <http://semver.org/>`_, but since we're still
pre-1.0 that doesn't mean much yet.
Release schedule
================
We intend to have about one feature release every month in periods of active
development. The feature releases are numbered 0.x.0. The features added is a
mix of what we feel is most important/requested of the missing features, and
features we develop just because we find them fun to make, even though they may
be useful for very few users or for a limited use case.
Bugfix releases, numbered 0.x.y, will be released whenever we discover bugs
that are too serious to wait for the next feature release. We will only release
bugfix releases for the last feature release. E.g. when 0.14.0 is released, we
will no longer provide bugfix releases for the 0.13 series. In other words,
there will be just a single supported release at any point in time. This is to
not spread our limited resources too thin.

View File

@ -41,8 +41,8 @@ After npm completes, you can import Mopidy.js using ``require()``:
Using the library
-----------------
See Mopidy's [HTTP frontend
documentation](http://docs.mopidy.com/en/latest/modules/frontends/http/).
See Mopidy's [HTTP API
documentation](http://docs.mopidy.com/en/latest/api/http/).
Building from source

View File

@ -26,15 +26,6 @@ MB = 1 << 20
class Audio(pykka.ThreadingActor):
"""
Audio output through `GStreamer <http://gstreamer.freedesktop.org/>`_.
**Default config:**
.. code-block:: ini
[audio]
mixer = autoaudiomixer
mixer_track =
output = autoaudiosink
"""
#: The GStreamer state mapped to :class:`mopidy.audio.PlaybackState`

View File

@ -2,11 +2,15 @@
This is Mopidy's default mixer.
**Dependencies**
Dependencies
============
None
**Configuration**
Configuration
=============
If this wasn't the default, you would set the :confval:`audio/mixer` config
value to ``autoaudiomixer`` to use this mixer.

View File

@ -1,10 +1,13 @@
"""Fake mixer for use in tests.
**Dependencies**
Dependencies
============
None
**Configuration**
Configuration
=============
Set the :confval:`audio/mixer:` config value to ``fakemixer`` to use this
mixer.

View File

@ -3,11 +3,15 @@
The NAD amplifier must be connected to the machine running Mopidy using a
serial cable.
**Dependencies**
Dependencies
============
.. literalinclude:: ../../../../requirements/external_mixers.txt
**Configuration**
Configuration
=============
Set the :confval:`audio/mixer` config value to ``nadmixer`` to use it. You
probably also needs to add some properties to the :confval:`audio/mixer` config

View File

@ -1,57 +1,9 @@
from __future__ import unicode_literals
import os
import mopidy
from mopidy import config, ext
from mopidy.utils import formatting
default_config = """
[local]
enabled = true
media_dir = $XDG_MUSIC_DIR
playlists_dir = $XDG_DATA_DIR/mopidy/local/playlists
tag_cache_file = $XDG_DATA_DIR/mopidy/local/tag_cache
"""
__doc__ = """A backend for playing music from a local music archive.
This backend handles URIs starting with ``file:``.
See :ref:`music-from-local-storage` for further instructions on using this
backend.
**Issues**
https://github.com/mopidy/mopidy/issues?labels=Local+backend
**Dependencies**
None
**Configuration**
.. confval:: local/enabled
If the local extension should be enabled or not.
.. confval:: local/media_dir
Path to directory with local media files.
.. confval:: local/playlists_dir
Path to playlists directory with m3u files for local media.
.. confval:: local/tag_cache_file
Path to tag cache for local media.
**Default config**
.. code-block:: ini
%(config)s
""" % {'config': formatting.indent(default_config)}
class Extension(ext.Extension):
@ -61,7 +13,8 @@ class Extension(ext.Extension):
version = mopidy.__version__
def get_default_config(self):
return default_config
conf_file = os.path.join(os.path.dirname(__file__), 'ext.conf')
return open(conf_file).read()
def get_config_schema(self):
schema = config.ExtensionConfigSchema()

View File

@ -0,0 +1,5 @@
[local]
enabled = true
media_dir = $XDG_MUSIC_DIR
playlists_dir = $XDG_DATA_DIR/mopidy/local/playlists
tag_cache_file = $XDG_DATA_DIR/mopidy/local/tag_cache

View File

@ -1,76 +1,9 @@
from __future__ import unicode_literals
import os
import mopidy
from mopidy import config, exceptions, ext
from mopidy.utils import formatting
default_config = """
[spotify]
enabled = true
username =
password =
bitrate = 160
timeout = 10
cache_dir = $XDG_CACHE_DIR/mopidy/spotify
"""
__doc__ = """A backend for playing music from Spotify
`Spotify <http://www.spotify.com/>`_ is a music streaming service. The backend
uses the official `libspotify
<http://developer.spotify.com/en/libspotify/overview/>`_ library and the
`pyspotify <http://github.com/mopidy/pyspotify/>`_ Python bindings for
libspotify. This backend handles URIs starting with ``spotify:``.
See :ref:`music-from-spotify` for further instructions on using this backend.
.. note::
This product uses SPOTIFY(R) CORE but is not endorsed, certified or
otherwise approved in any way by Spotify. Spotify is the registered
trade mark of the Spotify Group.
**Issues**
https://github.com/mopidy/mopidy/issues?labels=Spotify+backend
**Dependencies**
.. literalinclude:: ../../../requirements/spotify.txt
**Configuration**
.. confval:: spotify/enabled
If the Spotify extension should be enabled or not.
.. confval:: spotify/username
Your Spotify Premium username.
.. confval:: spotify/password
Your Spotify Premium password.
.. confval:: spotify/bitrate
The preferred audio bitrate. Valid values are 96, 160, 320.
.. confval:: spotify/timeout
Max number of seconds to wait for Spotify operations to complete.
.. confval:: spotify/cache_dir
Path to the Spotify data cache. Cannot be shared with other Spotify apps.
**Default config**
.. code-block:: ini
%(config)s
""" % {'config': formatting.indent(default_config)}
class Extension(ext.Extension):
@ -80,7 +13,8 @@ class Extension(ext.Extension):
version = mopidy.__version__
def get_default_config(self):
return default_config
conf_file = os.path.join(os.path.dirname(__file__), 'ext.conf')
return open(conf_file).read()
def get_config_schema(self):
schema = config.ExtensionConfigSchema()

View File

@ -0,0 +1,7 @@
[spotify]
enabled = true
username =
password =
bitrate = 160
timeout = 10
cache_dir = $XDG_CACHE_DIR/mopidy/spotify

View File

@ -1,53 +1,9 @@
from __future__ import unicode_literals
import os
import mopidy
from mopidy import config, ext
from mopidy.utils import formatting
default_config = """
[stream]
enabled = true
protocols =
http
https
mms
rtmp
rtmps
rtsp
"""
__doc__ = """
A backend for playing music for streaming music.
This backend will handle streaming of URIs matching the
:confval:`stream/protocols` config value, assuming the needed GStreamer plugins
are installed.
**Issues**
https://github.com/mopidy/mopidy/issues?labels=Stream+backend
**Dependencies**
None
**Configuration**
.. confval:: stream/enabled
If the stream extension should be enabled or not.
.. confval:: stream/protocols
Whitelist of URI schemas to allow streaming from.
**Default config**
.. code-block:: ini
%(config)s
""" % {'config': formatting.indent(default_config)}
class Extension(ext.Extension):
@ -57,7 +13,8 @@ class Extension(ext.Extension):
version = mopidy.__version__
def get_default_config(self):
return default_config
conf_file = os.path.join(os.path.dirname(__file__), 'ext.conf')
return open(conf_file).read()
def get_config_schema(self):
schema = config.ExtensionConfigSchema()

View File

@ -0,0 +1,9 @@
[stream]
enabled = true
protocols =
http
https
mms
rtmp
rtmps
rtsp

17
mopidy/default.conf Normal file
View File

@ -0,0 +1,17 @@
[logging]
console_format = %(levelname)-8s %(message)s
debug_format = %(levelname)-8s %(asctime)s [%(process)d:%(threadName)s] %(name)s\n %(message)s
debug_file = mopidy.log
[logging.levels]
pykka = info
[audio]
mixer = autoaudiomixer
mixer_track =
output = autoaudiosink
[proxy]
hostname =
username =
password =

View File

@ -1,533 +1,9 @@
from __future__ import unicode_literals
import os
import mopidy
from mopidy import config, exceptions, ext
from mopidy.utils import formatting
default_config = """
[http]
enabled = true
hostname = 127.0.0.1
port = 6680
static_dir =
[logging.levels]
cherrypy = warning
"""
__doc__ = """
The HTTP frontends lets you control Mopidy through HTTP and WebSockets, e.g.
from a web based client.
**Issues**
https://github.com/mopidy/mopidy/issues?labels=HTTP+frontend
**Dependencies**
.. literalinclude:: ../../../requirements/http.txt
**Configuration**
.. confval:: http/enabled
If the HTTP extension should be enabled or not.
.. confval:: http/hostname
Which address the HTTP server should bind to.
``127.0.0.1``
Listens only on the IPv4 loopback interface
``::1``
Listens only on the IPv6 loopback interface
``0.0.0.0``
Listens on all IPv4 interfaces
``::``
Listens on all interfaces, both IPv4 and IPv6
.. confval:: http/port
Which TCP port the HTTP server should listen to.
.. confval:: http/static_dir
Which directory the HTTP server should serve at "/"
Change this to have Mopidy serve e.g. files for your JavaScript client.
"/mopidy" will continue to work as usual even if you change this setting.
**Default config**
.. code-block:: ini
%(config)s
Setup
=====
The frontend is enabled by default if all dependencies are available.
When it is enabled it starts a web server at the port specified by the
:confval:`http/port` config value.
.. warning:: Security
As a simple security measure, the web server is by default only available
from localhost. To make it available from other computers, change the
:confval:`http/hostname` config value. Before you do so, note that the HTTP
frontend does not feature any form of user authentication or authorization.
Anyone able to access the web server can use the full core API of Mopidy.
Thus, you probably only want to make the web server available from your
local network or place it behind a web proxy which takes care or user
authentication. You have been warned.
Using a web based Mopidy client
===============================
The web server can also host any static files, for example the HTML, CSS,
JavaScript, and images needed for a web based Mopidy client. To host static
files, change the ``http/static_dir`` to point to the root directory of your
web client, e.g.::
[http]
static_dir = /home/alice/dev/the-client
If the directory includes a file named ``index.html``, it will be served on the
root of Mopidy's web server.
If you're making a web based client and wants to do server side development as
well, you are of course free to run your own web server and just use Mopidy's
web server for the APIs. But, for clients implemented purely in JavaScript,
letting Mopidy host the files is a simpler solution.
WebSocket API
=============
.. warning:: API stability
Since this frontend exposes our internal core API directly it is to be
regarded as **experimental**. We cannot promise to keep any form of
backwards compatibility between releases as we will need to change the core
API while working out how to support new use cases. Thus, if you use this
API, you must expect to do small adjustments to your client for every
release of Mopidy.
From Mopidy 1.0 and onwards, we intend to keep the core API far more
stable.
The web server exposes a WebSocket at ``/mopidy/ws/``. The WebSocket gives you
access to Mopidy's full API and enables Mopidy to instantly push events to the
client, as they happen.
On the WebSocket we send two different kind of messages: The client can send
JSON-RPC 2.0 requests, and the server will respond with JSON-RPC 2.0 responses.
In addition, the server will send event messages when something happens on the
server. Both message types are encoded as JSON objects.
Event messages
--------------
Event objects will always have a key named ``event`` whose value is the event
type. Depending on the event type, the event may include additional fields for
related data. The events maps directly to the :class:`mopidy.core.CoreListener`
API. Refer to the ``CoreListener`` method names is the available event types.
The ``CoreListener`` method's keyword arguments are all included as extra
fields on the event objects. Example event message::
{"event": "track_playback_started", "track": {...}}
JSON-RPC 2.0 messaging
----------------------
JSON-RPC 2.0 messages can be recognized by checking for the key named
``jsonrpc`` with the string value ``2.0``. For details on the messaging format,
please refer to the `JSON-RPC 2.0 spec
<http://www.jsonrpc.org/specification>`_.
All methods (not attributes) in the :ref:`core-api` is made available through
JSON-RPC calls over the WebSocket. For example,
:meth:`mopidy.core.PlaybackController.play` is available as the JSON-RPC method
``core.playback.play``.
The core API's attributes is made available through setters and getters. For
example, the attribute :attr:`mopidy.core.PlaybackController.current_track` is
available as the JSON-RPC method ``core.playback.get_current_track``.
Example JSON-RPC request::
{"jsonrpc": "2.0", "id": 1, "method": "core.playback.get_current_track"}
Example JSON-RPC response::
{"jsonrpc": "2.0", "id": 1, "result": {"__model__": "Track", "...": "..."}}
The JSON-RPC method ``core.describe`` returns a data structure describing all
available methods. If you're unsure how the core API maps to JSON-RPC, having a
look at the ``core.describe`` response can be helpful.
Mopidy.js JavaScript library
============================
We've made a JavaScript library, Mopidy.js, which wraps the WebSocket and gets
you quickly started with working on your client instead of figuring out how to
communicate with Mopidy.
Getting the library for browser use
-----------------------------------
Regular and minified versions of Mopidy.js, ready for use, is installed
together with Mopidy. When the HTTP frontend is running, the files are
available at:
- http://localhost:6680/mopidy/mopidy.js
- http://localhost:6680/mopidy/mopidy.min.js
You may need to adjust hostname and port for your local setup.
Thus, if you use Mopidy to host your web client, like described above, you can
load the latest version of Mopidy.js by adding the following script tag to your
HTML file:
.. code-block:: html
<script type="text/javascript" src="/mopidy/mopidy.min.js"></script>
If you don't use Mopidy to host your web client, you can find the JS files in
the Git repo at:
- ``mopidy/frontends/http/data/mopidy.js``
- ``mopidy/frontends/http/data/mopidy.min.js``
Getting the library for Node.js use
-----------------------------------
If you want to use Mopidy.js from Node.js instead of a browser, you can install
Mopidy.js using npm::
npm install mopidy
After npm completes, you can import Mopidy.js using ``require()``:
.. code-block:: js
var Mopidy = require("mopidy").Mopidy;
Getting the library for development on the library
--------------------------------------------------
If you want to work on the Mopidy.js library itself, you'll find a complete
development setup in the ``js/`` dir in our repo. The instructions in
``js/README.md`` will guide you on your way.
Creating an instance
--------------------
Once you got Mopidy.js loaded, you need to create an instance of the wrapper:
.. code-block:: js
var mopidy = new Mopidy();
When you instantiate ``Mopidy()`` without arguments, it will connect to
the WebSocket at ``/mopidy/ws/`` on the current host. Thus, if you don't host
your web client using Mopidy's web server, or if you use Mopidy.js from a
Node.js environment, you'll need to pass the URL to the WebSocket end point:
.. code-block:: js
var mopidy = new Mopidy({
webSocketUrl: "ws://localhost:6680/mopidy/ws/"
});
It is also possible to create an instance first and connect to the WebSocket
later:
.. code-block:: js
var mopidy = new Mopidy({autoConnect: false});
// ... do other stuff, like hooking up events ...
mopidy.connect();
Hooking up to events
--------------------
Once you have a Mopidy.js object, you can hook up to the events it emits. To
explore your possibilities, it can be useful to subscribe to all events and log
them:
.. code-block:: js
mopidy.on(console.log.bind(console));
Several types of events are emitted:
- You can get notified about when the Mopidy.js object is connected to the
server and ready for method calls, when it's offline, and when it's trying to
reconnect to the server by looking at the events ``state:online``,
``state:offline``, ``reconnectionPending``, and ``reconnecting``.
- You can get events sent from the Mopidy server by looking at the events with
the name prefix ``event:``, like ``event:trackPlaybackStarted``.
- You can introspect what happens internally on the WebSocket by looking at the
events emitted with the name prefix ``websocket:``.
Mopidy.js uses the event emitter library `BANE
<https://github.com/busterjs/bane>`_, so you should refer to BANE's
short API documentation to see how you can hook up your listeners to the
different events.
Calling core API methods
------------------------
Once your Mopidy.js object has connected to the Mopidy server and emits the
``state:online`` event, it is ready to accept core API method calls:
.. code-block:: js
mopidy.on("state:online", function () {
mopidy.playback.next();
});
Any calls you make before the ``state:online`` event is emitted will fail. If
you've hooked up an errback (more on that a bit later) to the promise returned
from the call, the errback will be called with an error message.
All methods in Mopidy's :ref:`core-api` is available via Mopidy.js. The core
API attributes is *not* available, but that shouldn't be a problem as we've
added (undocumented) getters and setters for all of them, so you can access the
attributes as well from JavaScript.
Both the WebSocket API and the JavaScript API are based on introspection of the
core Python API. Thus, they will always be up to date and immediately reflect
any changes we do to the core API.
The best way to explore the JavaScript API, is probably by opening your
browser's console, and using its tab completion to navigate the API. You'll
find the Mopidy core API exposed under ``mopidy.playback``,
``mopidy.tracklist``, ``mopidy.playlists``, and ``mopidy.library``.
All methods in the JavaScript API have an associated data structure describing
the Python params it expects, and most methods also have the Python API
documentation available. This is available right there in the browser console,
by looking at the method's ``description`` and ``params`` attributes:
.. code-block:: js
console.log(mopidy.playback.next.params);
console.log(mopidy.playback.next.description);
JSON-RPC 2.0 limits method parameters to be sent *either* by-position or
by-name. Combinations of both, like we're used to from Python, isn't supported
by JSON-RPC 2.0. To further limit this, Mopidy.js currently only supports
passing parameters by-position.
Obviously, you'll want to get a return value from many of your method calls.
Since everything is happening across the WebSocket and maybe even across the
network, you'll get the results asynchronously. Instead of having to pass
callbacks and errbacks to every method you call, the methods return "promise"
objects, which you can use to pipe the future result as input to another
method, or to hook up callback and errback functions.
.. code-block:: js
var track = mopidy.playback.getCurrentTrack();
// => ``track`` isn't a track, but a "promise" object
Instead, typical usage will look like this:
.. code-block:: js
var printCurrentTrack = function (track) {
if (track) {
console.log("Currently playing:", track.name, "by",
track.artists[0].name, "from", track.album.name);
} else {
console.log("No current track");
}
};
mopidy.playback.getCurrentTrack().then(
printCurrentTrack, console.error.bind(console));
The first function passed to ``then()``, ``printCurrentTrack``, is the callback
that will be called if the method call succeeds. The second function,
``console.error``, is the errback that will be called if anything goes wrong.
If you don't hook up an errback, debugging will be hard as errors will silently
go missing.
For debugging, you may be interested in errors from function without
interesting return values as well. In that case, you can pass ``null`` as the
callback:
.. code-block:: js
mopidy.playback.next().then(null, console.error.bind(console));
The promise objects returned by Mopidy.js adheres to the `CommonJS Promises/A
<http://wiki.commonjs.org/wiki/Promises/A>`_ standard. We use the
implementation known as `when.js <https://github.com/cujojs/when>`_. Please
refer to when.js' documentation or the standard for further details on how to
work with promise objects.
Cleaning up
-----------
If you for some reason want to clean up after Mopidy.js before the web page is
closed or navigated away from, you can close the WebSocket, unregister all
event listeners, and delete the object like this:
.. code-block:: js
// Close the WebSocket without reconnecting. Letting the object be garbage
// collected will have the same effect, so this isn't strictly necessary.
mopidy.close();
// Unregister all event listeners. If you don't do this, you may have
// lingering references to the object causing the garbage collector to not
// clean up after it.
mopidy.off();
// Delete your reference to the object, so it can be garbage collected.
mopidy = null;
Example to get started with
---------------------------
1. Make sure that you've installed all dependencies required by the HTTP
frontend.
2. Create an empty directory for your web client.
3. Change the :confval:`http/static_dir` config value to point to your new
directory.
4. Start/restart Mopidy.
5. Create a file in the directory named ``index.html`` containing e.g. "Hello,
world!".
6. Visit http://localhost:6680/ to confirm that you can view your new HTML file
there.
7. Include Mopidy.js in your web page:
.. code-block:: html
<script type="text/javascript" src="/mopidy/mopidy.min.js"></script>
8. Add one of the following Mopidy.js examples of how to queue and start
playback of your first playlist either to your web page or a JavaScript file
that you include in your web page.
"Imperative" style:
.. code-block:: js
var consoleError = console.error.bind(console);
var trackDesc = function (track) {
return track.name + " by " + track.artists[0].name +
" from " + track.album.name;
};
var queueAndPlayFirstPlaylist = function () {
mopidy.playlists.getPlaylists().then(function (playlists) {
var playlist = playlists[0];
console.log("Loading playlist:", playlist.name);
mopidy.tracklist.add(playlist.tracks).then(function (tlTracks) {
mopidy.playback.play(tlTracks[0]).then(function () {
mopidy.playback.getCurrentTrack().then(function (track) {
console.log("Now playing:", trackDesc(track));
}, consoleError);
}, consoleError);
}, consoleError);
}, consoleError);
};
var mopidy = new Mopidy(); // Connect to server
mopidy.on(console.log.bind(console)); // Log all events
mopidy.on("state:online", queueAndPlayFirstPlaylist);
Approximately the same behavior in a more functional style, using chaining
of promisies.
.. code-block:: js
var consoleError = console.error.bind(console);
var getFirst = function (list) {
return list[0];
};
var extractTracks = function (playlist) {
return playlist.tracks;
};
var printTypeAndName = function (model) {
console.log(model.__model__ + ": " + model.name);
// By returning the playlist, this function can be inserted
// anywhere a model with a name is piped in the chain.
return model;
};
var trackDesc = function (track) {
return track.name + " by " + track.artists[0].name +
" from " + track.album.name;
};
var printNowPlaying = function () {
// By returning any arguments we get, the function can be inserted
// anywhere in the chain.
var args = arguments;
return mopidy.playback.getCurrentTrack().then(function (track) {
console.log("Now playing:", trackDesc(track));
return args;
});
};
var queueAndPlayFirstPlaylist = function () {
mopidy.playlists.getPlaylists()
// => list of Playlists
.then(getFirst, consoleError)
// => Playlist
.then(printTypeAndName, consoleError)
// => Playlist
.then(extractTracks, consoleError)
// => list of Tracks
.then(mopidy.tracklist.add, consoleError)
// => list of TlTracks
.then(getFirst, consoleError)
// => TlTrack
.then(mopidy.playback.play, consoleError)
// => null
.then(printNowPlaying, consoleError);
};
var mopidy = new Mopidy(); // Connect to server
mopidy.on(console.log.bind(console)); // Log all events
mopidy.on("state:online", queueAndPlayFirstPlaylist);
9. The web page should now queue and play your first playlist every time your
load it. See the browser's console for output from the function, any errors,
and all events that are emitted.
""" % {'config': formatting.indent(default_config)}
class Extension(ext.Extension):
@ -537,7 +13,8 @@ class Extension(ext.Extension):
version = mopidy.__version__
def get_default_config(self):
return default_config
conf_file = os.path.join(os.path.dirname(__file__), 'ext.conf')
return open(conf_file).read()
def get_config_schema(self):
schema = config.ExtensionConfigSchema()

View File

@ -0,0 +1,8 @@
[http]
enabled = true
hostname = 127.0.0.1
port = 6680
static_dir =
[logging.levels]
cherrypy = warning

View File

@ -1,104 +1,9 @@
from __future__ import unicode_literals
import os
import mopidy
from mopidy import config, ext
from mopidy.utils import formatting
default_config = """
[mpd]
enabled = true
hostname = 127.0.0.1
port = 6600
password =
max_connections = 20
connection_timeout = 60
"""
__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
original MPD server.
**Issues**
https://github.com/mopidy/mopidy/issues?labels=MPD+frontend
**Dependencies**
None
**Configuration**
.. confval:: mpd/enabled
If the MPD extension should be enabled or not.
.. confval:: mpd/hostname
Which address the MPD server should bind to.
``127.0.0.1``
Listens only on the IPv4 loopback interface
``::1``
Listens only on the IPv6 loopback interface
``0.0.0.0``
Listens on all IPv4 interfaces
``::``
Listens on all interfaces, both IPv4 and IPv6
.. confval:: mpd/port
Which TCP port the MPD server should listen to.
.. confval:: mpd/password
The password required for connecting to the MPD server. If blank, no
password is required.
.. confval:: mpd/max_connections
The maximum number of concurrent connections the MPD server will accept.
.. confval:: mpd/connection_timeout
Number of seconds an MPD client can stay inactive before the connection is
closed by the server.
**Default config**
.. code-block:: ini
%(config)s
**Usage:**
The frontend is enabled by default.
**Limitations:**
This is a non exhaustive list of MPD features that Mopidy doesn't support.
Items on this list will probably not be supported in the near future.
- Toggling of audio outputs is not supported
- Channels for client-to-client communication are not supported
- Stickers are not supported
- Crossfade is not supported
- Replay gain is not supported
- ``count`` does not provide any statistics
- ``stats`` does not provide any statistics
- ``list`` does not support listing tracks by genre
- ``decoders`` does not provide information about available decoders
The following items are currently not supported, but should be added in the
near future:
- Modifying stored playlists is not supported
- ``tagtypes`` is not supported
- Browsing the file system is not supported
- Live update of the music database is not supported
""" % {'config': formatting.indent(default_config)}
class Extension(ext.Extension):
@ -108,7 +13,8 @@ class Extension(ext.Extension):
version = mopidy.__version__
def get_default_config(self):
return default_config
conf_file = os.path.join(os.path.dirname(__file__), 'ext.conf')
return open(conf_file).read()
def get_config_schema(self):
schema = config.ExtensionConfigSchema()

View File

@ -0,0 +1,7 @@
[mpd]
enabled = true
hostname = 127.0.0.1
port = 6600
password =
max_connections = 20
connection_timeout = 60

View File

@ -4,76 +4,6 @@ import os
import mopidy
from mopidy import config, exceptions, ext
from mopidy.utils import formatting
default_config = """
[mpris]
enabled = true
desktop_file = /usr/share/applications/mopidy.desktop
"""
__doc__ = """
Frontend which lets you control Mopidy through the Media Player Remote
Interfacing Specification (`MPRIS <http://www.mpris.org/>`_) D-Bus
interface.
An example of an MPRIS client is the `Ubuntu Sound Menu
<https://wiki.ubuntu.com/SoundMenu>`_.
**Dependencies**
- D-Bus Python bindings. The package is named ``python-dbus`` in
Ubuntu/Debian.
- ``libindicate`` Python bindings is needed to expose Mopidy in e.g. the
Ubuntu Sound Menu. The package is named ``python-indicate`` in
Ubuntu/Debian.
- An ``.desktop`` file for Mopidy installed at the path set in the
:confval:`mpris/desktop_file` config value. See :ref:`install-desktop-file`
for details.
**Configuration**
.. confval:: mpris/enabled
If the MPRIS extension should be enabled or not.
.. confval:: mpris/desktop_file
Location of the Mopidy ``.desktop`` file.
**Default config**
.. code-block:: ini
%(config)s
**Usage**
The frontend is enabled by default if all dependencies are available.
**Testing the frontend**
To test, start Mopidy, and then run the following in a Python shell::
import dbus
bus = dbus.SessionBus()
player = bus.get_object('org.mpris.MediaPlayer2.mopidy',
'/org/mpris/MediaPlayer2')
Now you can control Mopidy through the player object. Examples:
- To get some properties from Mopidy, run::
props = player.GetAll('org.mpris.MediaPlayer2',
dbus_interface='org.freedesktop.DBus.Properties')
- To quit Mopidy through D-Bus, run::
player.Quit(dbus_interface='org.mpris.MediaPlayer2')
""" % {'config': formatting.indent(default_config)}
class Extension(ext.Extension):
@ -83,7 +13,8 @@ class Extension(ext.Extension):
version = mopidy.__version__
def get_default_config(self):
return default_config
conf_file = os.path.join(os.path.dirname(__file__), 'ext.conf')
return open(conf_file).read()
def get_config_schema(self):
schema = config.ExtensionConfigSchema()

View File

@ -0,0 +1,3 @@
[mpris]
enabled = true
desktop_file = /usr/share/applications/mopidy.desktop

View File

@ -1,53 +1,9 @@
from __future__ import unicode_literals
import os
import mopidy
from mopidy import config, exceptions, ext
from mopidy.utils import formatting
default_config = """
[scrobbler]
enabled = true
username =
password =
"""
__doc__ = """
Frontend which scrobbles the music you play to your
`Last.fm <http://www.last.fm>`_ profile.
.. note::
This frontend requires a free user account at Last.fm.
**Dependencies**
.. literalinclude:: ../../../requirements/scrobbler.txt
**Configuration**
.. confval:: scrobbler/enabled
If the scrobbler extension should be enabled or not.
.. confval:: scrobbler/username
Your Last.fm username.
.. confval:: scrobbler/password
Your Last.fm password.
**Default config**
.. code-block:: ini
%(config)s
**Usage**
The frontend is enabled by default if all dependencies are available.
""" % {'config': formatting.indent(default_config)}
class Extension(ext.Extension):
@ -57,7 +13,8 @@ class Extension(ext.Extension):
version = mopidy.__version__
def get_default_config(self):
return default_config
conf_file = os.path.join(os.path.dirname(__file__), 'ext.conf')
return open(conf_file).read()
def get_config_schema(self):
schema = config.ExtensionConfigSchema()

View File

@ -0,0 +1,4 @@
[scrobbler]
enabled = true
username =
password =

View File

@ -154,8 +154,8 @@ class Scanner(object):
self.fakesink.connect('handoff', self.process_handoff)
self.uribin = gst.element_factory_make('uridecodebin')
self.uribin.set_property('caps',
gst.Caps(b'audio/x-raw-int; audio/x-raw-float'))
self.uribin.set_property(
'caps', gst.Caps(b'audio/x-raw-int; audio/x-raw-float'))
self.uribin.connect('pad-added', self.process_new_pad)
self.pipe = gst.element_factory_make('pipeline')

View File

@ -3,7 +3,7 @@ from __future__ import unicode_literals
import logging
import logging.handlers
from . import deps, versioning
from . import versioning
def setup_logging(config, verbosity_level, save_debug_log):

View File

@ -29,7 +29,8 @@ class ExceptionsTest(unittest.TestCase):
exceptions.ConfigError, exceptions.MopidyException))
def test_config_error_provides_getitem(self):
exception = exceptions.ConfigError({'field1': 'msg1', 'field2': 'msg2'})
exception = exceptions.ConfigError(
{'field1': 'msg1', 'field2': 'msg2'})
self.assertEqual('msg1', exception['field1'])
self.assertEqual('msg2', exception['field2'])
self.assertItemsEqual(['field1', 'field2'], exception)