core: Update browse to use uri isntead of path
This commit is contained in:
parent
826419d829
commit
999f478010
@ -37,6 +37,9 @@ class Backend(object):
|
||||
def has_library(self):
|
||||
return self.library is not None
|
||||
|
||||
def has_library_browse(self):
|
||||
return self.has_library() and self.library.root_directory is not None
|
||||
|
||||
def has_playback(self):
|
||||
return self.playback is not None
|
||||
|
||||
|
||||
@ -88,7 +88,7 @@ class Backends(list):
|
||||
super(Backends, self).__init__(backends)
|
||||
|
||||
self.with_library = collections.OrderedDict()
|
||||
self.with_browsable_library = collections.OrderedDict()
|
||||
self.with_library_browse = collections.OrderedDict()
|
||||
self.with_playback = collections.OrderedDict()
|
||||
self.with_playlists = collections.OrderedDict()
|
||||
|
||||
@ -97,6 +97,7 @@ class Backends(list):
|
||||
|
||||
for backend in backends:
|
||||
has_library = backend.has_library().get()
|
||||
has_library_browse = backend.has_library_browse().get()
|
||||
has_playback = backend.has_playback().get()
|
||||
has_playlists = backend.has_playlists().get()
|
||||
|
||||
@ -109,12 +110,9 @@ class Backends(list):
|
||||
|
||||
if has_library:
|
||||
self.with_library[scheme] = backend
|
||||
if has_library_browse:
|
||||
self.with_library_browse[scheme] = backend
|
||||
if has_playback:
|
||||
self.with_playback[scheme] = backend
|
||||
if has_playlists:
|
||||
self.with_playlists[scheme] = backend
|
||||
|
||||
if has_library:
|
||||
root_dir = backend.library.root_directory.get()
|
||||
if root_dir is not None:
|
||||
self.with_browsable_library[root_dir] = backend
|
||||
|
||||
@ -1,13 +1,10 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import collections
|
||||
import re
|
||||
import urlparse
|
||||
|
||||
import pykka
|
||||
|
||||
from mopidy.models import Ref
|
||||
|
||||
|
||||
class LibraryController(object):
|
||||
pykka_traversable = True
|
||||
@ -32,15 +29,16 @@ class LibraryController(object):
|
||||
(b, None) for b in self.backends.with_library.values()])
|
||||
return backends_to_uris
|
||||
|
||||
def browse(self, path):
|
||||
def browse(self, uri):
|
||||
"""
|
||||
Browse directories and tracks at the given ``path``.
|
||||
Browse directories and tracks at the given ``uri``.
|
||||
|
||||
``path`` is a string that always starts with "/". It points to a
|
||||
directory in Mopidy's virtual file system.
|
||||
``uri`` is a bytestring which represents some directory belonging to
|
||||
a backend. To get the intial root directories for backends pass None
|
||||
as the URI.
|
||||
|
||||
Returns a list of :class:`mopidy.models.Ref` objects for the
|
||||
directories and tracks at the given ``path``.
|
||||
directories and tracks at the given ``uri``.
|
||||
|
||||
The :class:`~mopidy.models.Ref` objects representing tracks keep the
|
||||
track's original URI. A matching pair of objects can look like this::
|
||||
@ -49,50 +47,28 @@ class LibraryController(object):
|
||||
Ref.track(uri='dummy:/foo.mp3', name='foo')
|
||||
|
||||
The :class:`~mopidy.models.Ref` objects representing directories have
|
||||
plain paths, not including any URI schema. For example, the dummy
|
||||
library's ``/bar`` directory is returned like this::
|
||||
backend specific URIs. These are opaque values, so no one but the
|
||||
backend that created them should try and derive any meaning from them.
|
||||
The only valid exception to this is checking the scheme, as it is used
|
||||
to route browse requests to the correct backend.
|
||||
|
||||
Ref.directory(uri='/dummy/bar', name='bar')
|
||||
For example, the dummy library's ``/bar`` directory could bereturned
|
||||
like this::
|
||||
|
||||
Note to backend implementors: The ``/dummy`` part of the URI is added
|
||||
by Mopidy core, not the individual backends.
|
||||
Ref.directory(uri='dummy:directory:/bar', name='bar')
|
||||
|
||||
:param path: path to browse
|
||||
:type path: string
|
||||
:param bytestring uri: uri to browse
|
||||
:rtype: list of :class:`mopidy.models.Ref`
|
||||
"""
|
||||
if not path.startswith('/'):
|
||||
return []
|
||||
if uri is None:
|
||||
backends = self.backends.with_library_browse.values()
|
||||
return [b.library.root_directory.get() for b in backends]
|
||||
|
||||
mapping = {}
|
||||
for ref in self.backends.with_browsable_library.keys():
|
||||
name = urlparse.urlparse(ref.uri).scheme
|
||||
mapping[name] = ref
|
||||
|
||||
if path == '/':
|
||||
return [Ref.directory(uri='/%s' % name, name=name)
|
||||
for name in sorted(mapping)]
|
||||
|
||||
groups = re.match('/(?P<library>[^/]+)(?P<path>.*)', path).groupdict()
|
||||
library_name = groups['library']
|
||||
backend_path = groups['path']
|
||||
if not backend_path.startswith('/'):
|
||||
backend_path = '/%s' % backend_path
|
||||
|
||||
backend = self.backends.with_browsable_library.get(
|
||||
mapping.get(library_name), None)
|
||||
scheme = urlparse.urlparse(uri).scheme
|
||||
backend = self.backends.with_library_browse.get(scheme)
|
||||
if not backend:
|
||||
return []
|
||||
|
||||
refs = backend.library.browse(backend_path).get()
|
||||
result = []
|
||||
for ref in refs:
|
||||
if ref.type == Ref.DIRECTORY:
|
||||
uri = '/'.join(['', library_name, ref.uri.lstrip('/')])
|
||||
result.append(ref.copy(uri=uri))
|
||||
else:
|
||||
result.append(ref)
|
||||
return result
|
||||
return backend.library.browse(uri).get()
|
||||
|
||||
def find_exact(self, query=None, uris=None, **kwargs):
|
||||
"""
|
||||
|
||||
@ -27,16 +27,17 @@ class CoreLibraryTest(unittest.TestCase):
|
||||
self.backend3 = mock.Mock()
|
||||
self.backend3.uri_schemes.get.return_value = ['dummy3']
|
||||
self.backend3.has_library().get.return_value = False
|
||||
self.backend3.has_library_browse().get.return_value = False
|
||||
|
||||
self.core = core.Core(audio=None, backends=[
|
||||
self.backend1, self.backend2, self.backend3])
|
||||
|
||||
def test_browse_root_returns_dir_ref_for_each_lib_with_root_dir_name(self):
|
||||
result = self.core.library.browse('/')
|
||||
result = self.core.library.browse(None)
|
||||
|
||||
self.assertEqual(result, [
|
||||
Ref.directory(uri='/dummy1', name='dummy1'),
|
||||
Ref.directory(uri='/dummy2', name='dummy2'),
|
||||
Ref.directory(uri='dummy1:directory', name='dummy1'),
|
||||
Ref.directory(uri='dummy2:directory', name='dummy2'),
|
||||
])
|
||||
self.assertFalse(self.library1.browse.called)
|
||||
self.assertFalse(self.library2.browse.called)
|
||||
@ -51,32 +52,32 @@ class CoreLibraryTest(unittest.TestCase):
|
||||
|
||||
def test_browse_dummy1_selects_dummy1_backend(self):
|
||||
self.library1.browse().get.return_value = [
|
||||
Ref.directory(uri='/foo/bar', name='bar'),
|
||||
Ref.track(uri='dummy1:/foo/baz.mp3', name='Baz'),
|
||||
Ref.directory(uri='dummy1:directory:/foo/bar', name='bar'),
|
||||
Ref.track(uri='dummy1:track:/foo/baz.mp3', name='Baz'),
|
||||
]
|
||||
self.library1.browse.reset_mock()
|
||||
|
||||
self.core.library.browse('/dummy1/foo')
|
||||
self.core.library.browse('dummy1:directory:/foo')
|
||||
|
||||
self.assertEqual(self.library1.browse.call_count, 1)
|
||||
self.assertEqual(self.library2.browse.call_count, 0)
|
||||
self.library1.browse.assert_called_with('/foo')
|
||||
self.library1.browse.assert_called_with('dummy1:directory:/foo')
|
||||
|
||||
def test_browse_dummy2_selects_dummy2_backend(self):
|
||||
self.library2.browse().get.return_value = [
|
||||
Ref.directory(uri='/bar/quux', name='quux'),
|
||||
Ref.track(uri='dummy2:/foo/baz.mp3', name='Baz'),
|
||||
Ref.directory(uri='dummy2:directory:/bar/baz', name='quux'),
|
||||
Ref.track(uri='dummy2:track:/bar/foo.mp3', name='Baz'),
|
||||
]
|
||||
self.library2.browse.reset_mock()
|
||||
|
||||
self.core.library.browse('/dummy2/bar')
|
||||
self.core.library.browse('dummy2:directory:/bar')
|
||||
|
||||
self.assertEqual(self.library1.browse.call_count, 0)
|
||||
self.assertEqual(self.library2.browse.call_count, 1)
|
||||
self.library2.browse.assert_called_with('/bar')
|
||||
self.library2.browse.assert_called_with('dummy2:directory:/bar')
|
||||
|
||||
def test_browse_dummy3_returns_nothing(self):
|
||||
result = self.core.library.browse('/dummy3')
|
||||
result = self.core.library.browse('dummy3:test')
|
||||
|
||||
self.assertEqual(result, [])
|
||||
self.assertEqual(self.library1.browse.call_count, 0)
|
||||
@ -84,16 +85,15 @@ class CoreLibraryTest(unittest.TestCase):
|
||||
|
||||
def test_browse_dir_returns_subdirs_and_tracks(self):
|
||||
self.library1.browse().get.return_value = [
|
||||
Ref.directory(uri='/foo/bar', name='bar'),
|
||||
Ref.track(uri='dummy1:/foo/baz.mp3', name='Baz'),
|
||||
Ref.directory(uri='dummy1:directory:/foo/bar', name='Bar'),
|
||||
Ref.track(uri='dummy1:track:/foo/baz.mp3', name='Baz'),
|
||||
]
|
||||
self.library1.browse.reset_mock()
|
||||
|
||||
result = self.core.library.browse('/dummy1/foo')
|
||||
|
||||
result = self.core.library.browse('dummy1:directory:/foo')
|
||||
self.assertEqual(result, [
|
||||
Ref.directory(uri='/dummy1/foo/bar', name='bar'),
|
||||
Ref.track(uri='dummy1:/foo/baz.mp3', name='Baz'),
|
||||
Ref.directory(uri='dummy1:directory:/foo/bar', name='Bar'),
|
||||
Ref.track(uri='dummy1:track:/foo/baz.mp3', name='Baz'),
|
||||
])
|
||||
|
||||
def test_lookup_selects_dummy1_backend(self):
|
||||
|
||||
Loading…
Reference in New Issue
Block a user