Merge branch 'master' of git://github.com/jodal/mopidy into gstreamer
This commit is contained in:
commit
da05ed8f87
@ -15,10 +15,21 @@ Another great release.
|
|||||||
- Exit early if not Python >= 2.6, < 3.
|
- Exit early if not Python >= 2.6, < 3.
|
||||||
- Include Sphinx scripts for building docs, pylintrc, tests and test data in
|
- Include Sphinx scripts for building docs, pylintrc, tests and test data in
|
||||||
the packages created by ``setup.py`` for i.e. PyPI.
|
the packages created by ``setup.py`` for i.e. PyPI.
|
||||||
|
- MPD frontend:
|
||||||
|
|
||||||
|
- Search improvements, including support for multi-word search.
|
||||||
|
|
||||||
- Backend API:
|
- Backend API:
|
||||||
|
|
||||||
- The ``id`` field of :class:`mopidy.models.Track` has been removed, as it is
|
- The ``id`` field of :class:`mopidy.models.Track` has been removed, as it is
|
||||||
no longer needed after the CPID refactoring.
|
no longer needed after the CPID refactoring.
|
||||||
|
- :meth:`mopidy.backends.BaseLibraryController.find_exact()` now accepts
|
||||||
|
keyword arguments of the form ``find_exact(artist=['foo'],
|
||||||
|
album=['bar'])``.
|
||||||
|
- :meth:`mopidy.backends.BaseLibraryController.search()` now accepts
|
||||||
|
keyword arguments of the form ``search(artist=['foo', 'fighters'],
|
||||||
|
album=['bar', 'grooves'])``.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
0.1.0a3 (2010-08-03)
|
0.1.0a3 (2010-08-03)
|
||||||
|
|||||||
@ -283,12 +283,21 @@ class BaseLibraryController(object):
|
|||||||
"""Cleanup after component."""
|
"""Cleanup after component."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def find_exact(self, query):
|
def find_exact(self, **query):
|
||||||
"""
|
"""
|
||||||
Find tracks in the library where ``field`` matches ``what`` exactly.
|
Search the library for tracks where ``field`` is ``values``.
|
||||||
|
|
||||||
:param query: Example: [(u'artist', u'anArtist'), (u'album', u'anAlbum')]
|
Examples::
|
||||||
:type query: list of (field, what) tuples.
|
|
||||||
|
# Returns results matching 'a'
|
||||||
|
find_exact(any=['a'])
|
||||||
|
# Returns results matching artist 'xyz'
|
||||||
|
find_exact(artist=['xyz'])
|
||||||
|
# Returns results matching 'a' and 'b' and artist 'xyz'
|
||||||
|
find_exact(any=['a', 'b'], artist=['xyz'])
|
||||||
|
|
||||||
|
:param query: one or more queries to search for
|
||||||
|
:type query: dict
|
||||||
:rtype: :class:`mopidy.models.Playlist`
|
:rtype: :class:`mopidy.models.Playlist`
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
@ -318,9 +327,12 @@ class BaseLibraryController(object):
|
|||||||
|
|
||||||
Examples::
|
Examples::
|
||||||
|
|
||||||
search(any=['a']) # Returns results matching 'a'
|
# Returns results matching 'a'
|
||||||
search(artist=['xyz']) # Returns results matching artist 'xyz'
|
search(any=['a'])
|
||||||
search(any=['a', 'b'], artist=['xyz']) # Returns results matching 'a' and 'b' and artist 'xyz'
|
# Returns results matching artist 'xyz'
|
||||||
|
search(artist=['xyz'])
|
||||||
|
# Returns results matching 'a' and 'b' and artist 'xyz'
|
||||||
|
search(any=['a', 'b'], artist=['xyz'])
|
||||||
|
|
||||||
:param query: one or more queries to search for
|
:param query: one or more queries to search for
|
||||||
:type query: dict
|
:type query: dict
|
||||||
|
|||||||
@ -53,6 +53,9 @@ class DespotifyCurrentPlaylistController(BaseCurrentPlaylistController):
|
|||||||
|
|
||||||
|
|
||||||
class DespotifyLibraryController(BaseLibraryController):
|
class DespotifyLibraryController(BaseLibraryController):
|
||||||
|
def find_exact(self, **query):
|
||||||
|
return self.search(**query)
|
||||||
|
|
||||||
def lookup(self, uri):
|
def lookup(self, uri):
|
||||||
track = self.backend.spotify.lookup(uri.encode(ENCODING))
|
track = self.backend.spotify.lookup(uri.encode(ENCODING))
|
||||||
return DespotifyTranslator.to_mopidy_track(track)
|
return DespotifyTranslator.to_mopidy_track(track)
|
||||||
@ -76,8 +79,6 @@ class DespotifyLibraryController(BaseLibraryController):
|
|||||||
return Playlist()
|
return Playlist()
|
||||||
return DespotifyTranslator.to_mopidy_playlist(result.playlist)
|
return DespotifyTranslator.to_mopidy_playlist(result.playlist)
|
||||||
|
|
||||||
find_exact = search
|
|
||||||
|
|
||||||
|
|
||||||
class DespotifyPlaybackController(BasePlaybackController):
|
class DespotifyPlaybackController(BasePlaybackController):
|
||||||
def _pause(self):
|
def _pause(self):
|
||||||
|
|||||||
@ -217,38 +217,55 @@ class GStreamerLibraryController(BaseLibraryController):
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
raise LookupError('%s not found.' % uri)
|
raise LookupError('%s not found.' % uri)
|
||||||
|
|
||||||
def find_exact(self, query):
|
def find_exact(self, **query):
|
||||||
for (field, what) in query:
|
self._validate_query(query)
|
||||||
if not what:
|
|
||||||
raise LookupError('Missing query')
|
|
||||||
|
|
||||||
result_tracks = self._uri_mapping.values()
|
result_tracks = self._uri_mapping.values()
|
||||||
for (field, what) in query:
|
|
||||||
if field == 'track':
|
|
||||||
filter_func = lambda t: t.name == what
|
|
||||||
elif field == 'album':
|
|
||||||
filter_func = lambda t: getattr(t, 'album', Album()).name == what
|
|
||||||
elif field == 'artist':
|
|
||||||
filter_func = lambda t: filter(lambda a: a.name == what, t.artists)
|
|
||||||
else:
|
|
||||||
raise LookupError('Invalid lookup field: %s' % field)
|
|
||||||
|
|
||||||
result_tracks = filter(filter_func, result_tracks)
|
for (field, values) in query.iteritems():
|
||||||
|
if not hasattr(values, '__iter__'):
|
||||||
|
values = [values]
|
||||||
|
# FIXME this is bound to be slow for large libraries
|
||||||
|
for value in values:
|
||||||
|
q = value.strip()
|
||||||
|
|
||||||
|
track_filter = lambda t: q == t.name
|
||||||
|
album_filter = lambda t: q == getattr(t, 'album', Album()).name
|
||||||
|
artist_filter = lambda t: filter(
|
||||||
|
lambda a: q == a.name, t.artists)
|
||||||
|
uri_filter = lambda t: q == t.uri
|
||||||
|
any_filter = lambda t: (track_filter(t) or album_filter(t) or
|
||||||
|
artist_filter(t) or uri_filter(t))
|
||||||
|
|
||||||
|
if field == 'track':
|
||||||
|
result_tracks = filter(track_filter, result_tracks)
|
||||||
|
elif field == 'album':
|
||||||
|
result_tracks = filter(album_filter, result_tracks)
|
||||||
|
elif field == 'artist':
|
||||||
|
result_tracks = filter(artist_filter, result_tracks)
|
||||||
|
elif field == 'uri':
|
||||||
|
result_tracks = filter(uri_filter, result_tracks)
|
||||||
|
elif field == 'any':
|
||||||
|
result_tracks = filter(any_filter, result_tracks)
|
||||||
|
else:
|
||||||
|
raise LookupError('Invalid lookup field: %s' % field)
|
||||||
return Playlist(tracks=result_tracks)
|
return Playlist(tracks=result_tracks)
|
||||||
|
|
||||||
def search(self, **query):
|
def search(self, **query):
|
||||||
|
self._validate_query(query)
|
||||||
result_tracks = self._uri_mapping.values()
|
result_tracks = self._uri_mapping.values()
|
||||||
for (field, values) in query:
|
|
||||||
|
for (field, values) in query.iteritems():
|
||||||
if not hasattr(values, '__iter__'):
|
if not hasattr(values, '__iter__'):
|
||||||
values = [values]
|
values = [values]
|
||||||
|
# FIXME this is bound to be slow for large libraries
|
||||||
for value in values:
|
for value in values:
|
||||||
q = value.strip().lower()
|
q = value.strip().lower()
|
||||||
|
|
||||||
# FIXME this is bound to be slow for large libraries
|
|
||||||
track_filter = lambda t: q in t.name.lower()
|
track_filter = lambda t: q in t.name.lower()
|
||||||
album_filter = lambda t: q in getattr(t, 'album', Album()).name.lower()
|
album_filter = lambda t: q in getattr(
|
||||||
artist_filter = lambda t: filter(lambda a: q in a.name.lower(),
|
t, 'album', Album()).name.lower()
|
||||||
t.artists)
|
artist_filter = lambda t: filter(
|
||||||
|
lambda a: q in a.name.lower(), t.artists)
|
||||||
uri_filter = lambda t: q in t.uri.lower()
|
uri_filter = lambda t: q in t.uri.lower()
|
||||||
any_filter = lambda t: track_filter(t) or album_filter(t) or \
|
any_filter = lambda t: track_filter(t) or album_filter(t) or \
|
||||||
artist_filter(t) or uri_filter(t)
|
artist_filter(t) or uri_filter(t)
|
||||||
@ -265,5 +282,12 @@ class GStreamerLibraryController(BaseLibraryController):
|
|||||||
result_tracks = filter(any_filter, result_tracks)
|
result_tracks = filter(any_filter, result_tracks)
|
||||||
else:
|
else:
|
||||||
raise LookupError('Invalid lookup field: %s' % field)
|
raise LookupError('Invalid lookup field: %s' % field)
|
||||||
|
|
||||||
return Playlist(tracks=result_tracks)
|
return Playlist(tracks=result_tracks)
|
||||||
|
|
||||||
|
def _validate_query(self, query):
|
||||||
|
for (field, values) in query.iteritems():
|
||||||
|
if not values:
|
||||||
|
raise LookupError('Missing query')
|
||||||
|
for value in values:
|
||||||
|
if not value:
|
||||||
|
raise LookupError('Missing query')
|
||||||
|
|||||||
@ -680,7 +680,9 @@ class MpdFrontend(object):
|
|||||||
Lists all tags of the specified type. ``TYPE`` should be ``album``,
|
Lists all tags of the specified type. ``TYPE`` should be ``album``,
|
||||||
``artist``, ``date``, or ``genre``.
|
``artist``, ``date``, or ``genre``.
|
||||||
|
|
||||||
``ARTIST`` is an optional parameter when type is ``album``, ``date``, or ``genre``
|
``ARTIST`` is an optional parameter when type is ``album``,
|
||||||
|
``date``, or ``genre``.
|
||||||
|
|
||||||
This filters the result list by an artist.
|
This filters the result list by an artist.
|
||||||
|
|
||||||
*GMPC:*
|
*GMPC:*
|
||||||
|
|||||||
@ -1079,120 +1079,120 @@ class BaseLibraryControllerTest(object):
|
|||||||
self.assertRaises(LookupError, test)
|
self.assertRaises(LookupError, test)
|
||||||
|
|
||||||
def test_find_exact_no_hits(self):
|
def test_find_exact_no_hits(self):
|
||||||
result = self.library.find_exact([('track', 'unknown track')])
|
result = self.library.find_exact(track=['unknown track'])
|
||||||
self.assertEqual(result, Playlist())
|
self.assertEqual(result, Playlist())
|
||||||
|
|
||||||
result = self.library.find_exact([('artist', 'unknown artist')])
|
result = self.library.find_exact(artist=['unknown artist'])
|
||||||
self.assertEqual(result, Playlist())
|
self.assertEqual(result, Playlist())
|
||||||
|
|
||||||
result = self.library.find_exact([('album', 'unknown artist')])
|
result = self.library.find_exact(album=['unknown artist'])
|
||||||
self.assertEqual(result, Playlist())
|
self.assertEqual(result, Playlist())
|
||||||
|
|
||||||
def test_find_exact_artist(self):
|
def test_find_exact_artist(self):
|
||||||
result = self.library.find_exact([('artist', 'artist1')])
|
result = self.library.find_exact(artist=['artist1'])
|
||||||
self.assertEqual(result, Playlist(tracks=self.tracks[:1]))
|
self.assertEqual(result, Playlist(tracks=self.tracks[:1]))
|
||||||
|
|
||||||
result = self.library.find_exact([('artist', 'artist2')])
|
result = self.library.find_exact(artist=['artist2'])
|
||||||
self.assertEqual(result, Playlist(tracks=self.tracks[1:2]))
|
self.assertEqual(result, Playlist(tracks=self.tracks[1:2]))
|
||||||
|
|
||||||
def test_find_exact_track(self):
|
def test_find_exact_track(self):
|
||||||
result = self.library.find_exact([('track', 'track1')])
|
result = self.library.find_exact(track=['track1'])
|
||||||
self.assertEqual(result, Playlist(tracks=self.tracks[:1]))
|
self.assertEqual(result, Playlist(tracks=self.tracks[:1]))
|
||||||
|
|
||||||
result = self.library.find_exact([('track', 'track2')])
|
result = self.library.find_exact(track=['track2'])
|
||||||
self.assertEqual(result, Playlist(tracks=self.tracks[1:2]))
|
self.assertEqual(result, Playlist(tracks=self.tracks[1:2]))
|
||||||
|
|
||||||
def test_find_exact_album(self):
|
def test_find_exact_album(self):
|
||||||
result = self.library.find_exact([('album', 'album1')])
|
result = self.library.find_exact(album=['album1'])
|
||||||
self.assertEqual(result, Playlist(tracks=self.tracks[:1]))
|
self.assertEqual(result, Playlist(tracks=self.tracks[:1]))
|
||||||
|
|
||||||
result = self.library.find_exact([('album', 'album2')])
|
result = self.library.find_exact(album=['album2'])
|
||||||
self.assertEqual(result, Playlist(tracks=self.tracks[1:2]))
|
self.assertEqual(result, Playlist(tracks=self.tracks[1:2]))
|
||||||
|
|
||||||
def test_find_exact_wrong_type(self):
|
def test_find_exact_wrong_type(self):
|
||||||
test = lambda: self.library.find_exact([('wrong', 'test')])
|
test = lambda: self.library.find_exact(wrong=['test'])
|
||||||
self.assertRaises(LookupError, test)
|
self.assertRaises(LookupError, test)
|
||||||
|
|
||||||
def test_find_exact_with_empty_query(self):
|
def test_find_exact_with_empty_query(self):
|
||||||
test = lambda: self.library.find_exact([('artist', '')])
|
test = lambda: self.library.find_exact(artist=[''])
|
||||||
self.assertRaises(LookupError, test)
|
self.assertRaises(LookupError, test)
|
||||||
|
|
||||||
test = lambda: self.library.find_exact([('track', '')])
|
test = lambda: self.library.find_exact(track=[''])
|
||||||
self.assertRaises(LookupError, test)
|
self.assertRaises(LookupError, test)
|
||||||
|
|
||||||
test = lambda: self.library.find_exact([('album', '')])
|
test = lambda: self.library.find_exact(album=[''])
|
||||||
self.assertRaises(LookupError, test)
|
self.assertRaises(LookupError, test)
|
||||||
|
|
||||||
def test_search_no_hits(self):
|
def test_search_no_hits(self):
|
||||||
result = self.library.search([('track', 'unknown track')])
|
result = self.library.search(track=['unknown track'])
|
||||||
self.assertEqual(result, Playlist())
|
self.assertEqual(result, Playlist())
|
||||||
|
|
||||||
result = self.library.search([('artist', 'unknown artist')])
|
result = self.library.search(artist=['unknown artist'])
|
||||||
self.assertEqual(result, Playlist())
|
self.assertEqual(result, Playlist())
|
||||||
|
|
||||||
result = self.library.search([('album', 'unknown artist')])
|
result = self.library.search(album=['unknown artist'])
|
||||||
self.assertEqual(result, Playlist())
|
self.assertEqual(result, Playlist())
|
||||||
|
|
||||||
result = self.library.search([('uri', 'unknown')])
|
result = self.library.search(uri=['unknown'])
|
||||||
self.assertEqual(result, Playlist())
|
self.assertEqual(result, Playlist())
|
||||||
|
|
||||||
result = self.library.search([('any', 'unknown')])
|
result = self.library.search(any=['unknown'])
|
||||||
self.assertEqual(result, Playlist())
|
self.assertEqual(result, Playlist())
|
||||||
|
|
||||||
def test_search_artist(self):
|
def test_search_artist(self):
|
||||||
result = self.library.search([('artist', 'Tist1')])
|
result = self.library.search(artist=['Tist1'])
|
||||||
self.assertEqual(result, Playlist(tracks=self.tracks[:1]))
|
self.assertEqual(result, Playlist(tracks=self.tracks[:1]))
|
||||||
|
|
||||||
result = self.library.search([('artist', 'Tist2')])
|
result = self.library.search(artist=['Tist2'])
|
||||||
self.assertEqual(result, Playlist(tracks=self.tracks[1:2]))
|
self.assertEqual(result, Playlist(tracks=self.tracks[1:2]))
|
||||||
|
|
||||||
def test_search_track(self):
|
def test_search_track(self):
|
||||||
result = self.library.search([('track', 'Rack1')])
|
result = self.library.search(track=['Rack1'])
|
||||||
self.assertEqual(result, Playlist(tracks=self.tracks[:1]))
|
self.assertEqual(result, Playlist(tracks=self.tracks[:1]))
|
||||||
|
|
||||||
result = self.library.search([('track', 'Rack2')])
|
result = self.library.search(track=['Rack2'])
|
||||||
self.assertEqual(result, Playlist(tracks=self.tracks[1:2]))
|
self.assertEqual(result, Playlist(tracks=self.tracks[1:2]))
|
||||||
|
|
||||||
def test_search_album(self):
|
def test_search_album(self):
|
||||||
result = self.library.search([('album', 'Bum1')])
|
result = self.library.search(album=['Bum1'])
|
||||||
self.assertEqual(result, Playlist(tracks=self.tracks[:1]))
|
self.assertEqual(result, Playlist(tracks=self.tracks[:1]))
|
||||||
|
|
||||||
result = self.library.search([('album', 'Bum2')])
|
result = self.library.search(album=['Bum2'])
|
||||||
self.assertEqual(result, Playlist(tracks=self.tracks[1:2]))
|
self.assertEqual(result, Playlist(tracks=self.tracks[1:2]))
|
||||||
|
|
||||||
def test_search_uri(self):
|
def test_search_uri(self):
|
||||||
result = self.library.search([('uri', 'RI1')])
|
result = self.library.search(uri=['RI1'])
|
||||||
self.assertEqual(result, Playlist(tracks=self.tracks[:1]))
|
self.assertEqual(result, Playlist(tracks=self.tracks[:1]))
|
||||||
|
|
||||||
result = self.library.search([('uri', 'RI2')])
|
result = self.library.search(uri=['RI2'])
|
||||||
self.assertEqual(result, Playlist(tracks=self.tracks[1:2]))
|
self.assertEqual(result, Playlist(tracks=self.tracks[1:2]))
|
||||||
|
|
||||||
def test_search_any(self):
|
def test_search_any(self):
|
||||||
result = self.library.search([('any', 'Tist1')])
|
result = self.library.search(any=['Tist1'])
|
||||||
self.assertEqual(result, Playlist(tracks=self.tracks[:1]))
|
self.assertEqual(result, Playlist(tracks=self.tracks[:1]))
|
||||||
result = self.library.search([('any', 'Rack1')])
|
result = self.library.search(any=['Rack1'])
|
||||||
self.assertEqual(result, Playlist(tracks=self.tracks[:1]))
|
self.assertEqual(result, Playlist(tracks=self.tracks[:1]))
|
||||||
result = self.library.search([('any', 'Bum1')])
|
result = self.library.search(any=['Bum1'])
|
||||||
self.assertEqual(result, Playlist(tracks=self.tracks[:1]))
|
self.assertEqual(result, Playlist(tracks=self.tracks[:1]))
|
||||||
result = self.library.search([('any', 'RI1')])
|
result = self.library.search(any=['RI1'])
|
||||||
self.assertEqual(result, Playlist(tracks=self.tracks[:1]))
|
self.assertEqual(result, Playlist(tracks=self.tracks[:1]))
|
||||||
|
|
||||||
def test_search_wrong_type(self):
|
def test_search_wrong_type(self):
|
||||||
test = lambda: self.library.search([('wrong', 'test')])
|
test = lambda: self.library.search(wrong=['test'])
|
||||||
self.assertRaises(LookupError, test)
|
self.assertRaises(LookupError, test)
|
||||||
|
|
||||||
def test_search_with_empty_query(self):
|
def test_search_with_empty_query(self):
|
||||||
test = lambda: self.library.search([('artist', '')])
|
test = lambda: self.library.search(artist=[''])
|
||||||
self.assertRaises(LookupError, test)
|
self.assertRaises(LookupError, test)
|
||||||
|
|
||||||
test = lambda: self.library.search([('track', '')])
|
test = lambda: self.library.search(track=[''])
|
||||||
self.assertRaises(LookupError, test)
|
self.assertRaises(LookupError, test)
|
||||||
|
|
||||||
test = lambda: self.library.search([('album', '')])
|
test = lambda: self.library.search(album=[''])
|
||||||
self.assertRaises(LookupError, test)
|
self.assertRaises(LookupError, test)
|
||||||
|
|
||||||
test = lambda: self.library.search([('uri', '')])
|
test = lambda: self.library.search(uri=[''])
|
||||||
self.assertRaises(LookupError, test)
|
self.assertRaises(LookupError, test)
|
||||||
|
|
||||||
test = lambda: self.library.search([('any', '')])
|
test = lambda: self.library.search(any=[''])
|
||||||
self.assertRaises(LookupError, test)
|
self.assertRaises(LookupError, test)
|
||||||
|
|||||||
@ -45,7 +45,8 @@ class MusicDatabaseHandlerTest(unittest.TestCase):
|
|||||||
self.assertEqual(result[0], u'ACK [2@0] {find} incorrect arguments')
|
self.assertEqual(result[0], u'ACK [2@0] {find} incorrect arguments')
|
||||||
|
|
||||||
def test_find_album_and_artist(self):
|
def test_find_album_and_artist(self):
|
||||||
result = self.h.handle_request(u'find album "album_what" artist "artist_what"')
|
result = self.h.handle_request(
|
||||||
|
u'find album "album_what" artist "artist_what"')
|
||||||
self.assert_(u'OK' in result)
|
self.assert_(u'OK' in result)
|
||||||
|
|
||||||
def test_findadd(self):
|
def test_findadd(self):
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user