From af04808941dc7ef7e5c99e818dfbaa4158b2488a Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Thu, 1 Nov 2012 00:29:28 +0100 Subject: [PATCH 01/10] Make Travis use IRC notice notifications without joining the channel --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 6120e2de..bbba0a94 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,3 +17,5 @@ notifications: - "irc.freenode.org#mopidy" on_success: change on_failure: change + use_notice: true + skip_join: true From a5b454acc0087197963ddc7874866abd11582a45 Mon Sep 17 00:00:00 2001 From: Fred Hatfull Date: Wed, 31 Oct 2012 23:45:13 -0700 Subject: [PATCH 02/10] Fixes support for MPD find/search by filename Extends `find_exact` and `search` in mopidy.backends.local.library to support the `filename` query field. This field can get passed in from the MPD frontend and would break with a `LookupError` when used. This patch fixes the issue and introduces two new tests to cover the added functionality. --- mopidy/backends/local/library.py | 4 ++-- tests/backends/base/library.py | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/mopidy/backends/local/library.py b/mopidy/backends/local/library.py index 600bfaaa..db37edb3 100644 --- a/mopidy/backends/local/library.py +++ b/mopidy/backends/local/library.py @@ -59,7 +59,7 @@ class LocalLibraryProvider(base.BaseLibraryProvider): result_tracks = filter(album_filter, result_tracks) elif field == 'artist': result_tracks = filter(artist_filter, result_tracks) - elif field == 'uri': + elif field == 'uri' or field == 'filename': result_tracks = filter(uri_filter, result_tracks) elif field == 'any': result_tracks = filter(any_filter, result_tracks) @@ -93,7 +93,7 @@ class LocalLibraryProvider(base.BaseLibraryProvider): result_tracks = filter(album_filter, result_tracks) elif field == 'artist': result_tracks = filter(artist_filter, result_tracks) - elif field == 'uri': + elif field == 'uri' or field == 'filename': result_tracks = filter(uri_filter, result_tracks) elif field == 'any': result_tracks = filter(any_filter, result_tracks) diff --git a/tests/backends/base/library.py b/tests/backends/base/library.py index cc2a0004..b7510dbb 100644 --- a/tests/backends/base/library.py +++ b/tests/backends/base/library.py @@ -79,6 +79,15 @@ class LibraryControllerTest(object): result = self.library.find_exact(album=['album2']) self.assertEqual(result, Playlist(tracks=self.tracks[1:2])) + def test_find_exact_filename(self): + track_1_filename = 'file://' + path_to_data_dir('uri1') + result = self.library.find_exact(filename=track_1_filename) + self.assertEqual(result, Playlist(tracks=self.tracks[:1])) + + track_2_filename = 'file://' + path_to_data_dir('uri2') + result = self.library.find_exact(filename=track_2_filename) + self.assertEqual(result, Playlist(tracks=self.tracks[1:2])) + def test_find_exact_wrong_type(self): test = lambda: self.library.find_exact(wrong=['test']) self.assertRaises(LookupError, test) @@ -137,6 +146,13 @@ class LibraryControllerTest(object): result = self.library.search(uri=['RI2']) self.assertEqual(result, Playlist(tracks=self.tracks[1:2])) + def test_search_filename(self): + result = self.library.search(filename=['RI1']) + self.assertEqual(result, Playlist(tracks=self.tracks[:1])) + + result = self.library.search(filename=['RI2']) + self.assertEqual(result, Playlist(tracks=self.tracks[1:2])) + def test_search_any(self): result = self.library.search(any=['Tist1']) self.assertEqual(result, Playlist(tracks=self.tracks[:1])) From c291c9c83e6429981ebc38714adc4eb23fb383af Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Thu, 1 Nov 2012 19:36:11 +0100 Subject: [PATCH 03/10] Style fix --- mopidy/backends/local/library.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mopidy/backends/local/library.py b/mopidy/backends/local/library.py index db37edb3..09484ed0 100644 --- a/mopidy/backends/local/library.py +++ b/mopidy/backends/local/library.py @@ -59,7 +59,7 @@ class LocalLibraryProvider(base.BaseLibraryProvider): result_tracks = filter(album_filter, result_tracks) elif field == 'artist': result_tracks = filter(artist_filter, result_tracks) - elif field == 'uri' or field == 'filename': + elif field in ('uri', 'filename'): result_tracks = filter(uri_filter, result_tracks) elif field == 'any': result_tracks = filter(any_filter, result_tracks) From 3ce986f61986670152f417946f5c6034b9613132 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Thu, 1 Nov 2012 19:36:24 +0100 Subject: [PATCH 04/10] Update changelog with search by filename --- docs/changes.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/changes.rst b/docs/changes.rst index 0203a89f..f3b2a25d 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -68,6 +68,8 @@ backends: - The Spotify backend now includes release year and artist on albums. +- Added support for search by filename to local backend. + v0.8.1 (2012-10-30) =================== From 590270546b68c9db1e30da8450de1fabee4af707 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Thu, 1 Nov 2012 20:15:20 +0100 Subject: [PATCH 05/10] Style fix --- mopidy/backends/local/library.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mopidy/backends/local/library.py b/mopidy/backends/local/library.py index 09484ed0..9abdf7ed 100644 --- a/mopidy/backends/local/library.py +++ b/mopidy/backends/local/library.py @@ -93,7 +93,7 @@ class LocalLibraryProvider(base.BaseLibraryProvider): result_tracks = filter(album_filter, result_tracks) elif field == 'artist': result_tracks = filter(artist_filter, result_tracks) - elif field == 'uri' or field == 'filename': + elif field in ('uri', 'filename'): result_tracks = filter(uri_filter, result_tracks) elif field == 'any': result_tracks = filter(any_filter, result_tracks) From 548dd186cfb77f043357873cdec157caa08f193c Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Thu, 1 Nov 2012 21:58:24 +0100 Subject: [PATCH 06/10] Don't include actor URN in MPD debug log --- mopidy/frontends/mpd/session.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/mopidy/frontends/mpd/session.py b/mopidy/frontends/mpd/session.py index b5368a08..5d535f75 100644 --- a/mopidy/frontends/mpd/session.py +++ b/mopidy/frontends/mpd/session.py @@ -25,17 +25,14 @@ class MpdSession(network.LineProtocol): self.send_lines([u'OK MPD %s' % protocol.VERSION]) def on_line_received(self, line): - logger.debug( - u'Request from [%s]:%s to %s: %s', - self.host, self.port, self.actor_urn, line) + logger.debug(u'Request from [%s]:%s: %s', self.host, self.port, line) response = self.dispatcher.handle_request(line) if not response: return logger.debug( - u'Response to [%s]:%s from %s: %s', - self.host, self.port, self.actor_urn, + u'Response to [%s]:%s: %s', self.host, self.port, formatting.indent(self.terminator.join(response))) self.send_lines(response) From 60112897d20d2b321a3fc819dad1a6bc4fe0c4e4 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Thu, 1 Nov 2012 22:27:53 +0100 Subject: [PATCH 07/10] MPD: Support listplaylist{,info} without quotes around spaceless playlist name (fixes #218) --- docs/changes.rst | 5 +++++ .../frontends/mpd/protocol/stored_playlists.py | 2 ++ .../mpd/protocol/stored_playlists_test.py | 18 ++++++++++++++++++ 3 files changed, 25 insertions(+) diff --git a/docs/changes.rst b/docs/changes.rst index f3b2a25d..34e155c9 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -70,6 +70,11 @@ backends: - Added support for search by filename to local backend. +**Bug fixes** + +- :issue:`218`: The MPD commands ``listplaylist`` and ``listplaylistinfo`` now + accepts unquotes playlist names if they don't contain spaces. + v0.8.1 (2012-10-30) =================== diff --git a/mopidy/frontends/mpd/protocol/stored_playlists.py b/mopidy/frontends/mpd/protocol/stored_playlists.py index ed1c38ab..17e5abf7 100644 --- a/mopidy/frontends/mpd/protocol/stored_playlists.py +++ b/mopidy/frontends/mpd/protocol/stored_playlists.py @@ -5,6 +5,7 @@ from mopidy.frontends.mpd.protocol import handle_request from mopidy.frontends.mpd.translator import playlist_to_mpd_format +@handle_request(r'^listplaylist (?P\S+)$') @handle_request(r'^listplaylist "(?P[^"]+)"$') def listplaylist(context, name): """ @@ -27,6 +28,7 @@ def listplaylist(context, name): raise MpdNoExistError(u'No such playlist', command=u'listplaylist') +@handle_request(r'^listplaylistinfo (?P\S+)$') @handle_request(r'^listplaylistinfo "(?P[^"]+)"$') def listplaylistinfo(context, name): """ diff --git a/tests/frontends/mpd/protocol/stored_playlists_test.py b/tests/frontends/mpd/protocol/stored_playlists_test.py index 8cfcb338..ae99fe2a 100644 --- a/tests/frontends/mpd/protocol/stored_playlists_test.py +++ b/tests/frontends/mpd/protocol/stored_playlists_test.py @@ -14,6 +14,14 @@ class StoredPlaylistsHandlerTest(protocol.BaseTestCase): self.assertInResponse(u'file: file:///dev/urandom') self.assertInResponse(u'OK') + def test_listplaylist_without_quotes(self): + self.core.stored_playlists.playlists = [ + Playlist(name='name', tracks=[Track(uri='file:///dev/urandom')])] + + self.sendRequest(u'listplaylist name') + self.assertInResponse(u'file: file:///dev/urandom') + self.assertInResponse(u'OK') + def test_listplaylist_fails_if_no_playlist_is_found(self): self.sendRequest(u'listplaylist "name"') self.assertEqualResponse(u'ACK [50@0] {listplaylist} No such playlist') @@ -28,6 +36,16 @@ class StoredPlaylistsHandlerTest(protocol.BaseTestCase): self.assertNotInResponse(u'Pos: 0') self.assertInResponse(u'OK') + def test_listplaylistinfo_without_quotes(self): + self.core.stored_playlists.playlists = [ + Playlist(name='name', tracks=[Track(uri='file:///dev/urandom')])] + + self.sendRequest(u'listplaylistinfo name') + self.assertInResponse(u'file: file:///dev/urandom') + self.assertInResponse(u'Track: 0') + self.assertNotInResponse(u'Pos: 0') + self.assertInResponse(u'OK') + def test_listplaylistinfo_fails_if_no_playlist_is_found(self): self.sendRequest(u'listplaylistinfo "name"') self.assertEqualResponse( From 0d16af97a5638450814ab7687e3147d4786115ed Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Thu, 1 Nov 2012 22:52:17 +0100 Subject: [PATCH 08/10] Document 'audio' constructor arg to playback providers --- mopidy/backends/base.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mopidy/backends/base.py b/mopidy/backends/base.py index 7ae2c3dc..9f6de405 100644 --- a/mopidy/backends/base.py +++ b/mopidy/backends/base.py @@ -70,6 +70,8 @@ class BaseLibraryProvider(object): class BasePlaybackProvider(object): """ + :param audio: the audio actor + :type audio: actor proxy to an instance of :class:`mopidy.audio.Audio` :param backend: the backend :type backend: :class:`mopidy.backends.base.Backend` """ From d985b8be380dadec850d9c5fb29c086aa115ae0b Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Thu, 1 Nov 2012 23:28:19 +0100 Subject: [PATCH 09/10] Fix plchanges so it returns nothing when nothing has changed --- docs/changes.rst | 3 +++ .../mpd/protocol/current_playlist.py | 2 +- .../mpd/protocol/current_playlist_test.py | 24 ++++++++++++++++++- 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/docs/changes.rst b/docs/changes.rst index 34e155c9..39ddc251 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -75,6 +75,9 @@ backends: - :issue:`218`: The MPD commands ``listplaylist`` and ``listplaylistinfo`` now accepts unquotes playlist names if they don't contain spaces. +- The MPD command ``plchanges`` always returned the entire playlist. It now + returns an empty response when the client has seen the latest version. + v0.8.1 (2012-10-30) =================== diff --git a/mopidy/frontends/mpd/protocol/current_playlist.py b/mopidy/frontends/mpd/protocol/current_playlist.py index 429af2cc..5a88d41b 100644 --- a/mopidy/frontends/mpd/protocol/current_playlist.py +++ b/mopidy/frontends/mpd/protocol/current_playlist.py @@ -307,7 +307,7 @@ def plchanges(context, version): - Calls ``plchanges "-1"`` two times per second to get the entire playlist. """ # XXX Naive implementation that returns all tracks as changed - if int(version) < context.core.current_playlist.version: + if int(version) < context.core.current_playlist.version.get(): return translator.tracks_to_mpd_format( context.core.current_playlist.cp_tracks.get()) diff --git a/tests/frontends/mpd/protocol/current_playlist_test.py b/tests/frontends/mpd/protocol/current_playlist_test.py index a64b08ea..bd58cf2d 100644 --- a/tests/frontends/mpd/protocol/current_playlist_test.py +++ b/tests/frontends/mpd/protocol/current_playlist_test.py @@ -364,7 +364,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase): self.sendRequest(u'playlistsearch any "needle"') self.assertEqualResponse(u'ACK [0@0] {} Not implemented') - def test_plchanges(self): + def test_plchanges_with_lower_version_returns_changes(self): self.core.current_playlist.append( [Track(name='a'), Track(name='b'), Track(name='c')]) @@ -374,6 +374,28 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase): self.assertInResponse(u'Title: c') self.assertInResponse(u'OK') + def test_plchanges_with_equal_version_returns_nothing(self): + self.core.current_playlist.append( + [Track(name='a'), Track(name='b'), Track(name='c')]) + + self.assertEqual(self.core.current_playlist.version.get(), 1) + self.sendRequest(u'plchanges "1"') + self.assertNotInResponse(u'Title: a') + self.assertNotInResponse(u'Title: b') + self.assertNotInResponse(u'Title: c') + self.assertInResponse(u'OK') + + def test_plchanges_with_greater_version_returns_nothing(self): + self.core.current_playlist.append( + [Track(name='a'), Track(name='b'), Track(name='c')]) + + self.assertEqual(self.core.current_playlist.version.get(), 1) + self.sendRequest(u'plchanges "2"') + self.assertNotInResponse(u'Title: a') + self.assertNotInResponse(u'Title: b') + self.assertNotInResponse(u'Title: c') + self.assertInResponse(u'OK') + def test_plchanges_with_minus_one_returns_entire_playlist(self): self.core.current_playlist.append( [Track(name='a'), Track(name='b'), Track(name='c')]) From 4aee340b77a717e16549bfbbef77e3e45f20c791 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sat, 3 Nov 2012 17:52:13 +0100 Subject: [PATCH 10/10] Add flake8 and pylint to test requirements --- requirements/tests.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/requirements/tests.txt b/requirements/tests.txt index e24edd3c..20aff929 100644 --- a/requirements/tests.txt +++ b/requirements/tests.txt @@ -1,6 +1,8 @@ coverage +flake8 mock >= 0.7 nose +pylint tox unittest2 yappi