Merge branch 'develop' into feature/local-uris

This commit is contained in:
Thomas Adamcik 2013-08-05 22:41:37 +02:00
commit 0ce791f215
17 changed files with 106 additions and 50 deletions

View File

@ -5,3 +5,5 @@ Kristian Klette <klette@samfundet.no>
Johannes Knutsen <johannes@knutseninfo.no> <johannes@iterate.no>
Johannes Knutsen <johannes@knutseninfo.no> <johannes@barbarmaclin.(none)>
John Bäckstrand <sopues@gmail.com> <sandos@XBMCLive.(none)>
Alexandre Petitjean <alpetitjean@gmail.com>
Alexandre Petitjean <alpetitjean@gmail.com> <alexandre.petitjean@lne.fr>

View File

@ -22,3 +22,4 @@
- Janez Troha <janez.troha@gmail.com>
- Tobias Sauerwein <cgtobi@gmail.com>
- alzeih <alzeih@gmail.com>
- Alexandre Petitjean <alpetitjean@gmail.com>

View File

@ -22,6 +22,11 @@ v0.15.0 (UNRELEASED)
- :option:`mopidy --show-config` will now take into consideration any
:option:`mopidy --option` arguments appearing later on the command line.
**Audio**
- Added support for viusalization. :confval:`audio/visualizer` can now be set
to GStreamer visualizers.
**Local backend**
- An album's number of discs and a track's disc number are now extracted when
@ -33,6 +38,9 @@ v0.15.0 (UNRELEASED)
hierarchy from your Spotify account is available in Mopidy. (Fixes:
:issue:`62`)
- Fix proxy config values that was broken with the config system change in
0.14. (Fixes: :issue:`472`)
v0.14.2 (2013-07-01)
====================

View File

@ -101,7 +101,7 @@ master_doc = 'index'
# General information about the project.
project = 'Mopidy'
copyright = '2010-2013, Stein Magnus Jodal and contributors'
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

View File

@ -90,6 +90,16 @@ Core configuration values
``gst-inspect-0.10`` to see what output properties can be set on the sink.
For example: ``gst-inspect-0.10 shout2send``
.. confval:: audio/visualizer
Visualizer to use.
Can be left blank if no visualizer is desired. Otherwise this expects a
GStreamer visualizer. Typical values are ``monoscope``, ``goom``,
``goom2k1`` or one of the `libvisual`_ visualizers.
.. _libvisual: http://gstreamer.freedesktop.org/data/doc/gstreamer/head/gst-plugins-base-plugins/html/gst-plugins-base-plugins-plugin-libvisual.html
.. confval:: logging/console_format
The log format used for informational logging.

View File

@ -48,7 +48,7 @@ About
:maxdepth: 1
authors
licenses
license
changelog
versioning

10
docs/license.rst Normal file
View File

@ -0,0 +1,10 @@
*******
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

@ -1,34 +0,0 @@
********
Licenses
********
For a list of contributors, see :doc:`authors`. For details on who have
contributed what, please refer to our git repository.
Source code license
===================
Copyright 2009-2013 Stein Magnus Jodal and contributors
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.
Documentation license
=====================
Copyright 2010-2013 Stein Magnus Jodal and contributors
This work is licensed under the Creative Commons Attribution-ShareAlike 3.0
Unported License. To view a copy of this license, visit
http://creativecommons.org/licenses/by-sa/3.0/ or send a letter to Creative
Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA.

View File

