parent
c54db3298f
commit
2abce2af38
@ -1,5 +1,6 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from collections import defaultdict
|
||||
import urlparse
|
||||
|
||||
import pykka
|
||||
@ -16,35 +17,60 @@ class LibraryController(object):
|
||||
uri_scheme = urlparse.urlparse(uri).scheme
|
||||
return self.backends.with_library_by_uri_scheme.get(uri_scheme, None)
|
||||
|
||||
def find_exact(self, query=None, **kwargs):
|
||||
def _get_backends_to_uris(self, uris):
|
||||
if uris:
|
||||
backends_to_uris = defaultdict(list)
|
||||
for uri in uris:
|
||||
backend = self._get_backend(uri)
|
||||
if backend is not None:
|
||||
backends_to_uris[backend].append(uri)
|
||||
else:
|
||||
backends_to_uris = dict([
|
||||
(b, None) for b in self.backends.with_library])
|
||||
return backends_to_uris
|
||||
|
||||
def find_exact(self, query=None, uris=None, **kwargs):
|
||||
"""
|
||||
Search the library for tracks where ``field`` is ``values``.
|
||||
|
||||
If the query is empty, and the backend can support it, all available
|
||||
tracks are returned.
|
||||
|
||||
If ``uris`` is given, the search is limited to results from within the
|
||||
URI roots. For example passing ``uris=['file:']`` will limit the search
|
||||
to the local backend.
|
||||
|
||||
Examples::
|
||||
|
||||
# Returns results matching 'a'
|
||||
# Returns results matching 'a' from any backend
|
||||
find_exact({'any': ['a']})
|
||||
find_exact(any=['a'])
|
||||
|
||||
# Returns results matching artist 'xyz'
|
||||
# Returns results matching artist 'xyz' from any backend
|
||||
find_exact({'artist': ['xyz']})
|
||||
find_exact(artist=['xyz'])
|
||||
|
||||
# Returns results matching 'a' and 'b' and artist 'xyz'
|
||||
# Returns results matching 'a' and 'b' and artist 'xyz' from any
|
||||
# backend
|
||||
find_exact({'any': ['a', 'b'], 'artist': ['xyz']})
|
||||
find_exact(any=['a', 'b'], artist=['xyz'])
|
||||
|
||||
# Returns results matching 'a' if within the given URI roots
|
||||
# "file:///media/music" and "spotify:"
|
||||
find_exact(
|
||||
{'any': ['a']}, uris=['file:///media/music', 'spotify:'])
|
||||
find_exact(any=['a'], uris=['file:///media/music', 'spotify:'])
|
||||
|
||||
:param query: one or more queries to search for
|
||||
:type query: dict
|
||||
:param uris: zero or more URI roots to limit the search to
|
||||
:type uris: list of strings or :class:`None`
|
||||
:rtype: list of :class:`mopidy.models.SearchResult`
|
||||
"""
|
||||
query = query or kwargs
|
||||
futures = [
|
||||
b.library.find_exact(query=query)
|
||||
for b in self.backends.with_library]
|
||||
backend.library.find_exact(query=query, uris=uris)
|
||||
for (backend, uris) in self._get_backends_to_uris(uris).items()]
|
||||
return [result for result in pykka.get_all(futures) if result]
|
||||
|
||||
def lookup(self, uri):
|
||||
@ -80,32 +106,45 @@ class LibraryController(object):
|
||||
b.library.refresh(uri) for b in self.backends.with_library]
|
||||
pykka.get_all(futures)
|
||||
|
||||
def search(self, query=None, **kwargs):
|
||||
def search(self, query=None, uris=None, **kwargs):
|
||||
"""
|
||||
Search the library for tracks where ``field`` contains ``values``.
|
||||
|
||||
If the query is empty, and the backend can support it, all available
|
||||
tracks are returned.
|
||||
|
||||
If ``uris`` is given, the search is limited to results from within the
|
||||
URI roots. For example passing ``uris=['file:']`` will limit the search
|
||||
to the local backend.
|
||||
|
||||
Examples::
|
||||
|
||||
# Returns results matching 'a'
|
||||
# Returns results matching 'a' in any backend
|
||||
search({'any': ['a']})
|
||||
search(any=['a'])
|
||||
|
||||
# Returns results matching artist 'xyz'
|
||||
# Returns results matching artist 'xyz' in any backend
|
||||
search({'artist': ['xyz']})
|
||||
search(artist=['xyz'])
|
||||
|
||||
# Returns results matching 'a' and 'b' and artist 'xyz'
|
||||
# Returns results matching 'a' and 'b' and artist 'xyz' in any
|
||||
# backend
|
||||
search({'any': ['a', 'b'], 'artist': ['xyz']})
|
||||
search(any=['a', 'b'], artist=['xyz'])
|
||||
|
||||
# Returns results matching 'a' if within the given URI roots
|
||||
# "file:///media/music" and "spotify:"
|
||||
search({'any': ['a']}, uris=['file:///media/music', 'spotify:'])
|
||||
search(any=['a'], uris=['file:///media/music', 'spotify:'])
|
||||
|
||||
:param query: one or more queries to search for
|
||||
:type query: dict
|
||||
:param uris: zero or more URI roots to limit the search to
|
||||
:type uris: list of strings or :class:`None`
|
||||
:rtype: list of :class:`mopidy.models.SearchResult`
|
||||
"""
|
||||
query = query or kwargs
|
||||
futures = [
|
||||
b.library.search(query=query) for b in self.backends.with_library]
|
||||
backend.library.search(query=query, uris=uris)
|
||||
for (backend, uris) in self._get_backends_to_uris(uris).items()]
|
||||
return [result for result in pykka.get_all(futures) if result]
|
||||
|
||||
@ -88,9 +88,26 @@ class CoreLibraryTest(unittest.TestCase):
|
||||
self.assertIn(result1, result)
|
||||
self.assertIn(result2, result)
|
||||
self.library1.find_exact.assert_called_once_with(
|
||||
query=dict(any=['a']))
|
||||
query=dict(any=['a']), uris=None)
|
||||
self.library2.find_exact.assert_called_once_with(
|
||||
query=dict(any=['a']))
|
||||
query=dict(any=['a']), uris=None)
|
||||
|
||||
def test_find_exact_with_uris_selects_dummy1_backend(self):
|
||||
self.core.library.find_exact(
|
||||
any=['a'], uris=['dummy1:', 'dummy1:foo', 'dummy3:'])
|
||||
|
||||
self.library1.find_exact.assert_called_once_with(
|
||||
query=dict(any=['a']), uris=['dummy1:', 'dummy1:foo'])
|
||||
self.assertFalse(self.library2.find_exact.called)
|
||||
|
||||
def test_find_exact_with_uris_selects_both_backends(self):
|
||||
self.core.library.find_exact(
|
||||
any=['a'], uris=['dummy1:', 'dummy1:foo', 'dummy2:'])
|
||||
|
||||
self.library1.find_exact.assert_called_once_with(
|
||||
query=dict(any=['a']), uris=['dummy1:', 'dummy1:foo'])
|
||||
self.library2.find_exact.assert_called_once_with(
|
||||
query=dict(any=['a']), uris=['dummy2:'])
|
||||
|
||||
def test_find_exact_filters_out_none(self):
|
||||
track1 = Track(uri='dummy1:a')
|
||||
@ -106,9 +123,9 @@ class CoreLibraryTest(unittest.TestCase):
|
||||
self.assertIn(result1, result)
|
||||
self.assertNotIn(None, result)
|
||||
self.library1.find_exact.assert_called_once_with(
|
||||
query=dict(any=['a']))
|
||||
query=dict(any=['a']), uris=None)
|
||||
self.library2.find_exact.assert_called_once_with(
|
||||
query=dict(any=['a']))
|
||||
query=dict(any=['a']), uris=None)
|
||||
|
||||
def test_find_accepts_query_dict_instead_of_kwargs(self):
|
||||
track1 = Track(uri='dummy1:a')
|
||||
@ -126,9 +143,9 @@ class CoreLibraryTest(unittest.TestCase):
|
||||
self.assertIn(result1, result)
|
||||
self.assertIn(result2, result)
|
||||
self.library1.find_exact.assert_called_once_with(
|
||||
query=dict(any=['a']))
|
||||
query=dict(any=['a']), uris=None)
|
||||
self.library2.find_exact.assert_called_once_with(
|
||||
query=dict(any=['a']))
|
||||
query=dict(any=['a']), uris=None)
|
||||
|
||||
def test_search_combines_results_from_all_backends(self):
|
||||
track1 = Track(uri='dummy1:a')
|
||||
@ -146,9 +163,26 @@ class CoreLibraryTest(unittest.TestCase):
|
||||
self.assertIn(result1, result)
|
||||
self.assertIn(result2, result)
|
||||
self.library1.search.assert_called_once_with(
|
||||
query=dict(any=['a']))
|
||||
query=dict(any=['a']), uris=None)
|
||||
self.library2.search.assert_called_once_with(
|
||||
query=dict(any=['a']))
|
||||
query=dict(any=['a']), uris=None)
|
||||
|
||||
def test_search_with_uris_selects_dummy1_backend(self):
|
||||
self.core.library.search(
|
||||
query=dict(any=['a']), uris=['dummy1:', 'dummy1:foo', 'dummy3:'])
|
||||
|
||||
self.library1.search.assert_called_once_with(
|
||||
query=dict(any=['a']), uris=['dummy1:', 'dummy1:foo'])
|
||||
self.assertFalse(self.library2.search.called)
|
||||
|
||||
def test_search_with_uris_selects_both_backends(self):
|
||||
self.core.library.search(
|
||||
query=dict(any=['a']), uris=['dummy1:', 'dummy1:foo', 'dummy2:'])
|
||||
|
||||
self.library1.search.assert_called_once_with(
|
||||
query=dict(any=['a']), uris=['dummy1:', 'dummy1:foo'])
|
||||
self.library2.search.assert_called_once_with(
|
||||
query=dict(any=['a']), uris=['dummy2:'])
|
||||
|
||||
def test_search_filters_out_none(self):
|
||||
track1 = Track(uri='dummy1:a')
|
||||
@ -164,9 +198,9 @@ class CoreLibraryTest(unittest.TestCase):
|
||||
self.assertIn(result1, result)
|
||||
self.assertNotIn(None, result)
|
||||
self.library1.search.assert_called_once_with(
|
||||
query=dict(any=['a']))
|
||||
query=dict(any=['a']), uris=None)
|
||||
self.library2.search.assert_called_once_with(
|
||||
query=dict(any=['a']))
|
||||
query=dict(any=['a']), uris=None)
|
||||
|
||||
def test_search_accepts_query_dict_instead_of_kwargs(self):
|
||||
track1 = Track(uri='dummy1:a')
|
||||
@ -184,6 +218,6 @@ class CoreLibraryTest(unittest.TestCase):
|
||||
self.assertIn(result1, result)
|
||||
self.assertIn(result2, result)
|
||||
self.library1.search.assert_called_once_with(
|
||||
query=dict(any=['a']))
|
||||
query=dict(any=['a']), uris=None)
|
||||
self.library2.search.assert_called_once_with(
|
||||
query=dict(any=['a']))
|
||||
query=dict(any=['a']), uris=None)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user