Release v0.16.1

This commit is contained in:
Stein Magnus Jodal 2013-11-02 23:44:28 +01:00
commit 578eed99ce
24 changed files with 669 additions and 396 deletions

View File

@ -8,5 +8,5 @@ John Bäckstrand <sopues@gmail.com> <sandos@XBMCLive.(none)>
Alli Witheford <alzeih@gmail.com>
Alexandre Petitjean <alpetitjean@gmail.com>
Alexandre Petitjean <alpetitjean@gmail.com> <alexandre.petitjean@lne.fr>
Javier Domingo Cansino <javier.domingo@fon.com> <javierdo1@gmail.com>
Javier Domingo Cansino <javierdo1@gmail.com> <javier.domingo@fon.com>
Lasse Bigum <lasse@bigum.org> <l.bigum@samsung.com>

View File

@ -4,7 +4,7 @@ install:
- "wget -O - http://apt.mopidy.com/mopidy.gpg | sudo apt-key add -"
- "sudo wget -O /etc/apt/sources.list.d/mopidy.list http://apt.mopidy.com/mopidy.list"
- "sudo apt-get update || true"
- "sudo apt-get install $(apt-cache depends mopidy | awk '$2 !~ /mopidy/ {print $2}')"
- "sudo apt-get install $(apt-cache depends mopidy | awk '$2 !~ /mopidy|python:any/ {print $2}')"
- "pip install coveralls flake8"
before_script:

View File

@ -24,6 +24,7 @@
- Alli Witheford <alzeih@gmail.com>
- Alexandre Petitjean <alpetitjean@gmail.com>
- Terje Larsen <terlar@gmail.com>
- Javier Domingo Cansino <javier.domingo@fon.com>
- Javier Domingo Cansino <javierdo1@gmail.com>
- Pavol Babincak <scroolik@gmail.com>
- Javier Domingo <javierdo1@gmail.com>
- Lasse Bigum <lasse@bigum.org>

View File

@ -4,7 +4,13 @@
Authors
*******
Contributors to Mopidy in the order of appearance:
Mopidy is copyright 2009-2013 Stein Magnus Jodal and contributors. Mopidy is
licensed under the `Apache License, Version 2.0
<http://www.apache.org/licenses/LICENSE-2.0>`_.
The following persons have contributed to Mopidy. The list is in the order of
first contribution. For details on who have contributed what, please refer to
our Git repository.
.. include:: ../AUTHORS

View File

@ -4,6 +4,30 @@ Changelog
This changelog is used to track all major changes to Mopidy.
v0.16.1 (2013-11-02)
====================
This is very small release to get Mopidy's Debian package ready for inclusion
in Debian.
**Commands**
- Fix removal of last dir level in paths to dependencies in
``mopidy --show-deps`` output.
- Add manpages for all commands.
**Local backend**
- Fix search filtering by track number that was added in 0.16.0.
**MPD frontend**
- Add support for ``list "albumartist" ...`` which was missed when ``find`` and
``search`` learned to handle ``albumartist`` in 0.16.0.
v0.16.0 (2013-10-27)
====================

13
docs/commands/index.rst Normal file
View File

@ -0,0 +1,13 @@
.. _commands:
********
Commands
********
Mopidy comes with the following commands:
.. toctree::
:maxdepth: 1
:glob:
**

View File

@ -0,0 +1,98 @@
.. _mopidy-convert-config:
*****************************
mopidy-convert-config command
*****************************
Synopsis
========
mopidy-convert-config
Description
===========
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.
The ``mopidy-convert-config`` command is used to convert ``settings.py``
configuration files used by ``mopidy`` < 0.14 to the ``mopidy.conf`` config
file used by ``mopidy`` >= 0.14.
Options
=======
.. program:: mopidy-convert-config
This program does not take any options. It looks for the pre-0.14 settings file
at ``$XDG_CONFIG_DIR/mopidy/settings.py``, and if it exists it converts it and
ouputs a Mopidy 0.14 compatible ini-format configuration. If you don't already
have a config file at ``$XDG_CONFIG_DIR/mopidy/mopidy.conf``, you're asked if
you want to save the converted config to that file.
Example
=======
Given the following contents in ``~/.config/mopidy/settings.py``:
::
LOCAL_MUSIC_PATH = u'~/music'
MPD_SERVER_HOSTNAME = u'::'
SPOTIFY_PASSWORD = u'secret'
SPOTIFY_USERNAME = u'alice'
Running ``mopidy-convert-config`` will convert the config and create a new
``mopidy.conf`` config file:
.. code-block:: none
$ mopidy-convert-config
Checking /home/alice/.config/mopidy/settings.py
Converted config:
[spotify]
username = alice
password = ********
[mpd]
hostname = ::
[local]
media_dir = ~/music
Write new config to /home/alice/.config/mopidy/mopidy.conf? [yN] y
Done.
Contents of ``~/.config/mopidy/mopidy.conf`` after the conversion:
.. code-block:: ini
[spotify]
username = alice
password = secret
[mpd]
hostname = ::
[local]
media_dir = ~/music
See also
========
:ref:`mopidy(1) <mopidy-cmd>`
Reporting bugs
==============
Report bugs to Mopidy's issue tracker at
<https://github.com/mopidy/mopidy/issues>

