commit
adb260af05
1
AUTHORS
1
AUTHORS
@ -76,3 +76,4 @@
|
||||
- Jelle van der Waa <jelle@vdwaa.nl>
|
||||
- Alex Malone <jalexmalone@gmail.com>
|
||||
- Daniel Hahler <git@thequod.de>
|
||||
- Bryan Bennett <bbenne10@gmail.com>
|
||||
|
||||
@ -55,20 +55,28 @@ Data model API
|
||||
:synopsis: Data model API
|
||||
|
||||
.. autoclass:: mopidy.models.Ref
|
||||
:members:
|
||||
|
||||
.. autoclass:: mopidy.models.Track
|
||||
:members:
|
||||
|
||||
.. autoclass:: mopidy.models.Album
|
||||
:members:
|
||||
|
||||
.. autoclass:: mopidy.models.Artist
|
||||
:members:
|
||||
|
||||
.. autoclass:: mopidy.models.Playlist
|
||||
:members:
|
||||
|
||||
.. autoclass:: mopidy.models.Image
|
||||
:members:
|
||||
|
||||
.. autoclass:: mopidy.models.TlTrack
|
||||
:members:
|
||||
|
||||
.. autoclass:: mopidy.models.SearchResult
|
||||
:members:
|
||||
|
||||
|
||||
Data model helpers
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
Authors
|
||||
*******
|
||||
|
||||
Mopidy is copyright 2009-2015 Stein Magnus Jodal and contributors. Mopidy is
|
||||
Mopidy is copyright 2009-2016 Stein Magnus Jodal and contributors. Mopidy is
|
||||
licensed under the `Apache License, Version 2.0
|
||||
<http://www.apache.org/licenses/LICENSE-2.0>`_.
|
||||
|
||||
|
||||
@ -141,11 +141,28 @@ Gapless
|
||||
cases. (Fixes: :issue:`1305` PR: :issue:`1346`)
|
||||
|
||||
|
||||
v1.1.2 (UNRELEASED)
|
||||
v1.1.2 (2016-01-18)
|
||||
===================
|
||||
|
||||
Bug fix release.
|
||||
|
||||
- Main: Catch errors when loading the :confval:`logging/config_file` file.
|
||||
(Fixes: :issue:`1320`)
|
||||
|
||||
- Core: If changing to another track while the player is paused, the new track
|
||||
would not be added to the history or marked as currently playing. (Fixes:
|
||||
:issue:`1352`, PR: :issue:`1356`)
|
||||
|
||||
- Core: Skips over unplayable tracks if the user attempts to change tracks
|
||||
while paused, like we already did if in playing state. (Fixes :issue:`1378`,
|
||||
PR: :issue:`1379`)
|
||||
|
||||
- Core: Make :meth:`~mopidy.core.LibraryController.lookup` ignore tracks with
|
||||
empty URIs. (Partly fixes: :issue:`1340`, PR: :issue:`1381`)
|
||||
|
||||
- Core: Fix crash if backends emits events with wrong names or arguments.
|
||||
(Fixes: :issue:`1383`)
|
||||
|
||||
- Stream: If an URI is considered playable, don't consider it as a candidate
|
||||
for playlist parsing. Just looking at MIME type prefixes isn't enough, as for
|
||||
example Ogg Vorbis has the MIME type ``application/ogg``. (Fixes:
|
||||
@ -154,6 +171,18 @@ Bug fix release.
|
||||
- Local: If the scan or clear commands are used on a library that does not
|
||||
exist, exit with an error. (Fixes: :issue:`1298`)
|
||||
|
||||
- MPD: Notify idling clients when a seek is performed. (Fixes: :issue:`1331`)
|
||||
|
||||
- MPD: Don't return tracks with empty URIs. (Partly fixes: :issue:`1340`, PR:
|
||||
:issue:`1343`)
|
||||
|
||||
- MPD: Add ``volume`` command that was reintroduced, though still as a
|
||||
deprecated command, in MPD 0.18 and is in use by some clients like mpc.
|
||||
(Fixes: :issue:`1393`, PR: :issue:`1397`)
|
||||
|
||||
- Proxy: Handle case where :confval:`proxy/port` is either missing from config
|
||||
or set to an empty string. (PR: :issue:`1371`)
|
||||
|
||||
|
||||
v1.1.1 (2015-09-14)
|
||||
===================
|
||||
|
||||
@ -167,5 +167,5 @@ projects are a real match made in heaven."
|
||||
Partify
|
||||
-------
|
||||
|
||||
`Partify <https://github.com/fhats/partify>`_ is a web based MPD client focussing on
|
||||
making music playing collaborative and social.
|
||||
`Partify <https://github.com/fhats/partify>`_ is a web based MPD client
|
||||
focussing on making music playing collaborative and social.
|
||||
|
||||
@ -93,14 +93,14 @@ source_suffix = '.rst'
|
||||
master_doc = 'index'
|
||||
|
||||
project = 'Mopidy'
|
||||
copyright = '2009-2015, Stein Magnus Jodal and contributors'
|
||||
copyright = '2009-2016, Stein Magnus Jodal and contributors'
|
||||
|
||||
from mopidy.internal.versioning import get_version
|
||||
release = get_version()
|
||||
version = '.'.join(release.split('.')[:2])
|
||||
|
||||
# To make the build reproducible, avoid using today's date in the manpages
|
||||
today = '2015'
|
||||
today = '2016'
|
||||
|
||||
exclude_trees = ['_build']
|
||||
|
||||
|
||||
129
docs/debian.rst
129
docs/debian.rst
@ -1,129 +0,0 @@
|
||||
.. _debian:
|
||||
|
||||
***************
|
||||
Debian packages
|
||||
***************
|
||||
|
||||
The Mopidy Debian package, ``mopidy``, is available from `apt.mopidy.com
|
||||
<http://apt.mopidy.com/>`__ as well as from Debian, Ubuntu and other
|
||||
Debian-based Linux distributions.
|
||||
|
||||
Some extensions are also available from all of these sources, while others,
|
||||
like Mopidy-Spotify and its dependencies, are only available from
|
||||
apt.mopidy.com. This may either be temporary until the package is uploaded to
|
||||
Debian and with time propagates to the other distributions. It may also be more
|
||||
long term, like in the Mopidy-Spotify case where there is uncertainities around
|
||||
licensing and distribution of non-free packages.
|
||||
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
See :ref:`debian-install`.
|
||||
|
||||
|
||||
Running as a system service
|
||||
===========================
|
||||
|
||||
The Debian package comes with an init script. It starts Mopidy as a system
|
||||
service running as the ``mopidy`` user, which is created by the package.
|
||||
|
||||
The Debian package version 0.18.3-1 and older starts Mopidy as a system
|
||||
service by default. Version 0.18.3-2 and newer asks if you want to run Mopidy
|
||||
as a system service, defaulting to not doing so.
|
||||
|
||||
If you're running 0.18.3-2 or newer, and you've changed your mind about whether
|
||||
or not to run Mopidy as a system service, just run the following command to
|
||||
reconfigure the package::
|
||||
|
||||
sudo dpkg-reconfigure mopidy
|
||||
|
||||
If you're running 0.18.3-1 or older, and don't want to use the init script to
|
||||
run Mopidy as a system service, but instead just run Mopidy manually using your
|
||||
own user, you need to disable the init script and stop Mopidy by running::
|
||||
|
||||
sudo update-rc.d mopidy disable
|
||||
sudo service mopidy stop
|
||||
|
||||
This way of disabling the system service is compatible with the improved
|
||||
0.18.3-2 or newer version of the Debian package, so if you later upgrade to a
|
||||
newer version, you can change your mind using the ``dpkg-reconfigure`` command
|
||||
above.
|
||||
|
||||
|
||||
Differences when running as a system service
|
||||
============================================
|
||||
|
||||
If you want to run Mopidy using the init script, there's a few differences
|
||||
from a regular Mopidy setup you'll want to know about.
|
||||
|
||||
- All configuration is in :file:`/etc/mopidy`, not in your user's home
|
||||
directory. The main configuration file is :file:`/etc/mopidy/mopidy.conf`.
|
||||
You can do all your changes in this file.
|
||||
|
||||
- Mopidy extensions installed from Debian packages will sometimes install
|
||||
additional configuration files in :file:`/usr/share/mopidy/conf.d/`. These
|
||||
files just provide different defaults for the extension when run as a system
|
||||
service. You can override anything from :file:`/usr/share/mopidy/conf.d/` in
|
||||
the :file:`/etc/mopidy/mopidy.conf` configuration file.
|
||||
|
||||
Previously, the extension's default config was installed in
|
||||
:file:`/etc/mopidy/extensions.d/`. This was removed with the Debian
|
||||
package mopidy 0.19.4-3. If you have modified any files in
|
||||
:file:`/etc/mopidy/extensions.d/`, you should redo your modifications in
|
||||
:file:`/etc/mopidy/mopidy.conf` and delete the
|
||||
:file:`/etc/mopidy/extensions.d/` directory.
|
||||
|
||||
- The init script runs Mopidy as the ``mopidy`` user. The ``mopidy`` user will
|
||||
need read access to any local music you want Mopidy to play.
|
||||
|
||||
- To run Mopidy subcommands with the same user and config files as the init
|
||||
script uses, you can use ``sudo mopidyctl <subcommand>``. In other words,
|
||||
where you'll usually run::
|
||||
|
||||
mopidy config
|
||||
|
||||
You should instead run the following to inspect the system service's
|
||||
configuration::
|
||||
|
||||
sudo mopidyctl config
|
||||
|
||||
The same applies to scanning your local music collection. Where you'll
|
||||
normally run::
|
||||
|
||||
mopidy local scan
|
||||
|
||||
You should instead run::
|
||||
|
||||
sudo mopidyctl local scan
|
||||
|
||||
Previously, you used ``sudo service mopidy run <subcommand>`` instead of
|
||||
``mopidyctl``. This was deprecated in Debian package version 0.19.4-3 in
|
||||
favor of ``mopidyctl``, which also work for systems using systemd instead of
|
||||
sysvinit and traditional init scripts.
|
||||
|
||||
- Mopidy is started, stopped, and restarted just like any other system
|
||||
service::
|
||||
|
||||
sudo service mopidy start
|
||||
sudo service mopidy stop
|
||||
sudo service mopidy restart
|
||||
|
||||
- You can check if Mopidy is currently running as a system service by running::
|
||||
|
||||
sudo service mopidy status
|
||||
|
||||
- Mopidy installed from a Debian package can use Mopidy extensions installed
|
||||
both from Debian packages and with pip. This has always been the case.
|
||||
|
||||
Mopidy installed with pip can use extensions installed with pip, but
|
||||
not extensions installed from a Debian package released before August 2015.
|
||||
This is because the Debian packages used to install extensions into
|
||||
:file:`/usr/share/mopidy` which is normally not on your ``PYTHONPATH``.
|
||||
Thus, your pip-installed Mopidy would not find the Debian package-installed
|
||||
extensions.
|
||||
|
||||
In August 2015, all Mopidy extension Debian packages was modified to install
|
||||
into :file:`/usr/lib/python2.7/dist-packages`, like any other Python Debian
|
||||
package. Thus, Mopidy installed with pip can now use extensions installed
|
||||
from Debian.
|
||||
BIN
docs/ext/spotmop.jpg
Normal file
BIN
docs/ext/spotmop.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 81 KiB |
@ -164,6 +164,22 @@ To install, run::
|
||||
pip install Mopidy-Simple-Webclient
|
||||
|
||||
|
||||
Mopidy-Spotmop
|
||||
==============
|
||||
|
||||
https://github.com/jaedb/spotmop
|
||||
|
||||
A client targeted at Spotify users. Made by James Barnsley.
|
||||
|
||||
.. image:: /ext/spotmop.jpg
|
||||
:width: 720
|
||||
:height: 455
|
||||
|
||||
To install, run::
|
||||
|
||||
pip install Mopidy-Spotmop
|
||||
|
||||
|
||||
Mopidy-WebSettings
|
||||
==================
|
||||
|
||||
|
||||
@ -81,8 +81,8 @@ announcements related to Mopidy and Mopidy extensions.
|
||||
installation/index
|
||||
config
|
||||
running
|
||||
service
|
||||
troubleshooting
|
||||
debian
|
||||
|
||||
|
||||
.. _ext:
|
||||
|
||||
@ -16,7 +16,8 @@ If you are running Arch Linux, you can install Mopidy using the
|
||||
pacman -Syu
|
||||
|
||||
#. Finally, you need to set a couple of :doc:`config values </config>`, and
|
||||
then you're ready to :doc:`run Mopidy </running>`.
|
||||
then you're ready to :doc:`run Mopidy </running>` or run Mopidy as a
|
||||
:ref:`service <service>`.
|
||||
|
||||
|
||||
Installing extensions
|
||||
|
||||
@ -48,12 +48,9 @@ and armhf (compatible with Raspberry Pi 1 and 2).
|
||||
sudo apt-get update
|
||||
sudo apt-get install mopidy
|
||||
|
||||
#. Before continuing, make sure you've read the :ref:`debian` section to learn
|
||||
about the differences between running Mopidy as a system service and
|
||||
manually as your own system user.
|
||||
|
||||
#. Finally, you need to set a couple of :doc:`config values </config>`, and then
|
||||
you're ready to :doc:`run Mopidy </running>`.
|
||||
you're ready to :doc:`run Mopidy </running>` or run Mopidy as a
|
||||
:ref:`service <service>`.
|
||||
|
||||
When a new release of Mopidy is out, and you can't wait for you system to
|
||||
figure it out for itself, run the following to upgrade right away::
|
||||
@ -87,44 +84,3 @@ about any other requirements needed for the extension to work properly.
|
||||
|
||||
For a full list of available Mopidy extensions, including those not
|
||||
installable from apt.mopidy.com, see :ref:`ext`.
|
||||
|
||||
|
||||
Missing extensions
|
||||
==================
|
||||
|
||||
If you've installed a Mopidy extension with pip, restarted Mopidy, and Mopidy
|
||||
doesn't find the extension, there's probably a simple explanation and solution.
|
||||
|
||||
Mopidy installed with APT can detect and use Mopidy extensions installed with
|
||||
both APT and pip. APT installs Mopidy as :file:`/usr/bin/mopidy`.
|
||||
|
||||
Mopidy installed with pip can only detect Mopidy extensions installed with pip.
|
||||
pip usually installs Mopidy as :file:`/usr/local/bin/mopidy`.
|
||||
|
||||
If you have Mopidy installed from both APT and pip, then the pip-installed
|
||||
Mopidy will probably shadow the APT-installed Mopidy because
|
||||
:file:`/usr/local/bin` usually has precedence over :file:`/usr/bin` in the
|
||||
``PATH`` environment variable. To check if this is the case on your system, you
|
||||
can use ``which`` to see what installation of Mopidy you use when you run
|
||||
``mopidy`` in your shell::
|
||||
|
||||
$ which mopidy
|
||||
/usr/local/bin/mopidy
|
||||
|
||||
If this is the case on your system, the recommended solution is to check that
|
||||
you have Mopidy installed from APT too::
|
||||
|
||||
$ /usr/bin/mopidy --version
|
||||
Mopidy 0.19.5
|
||||
|
||||
And then uninstall the pip-installed Mopidy::
|
||||
|
||||
sudo pip uninstall mopidy
|
||||
|
||||
Depending on what shell you use, the shell may still try to use
|
||||
:file:`/usr/local/bin/mopidy` even if it no longer exists. Check again with
|
||||
``which mopidy`` what your shell believes is the right ``mopidy`` executable to
|
||||
run. If the shell is still confused, you may need to restart it, or in the case
|
||||
of zsh, run ``rehash`` to update the shell.
|
||||
|
||||
For more details on why this works this way, see :ref:`debian`.
|
||||
|
||||
@ -86,6 +86,8 @@ For a full list of available Mopidy extensions, including those not installable
|
||||
from Homebrew, see :ref:`ext`.
|
||||
|
||||
|
||||
.. _osx-service:
|
||||
|
||||
Running Mopidy automatically on login
|
||||
=====================================
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 51 KiB |
@ -1,75 +1,68 @@
|
||||
.. _raspberrypi-installation:
|
||||
|
||||
*************************************
|
||||
Raspberry Pi: Mopidy on a credit card
|
||||
*************************************
|
||||
************
|
||||
Raspberry Pi
|
||||
************
|
||||
|
||||
Mopidy runs nicely on a `Raspberry Pi <https://www.raspberrypi.org/>`_. As of
|
||||
January 2013, Mopidy will run with Spotify support on both the armel
|
||||
(soft-float) and armhf (hard-float) architectures, which includes the Raspbian
|
||||
distribution.
|
||||
Mopidy runs on all versions of `Raspberry Pi <https://www.raspberrypi.org/>`_.
|
||||
However, note that Raspberry Pi 2 B's CPU is approximately six times as
|
||||
powerful as Raspberry Pi 1 and Raspberry Pi Zero, so Mopidy will be more joyful
|
||||
to use on a Raspberry Pi 2.
|
||||
|
||||
.. image:: raspberry-pi-by-jwrodgers.jpg
|
||||
.. image:: raspberrypi2.jpg
|
||||
:width: 640
|
||||
:height: 427
|
||||
:height: 363
|
||||
|
||||
|
||||
.. _raspi-wheezy:
|
||||
|
||||
How to for Raspbian "wheezy" and Debian "wheezy"
|
||||
================================================
|
||||
How to for Raspbian Jessie
|
||||
==========================
|
||||
|
||||
This guide applies for both:
|
||||
#. Download the latest Jessie or Jessie Lite disk image from
|
||||
http://www.raspberrypi.org/downloads/raspbian/.
|
||||
|
||||
- Raspbian "wheezy" for armhf (hard-float), and
|
||||
- Debian "wheezy" for armel (soft-float)
|
||||
If you're only using your Pi for Mopidy, go with Jessie Lite as you won't
|
||||
need the full graphical desktop included in the Jessie image.
|
||||
|
||||
If you don't know which one to select, go for the armhf variant, as it'll give
|
||||
you a lot better performance.
|
||||
#. Flash the Raspbian image you downloaded to your SD card.
|
||||
|
||||
#. Download the latest "wheezy" disk image from
|
||||
https://www.raspberrypi.org/downloads/. This was last tested with the images
|
||||
from 2013-05-25 for armhf and 2013-05-29 for armel.
|
||||
See the `Raspberry Pi installation docs
|
||||
<https://www.raspberrypi.org/documentation/installation/installing-images/README.md>`_
|
||||
for instructions.
|
||||
|
||||
#. Flash the OS image to your SD card. See
|
||||
http://elinux.org/RPi_Easy_SD_Card_Setup for help.
|
||||
#. If you connect a monitor and a keyboard, you'll see that the Pi boots right
|
||||
into the ``raspi-config`` tool.
|
||||
|
||||
#. If you have an SD card that's >2 GB, you don't have to resize the file
|
||||
systems on another computer. Just boot up your Raspberry Pi with the
|
||||
unaltered partions, and it will boot right into the ``raspi-config`` tool,
|
||||
which will let you grow the root file system to fill the SD card. This tool
|
||||
will also allow you do other useful stuff, like turning on the SSH server.
|
||||
If you boot with only a network cable connected, you'll have to find the IP
|
||||
address of the Pi yourself, e.g. by looking in the client list on your
|
||||
router/DHCP server. When you have found the Pi's IP address, you can SSH to
|
||||
the IP address and login with the user ``pi`` and password ``raspberry``.
|
||||
Once logged in, run ``sudo raspi-config`` to start the config tool as the
|
||||
``root`` user.
|
||||
|
||||
#. You can login to the default user using username ``pi`` and password
|
||||
``raspberry``. To become root, just enter ``sudo -i``.
|
||||
#. Use the ``raspi-config`` tool to setup the basics of your Pi. You might want
|
||||
to do one or more of the following:
|
||||
|
||||
#. To avoid a couple of potential problems with Mopidy, turn on IPv6 support:
|
||||
- Expand the file system to fill the SD card.
|
||||
- Change the password of the ``pi`` user.
|
||||
- Change the time zone.
|
||||
|
||||
- Load the IPv6 kernel module now::
|
||||
Under "Advanced Options":
|
||||
|
||||
sudo modprobe ipv6
|
||||
- Set a hostname.
|
||||
- Enable SSH if not already enabled.
|
||||
- If your will use HDMI for display and 3.5mm jack for audio, force the
|
||||
audio output to the 3.5mm jack. By default it will use HDMI for audio
|
||||
output if an HDMI cable is connected and the 3.5mm jack if not.
|
||||
|
||||
- Add ``ipv6`` to ``/etc/modules`` to ensure the IPv6 kernel module is
|
||||
loaded on boot::
|
||||
Once done, select "Finish" and restart your Pi.
|
||||
|
||||
echo ipv6 | sudo tee -a /etc/modules
|
||||
If you want to change any settings later, you can simply rerun ``sudo
|
||||
raspi-config``.
|
||||
|
||||
#. Since I have a HDMI cable connected, but want the sound on the analog sound
|
||||
connector, I have to run::
|
||||
|
||||
sudo amixer cset numid=3 1
|
||||
|
||||
to force it to use analog output. ``1`` means analog, ``0`` means auto, and
|
||||
is the default, while ``2`` means HDMI. You can test sound output
|
||||
independent of Mopidy by running::
|
||||
|
||||
aplay /usr/share/sounds/alsa/Front_Center.wav
|
||||
|
||||
If you hear a voice saying "Front Center", then your sound is working.
|
||||
|
||||
To make the change to analog output stick, you can add the ``amixer``
|
||||
command to e.g. ``/etc/rc.local``, which will be executed when the system is
|
||||
booting.
|
||||
#. Once you've rebooted and has logged in as the ``pi`` user, you can enter
|
||||
``sudo -i`` to become ``root``.
|
||||
|
||||
#. Install Mopidy and its dependencies as described in :ref:`debian-install`.
|
||||
|
||||
@ -79,114 +72,19 @@ you a lot better performance.
|
||||
starting at boot.
|
||||
|
||||
|
||||
Appendix A: Fixing audio quality issues
|
||||
=======================================
|
||||
Testing sound output
|
||||
====================
|
||||
|
||||
As of about April 2013 the following steps should resolve any audio
|
||||
issues for HDMI and analog without the use of an external USB sound
|
||||
card.
|
||||
You can test sound output independent of Mopidy by running::
|
||||
|
||||
#. Ensure your system is up to date. On Debian based systems run::
|
||||
aplay /usr/share/sounds/alsa/Front_Center.wav
|
||||
|
||||
sudo apt-get update
|
||||
sudo apt-get dist-upgrade
|
||||
If you hear a voice saying "Front Center", then your sound is working.
|
||||
|
||||
#. Ensure you have a new enough firmware. On Debian based systems
|
||||
`rpi-update <https://github.com/Hexxeh/rpi-update>`_
|
||||
can be used.
|
||||
If you want to change your audio output setting, simply rerun ``sudo
|
||||
raspi-config``. Alternatively, you can change the audio output setting
|
||||
directly by running:
|
||||
|
||||
#. Update either ``~/.asoundrc`` or ``/etc/asound.conf`` to the
|
||||
following::
|
||||
|
||||
pcm.!default {
|
||||
type hw
|
||||
card 0
|
||||
}
|
||||
ctl.!default {
|
||||
type hw
|
||||
card 0
|
||||
}
|
||||
|
||||
Note that if you have an ``~/.asoundrc`` it will overide any global
|
||||
settings from ``/etc/asound.conf``.
|
||||
|
||||
#. For Mopidy to output audio directly to ALSA, instead of Jack which
|
||||
GStreamer usually defaults to on Raspberry Pi, install the
|
||||
``gstreamer0.10-alsa`` package::
|
||||
|
||||
sudo apt-get install gstreamer0.10-alsa
|
||||
|
||||
Then update your ``~/.config/mopidy/mopidy.conf`` to contain::
|
||||
|
||||
[audio]
|
||||
output = alsasink
|
||||
|
||||
Following these steps you should be able to get crackle free sound on either
|
||||
HDMI or analog. Note that you might need to ensure that PulseAudio is no longer
|
||||
running to get this working nicely.
|
||||
|
||||
This recipe has been confirmed as working by a number of users on our issue
|
||||
tracker and IRC. As a reference, the following versions where used for testing
|
||||
this, however all newer and some older version are likely to work as we have
|
||||
not determined the exact revision that fixed this::
|
||||
|
||||
$ uname -a
|
||||
Linux raspberrypi 3.6.11+ #408 PREEMPT Wed Apr 10 20:33:39 BST 2013 armv6l GNU/Linux
|
||||
|
||||
$ /opt/vc/bin/vcgencmd version
|
||||
Apr 25 2013 01:07:36
|
||||
Copyright (c) 2012 Broadcom
|
||||
version 386589 (release)
|
||||
|
||||
The only remaining known issue is a slight gap in playback at track changes
|
||||
this is likely due to gapless playback not being implemented and is being
|
||||
worked on irrespective of Raspberry Pi related work.
|
||||
|
||||
|
||||
Appendix B: Raspbmc not booting
|
||||
===============================
|
||||
|
||||
Due to a dependency version problem where XBMC uses another version of libtag
|
||||
than what Debian originally ships with, you might have to make some minor
|
||||
changes for Raspbmc to start properly after installing Mopidy.
|
||||
|
||||
If you notice that XBMC is not starting but gets stuck in a loop,
|
||||
you need to make the following changes::
|
||||
|
||||
sudo ln -sf /home/pi/.xbmc-current/xbmc-bin/lib/xbmc/system/libtag.so.1 \
|
||||
/usr/lib/arm-linux-gnueabihf/libtag.so.1
|
||||
|
||||
However, this will not persist the changes. To persist the changes edit
|
||||
:file:`/etc/ld.so.conf.d/arm-linux-gnueabihf.conf` and add the following at the
|
||||
top::
|
||||
|
||||
/home/pi/.xbmc-current/xbmc-bin/lib/xbmc/system
|
||||
|
||||
It's very important to add it at the top of the file as this indicates the
|
||||
priority of the folder in which to look for shared libraries.
|
||||
|
||||
XBMC doesn't play nicely with the system wide installed version of libtag that
|
||||
got installed together with Mopidy, but rather vendors in its own version.
|
||||
|
||||
More info about this issue can be found in `this post
|
||||
<http://geeks.noeit.com/xbmc-library-dependency-error/>`_.
|
||||
|
||||
Please note that if you're running Xbian or another XBMC distribution these
|
||||
instructions might vary for your system.
|
||||
|
||||
|
||||
Appendix C: Installation on XBian
|
||||
=================================
|
||||
|
||||
Similar to the Raspbmc issue outlined in Appendix B, it's not possible to
|
||||
install Mopidy on XBian without first resolving a dependency problem between
|
||||
``gstreamer0.10-plugins-good`` and ``libtag1c2a``. More information can be
|
||||
found in `this issue
|
||||
<https://github.com/xbianonpi/xbian/issues/378#issuecomment-37723392>`_.
|
||||
|
||||
Run the following commands to remedy this and then install Mopidy as normal::
|
||||
|
||||
cd /tmp
|
||||
wget http://apt.xbian.org/pool/stable/rpi-wheezy/l/libtag1c2a/libtag1c2a_1.7.2-1_armhf.deb
|
||||
sudo dpkg -i libtag1c2a_1.7.2-1_armhf.deb
|
||||
rm libtag1c2a_1.7.2-1_armhf.deb
|
||||
- Auto (HDMI if connected, else 3.5mm jack): ``sudo amixer cset numid=3 0``
|
||||
- Use 3.5mm jack: ``sudo amixer cset numid=3 1``
|
||||
- Use HDMI: ``sudo amixer cset numid=3 2``
|
||||
|
||||
BIN
docs/installation/raspberrypi2.jpg
Normal file
BIN
docs/installation/raspberrypi2.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 95 KiB |
@ -39,17 +39,8 @@ using ``pkill``::
|
||||
pkill mopidy
|
||||
|
||||
|
||||
Init scripts
|
||||
============
|
||||
Running as a service
|
||||
====================
|
||||
|
||||
- The ``mopidy`` package at `apt.mopidy.com <http://apt.mopidy.com/>`__ comes
|
||||
with an `sysvinit init script
|
||||
<https://github.com/mopidy/mopidy/blob/debian/debian/mopidy.init>`_. For
|
||||
more details, see the :ref:`debian` section of the docs.
|
||||
|
||||
- The ``mopidy`` package in `Arch Linux
|
||||
<https://www.archlinux.org/packages/community/any/mopidy/>`__ comes with a systemd init
|
||||
script.
|
||||
|
||||
- Issue :issue:`266` contains a bunch of init scripts for Mopidy, including
|
||||
Upstart init scripts.
|
||||
Once you're done exploring Mopidy and want to run it as a proper service, check
|
||||
out :ref:`service`.
|
||||
|
||||
94
docs/service.rst
Normal file
94
docs/service.rst
Normal file
@ -0,0 +1,94 @@
|
||||
.. _service:
|
||||
|
||||
********************
|
||||
Running as a service
|
||||
********************
|
||||
|
||||
If you want to run Mopidy as a service using either an init script or a systemd
|
||||
service, there's a few differences from running Mopidy as your own user you'll
|
||||
want to know about. The following applies to Debian, Ubuntu, Raspbian, and
|
||||
Arch. Hopefully, other distributions packaging Mopidy will make sure this works
|
||||
the same way on their distribution.
|
||||
|
||||
|
||||
Configuration
|
||||
=============
|
||||
|
||||
All configuration is in :file:`/etc/mopidy/mopidy.conf`, not in your user's
|
||||
home directory.
|
||||
|
||||
|
||||
mopidy user
|
||||
===========
|
||||
|
||||
The Mopidy service runs as the ``mopidy`` user, which is automatically created
|
||||
when you install the Mopidy package. The ``mopidy`` user will need read access
|
||||
to any local music you want Mopidy to play.
|
||||
|
||||
|
||||
Subcommands
|
||||
===========
|
||||
|
||||
To run Mopidy subcommands with the same user and config files as the service
|
||||
uses, you can use ``sudo mopidyctl <subcommand>``. In other words, where you'll
|
||||
usually run::
|
||||
|
||||
mopidy config
|
||||
|
||||
You should instead run the following to inspect the service's configuration::
|
||||
|
||||
sudo mopidyctl config
|
||||
|
||||
The same applies to scanning your local music collection. Where you'll normally
|
||||
run::
|
||||
|
||||
mopidy local scan
|
||||
|
||||
You should instead run::
|
||||
|
||||
sudo mopidyctl local scan
|
||||
|
||||
|
||||
Service management with systemd
|
||||
===============================
|
||||
|
||||
On modern systems using systemd you can enable the Mopidy service by running::
|
||||
|
||||
sudo systemctl enable mopidy
|
||||
|
||||
This will make Mopidy start when the system boots.
|
||||
|
||||
Mopidy is started, stopped, and restarted just like any other systemd service::
|
||||
|
||||
sudo systemctl start mopidy
|
||||
sudo systemctl stop mopidy
|
||||
sudo systemctl restart mopidy
|
||||
|
||||
You can check if Mopidy is currently running as a service by running::
|
||||
|
||||
sudo systemctl status mopidy
|
||||
|
||||
|
||||
Service management on Debian
|
||||
============================
|
||||
|
||||
On Debian systems (both those using systemd and not) you can enable the Mopidy
|
||||
service by running::
|
||||
|
||||
sudo dpkg-reconfigure mopidy
|
||||
|
||||
Mopidy can be started, stopped, and restarted using the ``service`` command::
|
||||
|
||||
sudo service mopidy start
|
||||
sudo service mopidy stop
|
||||
sudo service mopidy restart
|
||||
|
||||
You can check if Mopidy is currently running as a service by running::
|
||||
|
||||
sudo service mopidy status
|
||||
|
||||
|
||||
Service on OS X
|
||||
===============
|
||||
|
||||
If you're installing Mopidy on OS X, see :ref:`osx-service`.
|
||||
@ -14,4 +14,4 @@ if not (2, 7) <= sys.version_info < (3,):
|
||||
warnings.filterwarnings('ignore', 'could not open display')
|
||||
|
||||
|
||||
__version__ = '1.1.1'
|
||||
__version__ = '1.1.2'
|
||||
|
||||
@ -236,7 +236,9 @@ class LibraryController(object):
|
||||
result = future.get()
|
||||
if result is not None:
|
||||
validation.check_instances(result, models.Track)
|
||||
results[u] = result
|
||||
# TODO Consider making Track.uri field mandatory, and
|
||||
# then remove this filtering of tracks without URIs.
|
||||
results[u] = [r for r in result if r.uri]
|
||||
|
||||
if uri:
|
||||
return results[uri]
|
||||
|
||||
@ -353,14 +353,14 @@ class SearchResult(ValidatedImmutableObject):
|
||||
:type albums: list of :class:`Album` elements
|
||||
"""
|
||||
|
||||
# The search result URI. Read-only.
|
||||
#: The search result URI. Read-only.
|
||||
uri = fields.URI()
|
||||
|
||||
# The tracks matching the search query. Read-only.
|
||||
#: The tracks matching the search query. Read-only.
|
||||
tracks = fields.Collection(type=Track, container=tuple)
|
||||
|
||||
# The artists matching the search query. Read-only.
|
||||
#: The artists matching the search query. Read-only.
|
||||
artists = fields.Collection(type=Artist, container=tuple)
|
||||
|
||||
# The albums matching the search query. Read-only.
|
||||
#: The albums matching the search query. Read-only.
|
||||
albums = fields.Collection(type=Album, container=tuple)
|
||||
|
||||
@ -426,3 +426,27 @@ def stop(context):
|
||||
Stops playing.
|
||||
"""
|
||||
context.core.playback.stop()
|
||||
|
||||
|
||||
@protocol.commands.add('volume', change=protocol.INT)
|
||||
def volume(context, change):
|
||||
"""
|
||||
*musicpd.org, playback section:*
|
||||
|
||||
``volume {CHANGE}``
|
||||
|
||||
Changes volume by amount ``CHANGE``.
|
||||
|
||||
Note: ``volume`` is deprecated, use ``setvol`` instead.
|
||||
"""
|
||||
if change < -100 or change > 100:
|
||||
raise exceptions.MpdArgError('Invalid volume value')
|
||||
|
||||
old_volume = context.core.mixer.get_volume().get()
|
||||
if old_volume is None:
|
||||
raise exceptions.MpdSystemError('problems setting volume')
|
||||
|
||||
new_volume = min(max(0, old_volume + change), 100)
|
||||
success = context.core.mixer.set_volume(new_volume).get()
|
||||
if not success:
|
||||
raise exceptions.MpdSystemError('problems setting volume')
|
||||
|
||||
@ -1,11 +1,15 @@
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
import datetime
|
||||
import logging
|
||||
import re
|
||||
|
||||
from mopidy.models import TlTrack
|
||||
from mopidy.mpd.protocol import tagtype_list
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# TODO: special handling of local:// uri scheme
|
||||
normalize_path_re = re.compile(r'[^/]+')
|
||||
|
||||
@ -34,8 +38,12 @@ def track_to_mpd_format(track, position=None, stream_title=None):
|
||||
else:
|
||||
(tlid, track) = (None, track)
|
||||
|
||||
if not track.uri:
|
||||
logger.warning('Ignoring track without uri')
|
||||
return []
|
||||
|
||||
result = [
|
||||
('file', track.uri or ''),
|
||||
('file', track.uri),
|
||||
('Time', track.length and (track.length // 1000) or 0),
|
||||
('Artist', concat_multi_values(track.artists, 'name')),
|
||||
('Album', track.album and track.album.name or ''),
|
||||
@ -164,7 +172,9 @@ def tracks_to_mpd_format(tracks, start=0, end=None):
|
||||
assert len(tracks) == len(positions)
|
||||
result = []
|
||||
for track, position in zip(tracks, positions):
|
||||
result.append(track_to_mpd_format(track, position))
|
||||
formatted_track = track_to_mpd_format(track, position)
|
||||
if formatted_track:
|
||||
result.append(formatted_track)
|
||||
return result
|
||||
|
||||
|
||||
|
||||
@ -153,8 +153,8 @@ class CoreLibraryTest(BaseCoreLibraryTest):
|
||||
self.core.library.lookup('dummy1:a', ['dummy2:a'])
|
||||
|
||||
def test_lookup_can_handle_uris(self):
|
||||
track1 = Track(name='abc')
|
||||
track2 = Track(name='def')
|
||||
track1 = Track(uri='dummy1:a', name='abc')
|
||||
track2 = Track(uri='dummy2:a', name='def')
|
||||
|
||||
self.library1.lookup().get.return_value = [track1]
|
||||
self.library2.lookup().get.return_value = [track2]
|
||||
@ -169,6 +169,15 @@ class CoreLibraryTest(BaseCoreLibraryTest):
|
||||
self.assertFalse(self.library1.lookup.called)
|
||||
self.assertFalse(self.library2.lookup.called)
|
||||
|
||||
def test_lookup_ignores_tracks_without_uri_set(self):
|
||||
track1 = Track(uri='dummy1:a', name='abc')
|
||||
track2 = Track()
|
||||
|
||||
self.library1.lookup().get.return_value = [track1, track2]
|
||||
|
||||
result = self.core.library.lookup(uris=['dummy1:a'])
|
||||
self.assertEqual(result, {'dummy1:a': [track1]})
|
||||
|
||||
def test_refresh_with_uri_selects_dummy1_backend(self):
|
||||
self.core.library.refresh('dummy1:a')
|
||||
|
||||
|
||||
@ -80,41 +80,6 @@ class PlaybackOptionsHandlerTest(protocol.BaseTestCase):
|
||||
self.assertTrue(self.core.tracklist.repeat.get())
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_setvol_below_min(self):
|
||||
self.send_request('setvol "-10"')
|
||||
self.assertEqual(0, self.core.mixer.get_volume().get())
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_setvol_min(self):
|
||||
self.send_request('setvol "0"')
|
||||
self.assertEqual(0, self.core.mixer.get_volume().get())
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_setvol_middle(self):
|
||||
self.send_request('setvol "50"')
|
||||
self.assertEqual(50, self.core.mixer.get_volume().get())
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_setvol_max(self):
|
||||
self.send_request('setvol "100"')
|
||||
self.assertEqual(100, self.core.mixer.get_volume().get())
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_setvol_above_max(self):
|
||||
self.send_request('setvol "110"')
|
||||
self.assertEqual(100, self.core.mixer.get_volume().get())
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_setvol_plus_is_ignored(self):
|
||||
self.send_request('setvol "+10"')
|
||||
self.assertEqual(10, self.core.mixer.get_volume().get())
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_setvol_without_quotes(self):
|
||||
self.send_request('setvol 50')
|
||||
self.assertEqual(50, self.core.mixer.get_volume().get())
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_single_off(self):
|
||||
self.send_request('single "0"')
|
||||
self.assertFalse(self.core.tracklist.single.get())
|
||||
@ -455,9 +420,83 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase):
|
||||
self.assertInResponse('OK')
|
||||
|
||||
|
||||
class PlaybackOptionsHandlerNoneMixerTest(protocol.BaseTestCase):
|
||||
class VolumeTest(protocol.BaseTestCase):
|
||||
|
||||
def test_setvol_below_min(self):
|
||||
self.send_request('setvol "-10"')
|
||||
self.assertEqual(0, self.core.mixer.get_volume().get())
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_setvol_min(self):
|
||||
self.send_request('setvol "0"')
|
||||
self.assertEqual(0, self.core.mixer.get_volume().get())
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_setvol_middle(self):
|
||||
self.send_request('setvol "50"')
|
||||
self.assertEqual(50, self.core.mixer.get_volume().get())
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_setvol_max(self):
|
||||
self.send_request('setvol "100"')
|
||||
self.assertEqual(100, self.core.mixer.get_volume().get())
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_setvol_above_max(self):
|
||||
self.send_request('setvol "110"')
|
||||
self.assertEqual(100, self.core.mixer.get_volume().get())
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_setvol_plus_is_ignored(self):
|
||||
self.send_request('setvol "+10"')
|
||||
self.assertEqual(10, self.core.mixer.get_volume().get())
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_setvol_without_quotes(self):
|
||||
self.send_request('setvol 50')
|
||||
self.assertEqual(50, self.core.mixer.get_volume().get())
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_volume_plus(self):
|
||||
self.core.mixer.set_volume(50)
|
||||
|
||||
self.send_request('volume +20')
|
||||
|
||||
self.assertEqual(70, self.core.mixer.get_volume().get())
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_volume_minus(self):
|
||||
self.core.mixer.set_volume(50)
|
||||
|
||||
self.send_request('volume -20')
|
||||
|
||||
self.assertEqual(30, self.core.mixer.get_volume().get())
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_volume_less_than_minus_100(self):
|
||||
self.core.mixer.set_volume(50)
|
||||
|
||||
self.send_request('volume -110')
|
||||
|
||||
self.assertEqual(50, self.core.mixer.get_volume().get())
|
||||
self.assertInResponse('ACK [2@0] {volume} Invalid volume value')
|
||||
|
||||
def test_volume_more_than_plus_100(self):
|
||||
self.core.mixer.set_volume(50)
|
||||
|
||||
self.send_request('volume +110')
|
||||
|
||||
self.assertEqual(50, self.core.mixer.get_volume().get())
|
||||
self.assertInResponse('ACK [2@0] {volume} Invalid volume value')
|
||||
|
||||
|
||||
class VolumeWithNoMixerTest(protocol.BaseTestCase):
|
||||
enable_mixer = False
|
||||
|
||||
def test_setvol_max_error(self):
|
||||
def test_setvol_without_mixer_fails(self):
|
||||
self.send_request('setvol "100"')
|
||||
self.assertInResponse('ACK [52@0] {setvol} problems setting volume')
|
||||
|
||||
def test_volume_without_mixer_failes(self):
|
||||
self.send_request('volume +100')
|
||||
self.assertInResponse('ACK [52@0] {volume} problems setting volume')
|
||||
|
||||
@ -56,7 +56,7 @@ class TrackMpdFormatTest(unittest.TestCase):
|
||||
|
||||
def test_track_to_mpd_format_with_position_and_tlid(self):
|
||||
result = translator.track_to_mpd_format(
|
||||
TlTrack(2, Track()), position=1)
|
||||
TlTrack(2, Track(uri='a uri')), position=1)
|
||||
self.assertIn(('Pos', 1), result)
|
||||
self.assertIn(('Id', 2), result)
|
||||
|
||||
@ -153,13 +153,17 @@ class PlaylistMpdFormatTest(unittest.TestCase):
|
||||
|
||||
def test_mpd_format(self):
|
||||
playlist = Playlist(tracks=[
|
||||
Track(track_no=1), Track(track_no=2), Track(track_no=3)])
|
||||
Track(uri='foo', track_no=1),
|
||||
Track(uri='bar', track_no=2),
|
||||
Track(uri='baz', track_no=3)])
|
||||
result = translator.playlist_to_mpd_format(playlist)
|
||||
self.assertEqual(len(result), 3)
|
||||
|
||||
def test_mpd_format_with_range(self):
|
||||
playlist = Playlist(tracks=[
|
||||
Track(track_no=1), Track(track_no=2), Track(track_no=3)])
|
||||
Track(uri='foo', track_no=1),
|
||||
Track(uri='bar', track_no=2),
|
||||
Track(uri='baz', track_no=3)])
|
||||
result = translator.playlist_to_mpd_format(playlist, 1, 2)
|
||||
self.assertEqual(len(result), 1)
|
||||
self.assertEqual(dict(result[0])['Track'], 2)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user