Merge branch 'develop' into feature/multi-backend

This commit is contained in:
Stein Magnus Jodal 2011-01-11 22:10:40 +01:00
commit a9461df4a0
42 changed files with 467 additions and 356 deletions

View File

@ -13,7 +13,7 @@ To install Mopidy, check out
* `Documentation (latest release) <http://www.mopidy.com/docs/master/>`_
* `Documentation (development version) <http://www.mopidy.com/docs/develop/>`_
* `Source code <http://github.com/jodal/mopidy>`_
* `Issue tracker <http://github.com/jodal/mopidy/issues>`_
* `Source code <http://github.com/mopidy/mopidy>`_
* `Issue tracker <http://github.com/mopidy/mopidy/issues>`_
* IRC: ``#mopidy`` at `irc.freenode.net <http://freenode.net/>`_
* `Download development snapshot <http://github.com/jodal/mopidy/tarball/develop#egg=mopidy-dev>`_
* `Download development snapshot <http://github.com/mopidy/mopidy/tarball/develop#egg=mopidy-dev>`_

View File

@ -7,4 +7,4 @@ Icon=audio-x-generic
TryExec=mopidy
Exec=mopidy
Terminal=true
Categories=AudioVideo;Audio;Player;ConsoleOnly
Categories=AudioVideo;Audio;Player;ConsoleOnly;

14
debian/TODO vendored
View File

@ -1,14 +0,0 @@
To do for Mopidy's Debian packaging
===================================
- Install data/mopidy.desktop into /usr/share/applications/
- Add manpages for all commands. Build the manpages with Sphinx
- Make init script run Mopidy as a daemon
- Make init script run Mopidy with its own user
- Add support for reading settings from /etc/mopidy/settings.py
- Log to /var/log
- Cache files in /var/cache
- Package pyspotify and add it to Recommends
- Package pylast and add it to Recommends
- Create GPG key for signing the package
- Host the packages at PPA or apt.mopidy.com

5
debian/changelog vendored
View File

@ -1,5 +0,0 @@
mopidy (0.2.0-1) unstable; urgency=low
* Initial release
-- Stein Magnus Jodal <stein.magnus@jodal.no> Sun, 31 Oct 2010 13:07:04 +0100

1
debian/compat vendored
View File

@ -1 +0,0 @@
7

22
debian/control vendored
View File

@ -1,22 +0,0 @@
Source: mopidy
Section: sound
Priority: optional
Maintainer: Stein Magnus Jodal <stein.magnus@jodal.no>
Build-Depends: debhelper (>= 7.0.50~), python-support, python (>= 2.6),
python-sphinx (>= 1.0), python-pygraphviz
Standards-Version: 3.9.1
Homepage: http://www.mopidy.com/
Vcs-Git: git://github.com/jodal/mopidy.git
Vcs-Browser: http://github.com/jodal/mopidy
Package: mopidy
Architecture: all
Depends: ${misc:Depends}, ${python:Depends}, python-gst0.10
Recommends: gstreamer0.10-plugins-good, gstreamer0.10-plugins-ugly
Suggests: python-alsaaudio (>= 0.2), python-serial
Description: music server with MPD client support
Mopidy is a music server which can play music from Spotify or from your
local hard drive. To search for music in Spotifys vast archive, manage
playlists, and play music, you can use most MPD clients. MPD clients are
available for most platforms, including Windows, Mac OS X, Linux, and
iPhone and Android phones.

38
debian/copyright vendored
View File

@ -1,38 +0,0 @@
This work was packaged for Debian by:
Stein Magnus Jodal <stein.magnus@jodal.no> on Sun, 31 Oct 2010 09:50:28 +0100
It was downloaded from:
http://pypi.python.org/packages/source/M/Mopidy/Mopidy-0.2.0.tar.gz
Upstream Author(s):
Stein Magnus Jodal <stein.magnus@jodal.no>
Copyright:
Copyright 2009-2010 Stein Magnus Jodal and contributors
License:
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied. See the License for the specific language governing
permissions and limitations under the License.
On Debian systems, the complete text of the Apache version 2.0 license
can be found in "/usr/share/common-licenses/Apache-2.0".
The Debian packaging is:
Copyright 2010 Stein Magnus Jodal <stein.magnus@jodal.no>
and is licensed under the Apache License, Version 2.0, see above.

2
debian/docs vendored
View File

@ -1,2 +0,0 @@
README.rst
docs/_build/html/

2
debian/menu vendored
View File

@ -1,2 +0,0 @@
?package(mopidy):needs="text" section="Applications/Sound"\
title="Mopidy" command="/usr/bin/mopidy"

1
debian/pyversions vendored
View File

@ -1 +0,0 @@
2.6-

27
debian/rules vendored
View File

@ -1,27 +0,0 @@
#!/usr/bin/make -f
# -*- makefile -*-
# Sample debian/rules that uses debhelper.
# This file was originally written by Joey Hess and Craig Small.
# As a special exception, when this file is copied by dh-make into a
# dh-make output file, you may use that output file without restriction.
# This special exception was added by Craig Small in version 0.37 of dh-make.
# Uncomment this to turn on verbose mode.
#export DH_VERBOSE=1
%:
dh $@
override_dh_clean:
make -C docs/ clean
dh_clean
override_dh_installchangelogs:
dh_installchangelogs docs/changes.rst
override_dh_installdocs:
make -C docs/ clean html
dh_installdocs
.PHONY: override_dh_clean override_dh_installchangelogs \
override_dh_installdocs override_dh_installinit

View File

@ -1 +0,0 @@
3.0 (quilt)

2
debian/watch vendored
View File

@ -1,2 +0,0 @@
version=3
http://pypi.python.org/packages/source/M/Mopidy/Mopidy-(.*)\.tar\.gz

View File