View File

@ -0,0 +1,59 @@
.. _mopidy-scan-cmd:
*******************
mopidy-scan command
*******************
Synopsis
========
mopidy-scan
[-h] [--version] [-q] [-v]
Description
===========
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.
The ``mopidy-scan`` command is used to index a music library to make it
available for playback with ``mopidy``.
Options
=======
.. program:: mopidy-scan
.. cmdoption:: --version
Show Mopidy's version number and exit.
.. cmdoption:: -h, --help
Show help message and exit.
.. cmdoption:: -q, --quiet
Show less output: warning level and higher.
.. cmdoption:: -v, --verbose
Show more output: debug level and higher.
See also
========
:ref:`mopidy(1) <mopidy-cmd>`
Reporting bugs
==============
Report bugs to Mopidy's issue tracker at
<https://github.com/mopidy/mopidy/issues>

124
docs/commands/mopidy.rst Normal file
View File

@ -0,0 +1,124 @@
.. _mopidy-cmd:
**************
mopidy command
**************
Synopsis
========
mopidy
[-h] [--version] [-q] [-v] [--save-debug-log] [--show-config]
[--show-deps] [--config CONFIG_FILES] [-o CONFIG_OVERRIDES]
Description
===========
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.
The ``mopidy`` command is used to start the server.
Options
=======
.. program:: mopidy
.. cmdoption:: -h, --help
Show help message and exit.
.. cmdoption:: --version
Show Mopidy's version number and exit.
.. cmdoption:: -q, --quiet
Show less output: warning level and higher.
.. cmdoption:: -v, --verbose
Show more output: debug level and higher.
.. cmdoption:: --save-debug-log
Save debug log to the file specified in the :confval:`logging/debug_file`
config value, typically ``./mopidy.log``.
.. cmdoption:: --show-config
Show the current effective config. All configuration sources are merged
together to show the effective document. Secret values like passwords are
masked out. Config for disabled extensions are not included.
.. cmdoption:: --show-deps
Show dependencies, their versions and installation location.
.. cmdoption:: --config <file>
Specify config file to use. To use multiple config files, separate them
with colon. The later files override the earlier ones if there's a
conflict.
.. cmdoption:: -o <option>, --option <option>
Specify additional config values in the ``section/key=value`` format. Can
be provided multiple times.
Files
=====
/etc/mopidy/mopidy.conf
System wide Mopidy configuration file.
~/.config/mopidy/mopidy.conf
Your personal Mopidy configuration file. Overrides any configs from the
system wide configuration file.
Examples
========
To start the music server, run::
mopidy
To start the server with an additional config file than can override configs
set in the default config files, run::
mopidy --config ./my-config.conf
To start the server and change a config value directly on the command line,
run::
mopidy --option mpd/enabled=false
The :option:`--option` flag may be repeated multiple times to change multiple
configs::
mopidy -o mpd/enabled=false -o spotify/bitrate=320
The :option:`--show-config` output shows the effect of the :option:`--option`
flags::
mopidy -o mpd/enabled=false -o spotify/bitrate=320 --show-config
See also
========
:ref:`mopidy-scan(1) <mopidy-scan-cmd>`, :ref:`mopidy-convert-config(1)
<mopidy-convert-config>`
Reporting bugs
==============
Report bugs to Mopidy's issue tracker at
<https://github.com/mopidy/mopidy/issues>

View File