@ -51,15 +51,15 @@ Building from source
1. Install [Node.js](http://nodejs.org/) and npm. There is a PPA if you're
running Ubuntu:
sudo apt-get install python-software-properties
sudo add-apt-repository ppa:chris-lea/node.js
sudo apt-get update
sudo apt-get install nodejs npm
sudo apt-get install python-software-properties
sudo add-apt-repository ppa:chris-lea/node.js
sudo apt-get update
sudo apt-get install nodejs
2. Enter the `js/` in Mopidy's Git repo dir and install all dependencies:
cd js/
npm install
cd js/
npm install
That's it.

View File

@ -22,6 +22,22 @@ mixers.register_mixers()
MB = 1 << 20
# GST_PLAY_FLAG_VIDEO (1<<0)
# GST_PLAY_FLAG_AUDIO (1<<1)
# GST_PLAY_FLAG_TEXT (1<<2)
# GST_PLAY_FLAG_VIS (1<<3)
# GST_PLAY_FLAG_SOFT_VOLUME (1<<4)
# GST_PLAY_FLAG_NATIVE_AUDIO (1<<5)
# GST_PLAY_FLAG_NATIVE_VIDEO (1<<6)
# GST_PLAY_FLAG_DOWNLOAD (1<<7)
# GST_PLAY_FLAG_BUFFERING (1<<8)
# GST_PLAY_FLAG_DEINTERLACE (1<<9)
# GST_PLAY_FLAG_SOFT_COLORBALANCE (1<<10)
# Default flags to use for playbin: AUDIO, SOFT_VOLUME, DOWNLOAD
PLAYBIN_FLAGS = (1<<1) | (1<<4) | (1<<7)
PLAYBIN_VIS_FLAGS = PLAYBIN_FLAGS | (1<<3)
class Audio(pykka.ThreadingActor):
"""
@ -55,6 +71,7 @@ class Audio(pykka.ThreadingActor):
try:
self._setup_playbin()
self._setup_output()
self._setup_visualizer()
self._setup_mixer()
self._setup_message_processor()
except gobject.GError as ex:
@ -78,9 +95,7 @@ class Audio(pykka.ThreadingActor):
def _setup_playbin(self):
playbin = gst.element_factory_make('playbin2')
fakesink = gst.element_factory_make('fakesink')
playbin.set_property('video-sink', fakesink)
playbin.set_property('flags', PLAYBIN_FLAGS)
self._connect(playbin, 'about-to-finish', self._on_about_to_finish)
self._connect(playbin, 'notify::source', self._on_new_source)
@ -149,6 +164,19 @@ class Audio(pykka.ThreadingActor):
'Failed to create audio output "%s": %s', output_desc, ex)
process.exit_process()
def _setup_visualizer(self):
visualizer_element = self._config['audio']['visualizer']
if not visualizer_element:
return
try:
visualizer = gst.element_factory_make(visualizer_element)
self._playbin.set_property('vis-plugin', visualizer)
self._playbin.set_property('flags', PLAYBIN_VIS_FLAGS)
logger.info('Audio visualizer set to "%s"', visualizer_element)
except gobject.GError as ex:
logger.error(
'Failed to create audio visualizer "%s": %s', visualizer_element, ex)
def _setup_mixer(self):
mixer_desc = self._config['audio']['mixer']
track_desc = self._config['audio']['mixer_track']

View File

@ -33,9 +33,17 @@ class SpotifySessionManager(process.BaseThread, PyspotifySessionManager):
self.cache_location = config['spotify']['cache_dir']
self.settings_location = config['spotify']['cache_dir']
full_proxy = ''
if config['proxy']['hostname']:
full_proxy = config['proxy']['hostname']
if config['proxy']['port']:
full_proxy += ':' + str(config['proxy']['port'])
if config['proxy']['scheme']:
full_proxy = config['proxy']['scheme'] + "://" + full_proxy
PyspotifySessionManager.__init__(
self, config['spotify']['username'], config['spotify']['password'],
proxy=config['proxy']['hostname'],
proxy=full_proxy,
proxy_username=config['proxy']['username'],
proxy_password=config['proxy']['password'])

View File

@ -24,9 +24,13 @@ _audio_schema = ConfigSchema('audio')
_audio_schema['mixer'] = String()
_audio_schema['mixer_track'] = String(optional=True)
_audio_schema['output'] = String()
_audio_schema['visualizer'] = String(optional=True)
_proxy_schema = ConfigSchema('proxy')
_proxy_schema['scheme'] = String(optional=True,
choices=['http', 'https', 'socks4', 'socks5'])
_proxy_schema['hostname'] = Hostname(optional=True)
_proxy_schema['port'] = Port(optional=True)
_proxy_schema['username'] = String(optional=True)
_proxy_schema['password'] = Secret(optional=True)

View File

@ -39,6 +39,7 @@ def convert(settings):
helper('audio/output', 'OUTPUT')
helper('proxy/hostname', 'SPOTIFY_PROXY_HOST')
helper('proxy/port', 'SPOTIFY_PROXY_PORT')
helper('proxy/username', 'SPOTIFY_PROXY_USERNAME')
helper('proxy/password', 'SPOTIFY_PROXY_PASSWORD')

View File

@ -11,8 +11,11 @@ pykka = info
mixer = autoaudiomixer
mixer_track =
output = autoaudiosink
visualizer =
[proxy]
scheme =
hostname =
port =
username =
password =

View File

@ -71,9 +71,9 @@ class String(ConfigValue):
def deserialize(self, value):
value = decode(value).strip()
validators.validate_required(value, self._required)
validators.validate_choice(value, self._choices)
if not value:
return None
validators.validate_choice(value, self._choices)
return value
def serialize(self, value, display=False):
@ -111,12 +111,16 @@ class Secret(ConfigValue):
class Integer(ConfigValue):
"""Integer value."""
def __init__(self, minimum=None, maximum=None, choices=None):
def __init__(self, minimum=None, maximum=None, choices=None, optional=False):
self._required = not optional
self._minimum = minimum
self._maximum = maximum
self._choices = choices
def deserialize(self, value):
validators.validate_required(value, self._required)
if not value:
return None
value = int(value)
validators.validate_choice(value, self._choices)
validators.validate_minimum(value, self._minimum)
@ -222,9 +226,9 @@ class Port(Integer):
allocate a port for us.
"""
# TODO: consider probing if port is free or not?
def __init__(self, choices=None):
def __init__(self, choices=None, optional=False):
super(Port, self).__init__(
minimum=0, maximum=2 ** 16 - 1, choices=choices)
minimum=0, maximum=2 ** 16 - 1, choices=choices, optional=optional)
class Path(ConfigValue):

