diff --git a/AUTHORS b/AUTHORS index 1bc7c73b..7a20f492 100644 --- a/AUTHORS +++ b/AUTHORS @@ -40,3 +40,4 @@ - Pierpaolo Frasa - Thomas Scholtes - Sam Willcocks +- Ignasi Fosch diff --git a/README.rst b/README.rst index 784abd24..10ad4d3c 100644 --- a/README.rst +++ b/README.rst @@ -2,24 +2,57 @@ Mopidy ****** -Mopidy is a music server which can play music both from multiple sources, like -your local hard drive, radio streams, and from Spotify and SoundCloud. Searches -combines results from all music sources, and you can mix tracks from all -sources in your play queue. Your playlists from Spotify or SoundCloud are also -available for use. +Mopidy is an extensible music server written in Python. -To control your Mopidy music server, you can use one of Mopidy's web clients, -the Ubuntu Sound Menu, any device on the same network which can control UPnP -MediaRenderers, or any MPD client. MPD clients are available for many -platforms, including Windows, OS X, Linux, Android and iOS. +Mopidy plays music from local disk, Spotify, SoundCloud, Google Play Music, and +more. You edit the playlist from any phone, tablet, or computer using a range +of MPD and web clients. -To get started with Mopidy, check out `the docs `_. +**Stream music from the cloud** + +Vanilla Mopidy only plays music from your local disk and radio streams. +Through extensions, Mopidy can play music from cloud services like Spotify, +SoundCloud, and Google Play Music. With Mopidy's extension support, backends +for new music sources can be easily added. + +**Mopidy is just a server** + +Mopidy is a Python application that runs in a terminal or in the background on +Linux computers or Macs that have network connectivity and audio output. Out of +the box, Mopidy is an MPD and HTTP server. Additional frontends for controlling +Mopidy can be installed from extensions. + +**Everybody use their favorite client** + +You and the people around you can all connect their favorite MPD or web client +to the Mopidy server to search for music and manage the playlist together. With +a browser or MPD client, which is available for all popular operating systems, +you can control the music from any phone, tablet, or computer. + +**Mopidy on Raspberry Pi** + +The Raspberry Pi is a popular device to run Mopidy on, either using Raspbian or +Arch Linux. It is quite slow, but it is very affordable. In fact, the +Kickstarter funded Gramofon: Modern Cloud Jukebox project used Mopidy on a +Raspberry Pi to prototype the Gramofon device. Mopidy is also a major building +block in the Pi Musicbox integrated audio jukebox system for Raspberry Pi. + +**Mopidy is hackable** + +Mopidy's extension support and Python, JSON-RPC, and JavaScript APIs makes +Mopidy perfect for building your own hacks. In one project, a Raspberry Pi was +embedded in an old cassette player. The buttons and volume control are wired up +with GPIO on the Raspberry Pi, and is used to control playback through a custom +Mopidy extension. The cassettes have NFC tags used to select playlists from +Spotify. + +To get started with Mopidy, check out +`the installation docs `_. - `Documentation `_ - `Source code `_ - `Issue tracker `_ -- `CI server `_ -- `Download development snapshot `_ +- `Development branch tarball `_ - IRC: ``#mopidy`` at `irc.freenode.net `_ - Mailing list: `mopidy@googlegroups.com `_ diff --git a/docs/changelog.rst b/docs/changelog.rst index f28bd8e9..0d32ae5b 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -4,6 +4,29 @@ Changelog This changelog is used to track all major changes to Mopidy. + +v0.19.2 (2014-07-26) +==================== + +Bug fix release, directly from the Mopidy development sprint at EuroPython 2014 +in Berlin. + +**Audio** + +- Make :confval:`audio/mixer_volume` work on the software mixer again. This + was broken with the mixer changes in 0.19.0. (Fixes: :issue:`791`) + +**HTTP frontend** + +- When using Tornado 4.0, allow WebSocket requests from other hosts. (Fixes: + :issue:`788`) + +**MPD frontend** + +- Fix crash when MPD commands are called with the wrong number of arguments. + This was broken with the MPD command changes in 0.19.0. (Fixes: :issue:`789`) + + v0.19.1 (2014-07-23) ==================== @@ -15,7 +38,7 @@ Bug fix release. Mopidy continue to work on Debian/Raspbian stable, where Tornado 2.3 is the newest version available. -**HTTP** +**HTTP frontend** - Add missing string interpolation placeholder. diff --git a/docs/installation/osx.rst b/docs/installation/osx.rst index 1513620c..9c0e059e 100644 --- a/docs/installation/osx.rst +++ b/docs/installation/osx.rst @@ -20,6 +20,9 @@ If you are running OS X, you can install everything needed with Homebrew. brew update brew upgrade + Notice that this will upgrade all software on your system that have been + installed with Homebrew. + #. Mopidy works out of box if you have installed Python from Homebrew:: brew install python @@ -36,6 +39,10 @@ If you are running OS X, you can install everything needed with Homebrew. export PYTHONPATH=$(brew --prefix)/lib/python2.7/site-packages:$PYTHONPATH + And then reload the shell's init file or restart your terminal:: + + source ~/.bashrc + Or, you can prefix the Mopidy command every time you run it:: PYTHONPATH=$(brew --prefix)/lib/python2.7/site-packages mopidy diff --git a/mopidy/__init__.py b/mopidy/__init__.py index 6ac5f6c0..c6593354 100644 --- a/mopidy/__init__.py +++ b/mopidy/__init__.py @@ -21,4 +21,4 @@ if (isinstance(pykka.__version__, basestring) warnings.filterwarnings('ignore', 'could not open display') -__version__ = '0.19.1' +__version__ = '0.19.2' diff --git a/mopidy/audio/actor.py b/mopidy/audio/actor.py index 55aa45b8..dcc51ea4 100644 --- a/mopidy/audio/actor.py +++ b/mopidy/audio/actor.py @@ -195,6 +195,14 @@ class Audio(pykka.ThreadingActor): self._connect(self._playbin, 'notify::volume', self._on_mixer_change) self._connect(self._playbin, 'notify::mute', self._on_mixer_change) + # The Mopidy startup procedure will set the initial volume of a mixer, + # but this happens before the audio actor is injected into the software + # mixer and has no effect. Thus, we need to set the initial volume + # again. + initial_volume = self._config['audio']['mixer_volume'] + if initial_volume is not None: + self._mixer.set_volume(initial_volume) + def _on_mixer_change(self, element, gparamspec): self._mixer.trigger_events_for_changed_values() diff --git a/mopidy/http/handlers.py b/mopidy/http/handlers.py index f35b5c7c..e212c77d 100644 --- a/mopidy/http/handlers.py +++ b/mopidy/http/handlers.py @@ -112,6 +112,11 @@ class WebSocketHandler(tornado.websocket.WebSocketHandler): logger.error('WebSocket request error: %s', e) self.close() + def check_origin(self, origin): + # Allow cross-origin WebSocket connections, like Tornado before 4.0 + # defaulted to. + return True + def set_mopidy_headers(request_handler): request_handler.set_header('Cache-Control', 'no-cache') diff --git a/mopidy/mpd/protocol/__init__.py b/mopidy/mpd/protocol/__init__.py index 3c501bbb..f0ae814b 100644 --- a/mopidy/mpd/protocol/__init__.py +++ b/mopidy/mpd/protocol/__init__.py @@ -138,7 +138,13 @@ class Commands(object): def validate(*args, **kwargs): if varargs: return func(*args, **kwargs) - callargs = inspect.getcallargs(func, *args, **kwargs) + + try: + callargs = inspect.getcallargs(func, *args, **kwargs) + except TypeError: + raise exceptions.MpdArgError( + 'wrong number of arguments for "%s"' % name) + for key, value in callargs.items(): default = defaults.get(key, object()) if key in validators and value != default: @@ -146,6 +152,7 @@ class Commands(object): callargs[key] = validators[key](value) except ValueError: raise exceptions.MpdArgError('incorrect arguments') + return func(**callargs) validate.auth_required = auth_required diff --git a/mopidy/utils/deps.py b/mopidy/utils/deps.py index 46feb711..ea64a0a0 100644 --- a/mopidy/utils/deps.py +++ b/mopidy/utils/deps.py @@ -143,21 +143,23 @@ def _gstreamer_check_elements(): # Spotify 'appsrc', - # Mixers and sinks - 'alsamixer', + # Audio sinks 'alsasink', - 'ossmixer', 'osssink', - 'oss4mixer', 'oss4sink', - 'pulsemixer', 'pulsesink', # MP3 encoding and decoding - 'mp3parse', + # + # One of flump3dec, mad, and mpg123audiodec is required for MP3 + # playback. + 'flump3dec', 'id3demux', 'id3v2mux', 'lame', + 'mad', + 'mp3parse', + # 'mpg123audiodec', # Only available in GStreamer 1.x # Ogg Vorbis encoding and decoding 'vorbisdec', diff --git a/tests/mpd/protocol/test_authentication.py b/tests/mpd/protocol/test_authentication.py index 6a39ba81..4937c04f 100644 --- a/tests/mpd/protocol/test_authentication.py +++ b/tests/mpd/protocol/test_authentication.py @@ -19,6 +19,12 @@ class AuthenticationActiveTest(protocol.BaseTestCase): self.assertFalse(self.dispatcher.authenticated) self.assertEqualResponse('ACK [3@0] {password} incorrect password') + def test_authentication_without_password_fails(self): + self.sendRequest('password') + self.assertFalse(self.dispatcher.authenticated) + self.assertEqualResponse( + 'ACK [2@0] {password} wrong number of arguments for "password"') + def test_anything_when_not_authenticated_should_fail(self): self.sendRequest('any request at all') self.assertFalse(self.dispatcher.authenticated) diff --git a/tests/mpd/test_commands.py b/tests/mpd/test_commands.py index bb4effb8..2b4205fe 100644 --- a/tests/mpd/test_commands.py +++ b/tests/mpd/test_commands.py @@ -169,15 +169,15 @@ class TestCommands(unittest.TestCase): def test_call_incorrect_args(self): self.commands.add('foo')(lambda context: context) - with self.assertRaises(TypeError): + with self.assertRaises(exceptions.MpdArgError): self.commands.call(['foo', 'bar']) self.commands.add('bar')(lambda context, required: context) - with self.assertRaises(TypeError): + with self.assertRaises(exceptions.MpdArgError): self.commands.call(['bar', 'bar', 'baz']) self.commands.add('baz')(lambda context, optional=None: context) - with self.assertRaises(TypeError): + with self.assertRaises(exceptions.MpdArgError): self.commands.call(['baz', 'bar', 'baz']) def test_validator_gets_applied_to_required_arg(self): diff --git a/tests/test_help.py b/tests/test_help.py index 75d545bc..6499cac1 100644 --- a/tests/test_help.py +++ b/tests/test_help.py @@ -14,7 +14,10 @@ class HelpTest(unittest.TestCase): args = [sys.executable, mopidy_dir, '--help'] process = subprocess.Popen( args, - env={'PYTHONPATH': os.path.join(mopidy_dir, '..')}, + env={'PYTHONPATH': ':'.join([ + os.path.join(mopidy_dir, '..'), + os.environ.get('PYTHONPATH', '') + ])}, stdout=subprocess.PIPE) output = process.communicate()[0] self.assertIn('--version', output) diff --git a/tests/test_version.py b/tests/test_version.py index c596defd..1f0006a6 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -47,5 +47,6 @@ class VersionTest(unittest.TestCase): self.assertLess(SV('0.18.1'), SV('0.18.2')) self.assertLess(SV('0.18.2'), SV('0.18.3')) self.assertLess(SV('0.18.3'), SV('0.19.0')) - self.assertLess(SV('0.19.0'), SV(__version__)) - self.assertLess(SV(__version__), SV('0.19.2')) + self.assertLess(SV('0.19.0'), SV('0.19.1')) + self.assertLess(SV('0.19.1'), SV(__version__)) + self.assertLess(SV(__version__), SV('0.19.3'))