@ -1,16 +1,6 @@
# -*- coding: utf-8 -*-
#
# Mopidy documentation build configuration file, created by
# sphinx-quickstart on Fri Feb 5 22:19:08 2010.
#
# This file is execfile()d with the current directory set to its containing
# dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
# encoding: utf-8
"""Mopidy documentation build configuration file"""
from __future__ import unicode_literals
@ -18,6 +8,17 @@ import os
import sys
# -- Read The Docs configuration ----------------------------------------------
RTD_NEW_THEME = True
# -- Workarounds to have autodoc generate API docs ----------------------------
sys.path.insert(0, os.path.abspath(os.path.dirname(__file__)))
sys.path.insert(0, os.path.abspath(os.path.dirname(__file__) + '/../'))
class Mock(object):
def __init__(self, *args, **kwargs):
pass
@ -43,7 +44,6 @@ class Mock(object):
else:
return Mock()
MOCK_MODULES = [
'cherrypy',
'dbus',
@ -68,213 +68,8 @@ MOCK_MODULES = [
for mod_name in MOCK_MODULES:
sys.modules[mod_name] = Mock()
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.insert(0, os.path.abspath(os.path.dirname(__file__)))
sys.path.insert(0, os.path.abspath(os.path.dirname(__file__) + '/../'))
# When RTD builds the project, it sets the READTHEDOCS environment variable to
# the string True.
on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
# Enable Read the Docs' new theme
RTD_NEW_THEME = True
# -- General configuration ----------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.extlinks',
'sphinx.ext.graphviz',
'sphinx.ext.viewcode',
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = 'Mopidy'
copyright = '2009-2013, Stein Magnus Jodal and contributors'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The full version, including alpha/beta/rc tags.
from mopidy.utils.versioning import get_version
release = get_version()
# The short X.Y version.
version = '.'.join(release.split('.')[:2])
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of documents that shouldn't be included in the build.
#unused_docs = []
# List of directories, relative to source directory, that shouldn't be searched
# for source files.
exclude_trees = ['_build']
# The reST default role (used for this markup: `text`) to use for all
# documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
modindex_common_prefix = ['mopidy.']
# -- Options for HTML output --------------------------------------------------
# The theme to use for HTML and HTML Help pages. Major themes that come with
# Sphinx are currently 'default' and 'sphinxdoc'.
html_theme = 'default'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
html_theme_path = ['_themes']
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
#html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
if on_rtd:
html_logo = '_static/mopidy.png'
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
#html_use_modindex = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = ''
# Output file base name for HTML help builder.
htmlhelp_basename = 'Mopidydoc'
# -- Options for LaTeX output -------------------------------------------------
# The paper size ('letter' or 'a4').
#latex_paper_size = 'letter'
# The font size ('10pt', '11pt' or '12pt').
#latex_font_size = '10pt'
# Grouping the document tree into LaTeX files. List of tuples (source start
# file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
(
'index',
'Mopidy.tex',
'Mopidy Documentation',
'Stein Magnus Jodal',
'manual'
),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# Additional stuff for the LaTeX preamble.
#latex_preamble = ''
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_use_modindex = True
needs_sphinx = '1.0'
extlinks = {'issue': ('https://github.com/mopidy/mopidy/issues/%s', '#')}
# -- Custom Sphinx object types -----------------------------------------------
def setup(app):
from sphinx.ext.autodoc import cut_lines
@ -283,3 +78,91 @@ def setup(app):
b'confval', 'confval',
objname='configuration value',
indextemplate='pair: %s; configuration value')
# -- General configuration ----------------------------------------------------
needs_sphinx = '1.0'
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.extlinks',
'sphinx.ext.graphviz',
'sphinx.ext.viewcode',
]
templates_path = ['_templates']
source_suffix = '.rst'
master_doc = 'index'
project = 'Mopidy'
copyright = '2009-2013, Stein Magnus Jodal and contributors'
from mopidy.utils.versioning import get_version
release = get_version()
version = '.'.join(release.split('.')[:2])
exclude_trees = ['_build']
pygments_style = 'sphinx'
modindex_common_prefix = ['mopidy.']
# -- Options for HTML output --------------------------------------------------
html_theme = 'default'
html_theme_path = ['_themes']
html_static_path = ['_static']
html_use_modindex = True
html_use_index = True
html_split_index = False
html_show_sourcelink = True
htmlhelp_basename = 'Mopidy'
# -- Options for LaTeX output -------------------------------------------------
latex_documents = [
(
'index',
'Mopidy.tex',
'Mopidy Documentation',
'Stein Magnus Jodal and contributors',
'manual'
),
]
# -- Options for manpages output ----------------------------------------------
man_pages = [
(
'commands/mopidy',
'mopidy',
'music server',
'',
'1'
),
(
'commands/mopidy-scan',
'mopidy-scan',
'index music for playback with mopidy',
'',
'1'
),
(
'commands/mopidy-convert-config',
'mopidy-convert-config',
'migrate config files from mopidy pre-0.14',
'',
'1'
),
]
# -- Options for extlink extension --------------------------------------------
extlinks = {'issue': ('https://github.com/mopidy/mopidy/issues/%s', '#')}

