diff --git a/docs/modules/local.rst b/docs/modules/local.rst index 014bac2a..395e8802 100644 --- a/docs/modules/local.rst +++ b/docs/modules/local.rst @@ -6,4 +6,18 @@ For details on how to use Mopidy's local backend, see :ref:`ext-local`. .. automodule:: mopidy.local :synopsis: Local backend + + +Local library API +================= + +.. autoclass:: mopidy.local.Library + :members: + + +Translation utils +================= + +.. automodule:: mopidy.local.translator + :synopsis: Translators for local library extensions :members: diff --git a/mopidy/local/playback.py b/mopidy/local/playback.py index 24038426..a851239d 100644 --- a/mopidy/local/playback.py +++ b/mopidy/local/playback.py @@ -7,5 +7,5 @@ from mopidy.local import translator class LocalPlaybackProvider(backend.PlaybackProvider): def translate_uri(self, uri): - return translator.local_track_uri_to_file_uri( + return translator.local_uri_to_file_uri( uri, self.backend.config['local']['media_dir']) diff --git a/mopidy/local/translator.py b/mopidy/local/translator.py index 6e5c9c01..6fc53f63 100644 --- a/mopidy/local/translator.py +++ b/mopidy/local/translator.py @@ -11,19 +11,35 @@ from mopidy.internal import path logger = logging.getLogger(__name__) -def local_track_uri_to_file_uri(uri, media_dir): - return path.path_to_uri(local_track_uri_to_path(uri, media_dir)) +def local_uri_to_file_uri(uri, media_dir): + """Convert local track or directory URI to file URI.""" + return path_to_file_uri(local_uri_to_path(uri, media_dir)) -def local_track_uri_to_path(uri, media_dir): - if not uri.startswith('local:track:'): +def local_uri_to_path(uri, media_dir): + """Convert local track or directory URI to absolute path.""" + if ( + not uri.startswith('local:directory:') and + not uri.startswith('local:track:')): raise ValueError('Invalid URI.') file_path = path.uri_to_path(uri).split(b':', 1)[1] return os.path.join(media_dir, file_path) +def local_track_uri_to_path(uri, media_dir): + # Deprecated version to keep old versions of Mopidy-Local-Sqlite working. + return local_uri_to_path(uri, media_dir) + + +def path_to_file_uri(abspath): + """Convert absolute path to file URI.""" + # Re-export internal method for use by Mopidy-Local-* extensions. + return path.path_to_uri(abspath) + + def path_to_local_track_uri(relpath): - """Convert path relative to media_dir to local track URI.""" + """Convert path relative to :confval:`local/media_dir` to local track + URI.""" if isinstance(relpath, compat.text_type): relpath = relpath.encode('utf-8') return b'local:track:%s' % urllib.quote(relpath) diff --git a/tests/local/test_translator.py b/tests/local/test_translator.py new file mode 100644 index 00000000..124766dd --- /dev/null +++ b/tests/local/test_translator.py @@ -0,0 +1,98 @@ +# encoding: utf-8 + +from __future__ import unicode_literals + +import pytest + +from mopidy.local import translator + + +@pytest.mark.parametrize('local_uri,file_uri', [ + ('local:directory:A/B', 'file:///home/alice/Music/A/B'), + ('local:directory:A%20B', 'file:///home/alice/Music/A%20B'), + ('local:directory:A+B', 'file:///home/alice/Music/A%2BB'), + ( + 'local:directory:%C3%A6%C3%B8%C3%A5', + 'file:///home/alice/Music/%C3%A6%C3%B8%C3%A5'), + ('local:track:A/B.mp3', 'file:///home/alice/Music/A/B.mp3'), + ('local:track:A%20B.mp3', 'file:///home/alice/Music/A%20B.mp3'), + ('local:track:A+B.mp3', 'file:///home/alice/Music/A%2BB.mp3'), + ( + 'local:track:%C3%A6%C3%B8%C3%A5.mp3', + 'file:///home/alice/Music/%C3%A6%C3%B8%C3%A5.mp3'), +]) +def test_local_uri_to_file_uri(local_uri, file_uri): + media_dir = b'/home/alice/Music' + + assert translator.local_uri_to_file_uri(local_uri, media_dir) == file_uri + + +@pytest.mark.parametrize('uri', [ + 'A/B', + 'local:foo:A/B', +]) +def test_local_uri_to_file_uri_errors(uri): + media_dir = b'/home/alice/Music' + + with pytest.raises(ValueError): + translator.local_uri_to_file_uri(uri, media_dir) + + +@pytest.mark.parametrize('uri,path', [ + ('local:directory:A/B', b'/home/alice/Music/A/B'), + ('local:directory:A%20B', b'/home/alice/Music/A B'), + ('local:directory:A+B', b'/home/alice/Music/A+B'), + ('local:directory:%C3%A6%C3%B8%C3%A5', b'/home/alice/Music/æøå'), + ('local:track:A/B.mp3', b'/home/alice/Music/A/B.mp3'), + ('local:track:A%20B.mp3', b'/home/alice/Music/A B.mp3'), + ('local:track:A+B.mp3', b'/home/alice/Music/A+B.mp3'), + ('local:track:%C3%A6%C3%B8%C3%A5.mp3', b'/home/alice/Music/æøå.mp3'), +]) +def test_local_uri_to_path(uri, path): + media_dir = b'/home/alice/Music' + + assert translator.local_uri_to_path(uri, media_dir) == path + + # Legacy version to keep old versions of Mopidy-Local-Sqlite working + assert translator.local_track_uri_to_path(uri, media_dir) == path + + +@pytest.mark.parametrize('uri', [ + 'A/B', + 'local:foo:A/B', +]) +def test_local_uri_to_path_errors(uri): + media_dir = b'/home/alice/Music' + + with pytest.raises(ValueError): + translator.local_uri_to_path(uri, media_dir) + + +@pytest.mark.parametrize('path,uri', [ + ('/foo', 'file:///foo'), + (b'/foo', 'file:///foo'), + ('/æøå', 'file:///%C3%A6%C3%B8%C3%A5'), + (b'/\x00\x01\x02', 'file:///%00%01%02'), +]) +def test_path_to_file_uri(path, uri): + assert translator.path_to_file_uri(path) == uri + + +@pytest.mark.parametrize('path,uri', [ + ('foo', 'local:track:foo'), + (b'foo', 'local:track:foo'), + ('æøå', 'local:track:%C3%A6%C3%B8%C3%A5'), + (b'\x00\x01\x02', 'local:track:%00%01%02'), +]) +def test_path_to_local_track_uri(path, uri): + assert translator.path_to_local_track_uri(path) == uri + + +@pytest.mark.parametrize('path,uri', [ + ('foo', 'local:directory:foo'), + (b'foo', 'local:directory:foo'), + ('æøå', 'local:directory:%C3%A6%C3%B8%C3%A5'), + (b'\x00\x01\x02', 'local:directory:%00%01%02'), +]) +def test_path_to_local_directory_uri(path, uri): + assert translator.path_to_local_directory_uri(path) == uri