Merge branch 'master' of git://github.com/jodal/mopidy into gstreamer

This commit is contained in:
Johannes Knutsen 2010-08-09 12:24:43 +02:00
commit da05ed8f87
7 changed files with 120 additions and 69 deletions

View File

@ -15,10 +15,21 @@ Another great release.
- Exit early if not Python >= 2.6, < 3.
- Include Sphinx scripts for building docs, pylintrc, tests and test data in
the packages created by ``setup.py`` for i.e. PyPI.
- MPD frontend:
- Search improvements, including support for multi-word search.
- Backend API:
- The ``id`` field of :class:`mopidy.models.Track` has been removed, as it is
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)

View File

@ -283,12 +283,21 @@ class BaseLibraryController(object):
"""Cleanup after component."""
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')]
:type query: list of (field, what) tuples.
Examples::
# 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`
"""
raise NotImplementedError
@ -318,9 +327,12 @@ class BaseLibraryController(object):
Examples::
search(any=['a']) # Returns results matching 'a'
search(artist=['xyz']) # Returns results matching artist 'xyz'
search(any=['a', 'b'], artist=['xyz']) # Returns results matching 'a' and 'b' and artist 'xyz'
# Returns results matching 'a'
search(any=['a'])
# 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
:type query: dict

View File

@ -53,6 +53,9 @@ class DespotifyCurrentPlaylistController(BaseCurrentPlaylistController):
class DespotifyLibraryController(BaseLibraryController):
def find_exact(self, **query):
return self.search(**query)
def lookup(self, uri):
track = self.backend.spotify.lookup(uri.encode(ENCODING))
return DespotifyTranslator.to_mopidy_track(track)
@ -76,8 +79,6 @@ class DespotifyLibraryController(BaseLibraryController):
return Playlist()
return DespotifyTranslator.to_mopidy_playlist(result.playlist)
find_exact = search
class DespotifyPlaybackController(BasePlaybackController):
def _pause(self):

View File

@ -217,38 +217,55 @@ class GStreamerLibraryController(BaseLibraryController):
except KeyError:
raise LookupError('%s not found.' % uri)
def find_exact(self, query):
for (field, what) in query:
if not what:
raise LookupError('Missing query')
def find_exact(self, **query):
self._validate_query(query)
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)
def search(self, **query):
self._validate_query(query)
result_tracks = self._uri_mapping.values()
for (field, values) in query:
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().lower()
# FIXME this is bound to be slow for large libraries
track_filter = lambda t: q in t.name.lower()
album_filter = lambda t: q in getattr(t, 'album', Album()).name.lower()
artist_filter = lambda t: filter(lambda a: q in a.name.lower(),
t.artists)
album_filter = lambda t: q in getattr(
t, 'album', Album()).name.lower()
artist_filter = lambda t: filter(
lambda a: q in a.name.lower(), t.artists)
uri_filter = lambda t: q in t.uri.lower()
any_filter = lambda t: track_filter(t) or album_filter(t) or \
artist_filter(t) or uri_filter(t)
@ -265,5 +282,12 @@ class GStreamerLibraryController(BaseLibraryController):
result_tracks = filter(any_filter, result_tracks)
else:
raise LookupError('Invalid lookup field: %s' % field)
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')

View File

@ -680,7 +680,9 @@ class MpdFrontend(object):
Lists all tags of the specified type. ``TYPE`` should be ``album``,
``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.
*GMPC:*

View File

@ -1079,120 +1079,120 @@ class BaseLibraryControllerTest(object):
self.assertRaises(LookupError, test)
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())
result = self.library.find_exact([('artist', 'unknown artist')])
result = self.library.find_exact(artist=['unknown artist'])
self.assertEqual(result, Playlist())
result = self.library.find_exact([('album', 'unknown artist')])
result = self.library.find_exact(album=['unknown artist'])
self.assertEqual(result, Playlist())
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]))
result = self.library.find_exact([('artist', 'artist2')])
result = self.library.find_exact(artist=['artist2'])
self.assertEqual(result, Playlist(tracks=self.tracks[1:2]))
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]))
result = self.library.find_exact([('track', 'track2')])
result = self.library.find_exact(track=['track2'])
self.assertEqual(result, Playlist(tracks=self.tracks[1:2]))
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]))
result = self.library.find_exact([('album', 'album2')])
result = self.library.find_exact(album=['album2'])
self.assertEqual(result, Playlist(tracks=self.tracks[1:2]))
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)
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)
test = lambda: self.library.find_exact([('track', '')])
test = lambda: self.library.find_exact(track=[''])
self.assertRaises(LookupError, test)
test = lambda: self.library.find_exact([('album', '')])
test = lambda: self.library.find_exact(album=[''])
self.assertRaises(LookupError, test)
def test_search_no_hits(self):
result = self.library.search([('track', 'unknown track')])
result = self.library.search(track=['unknown track'])
self.assertEqual(result, Playlist())
result = self.library.search([('artist', 'unknown artist')])
result = self.library.search(artist=['unknown artist'])
self.assertEqual(result, Playlist())
result = self.library.search([('album', 'unknown artist')])
result = self.library.search(album=['unknown artist'])
self.assertEqual(result, Playlist())
result = self.library.search([('uri', 'unknown')])
result = self.library.search(uri=['unknown'])
self.assertEqual(result, Playlist())
result = self.library.search([('any', 'unknown')])
result = self.library.search(any=['unknown'])
self.assertEqual(result, Playlist())
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]))
result = self.library.search([('artist', 'Tist2')])
result = self.library.search(artist=['Tist2'])
self.assertEqual(result, Playlist(tracks=self.tracks[1:2]))
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]))
result = self.library.search([('track', 'Rack2')])
result = self.library.search(track=['Rack2'])
self.assertEqual(result, Playlist(tracks=self.tracks[1:2]))
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]))
result = self.library.search([('album', 'Bum2')])
result = self.library.search(album=['Bum2'])
self.assertEqual(result, Playlist(tracks=self.tracks[1:2]))
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]))
result = self.library.search([('uri', 'RI2')])
result = self.library.search(uri=['RI2'])
self.assertEqual(result, Playlist(tracks=self.tracks[1:2]))
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]))
result = self.library.search([('any', 'Rack1')])
result = self.library.search(any=['Rack1'])
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]))
result = self.library.search([('any', 'RI1')])
result = self.library.search(any=['RI1'])
self.assertEqual(result, Playlist(tracks=self.tracks[:1]))
def test_search_wrong_type(self):
test = lambda: self.library.search([('wrong', 'test')])
test = lambda: self.library.search(wrong=['test'])
self.assertRaises(LookupError, test)
def test_search_with_empty_query(self):
test = lambda: self.library.search([('artist', '')])
test = lambda: self.library.search(artist=[''])
self.assertRaises(LookupError, test)
test = lambda: self.library.search([('track', '')])
test = lambda: self.library.search(track=[''])
self.assertRaises(LookupError, test)
test = lambda: self.library.search([('album', '')])
test = lambda: self.library.search(album=[''])
self.assertRaises(LookupError, test)
test = lambda: self.library.search([('uri', '')])
test = lambda: self.library.search(uri=[''])
self.assertRaises(LookupError, test)
test = lambda: self.library.search([('any', '')])
test = lambda: self.library.search(any=[''])
self.assertRaises(LookupError, test)

View File

@ -45,7 +45,8 @@ class MusicDatabaseHandlerTest(unittest.TestCase):
self.assertEqual(result[0], u'ACK [2@0] {find} incorrect arguments')
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)
def test_findadd(self):
@ -75,7 +76,7 @@ class MusicDatabaseHandlerTest(unittest.TestCase):
def test_list_album_with_artist(self):
result = self.h.handle_request(u'list "album" "anartist"')
self.assert_(u'OK' in result)
def test_list_album_artist_with_artist_without_quotes(self):
result = self.h.handle_request(u'list album artist "anartist"')
self.assert_(u'OK' in result)