View File

@ -98,24 +98,118 @@ Creating releases
#. Update changelog and commit it.
#. Bump the version number in ``mopidy/__init__.py``. Remember to update the
test case in ``tests/version_test.py``.
#. Merge the release branch (``develop`` in the example) into master::
git checkout master
git merge --no-ff -m "Release v0.2.0" develop
git merge --no-ff -m "Release v0.16.0" develop
#. Build package and test it manually in a new virtualenv. The following
assumes the use of virtualenvwrapper::
python setup.py sdist
mktmpenv
pip install path/to/dist/Mopidy-0.16.0.tar.gz
toggleglobalsitepackages
Then test Mopidy manually to confirm that the package is working correctly.
#. Tag the release::
git tag -a -m "Release v0.2.0" v0.2.0
git tag -a -m "Release v0.16.0" v0.16.0
#. Push to GitHub::
git push
git push --tags
#. Build package and upload to PyPI::
#. Build source package and upload to PyPI::
python setup.py sdist upload
#. Build wheel package and upload to PyPI::
pip install -U wheel
python setup.py bdist_wheel upload
#. Merge ``master`` back into ``develop`` and push the branch to GitHub.
#. Make sure the new tag is built by Read the Docs, and that the ``latest``
version shows the newly released version.
#. Spread the word through the topic on #mopidy on IRC, @mopidy on Twitter, and
on the mailing list.
#. Update the Debian package.
#. Spread the word.
Updating Debian packages
========================
This howto is not intended to learn you all the details, just to give someone
already familiar with Debian packaging an overview of how Mopidy's Debian
packages is maintained.
#. Install the basic packaging tools::
sudo apt-get install build-essential git-buildpackage
#. Check out the ``debian`` branch of the repo::
git checkout -t origin/debian
git pull
#. Merge the latest release tag into the ``debian`` branch::
git merge v0.16.0
#. Update the ``debian/changelog`` with a "New upstream release" entry::
dch -v 0.16.0-0mopidy1
git add debian/changelog
git commit -m "debian: New upstream release"
#. Check if any dependencies in ``debian/control`` or similar needs updating.
#. Install any Build-Deps listed in ``debian/control``.
#. Build the package and fix any issues repeatedly until the build succeeds and
the Lintian check at the end of the build is satisfactory::
git buildpackage -uc -us
#. Install and test newly built package::
sudo debi
#. If everything is OK, build the package a final time to tag the package
version::
git buildpackage -uc -us --git-tag
#. Push the changes you've done to the ``debian`` branch and the new tag::
git push
git push --tags
#. If you're building for multiple architectures, checkout the ``debian``
branch on the other builders and run::
git buildpackage -uc -us
#. Copy files to the APT server. Make sure to select the correct part of the
repo, e.g. main, contrib, or non-free::
scp ../mopidy*_0.16* bonobo.mopidy.com:/srv/apt.mopidy.com/app/incoming/stable/main
#. Update the APT repo::
ssh bonobo.mopidy.com
/srv/apt.mopidy.com/app/update.sh
#. Test installation from apt.mopidy.com::
sudo apt-get update
sudo apt-get dist-upgrade

View File

@ -48,7 +48,6 @@ About
:maxdepth: 1
authors
license
changelog
versioning
@ -72,6 +71,7 @@ Reference
:maxdepth: 2
glossary
commands/index
api/index
modules/index

View File