@ -214,7 +214,7 @@ p.admonition-title:after {
pre {
padding: 10px;
background-color: #fafafa;
background-color: #eeeeee;
color: #222222;
line-height: 1.5em;
font-size: 1.1em;

View File

@ -37,5 +37,5 @@ Backend provider implementations
================================
* :mod:`mopidy.backends.dummy`
* :mod:`mopidy.backends.libspotify`
* :mod:`mopidy.backends.spotify`
* :mod:`mopidy.backends.local`

View File

@ -12,9 +12,30 @@ No description yet.
**Important changes**
- If you use the Spotify backend, you need to upgrade to libspotify 0.0.6 and
the latest pyspotify from the Mopidy developers. Follow the instructions at
:ref:`/installation/libspotify/`.
- Spotify backend:
- If you use the Spotify backend, you need to upgrade to libspotify 0.0.6 and
the latest pyspotify from the Mopidy developers. Follow the instructions at
:doc:`/installation/libspotify/`.
- Support high bitrate (320k) audio. See
:attr:`mopidy.settings.SPOTIFY_HIGH_BITRATE` for details.
- Rename :mod:`mopidy.backends.libspotify` to :mod:`mopidy.backends.spotify`.
If you have set :attr:`mopidy.settings.BACKENDS` explicitly, you may need
to update the setting's value.
- Catch and log error caused by playlist folder boundaries being threated as
normal playlists. More permanent fix requires support for checking playlist
types in pyspotify.
- Last.fm frontend:
- If you use the Last.fm frontend, you need to upgrade to pylast 0.5.
- Update to use Last.fm's new Scrobbling 2.0 API, as the old Submissions
Protocol 1.2.1 is deprecated. (Fixes: :issue:`33`)
**Changes**
@ -37,10 +58,12 @@ No description yet.
application menus.
- Create infrastructure for creating Debian packages of Mopidy.
- Spotify backend:
- MPD frontend:
- Support high bitrate (320k). See
:attr:`mopidy.settings.SPOTIFY_HIGH_BITRATE` for details.
- Support ``setvol 50`` without quotes around the argument. Fixes volume
control in Droid MPD.
- Support ``seek 1 120`` without quotes around the arguments. Fixes seek in
Droid MPD.
- Local backend:
@ -80,6 +103,19 @@ No description yet.
:class:`mopidy.outputs.base.BaseOutput`.
0.2.1 (2011-01-07)
==================
This is a maintenance release without any new features.
**Bugfixes**
- Fix crash in :mod:`mopidy.frontends.lastfm` which occurred at playback if
either :mod:`pylast` was not installed or the Last.fm scrobbling was not
correctly configured. The scrobbling thread now shuts properly down at
failure.
0.2.0 (2010-10-24)
==================
@ -426,7 +462,7 @@ Mopidy is working and usable. 0.1.0a0 is an alpha release, which basicly means
we will still change APIs, add features, etc. before the final 0.1.0 release.
But the software is usable as is, so we release it. Please give it a try and
give us feedback, either at our IRC channel or through the `issue tracker
<http://github.com/jodal/mopidy/issues>`_. Thanks!
<http://github.com/mopidy/mopidy/issues>`_. Thanks!
**Changes**

View File

@ -202,4 +202,4 @@ latex_documents = [
needs_sphinx = '1.0'
extlinks = {'issue': ('http://github.com/jodal/mopidy/issues#issue/%s', 'GH-')}
extlinks = {'issue': ('http://github.com/mopidy/mopidy/issues#issue/%s', 'GH-')}

View File

@ -137,7 +137,7 @@ Then, to generate docs::
.. note::
The documentation at http://www.mopidy.com/ is automatically updated when a
documentation update is pushed to ``jodal/mopidy`` at GitHub.
documentation update is pushed to ``mopidy/mopidy`` at GitHub.
Documentation generated from the ``master`` branch is published at
http://www.mopidy.com/docs/master/, and will always be valid for the latest

View File

@ -27,7 +27,7 @@ Possible targets for the next version
- Write-support for Spotify, i.e. playlist management.
- Virtual directories with e.g. starred tracks from Spotify.
- **[WIP: possibly v0.3]** Support for 320 kbps audio.
- **[DONE: v0.3]** Support for 320 kbps audio.
- Local backend:
@ -47,7 +47,7 @@ Stuff we want to do, but not right now, and maybe never
recipies for all our dependencies and Mopidy itself to make OS X
installation a breeze. See `Homebrew's issue #1612
<http://github.com/mxcl/homebrew/issues/issue/1612>`_.
- **[WIP]** Create `Debian packages
- **[DONE]** Create `Debian packages
<http://www.debian.org/doc/maint-guide/>`_ of all our dependencies and
Mopidy itself (hosted in our own Debian repo until we get stuff into the
various distros) to make Debian/Ubuntu installation a breeze.

View File

@ -5,23 +5,32 @@ GStreamer installation
To use the Mopidy, you first need to install GStreamer and its Python bindings.
Installing GStreamer on Linux
=============================
Installing GStreamer
====================
GStreamer is packaged for most popular Linux distributions. If you use
Debian/Ubuntu you can install GStreamer with Aptitude::
On Linux
--------
sudo aptitude install python-gst0.10 gstreamer0.10-plugins-good \
GStreamer is packaged for most popular Linux distributions. Search for
GStreamer in your package manager, and make sure to install the Python
bindings, and the "good" and "ugly" plugin sets.
If you use Debian/Ubuntu you can install GStreamer like this::
sudo apt-get install python-gst0.10 gstreamer0.10-plugins-good \
gstreamer0.10-plugins-ugly
If you install Mopidy from our APT archive, you don't need to install GStreamer
yourself. The Mopidy Debian package will handle it for you.
Installing GStreamer on OS X
============================
On OS X from Homebrew
---------------------
.. note::
We have created GStreamer formulas for Homebrew to make the GStreamer
installation easy for you, but our formulas has not been merged into
installation easy for you, but not all our formulas have been merged into
Homebrew's master branch yet. You should either fetch the formula files
from `Homebrew's issue #1612
<http://github.com/mxcl/homebrew/issues/issue/1612>`_ yourself, or fall
@ -31,6 +40,10 @@ To install GStreamer on OS X using Homebrew::
brew install gst-python gst-plugins-good gst-plugins-ugly
On OS X from MacPorts
---------------------
To install GStreamer on OS X using MacPorts::
sudo port install py26-gst-python gstreamer-plugins-good \
@ -46,3 +59,19 @@ you should see a long listing of installed plugins, ending in a summary line::
$ gst-inspect-0.10
... long list of installed plugins ...
Total count: 218 plugins (1 blacklist entry not shown), 1031 features
You should be able to produce a audible tone by running::
gst-launch-0.10 audiotestsrc ! autoaudiosink
If you cannot hear any sound when running this command, you won't hear any
sound from Mopidy either, as Mopidy uses GStreamer's ``autoaudiosink`` to play
audio. Thus, make this work before you continue installing Mopidy.
Using a custom audio sink
=========================
If you for some reason want to use some other GStreamer audio sink than
``autoaudiosink``, you can change :attr:`mopidy.settings.GSTREAMER_AUDIO_SINK`
in your ``settings.py`` file.

View File

@ -2,10 +2,9 @@
Installation
************
To get a basic version of Mopidy running, you need Python and the
:doc:`GStreamer library <gstreamer>`. To use Spotify with Mopidy, you also need
:doc:`libspotify and pyspotify <libspotify>`. Mopidy itself can either be
installed from the Python package index, PyPI, or from git.
There are several ways to install Mopidy. What way is best depends upon your
setup and whether you want to use stable releases or less stable development
versions.
Install dependencies
@ -17,97 +16,155 @@ Install dependencies
gstreamer
libspotify
Make sure you got the required dependencies installed.
If you install Mopidy from the APT archive, as described below, you can skip
the dependency installation part.
Otherwise, make sure you got the required dependencies installed.
- Python >= 2.6, < 3
- :doc:`GStreamer <gstreamer>` >= 0.10, with Python bindings
- Dependencies for at least one Mopidy mixer:
- :mod:`mopidy.mixers.alsa` (Linux only)
- GStreamer >= 0.10, with Python bindings. See :doc:`gstreamer`.
- pyalsaaudio >= 0.2 (Debian/Ubuntu package: python-alsaaudio)
- :mod:`mopidy.mixers.denon` (Linux, OS X, and Windows)
- pyserial (Debian/Ubuntu package: python-serial)
- *Default:* :mod:`mopidy.mixers.gstreamer_software` (Linux, OS X, and
Windows)
- No additional dependencies.
- :mod:`mopidy.mixers.nad` (Linux, OS X, and Windows)
- pyserial (Debian/Ubuntu package: python-serial)
- :mod:`mopidy.mixers.osa` (OS X only)
- No additional dependencies.
- Mixer dependencies: The default mixer does not require any additional
dependencies. If you use another mixer, see the mixer's docs for any
additional requirements.
- Dependencies for at least one Mopidy backend:
- *Default:* :mod:`mopidy.backends.libspotify` (Linux, OS X, and Windows)
- The default backend, :mod:`mopidy.backends.spotify`, requires libspotify
and pyspotify. See :doc:`libspotify`.
- :doc:`libspotify and pyspotify <libspotify>`
- :mod:`mopidy.backends.local` (Linux, OS X, and Windows)
- No additional dependencies.
- The local backend, :mod:`mopidy.backends.local`, requires no additional
dependencies.
- Optional dependencies:
- :mod:`mopidy.frontends.lastfm`
- pylast >= 4.3.0
- To use the Last.FM scrobbler, see :mod:`mopidy.frontends.lastfm` for
additional requirements.
Install latest release
======================
To install the currently latest release of Mopidy using ``pip``::
sudo aptitude install python-setuptools python-pip # On Ubuntu/Debian
sudo brew install pip # On OS X
sudo pip install mopidy
To later upgrade to the latest release::
sudo pip install -U mopidy
If you for some reason can't use ``pip``, try ``easy_install``.
Next, you need to set a couple of :doc:`settings </settings>`, and then you're
ready to :doc:`run Mopidy </running>`.
Install development snapshot
============================
If you want to follow Mopidy development closer, you may install a snapshot of
Mopidy's ``develop`` branch::
sudo aptitude install python-setuptools python-pip # On Ubuntu/Debian
sudo brew install pip # On OS X
sudo pip install mopidy==dev
Next, you need to set a couple of :doc:`settings </settings>`, and then you're
ready to :doc:`run Mopidy </running>`.
Run from source code checkout
Install latest stable release
=============================
If you may want to contribute to Mopidy, and want access to other branches as
well, you can checkout the Mopidy source from Git and run it directly from the
ckeckout::
sudo aptitude install git-core # On Ubuntu/Debian
sudo brew install git # On OS X
git clone git://github.com/jodal/mopidy.git
cd mopidy/
python mopidy # Yes, 'mopidy' is a dir
From APT archive
----------------
To later update to the very latest version::
If you run a Debian based Linux distribution, like Ubuntu, the easiest way to
install Mopidy is from the Mopidy APT archive. When installing from the APT
archive, you will automatically get updates to Mopidy in the same way as you
get updates to the rest of your distribution.
#. Add the archive's GPG key::
wget -q -O - http://apt.mopidy.com/mopidy.gpg | sudo apt-key add -
#. Add the following to ``/etc/apt/sources.list``, or if you have the directory
``/etc/apt/sources.list.d/``, add it to a file called ``mopidy.list`` in
that directory::
# Mopidy APT archive
deb http://apt.mopidy.com/ stable main contrib non-free
deb-src http://apt.mopidy.com/ stable main contrib non-free
#. Install Mopidy and all dependencies::
sudo apt-get update
sudo apt-get install mopidy
#. Next, you need to set a couple of :doc:`settings </settings>`, and then
you're ready to :doc:`run Mopidy </running>`.
When a new release is out, and you can't wait for you system to figure it out
for itself, run the following to force an upgrade::
sudo apt-get update
sudo apt-get dist-upgrade
From PyPI using Pip
-------------------
If you are on OS X or on Linux, but can't install from the APT archive, you can
install Mopidy from PyPI using Pip.
#. When you install using Pip, you first need to ensure that all of Mopidy's
dependencies have been installed. See the section on dependencies above.
#. Then, you need to install Pip::
sudo aptitude install python-setuptools python-pip # On Ubuntu/Debian
sudo brew install pip # On OS X
#. To install the currently latest stable release of Mopidy::
sudo pip install -U Mopidy
To upgrade Mopidy to future releases, just rerun this command.
#. Next, you need to set a couple of :doc:`settings </settings>`, and then
you're ready to :doc:`run Mopidy </running>`.
If you for some reason can't use Pip, try ``easy_install`` instead.
Install development version
===========================
If you want to follow the development of Mopidy closer, you may install a
development version of Mopidy. These are not as stable as the releases, but
you'll get access to new features earlier and may help us by reporting issues.
From snapshot using Pip
-----------------------
If you want to follow Mopidy development closer, you may install a snapshot of
Mopidy's ``develop`` branch.
#. When you install using Pip, you first need to ensure that all of Mopidy's
dependencies have been installed. See the section on dependencies above.
#. Then, you need to install Pip::
sudo aptitude install python-setuptools python-pip # On Ubuntu/Debian
sudo brew install pip # On OS X
#. To install the latest snapshot of Mopidy, run::
sudo pip install mopidy==dev
To upgrade Mopidy to future releases, just rerun this command.
#. Next, you need to set a couple of :doc:`settings </settings>`, and then
you're ready to :doc:`run Mopidy </running>`.
From Git
--------
If you want to contribute to Mopidy, you should install Mopidy using Git.
#. When you install from Git, you first need to ensure that all of Mopidy's
dependencies have been installed. See the section on dependencies above.
#. Then install Git, if haven't already::
sudo aptitude install git-core # On Ubuntu/Debian
sudo brew install git # On OS X
#. Clone the official Mopidy repository, or your own fork of it::
git clone git://github.com/mopidy/mopidy.git
#. Next, you need to set a couple of :doc:`settings </settings>`.
#. 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
#. Later, to get the latest changes to Mopidy::
cd mopidy/
git pull
@ -115,3 +172,26 @@ To later update to the very latest version::
For an introduction to ``git``, please visit `git-scm.com
<http://git-scm.com/>`_. Also, please read our :doc:`developer documentation
</development/index>`.
From AUR on ArchLinux
---------------------
If you are running ArchLinux, you can install a development snapshot of Mopidy
using the package found at http://aur.archlinux.org/packages.php?ID=44026.
#. First, you should consider installing any optional dependencies not included
by the AUR package, like required for e.g. Last.fm scrobbling.
#. To install Mopidy with GStreamer, libspotify and pyspotify, you can use
``packer``, ``yaourt``, or do it by hand like this::
wget http://aur.archlinux.org/packages/mopidy-git/mopidy-git.tar.gz
tar xf mopidy-git.tar.gz
cd mopidy-git/
makepkg -si
To upgrade Mopidy to future releases, just rerun ``makepkg``.
#. Next, you need to set a couple of :doc:`settings </settings>`, and then
you're ready to :doc:`run Mopidy </running>`.

View File

@ -7,9 +7,9 @@ Mopidy uses `libspotify
the Spotify music service. To use :mod:`mopidy.backends.libspotify` you must
install libspotify and `pyspotify <http://github.com/mopidy/pyspotify>`_.
.. warning::
.. note::
This backend requires a `Spotify premium account
This backend requires a paid `Spotify premium account
<http://www.spotify.com/no/get-spotify/premium/>`_.
.. note::
@ -19,8 +19,25 @@ install libspotify and `pyspotify <http://github.com/mopidy/pyspotify>`_.
Spotify Group.
Installing libspotify on Linux
==============================
Installing libspotify
=====================
On Linux from APT archive
-------------------------
If you run a Debian based Linux distribution, like Ubuntu, see
http://apt.mopidy.com/ for how to the Mopidy APT archive as a software source
on your installation. Then, simply run::
sudo apt-get install libspotify6
When libspotify has been installed, continue with
:ref:`pyspotify_installation`.
On Linux from source
--------------------
Download and install libspotify 0.0.6 for your OS and CPU architecture from
https://developer.spotify.com/en/libspotify/.
@ -37,8 +54,8 @@ When libspotify has been installed, continue with
:ref:`pyspotify_installation`.
Installing libspotify on OS X
=============================
On OS X from Homebrew
---------------------
In OS X you need to have `XCode <http://developer.apple.com/tools/xcode/>`_ and
`Homebrew <http://mxcl.github.com/homebrew/>`_ installed. Then, to install
@ -55,34 +72,45 @@ When libspotify has been installed, continue with
:ref:`pyspotify_installation`.
Install libspotify on Windows
=============================
**TODO** Test and document installation on Windows.
.. _pyspotify_installation:
Installing pyspotify
====================
Install pyspotify's dependencies. At Debian/Ubuntu systems::
When you've installed libspotify, it's time for making it available from Python
by installing pyspotify.
sudo aptitude install python-dev
In OS X no additional dependencies are needed.
On Linux from APT archive
-------------------------
Check out the pyspotify code, and install it::
Assuming that you've already set up http://apt.mopidy.com/ as a software
source, run::
sudo apt-get install python-spotify
If you haven't already installed libspotify, this command will install both
libspotify and pyspotify for you.
On Linux/OS X from source
-------------------------
On Linux, you need to get the Python development files installed. On
Debian/Ubuntu systems run::
sudo apt-get install python-dev
On OS X no additional dependencies are needed.
Get the pyspotify code, and install it::
wget --no-check-certificate -O pyspotify.tar.gz https://github.com/mopidy/pyspotify/tarball/mopidy
tar zxfv pyspotify.tar.gz
cd pyspotify/pyspotify/
sudo rm -rf build/ # If you are upgrading pyspotify
cd pyspotify/
sudo python setup.py install
.. note::
The ``sudo rm -rf build/`` step is needed if you are upgrading pyspotify.
Simply running ``python setup.py clean`` will *not* clean out the C parts
of the ``build/`` directory, and you will thus not get any changes to the C
code included in your installation.
It is important that you install pyspotify from the ``mopidy`` branch of the
``mopidy/pyspotify`` repository, as the upstream repository at
``winjer/pyspotify`` is not updated with changes needed to support e.g.
libspotify 0.0.6 and high bitrate audio.

View File

@ -1,7 +0,0 @@
*******************************************************
:mod:`mopidy.backends.libspotify` -- Libspotify backend
*******************************************************
.. automodule:: mopidy.backends.libspotify
:synopsis: Spotify backend using the libspotify library
:members:

View File

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

View File

@ -20,7 +20,7 @@ class LocalBackend(Backend):
"""
A backend for playing music from a local music archive.
**Issues:** http://github.com/jodal/mopidy/issues/labels/backend-local
**Issues:** http://github.com/mopidy/mopidy/issues/labels/backend-local
**Settings:**

View File

@ -4,53 +4,55 @@ from mopidy import settings
from mopidy.backends.base import (Backend, CurrentPlaylistController,
LibraryController, PlaybackController, StoredPlaylistsController)
logger = logging.getLogger('mopidy.backends.libspotify')
logger = logging.getLogger('mopidy.backends.spotify')
ENCODING = 'utf-8'
class LibspotifyBackend(Backend):
class SpotifyBackend(Backend):
"""
A `Spotify <http://www.spotify.com/>`_ backend which uses the official
`libspotify <http://developer.spotify.com/en/libspotify/overview/>`_
library and the `pyspotify <http://github.com/winjer/pyspotify/>`_ Python
bindings for libspotify.
**Issues:** http://github.com/jodal/mopidy/issues/labels/backend-libspotify
**Settings:**
- :attr:`mopidy.settings.SPOTIFY_CACHE_PATH`
- :attr:`mopidy.settings.SPOTIFY_USERNAME`
- :attr:`mopidy.settings.SPOTIFY_PASSWORD`
A backend for playing music from the `Spotify <http://www.spotify.com/>`_
music streaming service. The backend uses the official `libspotify
<http://developer.spotify.com/en/libspotify/overview/>`_ library and the
`pyspotify <http://github.com/winjer/pyspotify/>`_ Python bindings for
libspotify.
.. 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:**
http://github.com/mopidy/mopidy/issues/labels/backend-spotify
**Settings:**
- :attr:`mopidy.settings.SPOTIFY_CACHE_PATH`
- :attr:`mopidy.settings.SPOTIFY_USERNAME`
- :attr:`mopidy.settings.SPOTIFY_PASSWORD`
"""
# Imports inside methods are to prevent loading of __init__.py to fail on
# missing spotify dependencies.
def __init__(self, *args, **kwargs):
from .library import LibspotifyLibraryProvider
from .playback import LibspotifyPlaybackProvider
from .stored_playlists import LibspotifyStoredPlaylistsProvider
from .library import SpotifyLibraryProvider
from .playback import SpotifyPlaybackProvider
from .stored_playlists import SpotifyStoredPlaylistsProvider
super(LibspotifyBackend, self).__init__(*args, **kwargs)
super(SpotifyBackend, self).__init__(*args, **kwargs)
self.current_playlist = CurrentPlaylistController(backend=self)
library_provider = LibspotifyLibraryProvider(backend=self)
library_provider = SpotifyLibraryProvider(backend=self)
self.library = LibraryController(backend=self,
provider=library_provider)
playback_provider = LibspotifyPlaybackProvider(backend=self)
playback_provider = SpotifyPlaybackProvider(backend=self)
self.playback = PlaybackController(backend=self,
provider=playback_provider)
stored_playlists_provider = LibspotifyStoredPlaylistsProvider(
stored_playlists_provider = SpotifyStoredPlaylistsProvider(
backend=self)
self.stored_playlists = StoredPlaylistsController(backend=self,
provider=stored_playlists_provider)
@ -60,11 +62,11 @@ class LibspotifyBackend(Backend):
self.spotify = self._connect()
def _connect(self):
from .session_manager import LibspotifySessionManager
from .session_manager import SpotifySessionManager
logger.info(u'Mopidy uses SPOTIFY(R) CORE')
logger.debug(u'Connecting to Spotify')
spotify = LibspotifySessionManager(
spotify = SpotifySessionManager(
settings.SPOTIFY_USERNAME, settings.SPOTIFY_PASSWORD,
core_queue=self.core_queue,
output=self.output)

View File

@ -4,13 +4,13 @@ import multiprocessing
from spotify import Link, SpotifyError
from mopidy.backends.base import BaseLibraryProvider
from mopidy.backends.libspotify import ENCODING
from mopidy.backends.libspotify.translator import LibspotifyTranslator
from mopidy.backends.spotify import ENCODING
from mopidy.backends.spotify.translator import SpotifyTranslator
from mopidy.models import Playlist
logger = logging.getLogger('mopidy.backends.libspotify.library')
logger = logging.getLogger('mopidy.backends.spotify.library')
class LibspotifyLibraryProvider(BaseLibraryProvider):
class SpotifyLibraryProvider(BaseLibraryProvider):
def find_exact(self, **query):
return self.search(**query)
@ -20,7 +20,7 @@ class LibspotifyLibraryProvider(BaseLibraryProvider):
# TODO Block until metadata_updated callback is called. Before that
# the track will be unloaded, unless it's already in the stored
# playlists.
return LibspotifyTranslator.to_mopidy_track(spotify_track)
return SpotifyTranslator.to_mopidy_track(spotify_track)
except SpotifyError as e:
logger.warning(u'Failed to lookup: %s', uri, e)
return None

View File

@ -4,9 +4,9 @@ from spotify import Link, SpotifyError
from mopidy.backends.base import BasePlaybackProvider
logger = logging.getLogger('mopidy.backends.libspotify.playback')
logger = logging.getLogger('mopidy.backends.spotify.playback')
class LibspotifyPlaybackProvider(BasePlaybackProvider):
class SpotifyPlaybackProvider(BasePlaybackProvider):
def pause(self):
return self.backend.output.set_state('PAUSED')

View File

@ -2,28 +2,29 @@ import logging
import os
import threading
from spotify.manager import SpotifySessionManager
import spotify.manager
from mopidy import get_version, settings
from mopidy.backends.libspotify.translator import LibspotifyTranslator
from mopidy.backends.spotify.translator import SpotifyTranslator
from mopidy.models import Playlist
from mopidy.utils.process import BaseThread
logger = logging.getLogger('mopidy.backends.libspotify.session_manager')
logger = logging.getLogger('mopidy.backends.spotify.session_manager')
# pylint: disable = R0901
# LibspotifySessionManager: Too many ancestors (9/7)
# SpotifySessionManager: Too many ancestors (9/7)
class LibspotifySessionManager(SpotifySessionManager, BaseThread):
class SpotifySessionManager(spotify.manager.SpotifySessionManager, BaseThread):
cache_location = settings.SPOTIFY_CACHE_PATH
settings_location = settings.SPOTIFY_CACHE_PATH
appkey_file = os.path.join(os.path.dirname(__file__), 'spotify_appkey.key')
user_agent = 'Mopidy %s' % get_version()
def __init__(self, username, password, core_queue, output):
SpotifySessionManager.__init__(self, username, password)
spotify.manager.SpotifySessionManager.__init__(
self, username, password)
BaseThread.__init__(self, core_queue)
self.name = 'LibspotifySMThread'
self.name = 'SpotifySMThread'
self.output = output
self.connected = threading.Event()
self.session = None
@ -36,10 +37,10 @@ class LibspotifySessionManager(SpotifySessionManager, BaseThread):
logger.info(u'Connected to Spotify')
self.session = session
if settings.SPOTIFY_HIGH_BITRATE:
logger.debug(u'Prefer high bitrate')
logger.debug(u'Preferring high bitrate from Spotify')
self.session.set_preferred_bitrate(1)
else:
logger.debug(u'Prefer normal bitrate')
logger.debug(u'Preferring normal bitrate from Spotify')
self.session.set_preferred_bitrate(0)
self.connected.set()
@ -49,15 +50,8 @@ class LibspotifySessionManager(SpotifySessionManager, BaseThread):
def metadata_updated(self, session):
"""Callback used by pyspotify"""
logger.debug(u'Metadata updated, refreshing stored playlists')
playlists = []
for spotify_playlist in session.playlist_container():
playlists.append(
LibspotifyTranslator.to_mopidy_playlist(spotify_playlist))
self.core_queue.put({
'command': 'set_stored_playlists',
'playlists': playlists,
})
logger.debug(u'Metadata updated')
self.refresh_stored_playlists()
def connection_error(self, session, error):
"""Callback used by pyspotify"""
@ -105,12 +99,26 @@ class LibspotifySessionManager(SpotifySessionManager, BaseThread):
logger.debug(u'End of data stream reached')
self.output.end_of_data_stream()
def refresh_stored_playlists(self):
"""Refresh the stored playlists in the backend with fresh meta data
from Spotify"""
playlists = []
for spotify_playlist in self.session.playlist_container():
playlists.append(
SpotifyTranslator.to_mopidy_playlist(spotify_playlist))
playlists = filter(None, playlists)
self.core_queue.put({
'command': 'set_stored_playlists',
'playlists': playlists,
})
logger.debug(u'Refreshed %d stored playlist(s)', len(playlists))
def search(self, query, connection):
"""Search method used by Mopidy backend"""
def callback(results, userdata=None):
# TODO Include results from results.albums(), etc. too
playlist = Playlist(tracks=[
LibspotifyTranslator.to_mopidy_track(t)
SpotifyTranslator.to_mopidy_track(t)
for t in results.tracks()])
connection.send(playlist)
self.connected.wait()

View File

@ -1,6 +1,6 @@
from mopidy.backends.base import BaseStoredPlaylistsProvider
class LibspotifyStoredPlaylistsProvider(BaseStoredPlaylistsProvider):
class SpotifyStoredPlaylistsProvider(BaseStoredPlaylistsProvider):
def create(self, name):
pass # TODO

View File

@ -1,11 +1,15 @@
import datetime as dt
import logging
from spotify import Link
from spotify import Link, SpotifyError
from mopidy import settings
from mopidy.backends.spotify import ENCODING
from mopidy.models import Artist, Album, Track, Playlist
from mopidy.backends.libspotify import ENCODING
class LibspotifyTranslator(object):
logger = logging.getLogger('mopidy.backends.spotify.translator')
class SpotifyTranslator(object):
@classmethod
def to_mopidy_artist(cls, spotify_artist):
if not spotify_artist.is_loaded():
@ -39,15 +43,22 @@ class LibspotifyTranslator(object):
track_no=spotify_track.index(),
date=date,
length=spotify_track.duration(),
bitrate=160,
bitrate=(settings.SPOTIFY_HIGH_BITRATE and 320 or 160),
)
@classmethod
def to_mopidy_playlist(cls, spotify_playlist):
if not spotify_playlist.is_loaded():
return Playlist(name=u'[loading...]')
return Playlist(
uri=str(Link.from_playlist(spotify_playlist)),
name=spotify_playlist.name().decode(ENCODING),
tracks=[cls.to_mopidy_track(t) for t in spotify_playlist],
)
# FIXME Replace this try-except with a check on the playlist type,
# which is currently not supported by pyspotify, to avoid handling
# playlist folder boundaries like normal playlists.
try:
return Playlist(
uri=str(Link.from_playlist(spotify_playlist)),
name=spotify_playlist.name().decode(ENCODING),
tracks=[cls.to_mopidy_track(t) for t in spotify_playlist],
)
except SpotifyError, e:
logger.warning(u'Failed translating Spotify playlist '
'(probably a playlist folder boundary): %s', e)

View File

@ -15,13 +15,8 @@ from mopidy.utils.process import BaseThread
logger = logging.getLogger('mopidy.frontends.lastfm')
CLIENT_ID = u'mop'
CLIENT_VERSION = get_version()
# pylast raises UnicodeEncodeError on conversion from unicode objects to
# ascii-encoded bytestrings, so we explicitly encode as utf-8 before passing
# strings to pylast.
ENCODING = u'utf-8'
API_KEY = '2236babefa8ebb3d93ea467560d00d04'
API_SECRET = '94d9a09c0cd5be955c4afaeaffcaefcd'
class LastfmFrontend(BaseFrontend):
"""
@ -34,7 +29,7 @@ class LastfmFrontend(BaseFrontend):
**Dependencies:**
- `pylast <http://code.google.com/p/pylast/>`_ >= 0.4.30
- `pylast <http://code.google.com/p/pylast/>`_ >= 0.5
**Settings:**
@ -54,7 +49,8 @@ class LastfmFrontend(BaseFrontend):
self.thread.destroy()
def process_message(self, message):
self.connection.send(message)
if self.thread.is_alive():
self.connection.send(message)
class LastfmFrontendThread(BaseThread):
@ -63,12 +59,11 @@ class LastfmFrontendThread(BaseThread):
self.name = u'LastfmFrontendThread'
self.connection = connection
self.lastfm = None
self.scrobbler = None
self.last_start_time = None
def run_inside_try(self):
self.setup()
while True:
while self.lastfm is not None:
self.connection.poll(None)
message = self.connection.recv()
self.process_message(message)
@ -77,10 +72,9 @@ class LastfmFrontendThread(BaseThread):
try:
username = settings.LASTFM_USERNAME
password_hash = pylast.md5(settings.LASTFM_PASSWORD)
self.lastfm = pylast.get_lastfm_network(
self.lastfm = pylast.LastFMNetwork(
api_key=API_KEY, api_secret=API_SECRET,
username=username, password_hash=password_hash)
self.scrobbler = self.lastfm.get_scrobbler(
CLIENT_ID, CLIENT_VERSION)
logger.info(u'Connected to Last.fm')
except SettingsError as e:
logger.info(u'Last.fm scrobbler not started')
@ -102,12 +96,13 @@ class LastfmFrontendThread(BaseThread):
self.last_start_time = int(time.time())
logger.debug(u'Now playing track: %s - %s', artists, track.name)
try:
self.scrobbler.report_now_playing(
artists.encode(ENCODING),
track.name.encode(ENCODING),
album=track.album.name.encode(ENCODING),
duration=duration,
track_number=track.track_no)
self.lastfm.update_now_playing(
artists,
track.name,
album=track.album.name,
duration=str(duration),
track_number=str(track.track_no),
mbid=(track.musicbrainz_id or ''))
except (pylast.ScrobblingError, socket.error) as e:
logger.warning(u'Last.fm now playing error: %s', e)
@ -126,14 +121,13 @@ class LastfmFrontendThread(BaseThread):
self.last_start_time = int(time.time()) - duration
logger.debug(u'Scrobbling track: %s - %s', artists, track.name)
try:
self.scrobbler.scrobble(
artists.encode(ENCODING),
track.name.encode(ENCODING),
time_started=self.last_start_time,
source=pylast.SCROBBLE_SOURCE_USER,
mode=pylast.SCROBBLE_MODE_PLAYED,
duration=duration,
album=track.album.name.encode(ENCODING),
track_number=track.track_no)
self.lastfm.scrobble(
artists,
track.name,
str(self.last_start_time),
album=track.album.name,
track_number=str(track.track_no),
duration=str(duration),
mbid=(track.musicbrainz_id or ''))
except (pylast.ScrobblingError, socket.error) as e:
logger.warning(u'Last.fm scrobbling error: %s', e)

View File

@ -293,6 +293,7 @@ def replay_gain_status(frontend):
"""
return u'off' # TODO
@handle_pattern(r'^seek (?P<songpos>\d+) (?P<seconds>\d+)$')
@handle_pattern(r'^seek "(?P<songpos>\d+)" "(?P<seconds>\d+)"$')
def seek(frontend, songpos, seconds):
"""
@ -302,6 +303,10 @@ def seek(frontend, songpos, seconds):
Seeks to the position ``TIME`` (in seconds) of entry ``SONGPOS`` in
the playlist.
*Droid MPD:*
- issues ``seek 1 120`` without quotes around the arguments.
"""
if frontend.backend.playback.current_playlist_position != songpos:
playpos(frontend, songpos)
@ -320,6 +325,7 @@ def seekid(frontend, cpid, seconds):
playid(frontend, cpid)
frontend.backend.playback.seek(int(seconds) * 1000)
@handle_pattern(r'^setvol (?P<volume>[-+]*\d+)$')
@handle_pattern(r'^setvol "(?P<volume>[-+]*\d+)"$')
def setvol(frontend, volume):
"""
@ -328,6 +334,10 @@ def setvol(frontend, volume):
``setvol {VOL}``
Sets volume to ``VOL``, the range of volume is 0-100.
*Droid MPD:*
- issues ``setvol 50`` without quotes around the argument.
"""
volume = int(volume)
if volume < 0:

View File

@ -11,6 +11,10 @@ class AlsaMixer(BaseMixer):
Mixer which uses the Advanced Linux Sound Architecture (ALSA) to control
volume.
**Dependencies:**
- pyalsaaudio >= 0.2 (python-alsaaudio on Debian/Ubuntu)
**Settings:**
- :attr:`mopidy.settings.MIXER_ALSA_CONTROL`

View File

@ -1,8 +1,6 @@
import logging
from threading import Lock
from serial import Serial
from mopidy import settings
from mopidy.mixers.base import BaseMixer
@ -33,8 +31,11 @@ class DenonMixer(BaseMixer):
"""
super(DenonMixer, self).__init__(*args, **kwargs)
device = kwargs.get('device', None)
self._device = device or Serial(port=settings.MIXER_EXT_PORT,
timeout=0.2)
if device:
self._device = device
else:
from serial import Serial
self._device = Serial(port=settings.MIXER_EXT_PORT, timeout=0.2)
self._levels = ['99'] + ["%(#)02d" % {'#': v} for v in range(0, 99)]
self._volume = 0
self._lock = Lock()

View File

@ -4,7 +4,18 @@ import time
from mopidy.mixers.base import BaseMixer
class OsaMixer(BaseMixer):
"""Mixer which uses ``osascript`` on OS X to control volume."""
"""
Mixer which uses ``osascript`` on OS X to control volume.
**Dependencies:**
- None
**Settings:**
- None
"""
CACHE_TTL = 30

View File

@ -12,12 +12,12 @@ Available settings and their default values.
#:
#: Default::
#:
#: BACKENDS = (u'mopidy.backends.libspotify.LibspotifyBackend',)
#: BACKENDS = (u'mopidy.backends.spotify.SpotifyBackend',)
#:
#: .. note::
#: Currently only the first backend in the list is used.
BACKENDS = (
u'mopidy.backends.libspotify.LibspotifyBackend',
u'mopidy.backends.spotify.SpotifyBackend',
)
#: The log format used for informational logging.
@ -169,24 +169,24 @@ MPD_SERVER_HOSTNAME = u'127.0.0.1'
#: Default: 6600
MPD_SERVER_PORT = 6600
#: Path to the libspotify cache.
#: Path to the Spotify cache.
#:
#: Used by :mod:`mopidy.backends.libspotify`.
SPOTIFY_CACHE_PATH = u'~/.mopidy/libspotify_cache'
#: Used by :mod:`mopidy.backends.spotify`.
SPOTIFY_CACHE_PATH = u'~/.mopidy/spotify_cache'
#: Your Spotify Premium username.
#:
#: Used by :mod:`mopidy.backends.libspotify`.
#: Used by :mod:`mopidy.backends.spotify`.
SPOTIFY_USERNAME = u''
#: Your Spotify Premium password.
#:
#: Used by :mod:`mopidy.backends.libspotify`.
#: Used by :mod:`mopidy.backends.spotify`.
SPOTIFY_PASSWORD = u''
#: Do you prefer high bitrate (320k)?
#:
#: Used by :mod:`mopidy.backends.libspotify`.
#: Used by :mod:`mopidy.backends.spotify`.
#
#: Default::
#:

View File

@ -1 +1 @@
pylast >= 0.4.30
pylast >= 0.5

View File

@ -77,7 +77,7 @@ setup(
author='Stein Magnus Jodal',
author_email='stein.magnus@jodal.no',
packages=packages,
package_data={'mopidy': ['backends/libspotify/spotify_appkey.key']},
package_data={'mopidy': ['backends/spotify/spotify_appkey.key']},
cmdclass=cmdclasses,
data_files=data_files,
scripts=['bin/mopidy', 'bin/mopidy-scan'],

View File

@ -103,6 +103,11 @@ class PlaybackOptionsHandlerTest(unittest.TestCase):
self.assert_(u'OK' in result)
self.assertEqual(10, self.b.mixer.volume)
def test_setvol_without_quotes(self):
result = self.h.handle_request(u'setvol 50')
self.assert_(u'OK' in result)
self.assertEqual(50, self.b.mixer.volume)
def test_single_off(self):
result = self.h.handle_request(u'single "0"')
self.assertFalse(self.b.playback.single)
@ -319,6 +324,13 @@ class PlaybackControlHandlerTest(unittest.TestCase):
result = self.h.handle_request(u'seek "1" "30"')
self.assertEqual(self.b.playback.current_track, seek_track)
def test_seek_without_quotes(self):
self.b.current_playlist.append([Track(length=40000)])
self.h.handle_request(u'seek 0')
result = self.h.handle_request(u'seek 0 30')
self.assert_(u'OK' in result)
self.assert_(self.b.playback.time_position >= 30000)
def test_seekid(self):
self.b.current_playlist.append([Track(length=40000)])
result = self.h.handle_request(u'seekid "0" "30"')

View File

@ -7,7 +7,7 @@ from mopidy.models import Track
class IssueGH17RegressionTest(unittest.TestCase):
"""
The issue: http://github.com/jodal/mopidy/issues#issue/17
The issue: http://github.com/mopidy/mopidy/issues#issue/17
How to reproduce:
@ -41,7 +41,7 @@ class IssueGH17RegressionTest(unittest.TestCase):
class IssueGH18RegressionTest(unittest.TestCase):
"""
The issue: http://github.com/jodal/mopidy/issues#issue/18
The issue: http://github.com/mopidy/mopidy/issues#issue/18
How to reproduce:
@ -78,7 +78,7 @@ class IssueGH18RegressionTest(unittest.TestCase):
class IssueGH22RegressionTest(unittest.TestCase):
"""
The issue: http://github.com/jodal/mopidy/issues/#issue/22
The issue: http://github.com/mopidy/mopidy/issues/#issue/22
How to reproduce: