From 85a34d943be2bf73e3bcde723b3c21909eb2c6ab Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sun, 14 Feb 2010 01:01:45 +0100 Subject: [PATCH 01/19] Add paragraph on code style --- docs/development.rst | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/docs/development.rst b/docs/development.rst index 2a39b327..3f4897db 100644 --- a/docs/development.rst +++ b/docs/development.rst @@ -24,10 +24,25 @@ modular, so we can extend it with other backends in the future, like file playback and other online music services such as Last.fm. +Code style +========== + +We generally follow the `PEP-8 `_ +style guidelines, with a couple of notable exceptions: + +- We only indent continuation lines with an addition four spaces relative to + the previous line. +- An exception to the previous exception: When continuing control flow + statements like ``if``, ``for`` and ``while``, indent the continuation lines + with eight spaces, so that it is indented an additional level compared to the + following block of code. + + Running tests ============= -To run tests, you need a couple of dependiencies. Some can be installed through Debian/Ubuntu package management:: +To run tests, you need a couple of dependencies. Some can be installed through +Debian/Ubuntu package management:: sudo aptitude install python-coverage From 0e3a98b328c174e2d8571c2c3bfc9fcf9c5c2e52 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sun, 14 Feb 2010 01:04:27 +0100 Subject: [PATCH 02/19] Document return type of get_by_uri and get_by_id --- mopidy/backends/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mopidy/backends/__init__.py b/mopidy/backends/__init__.py index 8b6de67c..962e91ba 100644 --- a/mopidy/backends/__init__.py +++ b/mopidy/backends/__init__.py @@ -74,6 +74,7 @@ class BaseCurrentPlaylistController(object): :param id: track ID :type id: int + :rtype: :class:`mopidy.models.Track` """ matches = filter(lambda t: t.id == id, self._playlist.tracks) if matches: @@ -87,6 +88,7 @@ class BaseCurrentPlaylistController(object): :param uri: track URI :type uri: string + :rtype: :class:`mopidy.models.Track` """ matches = filter(lambda t: t.uri == uri, self._playlist.tracks) if matches: From 8c983f20149cf910930f25ce36b9efc317218eb3 Mon Sep 17 00:00:00 2001 From: Johannes Knutsen Date: Sun, 14 Feb 2010 01:16:48 +0100 Subject: [PATCH 03/19] Only handle response in session if the response is not none --- mopidy/mpd/session.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mopidy/mpd/session.py b/mopidy/mpd/session.py index edd2a95c..c85ed3a0 100644 --- a/mopidy/mpd/session.py +++ b/mopidy/mpd/session.py @@ -48,7 +48,8 @@ class MpdSession(asynchat.async_chat): def handle_request(self, input): try: response = self.handler.handle_request(input) - self.handle_response(response) + if response is not None: + self.handle_response(response) except MpdAckError, e: logger.warning(e) return self.send_response(u'ACK %s' % e) From 527d345488ed931f16a2597f09af428bc43b064b Mon Sep 17 00:00:00 2001 From: Johannes Knutsen Date: Sun, 14 Feb 2010 01:19:21 +0100 Subject: [PATCH 04/19] Send OK only when no ACK in command list --- mopidy/mpd/handler.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mopidy/mpd/handler.py b/mopidy/mpd/handler.py index ef7274f8..65c00ceb 100644 --- a/mopidy/mpd/handler.py +++ b/mopidy/mpd/handler.py @@ -66,7 +66,7 @@ class MpdHandler(object): response.append(u'%s: %s' % (key, value)) else: response.append(line) - if add_ok: + if add_ok and (not response or not response[-1].startswith(u'ACK')): response.append(u'OK') return response @@ -109,6 +109,8 @@ class MpdHandler(object): response = self.handle_request(command, add_ok=False) if response is not None: result.append(response) + if response and response[-1].startswith(u'ACK'): + return result if command_list_ok: response.append(u'list_OK') return result From b1e15a3830d6fa79d4ebc2c23bdd05914ba368e7 Mon Sep 17 00:00:00 2001 From: Johannes Knutsen Date: Sun, 14 Feb 2010 01:20:18 +0100 Subject: [PATCH 05/19] Implemented deleteid in MPD handler --- mopidy/mpd/handler.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/mopidy/mpd/handler.py b/mopidy/mpd/handler.py index 65c00ceb..98a0f97b 100644 --- a/mopidy/mpd/handler.py +++ b/mopidy/mpd/handler.py @@ -144,7 +144,12 @@ class MpdHandler(object): @register(r'^deleteid "(?P\d+)"$') def _deleteid(self, songid): - raise MpdNotImplemented # TODO + songid = int(songid) + try: + track = self.backend.current_playlist.get_by_id(songid) + return self.backend.current_playlist.remove(track) + except KeyError, e: + raise MpdAckError(unicode(e)) @register(r'^$') def _empty(self): From 328191d6573c69af036a4f8b16397976349fe79b Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sun, 14 Feb 2010 01:20:54 +0100 Subject: [PATCH 06/19] Add some missing MPD commands --- mopidy/mpd/handler.py | 24 ++++++++++++++++++++++++ tests/mpd/handlertest.py | 24 ++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/mopidy/mpd/handler.py b/mopidy/mpd/handler.py index ef7274f8..b5cc499e 100644 --- a/mopidy/mpd/handler.py +++ b/mopidy/mpd/handler.py @@ -113,6 +113,10 @@ class MpdHandler(object): response.append(u'list_OK') return result + @register(r'^commands$') + def _commands(self): + raise MpdNotImplemented # TODO + @register(r'^consume "(?P[01])"$') def _consume(self, state): state = int(state) @@ -135,6 +139,10 @@ class MpdHandler(object): if self.backend.playback.current_track is not None: return self.backend.playback.current_track.mpd_format() + @register(r'^decoders$') + def _decoders(self): + raise MpdNotImplemented # TODO + @register(r'^delete "(?P\d+)"$') @register(r'^delete "(?P\d+):(?P\d+)*"$') def _delete(self, songpos=None, start=None, end=None): @@ -144,10 +152,18 @@ class MpdHandler(object): def _deleteid(self, songid): raise MpdNotImplemented # TODO + @register(r'^disableoutput "(?P\d+)"$') + def _disableoutput(self, outputid): + raise MpdNotImplemented # TODO + @register(r'^$') def _empty(self): pass + @register(r'^enableoutput "(?P\d+)"$') + def _enableoutput(self, outputid): + raise MpdNotImplemented # TODO + @register(r'^find "(?P(album|artist|title))" "(?P[^"]+)"$') def _find(self, type, what): raise MpdNotImplemented # TODO @@ -220,6 +236,10 @@ class MpdHandler(object): def _next(self): return self.backend.playback.next() + @register(r'^notcommands$') + def _notcommands(self): + raise MpdNotImplemented # TODO + @register(r'^outputs$') def _outputs(self): return [ @@ -519,6 +539,10 @@ class MpdHandler(object): def _swapid(self, songid1, songid2): raise MpdNotImplemented # TODO + @register(r'^tagtypes$') + def _tagtypes(self): + raise MpdNotImplemented # TODO + @register(r'^update( "(?P[^"]+)")*$') def _update(self, uri=None, rescan_unmodified_files=False): return {'updating_db': 0} # TODO diff --git a/tests/mpd/handlertest.py b/tests/mpd/handlertest.py index a9a9d653..ccac10d2 100644 --- a/tests/mpd/handlertest.py +++ b/tests/mpd/handlertest.py @@ -779,6 +779,14 @@ class AudioOutputHandlerTest(unittest.TestCase): def setUp(self): self.h = handler.MpdHandler(backend=DummyBackend()) + def test_enableoutput(self): + result = self.h.handle_request(u'enableoutput "0"') + self.assert_(u'ACK Not implemented' in result) + + def test_disableoutput(self): + result = self.h.handle_request(u'disableoutput "0"') + self.assert_(u'ACK Not implemented' in result) + def test_outputs(self): result = self.h.handle_request(u'outputs') self.assert_(u'outputid: 0' in result) @@ -791,6 +799,22 @@ class ReflectionHandlerTest(unittest.TestCase): def setUp(self): self.h = handler.MpdHandler(backend=DummyBackend()) + def test_commands(self): + result = self.h.handle_request(u'commands') + self.assert_(u'ACK Not implemented' in result) + + def test_decoders(self): + result = self.h.handle_request(u'decoders') + self.assert_(u'ACK Not implemented' in result) + + def test_notcommands(self): + result = self.h.handle_request(u'notcommands') + self.assert_(u'ACK Not implemented' in result) + + def test_tagtypes(self): + result = self.h.handle_request(u'tagtypes') + self.assert_(u'ACK Not implemented' in result) + def test_urlhandlers(self): result = self.h.handle_request(u'urlhandlers') self.assert_(u'OK' in result) From afa107f31d22f17ad10fe63d68b8973e66ad485d Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sun, 14 Feb 2010 01:43:00 +0100 Subject: [PATCH 07/19] Add empty handlers for sticker commands --- mopidy/mpd/handler.py | 20 ++++++++++++++++++++ tests/mpd/handlertest.py | 32 +++++++++++++++++++++++++++++--- 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/mopidy/mpd/handler.py b/mopidy/mpd/handler.py index 264bf747..475a6bb0 100644 --- a/mopidy/mpd/handler.py +++ b/mopidy/mpd/handler.py @@ -538,6 +538,26 @@ class MpdHandler(object): def _status_xfade(self): return 0 # TODO + @register(r'^sticker delete "(?P[^"]+)" "(?P[^"]+)"( "(?P[^"]+)")*$') + def _sticker_delete(self, type, uri, name=None): + raise MpdNotImplemented # TODO + + @register(r'^sticker find "(?P[^"]+)" "(?P[^"]+)" "(?P[^"]+)"$') + def sticker_find(self, type, uri, name): + raise MpdNotImplemented # TODO + + @register(r'^sticker get "(?P[^"]+)" "(?P[^"]+)" "(?P[^"]+)"$') + def _sticker_get(self, type, uri, name): + raise MpdNotImplemented # TODO + + @register(r'^sticker list "(?P[^"]+)" "(?P[^"]+)"$') + def _sticker_list(self, type, uri): + raise MpdNotImplemented # TODO + + @register(r'^sticker set "(?P[^"]+)" "(?P[^"]+)" "(?P[^"]+)" "(?P[^"]+)"$') + def _sticker_set(self, type, uri, name, value): + raise MpdNotImplemented # TODO + @register(r'^swap "(?P\d+)" "(?P\d+)"$') def _swap(self, songpos1, songpos2): raise MpdNotImplemented # TODO diff --git a/tests/mpd/handlertest.py b/tests/mpd/handlertest.py index ccac10d2..9dbe44e7 100644 --- a/tests/mpd/handlertest.py +++ b/tests/mpd/handlertest.py @@ -746,7 +746,35 @@ class StickersHandlerTest(unittest.TestCase): def setUp(self): self.h = handler.MpdHandler(backend=DummyBackend()) - pass # TODO + def test_sticker_get(self): + result = self.h.handle_request( + u'sticker get "song" "file:///dev/urandom" "a_name"') + self.assert_(u'ACK Not implemented' in result) + + def test_sticker_set(self): + result = self.h.handle_request( + u'sticker set "song" "file:///dev/urandom" "a_name" "a_value"') + self.assert_(u'ACK Not implemented' in result) + + def test_sticker_delete_with_name(self): + result = self.h.handle_request( + u'sticker delete "song" "file:///dev/urandom" "a_name"') + self.assert_(u'ACK Not implemented' in result) + + def test_sticker_delete_without_name(self): + result = self.h.handle_request( + u'sticker delete "song" "file:///dev/urandom"') + self.assert_(u'ACK Not implemented' in result) + + def test_sticker_list(self): + result = self.h.handle_request( + u'sticker list "song" "file:///dev/urandom"') + self.assert_(u'ACK Not implemented' in result) + + def test_sticker_find(self): + result = self.h.handle_request( + u'sticker find "song" "file:///dev/urandom" "a_name"') + self.assert_(u'ACK Not implemented' in result) class ConnectionHandlerTest(unittest.TestCase): @@ -820,5 +848,3 @@ class ReflectionHandlerTest(unittest.TestCase): self.assert_(u'OK' in result) result = result[0] self.assert_('dummy:' in result) - - pass # TODO From 39c44bfed63c3dd9babc0dd2e5cfa663456db158 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sun, 14 Feb 2010 01:47:42 +0100 Subject: [PATCH 08/19] Update tests for 'deleteid' --- tests/mpd/handlertest.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/mpd/handlertest.py b/tests/mpd/handlertest.py index 9dbe44e7..3f5e4bb5 100644 --- a/tests/mpd/handlertest.py +++ b/tests/mpd/handlertest.py @@ -460,7 +460,8 @@ class PlaybackControlHandlerTest(unittest.TestCase): class CurrentPlaylistHandlerTest(unittest.TestCase): def setUp(self): - self.h = handler.MpdHandler(backend=DummyBackend()) + self.b = DummyBackend() + self.h = handler.MpdHandler(backend=self.b) def test_add(self): result = self.h.handle_request(u'add "file:///dev/urandom"') @@ -491,8 +492,13 @@ class CurrentPlaylistHandlerTest(unittest.TestCase): self.assert_(u'ACK Not implemented' in result) def test_deleteid(self): + self.b.current_playlist.load(Playlist(tracks=[Track(id=0)])) result = self.h.handle_request(u'deleteid "0"') - self.assert_(u'ACK Not implemented' in result) + self.assert_(u'OK' in result) + + def test_deleteid_does_not_exist(self): + result = self.h.handle_request(u'deleteid "0"') + self.assert_(u'ACK Track with ID "0" not found' in result) def test_move_songpos(self): result = self.h.handle_request(u'move "5" "0"') From 4e16e403d2a1dc4f30508ac8220e7d9bae4e8495 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sun, 14 Feb 2010 01:54:03 +0100 Subject: [PATCH 09/19] Test behaviour when errors happens during command_list processing --- mopidy/mpd/handler.py | 9 +++++++++ tests/mpd/handlertest.py | 6 ++++++ 2 files changed, 15 insertions(+) diff --git a/mopidy/mpd/handler.py b/mopidy/mpd/handler.py index 475a6bb0..aacad28c 100644 --- a/mopidy/mpd/handler.py +++ b/mopidy/mpd/handler.py @@ -70,6 +70,15 @@ class MpdHandler(object): response.append(u'OK') return response + @register(r'^ack$') + def _ack(self): + """ + Always returns an 'ACK' and not 'OK'. + + Not a part of the MPD protocol. + """ + raise MpdNotImplemented + @register(r'^add "(?P[^"]*)"$') def _add(self, uri): raise MpdNotImplemented # TODO diff --git a/tests/mpd/handlertest.py b/tests/mpd/handlertest.py index 3f5e4bb5..339d73df 100644 --- a/tests/mpd/handlertest.py +++ b/tests/mpd/handlertest.py @@ -66,6 +66,12 @@ class CommandListsTest(unittest.TestCase): self.assert_(u'OK' in result) self.assertEquals(False, self.h.command_list) + def test_command_list_with_error(self): + self.h.handle_request(u'command_list_begin') + self.h.handle_request(u'ack') + result = self.h.handle_request(u'command_list_end') + self.assert_(u'ACK' in result[-1]) + def test_command_list_ok_begin(self): result = self.h.handle_request(u'command_list_ok_begin') self.assert_(result is None) From 9763cb55a6363e193b892bbb1f1658e1db78af28 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sun, 14 Feb 2010 02:57:05 +0100 Subject: [PATCH 10/19] Add examples of code style --- docs/development.rst | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/docs/development.rst b/docs/development.rst index 3f4897db..826dd215 100644 --- a/docs/development.rst +++ b/docs/development.rst @@ -30,12 +30,33 @@ Code style We generally follow the `PEP-8 `_ style guidelines, with a couple of notable exceptions: -- We only indent continuation lines with an addition four spaces relative to - the previous line. +- We indent continuation lines with four spaces more than the previous line. + For example:: + + from mopidy.backends import (BaseBackend, BaseCurrentPlaylistController, + BasePlaybackController, BaseLibraryController, + BaseStoredPlaylistsController) + + And not:: + + from mopidy.backends import (BaseBackend, BaseCurrentPlaylistController, + BasePlaybackController, BaseLibraryController, + BaseStoredPlaylistsController) + - An exception to the previous exception: When continuing control flow - statements like ``if``, ``for`` and ``while``, indent the continuation lines - with eight spaces, so that it is indented an additional level compared to the - following block of code. + statements like ``if``, ``for`` and ``while``, we indent with eight spaces + more than the previous line. In other words, the line is indented one level + further to the right than the following block of code. For example:: + + if (old_state in (self.PLAYING, self.STOPPED) + and new_state == self.PLAYING): + self._play_time_start() + + And not:: + + if (old_state in (self.PLAYING, self.STOPPED) + and new_state == self.PLAYING): + self._play_time_start() Running tests From 2eff17315c3fc8fa7bbe2477df2868f782ac5b73 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sun, 14 Feb 2010 03:33:06 +0100 Subject: [PATCH 11/19] docs: Highlight Premium/appkey requirements --- docs/installation.rst | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index 0d5f29be..8d812310 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -27,11 +27,13 @@ despotify backend To use the despotify backend, you first need to install despotify and spytify. -*This backend requires a Spotify premium account.* +.. note:: + + This backend requires a Spotify premium account. -Installing despotify and spytify --------------------------------- +Installing despotify +-------------------- Install despotify's dependencies. At Debian/Ubuntu systems:: @@ -48,6 +50,12 @@ Build and install despotify:: make sudo make install + +Installing spytify +------------------ + +spytify's source comes bundled with despotify. + Build and install spytify:: cd despotify/src/bindings/python/ @@ -64,6 +72,7 @@ Spotify Premium account), ask for a search query, list all your playlists with tracks, play 10s from a random song from the search result, pause for two seconds, play for five more seconds, and quit. + .. _libspotify: libspotify backend @@ -73,13 +82,14 @@ As an alternative to the despotify backend, we are working on a libspotify backend. To use the libspotify backend you must install libspotify and pyspotify. -*This backend requires a Spotify premium account.* +.. note:: -*This backend requires you to get an application key from Spotify before use.* + This backend requires a Spotify premium account, and it requires you to get + an application key from Spotify before use. -Installing libspotify and pyspotify ------------------------------------ +Installing libspotify +--------------------- As libspotify's installation script at the moment is somewhat broken (see this `GetSatisfaction thread `_ @@ -87,6 +97,10 @@ for details), it is easiest to use the libspotify files bundled with pyspotify. The files bundled with pyspotify are for 64-bit, so if you run a 32-bit OS, you must get libspotify from https://developer.spotify.com/en/libspotify/. + +Installing pyspotify +-------------------- + Install pyspotify's dependencies. At Debian/Ubuntu systems:: sudo aptitude install python-alsaaudio @@ -106,9 +120,11 @@ Test your libspotify setup:: ./example1.py -u USERNAME -p PASSWORD -Until Spotify fixes their installation script, you'll have to set -``LD_LIBRARY_PATH`` every time you are going to use libspotify (in other words -before starting Mopidy). +.. note:: + + Until Spotify fixes their installation script, you'll have to set + ``LD_LIBRARY_PATH`` every time you are going to use libspotify (in other + words before starting Mopidy). Running Mopidy From b11c8475a9fb272c86bdf7d9945503aba92c0bf2 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Mon, 15 Feb 2010 19:51:03 +0100 Subject: [PATCH 12/19] docs: How to install despotify on OS X --- docs/installation.rst | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index 8d812310..a20fbc20 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -35,16 +35,31 @@ To use the despotify backend, you first need to install despotify and spytify. Installing despotify -------------------- -Install despotify's dependencies. At Debian/Ubuntu systems:: +*Linux:* Install despotify's dependencies. At Debian/Ubuntu systems:: sudo aptitude install libssl-dev zlib1g-dev libvorbis-dev \ libtool libncursesw5-dev libao-dev -Check out revision 503 of the despotify source code:: +*OS X:* In OS X you need to have `XCode +`_ and `MacPorts +`_ installed. Then, to install despotify's +dependencies:: - svn co https://despotify.svn.sourceforge.net/svnroot/despotify@503 despotify + sudo port install openssl zlib libvorbis libtool ncursesw libao -Build and install despotify:: +*All OS:* Check out revision 503 of the despotify source code:: + + svn co https://despotify.svn.sourceforge.net/svnroot/despotify@503 + +*OS X:* Edit ``despotify/src/Makefile.local.mk`` and uncomment the last two +lines so that it reads:: + + ## If you're on Mac OS X and have installed libvorbisfile + ## via 'port install ..', try uncommenting these lines + CFLAGS += -I/opt/local/include + LDFLAGS += -L/opt/local/lib + +*All OS:* Build and install despotify:: cd despotify/src/ make @@ -59,6 +74,7 @@ spytify's source comes bundled with despotify. Build and install spytify:: cd despotify/src/bindings/python/ + export PKG_CONFIG_PATH=../../lib # Needed on OS X make sudo make install From 1d6ecace000b71c1f73b6240011ebf611d3371ed Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Mon, 15 Feb 2010 19:51:22 +0100 Subject: [PATCH 13/19] docs: Adjust headers on installation page --- docs/installation.rst | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index a20fbc20..f924fd72 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -23,7 +23,7 @@ Dependencies .. _despotify: despotify backend -================= +----------------- To use the despotify backend, you first need to install despotify and spytify. @@ -33,7 +33,7 @@ To use the despotify backend, you first need to install despotify and spytify. Installing despotify --------------------- +^^^^^^^^^^^^^^^^^^^^ *Linux:* Install despotify's dependencies. At Debian/Ubuntu systems:: @@ -67,7 +67,7 @@ lines so that it reads:: Installing spytify ------------------- +^^^^^^^^^^^^^^^^^^ spytify's source comes bundled with despotify. @@ -92,7 +92,7 @@ seconds, play for five more seconds, and quit. .. _libspotify: libspotify backend -================== +------------------ As an alternative to the despotify backend, we are working on a libspotify backend. To use the libspotify backend you must install libspotify and @@ -105,7 +105,7 @@ pyspotify. Installing libspotify ---------------------- +^^^^^^^^^^^^^^^^^^^^^ As libspotify's installation script at the moment is somewhat broken (see this `GetSatisfaction thread `_ @@ -115,7 +115,7 @@ must get libspotify from https://developer.spotify.com/en/libspotify/. Installing pyspotify --------------------- +^^^^^^^^^^^^^^^^^^^^ Install pyspotify's dependencies. At Debian/Ubuntu systems:: @@ -143,8 +143,8 @@ Test your libspotify setup:: words before starting Mopidy). -Running Mopidy -============== +Settings +======== Create a file name ``local_settings.py`` in the same directory as ``settings.py``. Enter your Spotify Premium account's username and password @@ -160,6 +160,9 @@ libspotify backend, copy the Spotify application key to BACKEND = u'mopidy.backends.libspotify.LibspotifyBackend' +Running Mopidy +============== + To start Mopidy, go to the root of the Mopidy project, then simply run:: python mopidy From 0ab6a311e548b50e1ece97083398aa2e2daa301d Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Mon, 15 Feb 2010 22:37:45 +0100 Subject: [PATCH 14/19] Document ImmutableObject class --- mopidy/models.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/mopidy/models.py b/mopidy/models.py index 86561bc3..934ca8ac 100644 --- a/mopidy/models.py +++ b/mopidy/models.py @@ -1,6 +1,14 @@ from copy import copy class ImmutableObject(object): + """ + Superclass for immutable objects whose fields can only be modified via the + constructor. + + :param kwargs: kwargs to set as fields on the object + :type kwargs: any + """ + def __init__(self, *args, **kwargs): self.__dict__.update(kwargs) From a2ec5ff5b44d22cba5992128802b4334ba407ffe Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Mon, 15 Feb 2010 22:59:29 +0100 Subject: [PATCH 15/19] Switch from time.sleep to threading.Event for making search non-async --- mopidy/backends/libspotify.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/mopidy/backends/libspotify.py b/mopidy/backends/libspotify.py index b27333fd..1e11a261 100644 --- a/mopidy/backends/libspotify.py +++ b/mopidy/backends/libspotify.py @@ -42,21 +42,22 @@ class LibspotifyCurrentPlaylistController(BaseCurrentPlaylistController): class LibspotifyLibraryController(BaseLibraryController): - search_results = False + _search_results = None + _search_results_received = threading.Event() def search(self, type, what): - # XXX This is slow - self.search_results = None + # FIXME This is slow, like 12-14s between querying and getting results + self._search_results_received.clear() + query = u'%s:%s' % (type, what) def callback(results, userdata): logger.debug(u'Search results received') - self.search_results = results - query = u'%s:%s' % (type, what) + self._search_results = results + self._search_results_received.set() self.backend.spotify.search(query.encode(ENCODING), callback) - while self.search_results is None: - time.sleep(0.01) + self._search_results_received.wait() result = Playlist(tracks=[self.backend.translate.to_mopidy_track(t) - for t in self.search_results.tracks()]) - self.search_results = False + for t in self._search_results.tracks()]) + self._search_results = None return result From b940c4f3668eb8c5839c958f59e13c3cd7a7f194 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Mon, 15 Feb 2010 23:11:44 +0100 Subject: [PATCH 16/19] libspotify: Add new info to search slowness comment --- mopidy/backends/libspotify.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mopidy/backends/libspotify.py b/mopidy/backends/libspotify.py index 1e11a261..ce59a7f0 100644 --- a/mopidy/backends/libspotify.py +++ b/mopidy/backends/libspotify.py @@ -46,7 +46,8 @@ class LibspotifyLibraryController(BaseLibraryController): _search_results_received = threading.Event() def search(self, type, what): - # FIXME This is slow, like 12-14s between querying and getting results + # FIXME When searching while playing music, this is really slow, like + # 12-14s between querying and getting results. self._search_results_received.clear() query = u'%s:%s' % (type, what) def callback(results, userdata): From 115e726dea52a3ddfbe0dbfa36f82ff3d99334f8 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Mon, 15 Feb 2010 23:20:48 +0100 Subject: [PATCH 17/19] Fix missing song position in 'currentsong' output --- mopidy/mpd/handler.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mopidy/mpd/handler.py b/mopidy/mpd/handler.py index aacad28c..d9ba1713 100644 --- a/mopidy/mpd/handler.py +++ b/mopidy/mpd/handler.py @@ -148,7 +148,8 @@ class MpdHandler(object): @register(r'^currentsong$') def _currentsong(self): if self.backend.playback.current_track is not None: - return self.backend.playback.current_track.mpd_format() + return self.backend.playback.current_track.mpd_format( + position=self.backend.playback.playlist_position) @register(r'^decoders$') def _decoders(self): From 59f1307d83f89867cee1ddce3c659ebcc9fec90d Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Mon, 15 Feb 2010 23:22:37 +0100 Subject: [PATCH 18/19] Update 'currentsong' test --- tests/mpd/handlertest.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/mpd/handlertest.py b/tests/mpd/handlertest.py index 339d73df..8996d6af 100644 --- a/tests/mpd/handlertest.py +++ b/tests/mpd/handlertest.py @@ -100,7 +100,9 @@ class StatusHandlerTest(unittest.TestCase): self.assert_(u'ACK Not implemented' in result) def test_currentsong(self): - self.b.playback.current_track = Track() + track = Track() + self.b.current_playlist.playlist = Playlist(tracks=[track]) + self.b.playback.current_track = track result = self.h.handle_request(u'currentsong') self.assert_(u'file: ' in result) self.assert_(u'Time: 0' in result) From 88b4cc30472d8ba082c87646fb469a63f06b652d Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Tue, 16 Feb 2010 07:40:21 +0100 Subject: [PATCH 19/19] libspotify: Remove unused import --- mopidy/backends/libspotify.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mopidy/backends/libspotify.py b/mopidy/backends/libspotify.py index ce59a7f0..dfe40609 100644 --- a/mopidy/backends/libspotify.py +++ b/mopidy/backends/libspotify.py @@ -1,7 +1,6 @@ import datetime as dt import logging import threading -import time from spotify import Link from spotify.manager import SpotifySessionManager