@ -71,25 +71,22 @@ it out.
Arch Linux: Install from AUR
============================
If you are running Arch Linux, you can install the latest release of Mopidy
using the `mopidy-git <https://aur.archlinux.org/packages/mopidy-git/>`_
package found in AUR. The package installs from the ``master`` branch of the
Mopidy Git repo, which always corresponds to the latest release.
If you are running Arch Linux, you can install Mopidy
using the `mopidy <https://aur.archlinux.org/packages/mopidy/>`_
package found in AUR.
#. To install Mopidy with GStreamer, libspotify and pyspotify, you can use
``packer``, ``yaourt``, or do it by hand like this::
#. To install Mopidy with all dependencies, you can use
for example `yaourt <https://wiki.archlinux.org/index.php/yaourt>`_::
wget http://aur.archlinux.org/packages/mopidy-git/mopidy-git.tar.gz
tar xf mopidy-git.tar.gz
cd mopidy-git/
makepkg -si
yaourt -S mopidy
To upgrade Mopidy to future releases, just rerun ``makepkg``.
To upgrade Mopidy to future releases, just upgrade your system using::
#. Optional: If you want to scrobble your played tracks to Last.fm, you need to
install `python2-pylast`::
yaourt -Syu
sudo pacman -S python2-pylast
#. Optional: If you want to use any Mopidy extensions, like Spotify support or
Last.fm scrobbling, AUR also got `packages for several Mopidy extensions
<https://aur.archlinux.org/packages/?K=mopidy>`_.
#. Finally, you need to set a couple of :doc:`config values </config>`, and
then you're ready to :doc:`run Mopidy </running>`.

View File

@ -1,10 +0,0 @@
*******
License
*******
Mopidy is copyright 2009-2013 Stein Magnus Jodal and contributors. For a list
of contributors, see :doc:`authors`. For details on who have contributed what,
please refer to our git repository.
Mopidy is licensed under the `Apache License, Version 2.0
<http://www.apache.org/licenses/LICENSE-2.0>`_.

View File

@ -6,10 +6,17 @@ To start Mopidy, simply open a terminal and run::
mopidy
For a complete reference to the Mopidy commands and their command line options,
see :ref:`mopidy-cmd` and :ref:`mopidy-scan-cmd`.
When Mopidy says ``MPD server running at [127.0.0.1]:6600`` it's ready to
accept connections by any MPD client. Check out our non-exhaustive
:doc:`/clients/mpd` list to find recommended clients.
Stopping Mopidy
===============
To stop Mopidy, press ``CTRL+C`` in the terminal where you started Mopidy.
Mopidy will also shut down properly if you send it the TERM signal, e.g. by
@ -18,124 +25,3 @@ using ``kill``::
kill `ps ax | grep mopidy | grep -v grep | cut -d' ' -f1`
This can be useful e.g. if you create init script for managing Mopidy.
mopidy command
==============
.. program:: mopidy
.. cmdoption:: --version
Show Mopidy's version number and exit.
.. cmdoption:: -h, --help
Show help message and exit.
.. cmdoption:: -q, --quiet
Show less output: warning level and higher.
.. cmdoption:: -v, --verbose
Show more output: debug level and higher.
.. cmdoption:: --save-debug-log
Save debug log to the file specified in the :confval:`logging/debug_file`
config value, typically ``./mopidy.conf``.
.. cmdoption:: --show-config
Show the current effective config. All configuration sources are merged
together to show the effective document. Secret values like passwords are
masked out. Config for disabled extensions are not included.
.. cmdoption:: --show-deps
Show dependencies, their versions and installation location.
.. cmdoption:: --config <file>
Specify config file to use. To use multiple config files, separate them
with colon. The later files override the earlier ones if there's a
conflict.
.. cmdoption:: -o <option>, --option <option>
Specify additional config values in the ``section/key=value`` format. Can
be provided multiple times.
mopidy-scan command
===================
.. program:: mopidy-scan
.. cmdoption:: --version
Show Mopidy's version number and exit.
.. cmdoption:: -h, --help
Show help message and exit.
.. cmdoption:: -q, --quiet
Show less output: warning level and higher.
.. cmdoption:: -v, --verbose
Show more output: debug level and higher.
.. _mopidy-convert-config:
mopidy-convert-config command
=============================
.. program:: mopidy-convert-config
This program does not take any options. It looks for the pre-0.14 settings file
at ``$XDG_CONFIG_DIR/mopidy/settings.py``, and if it exists it converts it and
ouputs a Mopidy 0.14 compatible ini-format configuration. If you don't already
have a config file at ``$XDG_CONFIG_DIR/mopidy/mopidy.conf``, you're asked if
you want to save the converted config to that file.
Example usage::
$ cat ~/.config/mopidy/settings.py
LOCAL_MUSIC_PATH = u'~/music'
MPD_SERVER_HOSTNAME = u'::'
SPOTIFY_PASSWORD = u'secret'
SPOTIFY_USERNAME = u'alice'
$ mopidy-convert-config
Checking /home/alice/.config/mopidy/settings.py
Converted config:
[spotify]
username = alice
password = ********
[mpd]
hostname = ::
[local]
media_dir = ~/music
Write new config to /home/alice/.config/mopidy/mopidy.conf? [yN] y
Done.
$ cat ~/.config/mopidy/mopidy.conf
[spotify]
username = alice
password = secret
[mpd]
hostname = ::
[local]
media_dir = ~/music

View File

@ -21,4 +21,4 @@ if (isinstance(pykka.__version__, basestring)
warnings.filterwarnings('ignore', 'could not open display')
__version__ = '0.16.0'
__version__ = '0.16.1'

View File

@ -21,6 +21,12 @@ class LocalLibraryProvider(base.BaseLibraryProvider):
self._tag_cache_file = self.backend.config['local']['tag_cache_file']
self.refresh()
def _convert_to_int(self, string):
try:
return int(string)
except ValueError:
return object()
def refresh(self, uri=None):
logger.debug(
'Loading local tracks from %s using %s',
@ -61,7 +67,7 @@ class LocalLibraryProvider(base.BaseLibraryProvider):
# FIXME this is bound to be slow for large libraries
for value in values:
if field == 'track_no':
q = value
q = self._convert_to_int(value)
else:
q = value.strip()
@ -81,7 +87,6 @@ class LocalLibraryProvider(base.BaseLibraryProvider):
album_filter(t) or
artist_filter(t) or
albumartist_filter(t) or
track_no_filter(t) or
date_filter(t))
if field == 'uri':
@ -119,7 +124,7 @@ class LocalLibraryProvider(base.BaseLibraryProvider):
# FIXME this is bound to be slow for large libraries
for value in values:
if field == 'track_no':
q = value
q = self._convert_to_int(value)
else:
q = value.strip().lower()
@ -140,7 +145,6 @@ class LocalLibraryProvider(base.BaseLibraryProvider):
album_filter(t) or
artist_filter(t) or
albumartist_filter(t) or
track_no_filter(t) or
date_filter(t))
if field == 'uri':

View File

@ -163,7 +163,7 @@ def validate_extension(extension):
def register_gstreamer_elements(enabled_extensions):
"""Registers custom GStreamer elements from extensions.
:params enabled_extensions: list of enabled extensions
:param enabled_extensions: list of enabled extensions
"""
for extension in enabled_extensions:

View File

