From 6aef96a0d3a6d609abeb7dd7f3a7b17cfe3a6f04 Mon Sep 17 00:00:00 2001 From: Thomas Kemmer Date: Sun, 14 Feb 2016 12:07:22 +0100 Subject: [PATCH] Fix #1428: Add m3u/base_dir confval. --- docs/changelog.rst | 3 +++ docs/ext/m3u.rst | 6 ++++++ mopidy/m3u/__init__.py | 1 + mopidy/m3u/ext.conf | 1 + mopidy/m3u/playlists.py | 5 +++-- tests/m3u/test_playlists.py | 35 +++++++++++++++++++++++++++++++++++ 6 files changed, 49 insertions(+), 2 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 931e1437..32453c13 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -55,6 +55,9 @@ Local backend M3U backend ----------- +- Add :confval:`m3u/base_dir` for resolving relative paths in M3U + files. (Fixes: :issue:`1428`, PR: :issue:`1442`) + - Derive track name from file name for non-extended M3U playlists. (Fixes: :issue:`1364`, PR: :issue:`1369`) diff --git a/docs/ext/m3u.rst b/docs/ext/m3u.rst index 37dc60be..35bd2036 100644 --- a/docs/ext/m3u.rst +++ b/docs/ext/m3u.rst @@ -55,6 +55,12 @@ See :ref:`config` for general help on configuring Mopidy. Path to directory with M3U files. Unset by default, in which case the extension's data dir is used to store playlists. +.. confval:: m3u/base_dir + + Path to base directory for resolving relative paths in M3U files. + If not set, relative paths are resolved based on the M3U file's + location. + .. confval:: m3u/default_encoding Text encoding used for files with extension ``.m3u``. Default is diff --git a/mopidy/m3u/__init__.py b/mopidy/m3u/__init__.py index df769c88..6a7fad9a 100644 --- a/mopidy/m3u/__init__.py +++ b/mopidy/m3u/__init__.py @@ -21,6 +21,7 @@ class Extension(ext.Extension): def get_config_schema(self): schema = super(Extension, self).get_config_schema() + schema['base_dir'] = config.Path(optional=True) schema['default_encoding'] = config.String() schema['default_extension'] = config.String(choices=['.m3u', '.m3u8']) schema['playlists_dir'] = config.Path(optional=True) diff --git a/mopidy/m3u/ext.conf b/mopidy/m3u/ext.conf index 862bc6f7..16291c83 100644 --- a/mopidy/m3u/ext.conf +++ b/mopidy/m3u/ext.conf @@ -1,5 +1,6 @@ [m3u] enabled = true playlists_dir = +base_dir = $XDG_MUSIC_DIR default_encoding = latin-1 default_extension = .m3u8 diff --git a/mopidy/m3u/playlists.py b/mopidy/m3u/playlists.py index 7e4e39ff..28be28d9 100644 --- a/mopidy/m3u/playlists.py +++ b/mopidy/m3u/playlists.py @@ -60,6 +60,7 @@ class M3UPlaylistsProvider(backend.PlaylistsProvider): self._playlists_dir = Extension.get_data_dir(config) else: self._playlists_dir = ext_config['playlists_dir'] + self._base_dir = ext_config['base_dir'] or self._playlists_dir self._default_encoding = ext_config['default_encoding'] self._default_extension = ext_config['default_extension'] @@ -97,7 +98,7 @@ class M3UPlaylistsProvider(backend.PlaylistsProvider): path = translator.uri_to_path(uri) try: with self._open(path, 'r') as fp: - items = translator.load_items(fp, self._playlists_dir) + items = translator.load_items(fp, self._base_dir) except EnvironmentError as e: log_environment_error('Error reading playlist %s' % uri, e) else: @@ -107,7 +108,7 @@ class M3UPlaylistsProvider(backend.PlaylistsProvider): path = translator.uri_to_path(uri) try: with self._open(path, 'r') as fp: - items = translator.load_items(fp, self._playlists_dir) + items = translator.load_items(fp, self._base_dir) mtime = os.path.getmtime(self._abspath(path)) except EnvironmentError as e: log_environment_error('Error reading playlist %s' % uri, e) diff --git a/tests/m3u/test_playlists.py b/tests/m3u/test_playlists.py index 664da9e9..e0ea1ce4 100644 --- a/tests/m3u/test_playlists.py +++ b/tests/m3u/test_playlists.py @@ -24,6 +24,7 @@ class M3UPlaylistsProviderTest(unittest.TestCase): config = { 'm3u': { 'enabled': True, + 'base_dir': None, 'default_encoding': 'latin-1', 'default_extension': '.m3u', 'playlists_dir': path_to_data_dir(''), @@ -33,6 +34,7 @@ class M3UPlaylistsProviderTest(unittest.TestCase): def setUp(self): # noqa: N802 self.config['m3u']['playlists_dir'] = tempfile.mkdtemp() self.playlists_dir = self.config['m3u']['playlists_dir'] + self.base_dir = self.config['m3u']['base_dir'] or self.playlists_dir audio = dummy_audio.create_proxy() backend = M3UBackend.start( @@ -261,6 +263,32 @@ class M3UPlaylistsProviderTest(unittest.TestCase): self.assertEqual(playlist.name, result.name) self.assertEqual(track.uri, result.tracks[0].uri) + def test_playlist_with_absolute_path(self): + track = Track(uri='/tmp/test.mp3') + filepath = b'/tmp/test.mp3' + playlist = self.core.playlists.create('test') + playlist = playlist.replace(tracks=[track]) + playlist = self.core.playlists.save(playlist) + + self.assertEqual(len(self.core.playlists.as_list()), 1) + result = self.core.playlists.lookup('m3u:test.m3u') + self.assertEqual('m3u:test.m3u', result.uri) + self.assertEqual(playlist.name, result.name) + self.assertEqual('file://' + filepath, result.tracks[0].uri) + + def test_playlist_with_relative_path(self): + track = Track(uri='test.mp3') + filepath = os.path.join(self.base_dir, b'test.mp3') + playlist = self.core.playlists.create('test') + playlist = playlist.replace(tracks=[track]) + playlist = self.core.playlists.save(playlist) + + self.assertEqual(len(self.core.playlists.as_list()), 1) + result = self.core.playlists.lookup('m3u:test.m3u') + self.assertEqual('m3u:test.m3u', result.uri) + self.assertEqual(playlist.name, result.name) + self.assertEqual('file://' + filepath, result.tracks[0].uri) + def test_playlist_sort_order(self): def check_order(playlists, names): self.assertEqual(names, [playlist.name for playlist in playlists]) @@ -303,6 +331,13 @@ class M3UPlaylistsProviderTest(unittest.TestCase): self.assertIsNone(item_refs) +class M3UPlaylistsProviderBaseDirectoryTest(M3UPlaylistsProviderTest): + + def setUp(self): # noqa: N802 + self.config['m3u']['base_dir'] = tempfile.mkdtemp() + super(M3UPlaylistsProviderBaseDirectoryTest, self).setUp() + + class DeprecatedM3UPlaylistsProviderTest(M3UPlaylistsProviderTest): def run(self, result=None):