View File

@ -21,6 +21,7 @@ class AudioTest(unittest.TestCase):
'mixer': 'fakemixer track_max_volume=65536',
'mixer_track': None,
'output': 'fakesink',
'visualizer': None,
}
}
self.song_uri = path_to_uri(path_to_data_dir('song1.wav'))
@ -70,6 +71,7 @@ class AudioTest(unittest.TestCase):
'mixer': 'fakemixer track_max_volume=40',
'mixer_track': None,
'output': 'fakesink',
'visualizer': None,
}
}
self.audio = audio.Audio.start(config=config).proxy()

View File

@ -98,6 +98,11 @@ class StringTest(unittest.TestCase):
self.assertIsInstance(result, bytes)
self.assertEqual(b'', result)
def test_deserialize_enforces_choices_optional(self):
value = types.String(optional=True, choices=['foo', 'bar', 'baz'])
self.assertEqual(None, value.deserialize(b''))
self.assertRaises(ValueError, value.deserialize, b'foobar')
class SecretTest(unittest.TestCase):
def test_deserialize_passes_through(self):
@ -163,6 +168,10 @@ class IntegerTest(unittest.TestCase):
self.assertEqual(5, value.deserialize('5'))
self.assertRaises(ValueError, value.deserialize, '15')
def test_deserialize_respects_optional(self):
value = types.Integer(optional=True)
self.assertEqual(None, value.deserialize(''))
class BooleanTest(unittest.TestCase):
def test_deserialize_conversion_success(self):