@ -127,8 +127,8 @@ def findadd(context, mpd_query):
@handle_request(
r'^list "?(?P<field>([Aa]rtist|[Aa]lbum|[Dd]ate|[Gg]enre))"?'
r'( (?P<mpd_query>.*))?$')
r'^list "?(?P<field>([Aa]rtist|[Aa]lbumartist|[Aa]lbum|[Dd]ate|'
r'[Gg]enre))"?( (?P<mpd_query>.*))?$')
def list_(context, field, mpd_query=None):
"""
*musicpd.org, music database section:*
@ -136,7 +136,7 @@ def list_(context, field, mpd_query=None):
``list {TYPE} [ARTIST]``
Lists all tags of the specified type. ``TYPE`` should be ``album``,
``artist``, ``date``, or ``genre``.
``artist``, ``albumartist``, ``date``, or ``genre``.
``ARTIST`` is an optional parameter when type is ``album``,
``date``, or ``genre``. This filters the result list by an artist.
@ -218,6 +218,8 @@ def list_(context, field, mpd_query=None):
return
if field == 'artist':
return _list_artist(context, query)
if field == 'albumartist':
return _list_albumartist(context, query)
elif field == 'album':
return _list_album(context, query)
elif field == 'date':
@ -236,6 +238,17 @@ def _list_artist(context, query):
return artists
def _list_albumartist(context, query):
albumartists = set()
results = context.core.library.find_exact(**query).get()
for track in _get_tracks(results):
if track.album:
for artist in track.album.artists:
if artist.name:
albumartists.add(('AlbumArtist', artist.name))
return albumartists
def _list_album(context, query):
albums = set()
results = context.core.library.find_exact(**query).get()

View File

@ -41,7 +41,7 @@ def _format_dependency(dep_info):
lines.append('%s: not found' % dep_info['name'])
else:
if 'path' in dep_info:
source = ' from %s' % os.path.dirname(dep_info['path'])
source = ' from %s' % dep_info['path']
else:
source = ''
lines.append('%s: %s%s' % (
@ -75,7 +75,7 @@ def python_info():
'name': 'Python',
'version': '%s %s' % (
platform.python_implementation(), platform.python_version()),
'path': platform.__file__,
'path': os.path.dirname(platform.__file__),
}
@ -127,7 +127,7 @@ def gstreamer_info():
return {
'name': 'GStreamer',
'version': '.'.join(map(str, gst.get_gst_version())),
'path': gst.__file__,
'path': os.path.dirname(gst.__file__),
'other': '\n'.join(other),
}

View File

@ -108,13 +108,19 @@ class LocalLibraryProviderTest(unittest.TestCase):
result = self.library.find_exact(artist=['unknown artist'])
self.assertEqual(list(result[0].tracks), [])
result = self.library.find_exact(albumartist=['unknown albumartist'])
self.assertEqual(list(result[0].tracks), [])
result = self.library.find_exact(album=['unknown artist'])
self.assertEqual(list(result[0].tracks), [])
result = self.library.find_exact(date=['1990'])
self.assertEqual(list(result[0].tracks), [])
result = self.library.find_exact(track_no=[9])
result = self.library.find_exact(track_no=['9'])
self.assertEqual(list(result[0].tracks), [])
result = self.library.find_exact(track_no=['no_match'])
self.assertEqual(list(result[0].tracks), [])
result = self.library.find_exact(uri=['fake uri'])
@ -167,10 +173,10 @@ class LocalLibraryProviderTest(unittest.TestCase):
self.assertEqual(list(result[0].tracks), [self.tracks[2]])
def test_find_exact_track_no(self):
result = self.library.find_exact(track_no=[1])
result = self.library.find_exact(track_no=['1'])
self.assertEqual(list(result[0].tracks), self.tracks[:1])
result = self.library.find_exact(track_no=[2])
result = self.library.find_exact(track_no=['2'])
self.assertEqual(list(result[0].tracks), self.tracks[1:2])
def test_find_exact_date(self):
@ -222,13 +228,16 @@ class LocalLibraryProviderTest(unittest.TestCase):
test = lambda: self.library.find_exact(artist=[''])
self.assertRaises(LookupError, test)
test = lambda: self.library.find_exact(albumartist=[''])
self.assertRaises(LookupError, test)
test = lambda: self.library.find_exact(track=[''])
self.assertRaises(LookupError, test)
test = lambda: self.library.find_exact(album=[''])
self.assertRaises(LookupError, test)
test = lambda: self.library.find_exact(track_no=[])
test = lambda: self.library.find_exact(track_no=[''])
self.assertRaises(LookupError, test)
test = lambda: self.library.find_exact(date=[''])
@ -244,10 +253,16 @@ class LocalLibraryProviderTest(unittest.TestCase):
result = self.library.search(artist=['unknown artist'])
self.assertEqual(list(result[0].tracks), [])
result = self.library.search(albumartist=['unknown albumartist'])
self.assertEqual(list(result[0].tracks), [])
result = self.library.search(album=['unknown artist'])
self.assertEqual(list(result[0].tracks), [])
result = self.library.search(track_no=[9])
result = self.library.search(track_no=['9'])
self.assertEqual(list(result[0].tracks), [])
result = self.library.search(track_no=['no_match'])
self.assertEqual(list(result[0].tracks), [])
result = self.library.search(date=['unknown date'])
@ -314,10 +329,10 @@ class LocalLibraryProviderTest(unittest.TestCase):
self.assertEqual(list(result[0].tracks), self.tracks[1:2])
def test_search_track_no(self):
result = self.library.search(track_no=[1])
result = self.library.search(track_no=['1'])
self.assertEqual(list(result[0].tracks), self.tracks[:1])
result = self.library.search(track_no=[2])
result = self.library.search(track_no=['2'])
self.assertEqual(list(result[0].tracks), self.tracks[1:2])
def test_search_any(self):
@ -352,6 +367,9 @@ class LocalLibraryProviderTest(unittest.TestCase):
test = lambda: self.library.search(artist=[''])
self.assertRaises(LookupError, test)
test = lambda: self.library.search(albumartist=[''])
self.assertRaises(LookupError, test)
test = lambda: self.library.search(track=[''])
self.assertRaises(LookupError, test)

View File

@ -398,6 +398,66 @@ class MusicDatabaseListTest(protocol.BaseTestCase):
self.assertNotInResponse('Artist: ')
self.assertInResponse('OK')
### Albumartist
def test_list_albumartist_with_quotes(self):
self.sendRequest('list "albumartist"')
self.assertInResponse('OK')
def test_list_albumartist_without_quotes(self):
self.sendRequest('list albumartist')
self.assertInResponse('OK')
def test_list_albumartist_without_quotes_and_capitalized(self):
self.sendRequest('list Albumartist')
self.assertInResponse('OK')
def test_list_albumartist_with_query_of_one_token(self):
self.sendRequest('list "albumartist" "anartist"')
self.assertEqualResponse(
'ACK [2@0] {list} should be "Album" for 3 arguments')
def test_list_albumartist_with_unknown_field_in_query_returns_ack(self):
self.sendRequest('list "albumartist" "foo" "bar"')
self.assertEqualResponse('ACK [2@0] {list} not able to parse args')
def test_list_albumartist_by_artist(self):
self.sendRequest('list "albumartist" "artist" "anartist"')
self.assertInResponse('OK')
def test_list_albumartist_by_album(self):
self.sendRequest('list "albumartist" "album" "analbum"')
self.assertInResponse('OK')
def test_list_albumartist_by_full_date(self):
self.sendRequest('list "albumartist" "date" "2001-01-01"')
self.assertInResponse('OK')
def test_list_albumartist_by_year(self):
self.sendRequest('list "albumartist" "date" "2001"')
self.assertInResponse('OK')
def test_list_albumartist_by_genre(self):
self.sendRequest('list "albumartist" "genre" "agenre"')
self.assertInResponse('OK')
def test_list_albumartist_by_artist_and_album(self):
self.sendRequest(
'list "albumartist" "artist" "anartist" "album" "analbum"')
self.assertInResponse('OK')
def test_list_albumartist_without_filter_value(self):
self.sendRequest('list "albumartist" "artist" ""')
self.assertInResponse('OK')
def test_list_albumartist_should_not_return_artists_without_names(self):
self.backend.library.dummy_find_exact_result = SearchResult(
tracks=[Track(album=Album(artists=[Artist(name='')]))])
self.sendRequest('list "albumartist"')
self.assertNotInResponse('Artist: ')
self.assertInResponse('OK')
### Album
def test_list_album_with_quotes(self):

View File

@ -20,7 +20,7 @@ class DepsTest(unittest.TestCase):
lambda: dict(name='Platform', version='Loonix 4.0.1'),
lambda: dict(
name='Pykka', version='1.1',
path='/foo/bar/baz.py', other='Quux'),
path='/foo/bar', other='Quux'),
lambda: dict(name='Foo'),
lambda: dict(name='Mopidy', version='0.13', dependencies=[
dict(name='pylast', version='0.5', dependencies=[
@ -58,6 +58,7 @@ class DepsTest(unittest.TestCase):
self.assertIn(platform.python_implementation(), result['version'])
self.assertIn(platform.python_version(), result['version'])
self.assertIn('python', result['path'])
self.assertNotIn('platform.py', result['path'])
def test_gstreamer_info(self):
result = deps.gstreamer_info()
@ -66,6 +67,7 @@ class DepsTest(unittest.TestCase):
self.assertEquals(
'.'.join(map(str, gst.get_gst_version())), result['version'])
self.assertIn('gst', result['path'])
self.assertNotIn('__init__.py', result['path'])
self.assertIn('Python wrapper: gst-python', result['other'])
self.assertIn(
'.'.join(map(str, gst.get_pygst_version())), result['other'])

View File

@ -40,5 +40,6 @@ class VersionTest(unittest.TestCase):
self.assertLess(SV('0.14.0'), SV('0.14.1'))
self.assertLess(SV('0.14.1'), SV('0.14.2'))
self.assertLess(SV('0.14.2'), SV('0.15.0'))
self.assertLess(SV('0.15.0'), SV(__version__))
self.assertLess(SV(__version__), SV('0.16.1'))
self.assertLess(SV('0.15.0'), SV('0.16.0'))
self.assertLess(SV('0.16.0'), SV(__version__))
self.assertLess(SV(__version__), SV('0.17.1'))