From f9f80e264e872b6815d7818a4a08d7aaa420d694 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Tue, 9 Apr 2013 11:05:32 +0200 Subject: [PATCH 01/15] local: Create empty tag_cache if it doesn't exist --- mopidy/backends/local/library.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/mopidy/backends/local/library.py b/mopidy/backends/local/library.py index a76ce594..148ff24f 100644 --- a/mopidy/backends/local/library.py +++ b/mopidy/backends/local/library.py @@ -1,9 +1,11 @@ from __future__ import unicode_literals import logging +import os from mopidy.backends import base from mopidy.models import Album, SearchResult +from mopidy.utils.encoding import locale_decode from .translator import parse_mpd_tag_cache @@ -19,12 +21,23 @@ class LocalLibraryProvider(base.BaseLibraryProvider): self.refresh() def refresh(self, uri=None): - tracks = parse_mpd_tag_cache(self._tag_cache_file, self._media_dir) + try: + if not os.path.exists(self._tag_cache_file): + basedir = os.path.dirname(self._tag_cache_file) + if not os.path.exists(basedir): + os.makedirs(basedir) + open(self._tag_cache_file, 'a').close() + except IOError as error: + logger.warning( + 'Could not create empty tag cache: %s', locale_decode(error)) + return logger.info( 'Loading tracks from %s using %s', self._media_dir, self._tag_cache_file) + tracks = parse_mpd_tag_cache(self._tag_cache_file, self._media_dir) + for track in tracks: self._uri_mapping[track.uri] = track From 796f7302aa97767aaef6a9ca8f8be6b09f21ac13 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Tue, 9 Apr 2013 11:05:55 +0200 Subject: [PATCH 02/15] local: Log how many tracks and playlists are loaded --- mopidy/backends/local/library.py | 8 ++++---- mopidy/backends/local/playlists.py | 6 ++++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/mopidy/backends/local/library.py b/mopidy/backends/local/library.py index 148ff24f..f7a1225a 100644 --- a/mopidy/backends/local/library.py +++ b/mopidy/backends/local/library.py @@ -32,15 +32,15 @@ class LocalLibraryProvider(base.BaseLibraryProvider): 'Could not create empty tag cache: %s', locale_decode(error)) return - logger.info( - 'Loading tracks from %s using %s', - self._media_dir, self._tag_cache_file) - tracks = parse_mpd_tag_cache(self._tag_cache_file, self._media_dir) for track in tracks: self._uri_mapping[track.uri] = track + logger.info( + 'Loaded %d local tracks from %s using %s', + len(tracks), self._media_dir, self._tag_cache_file) + def lookup(self, uri): try: return [self._uri_mapping[uri]] diff --git a/mopidy/backends/local/playlists.py b/mopidy/backends/local/playlists.py index 3b9e1d73..cd370eaa 100644 --- a/mopidy/backends/local/playlists.py +++ b/mopidy/backends/local/playlists.py @@ -42,8 +42,6 @@ class LocalPlaylistsProvider(base.BasePlaylistsProvider): return playlist def refresh(self): - logger.info('Loading playlists from %s', self._playlists_dir) - playlists = [] for m3u in glob.glob(os.path.join(self._playlists_dir, '*.m3u')): @@ -65,6 +63,10 @@ class LocalPlaylistsProvider(base.BasePlaylistsProvider): self.playlists = playlists listener.BackendListener.send('playlists_loaded') + logger.info( + 'Loaded %d local playlists from %s', + len(playlists), self._playlists_dir) + def save(self, playlist): assert playlist.uri, 'Cannot save playlist without URI' From da7e36d94465ff3842b6cd1774a4cfbf66dc6a4d Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Tue, 9 Apr 2013 11:06:24 +0200 Subject: [PATCH 03/15] spotify: Use 'playlists' instead of 'playlist(s)' for consistency --- mopidy/backends/spotify/session_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mopidy/backends/spotify/session_manager.py b/mopidy/backends/spotify/session_manager.py index a830138f..c0592ea7 100644 --- a/mopidy/backends/spotify/session_manager.py +++ b/mopidy/backends/spotify/session_manager.py @@ -182,7 +182,7 @@ class SpotifySessionManager(process.BaseThread, PyspotifySessionManager): bitrate=self.bitrate, username=self.username)) playlists = filter(None, playlists) self.backend.playlists.playlists = playlists - logger.info('Loaded %d Spotify playlist(s)', len(playlists)) + logger.info('Loaded %d Spotify playlists', len(playlists)) BackendListener.send('playlists_loaded') def logout(self): From 919becd05a38300268686c40a5087bcd3010c33c Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Tue, 9 Apr 2013 11:07:52 +0200 Subject: [PATCH 04/15] main: Show 'Disabled extensions: none' instead of nothing --- mopidy/__main__.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mopidy/__main__.py b/mopidy/__main__.py index 6a691cce..2607db13 100644 --- a/mopidy/__main__.py +++ b/mopidy/__main__.py @@ -236,8 +236,10 @@ def filter_enabled_extensions(raw_config, extensions): else: disabled_names.append(extension.ext_name) - logging.info('Enabled extensions: %s', ', '.join(enabled_names)) - logging.info('Disabled extensions: %s', ', '.join(disabled_names)) + logging.info( + 'Enabled extensions: %s', ', '.join(enabled_names) or 'none') + logging.info( + 'Disabled extensions: %s', ', '.join(disabled_names) or 'none') return enabled_extensions From 0b09ea34110a0a01b5860d8e98bc16e5d552c6d1 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Tue, 9 Apr 2013 11:21:25 +0200 Subject: [PATCH 05/15] main: Log what backends/frontends are started --- mopidy/__main__.py | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/mopidy/__main__.py b/mopidy/__main__.py index 2607db13..2709d519 100644 --- a/mopidy/__main__.py +++ b/mopidy/__main__.py @@ -327,12 +327,19 @@ def stop_audio(): def setup_backends(config, extensions, audio): - logger.info('Starting Mopidy backends') - backends = [] + backend_classes = [] for extension in extensions: - for backend_class in extension.get_backend_classes(): - backend = backend_class.start(config=config, audio=audio).proxy() - backends.append(backend) + backend_classes.extend(extension.get_backend_classes()) + + logger.info( + 'Starting Mopidy backends: %s', + ', '.join(b.__name__ for b in backend_classes)) + + backends = [] + for backend_class in backend_classes: + backend = backend_class.start(config=config, audio=audio).proxy() + backends.append(backend) + return backends @@ -354,10 +361,16 @@ def stop_core(): def setup_frontends(config, extensions, core): - logger.info('Starting Mopidy frontends') + frontend_classes = [] for extension in extensions: - for frontend_class in extension.get_frontend_classes(): - frontend_class.start(config=config, core=core) + frontend_classes.extend(extension.get_frontend_classes()) + + logger.info( + 'Starting Mopidy frontends: %s', + ', '.join(f.__name__ for f in frontend_classes)) + + for frontend_class in frontend_classes: + frontend_class.start(config=config, core=core) def stop_frontends(extensions): From 2ffef53b9a00a83f12aa085c05750365b161bff9 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Tue, 9 Apr 2013 11:22:56 +0200 Subject: [PATCH 06/15] mpris: Log who connected to D-Bus --- mopidy/frontends/mpris/objects.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mopidy/frontends/mpris/objects.py b/mopidy/frontends/mpris/objects.py index 696e39bd..a7f049d2 100644 --- a/mopidy/frontends/mpris/objects.py +++ b/mopidy/frontends/mpris/objects.py @@ -93,7 +93,7 @@ class MprisObject(dbus.service.Object): mainloop = dbus.mainloop.glib.DBusGMainLoop() bus_name = dbus.service.BusName( BUS_NAME, dbus.SessionBus(mainloop=mainloop)) - logger.info('Connected to D-Bus') + logger.info('MPRIS server connected to D-Bus') return bus_name def get_playlist_id(self, playlist_uri): From 3b7d38e8bcf1efda324e5cc863fd5db50f29c8ed Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Tue, 9 Apr 2013 11:23:06 +0200 Subject: [PATCH 07/15] scrobbler: Log who connected to Last.fm --- mopidy/frontends/scrobbler/actor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mopidy/frontends/scrobbler/actor.py b/mopidy/frontends/scrobbler/actor.py index 1809661a..74a11f82 100644 --- a/mopidy/frontends/scrobbler/actor.py +++ b/mopidy/frontends/scrobbler/actor.py @@ -32,7 +32,7 @@ class ScrobblerFrontend(pykka.ThreadingActor, CoreListener): api_key=API_KEY, api_secret=API_SECRET, username=self.config['scrobbler']['username'], password_hash=pylast.md5(self.config['scrobbler']['password'])) - logger.info('Connected to Last.fm') + logger.info('Scrobbler connected to Last.fm') except (pylast.NetworkError, pylast.MalformedResponseError, pylast.WSError) as e: logger.error('Error during Last.fm setup: %s', e) From 339fbbc2ddbe1d502c7e9cf34d5f1fbddad11dee Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Tue, 9 Apr 2013 12:20:36 +0200 Subject: [PATCH 08/15] main: Use 'none' instead of emptry string Same logic as for extensions are now applied to backends and frontends. --- mopidy/__main__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mopidy/__main__.py b/mopidy/__main__.py index 2709d519..d5f8c93e 100644 --- a/mopidy/__main__.py +++ b/mopidy/__main__.py @@ -333,7 +333,7 @@ def setup_backends(config, extensions, audio): logger.info( 'Starting Mopidy backends: %s', - ', '.join(b.__name__ for b in backend_classes)) + ', '.join(b.__name__ for b in backend_classes) or 'none') backends = [] for backend_class in backend_classes: @@ -367,7 +367,7 @@ def setup_frontends(config, extensions, core): logger.info( 'Starting Mopidy frontends: %s', - ', '.join(f.__name__ for f in frontend_classes)) + ', '.join(f.__name__ for f in frontend_classes) or 'none') for frontend_class in frontend_classes: frontend_class.start(config=config, core=core) From 40dd539d061040f99da00aa955a7ce3cf78c9763 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Tue, 9 Apr 2013 12:38:47 +0200 Subject: [PATCH 09/15] path: Test get_or_create_file() --- tests/utils/path_test.py | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/tests/utils/path_test.py b/tests/utils/path_test.py index a73cb8e4..d40c822f 100644 --- a/tests/utils/path_test.py +++ b/tests/utils/path_test.py @@ -24,7 +24,6 @@ class GetOrCreateDirTest(unittest.TestCase): def test_creating_dir(self): dir_path = os.path.join(self.parent, 'test') self.assert_(not os.path.exists(dir_path)) - self.assert_(not os.path.isdir(dir_path)) created = path.get_or_create_dir(dir_path) self.assert_(os.path.exists(dir_path)) self.assert_(os.path.isdir(dir_path)) @@ -34,9 +33,7 @@ class GetOrCreateDirTest(unittest.TestCase): level2_dir = os.path.join(self.parent, 'test') level3_dir = os.path.join(self.parent, 'test', 'test') self.assert_(not os.path.exists(level2_dir)) - self.assert_(not os.path.isdir(level2_dir)) self.assert_(not os.path.exists(level3_dir)) - self.assert_(not os.path.isdir(level3_dir)) created = path.get_or_create_dir(level3_dir) self.assert_(os.path.exists(level2_dir)) self.assert_(os.path.isdir(level2_dir)) @@ -57,6 +54,35 @@ class GetOrCreateDirTest(unittest.TestCase): self.assertRaises(OSError, path.get_or_create_dir, dir_path) +class GetOrCreateFileTest(unittest.TestCase): + def setUp(self): + self.parent = tempfile.mkdtemp() + + def tearDown(self): + if os.path.isdir(self.parent): + shutil.rmtree(self.parent) + + def test_creating_file(self): + file_path = os.path.join(self.parent, 'test') + self.assert_(not os.path.exists(file_path)) + created = path.get_or_create_file(file_path) + self.assert_(os.path.exists(file_path)) + self.assert_(os.path.isfile(file_path)) + self.assertEqual(created, file_path) + + def test_creating_existing_file(self): + file_path = os.path.join(self.parent, 'test') + path.get_or_create_file(file_path) + created = path.get_or_create_file(file_path) + self.assert_(os.path.exists(file_path)) + self.assert_(os.path.isfile(file_path)) + self.assertEqual(created, file_path) + + def test_create_file_with_name_of_existing_dir_throws_ioerror(self): + conflicting_dir = os.path.join(self.parent) + self.assertRaises(IOError, path.get_or_create_file, conflicting_dir) + + class PathToFileURITest(unittest.TestCase): def test_simple_path(self): if sys.platform == 'win32': From 53827aa0227fd81e03f0ffe60766f4df8c09639e Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Tue, 9 Apr 2013 12:39:29 +0200 Subject: [PATCH 10/15] path: Close create file right away --- mopidy/utils/path.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/mopidy/utils/path.py b/mopidy/utils/path.py index 699b361f..8f842741 100644 --- a/mopidy/utils/path.py +++ b/mopidy/utils/path.py @@ -39,12 +39,12 @@ def get_or_create_dir(dir_path): return dir_path -def get_or_create_file(filename): - filename = expand_path(filename) - if not os.path.isfile(filename): - logger.info('Creating file %s', filename) - open(filename, 'w') - return filename +def get_or_create_file(file_path): + file_path = expand_path(file_path) + if not os.path.isfile(file_path): + logger.info('Creating file %s', file_path) + open(file_path, 'w').close() + return file_path def path_to_uri(*paths): From 02f9db451836ee1e6de0459ba7e6a3480dfe7650 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Tue, 9 Apr 2013 12:40:28 +0200 Subject: [PATCH 11/15] path: Let get_or_create_file() create missing dirs --- mopidy/utils/path.py | 1 + tests/utils/path_test.py | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/mopidy/utils/path.py b/mopidy/utils/path.py index 8f842741..2ad51368 100644 --- a/mopidy/utils/path.py +++ b/mopidy/utils/path.py @@ -41,6 +41,7 @@ def get_or_create_dir(dir_path): def get_or_create_file(file_path): file_path = expand_path(file_path) + get_or_create_dir(os.path.dirname(file_path)) if not os.path.isfile(file_path): logger.info('Creating file %s', file_path) open(file_path, 'w').close() diff --git a/tests/utils/path_test.py b/tests/utils/path_test.py index d40c822f..9d1c16d3 100644 --- a/tests/utils/path_test.py +++ b/tests/utils/path_test.py @@ -70,6 +70,18 @@ class GetOrCreateFileTest(unittest.TestCase): self.assert_(os.path.isfile(file_path)) self.assertEqual(created, file_path) + def test_creating_nested_file(self): + level2_dir = os.path.join(self.parent, 'test') + file_path = os.path.join(self.parent, 'test', 'test') + self.assert_(not os.path.exists(level2_dir)) + self.assert_(not os.path.exists(file_path)) + created = path.get_or_create_file(file_path) + self.assert_(os.path.exists(level2_dir)) + self.assert_(os.path.isdir(level2_dir)) + self.assert_(os.path.exists(file_path)) + self.assert_(os.path.isfile(file_path)) + self.assertEqual(created, file_path) + def test_creating_existing_file(self): file_path = os.path.join(self.parent, 'test') path.get_or_create_file(file_path) From 87b526f8d3324236ecd0ca4a1723bef2fac7d094 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Tue, 9 Apr 2013 12:41:07 +0200 Subject: [PATCH 12/15] main: Remove redundant dir creation --- mopidy/__main__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mopidy/__main__.py b/mopidy/__main__.py index d5f8c93e..7e1677ef 100644 --- a/mopidy/__main__.py +++ b/mopidy/__main__.py @@ -312,7 +312,6 @@ def validate_config(raw_config, schemas, extensions=None): def create_file_structures(): path.get_or_create_dir('$XDG_DATA_DIR/mopidy') - path.get_or_create_dir('$XDG_CONFIG_DIR/mopidy') path.get_or_create_file('$XDG_CONFIG_DIR/mopidy/mopidy.conf') From 172b6f046131e9735ab4310e8d499bea16fe2131 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Tue, 9 Apr 2013 12:44:16 +0200 Subject: [PATCH 13/15] local: Use path helpers to create tag cache --- mopidy/backends/local/library.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/mopidy/backends/local/library.py b/mopidy/backends/local/library.py index f7a1225a..6b953823 100644 --- a/mopidy/backends/local/library.py +++ b/mopidy/backends/local/library.py @@ -1,11 +1,9 @@ from __future__ import unicode_literals import logging -import os - from mopidy.backends import base from mopidy.models import Album, SearchResult -from mopidy.utils.encoding import locale_decode +from mopidy.utils import encoding, path from .translator import parse_mpd_tag_cache @@ -22,14 +20,11 @@ class LocalLibraryProvider(base.BaseLibraryProvider): def refresh(self, uri=None): try: - if not os.path.exists(self._tag_cache_file): - basedir = os.path.dirname(self._tag_cache_file) - if not os.path.exists(basedir): - os.makedirs(basedir) - open(self._tag_cache_file, 'a').close() - except IOError as error: + path.get_or_create_file(self._tag_cache_file) + except EnvironmentError as error: logger.warning( - 'Could not create empty tag cache: %s', locale_decode(error)) + 'Could not create empty tag cache: %s', + encoding.locale_decode(error)) return tracks = parse_mpd_tag_cache(self._tag_cache_file, self._media_dir) From bc959e2240f708b6f55b9c51de3b7bd8b7796e54 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Tue, 9 Apr 2013 12:44:35 +0200 Subject: [PATCH 14/15] local: Move data files into an extension specific dir --- mopidy/backends/local/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mopidy/backends/local/__init__.py b/mopidy/backends/local/__init__.py index ffcf5869..0367cf15 100644 --- a/mopidy/backends/local/__init__.py +++ b/mopidy/backends/local/__init__.py @@ -9,8 +9,8 @@ default_config = """ [local] enabled = true media_dir = $XDG_MUSIC_DIR -playlists_dir = $XDG_DATA_DIR/mopidy/playlists -tag_cache_file = $XDG_DATA_DIR/mopidy/tag_cache +playlists_dir = $XDG_DATA_DIR/mopidy/local/playlists +tag_cache_file = $XDG_DATA_DIR/mopidy/local/tag_cache """ __doc__ = """A backend for playing music from a local music archive. From bf83a159fedb54d9a12cf24f28ba012d9445f457 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Tue, 9 Apr 2013 12:51:34 +0200 Subject: [PATCH 15/15] local: Create all needed dirs/files on startup --- mopidy/backends/local/actor.py | 25 +++++++++++++++++++++++++ mopidy/backends/local/library.py | 11 +++-------- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/mopidy/backends/local/actor.py b/mopidy/backends/local/actor.py index a1655dd9..1817e133 100644 --- a/mopidy/backends/local/actor.py +++ b/mopidy/backends/local/actor.py @@ -5,6 +5,7 @@ import logging import pykka from mopidy.backends import base +from mopidy.utils import encoding, path from .library import LocalLibraryProvider from .playlists import LocalPlaylistsProvider @@ -18,8 +19,32 @@ class LocalBackend(pykka.ThreadingActor, base.Backend): self.config = config + self.create_dirs_and_files() + self.library = LocalLibraryProvider(backend=self) self.playback = base.BasePlaybackProvider(audio=audio, backend=self) self.playlists = LocalPlaylistsProvider(backend=self) self.uri_schemes = ['file'] + + def create_dirs_and_files(self): + try: + path.get_or_create_dir(self.config['local']['media_dir']) + except EnvironmentError as error: + logger.warning( + 'Could not create local media dir: %s', + encoding.locale_decode(error)) + + try: + path.get_or_create_dir(self.config['local']['playlists_dir']) + except EnvironmentError as error: + logger.warning( + 'Could not create local playlists dir: %s', + encoding.locale_decode(error)) + + try: + path.get_or_create_file(self.config['local']['tag_cache_file']) + except EnvironmentError as error: + logger.warning( + 'Could not create empty tag cache file: %s', + encoding.locale_decode(error)) diff --git a/mopidy/backends/local/library.py b/mopidy/backends/local/library.py index 6b953823..669e72d7 100644 --- a/mopidy/backends/local/library.py +++ b/mopidy/backends/local/library.py @@ -3,7 +3,6 @@ from __future__ import unicode_literals import logging from mopidy.backends import base from mopidy.models import Album, SearchResult -from mopidy.utils import encoding, path from .translator import parse_mpd_tag_cache @@ -19,13 +18,9 @@ class LocalLibraryProvider(base.BaseLibraryProvider): self.refresh() def refresh(self, uri=None): - try: - path.get_or_create_file(self._tag_cache_file) - except EnvironmentError as error: - logger.warning( - 'Could not create empty tag cache: %s', - encoding.locale_decode(error)) - return + logger.debug( + 'Loading local tracks from %s using %s', + self._media_dir, self._tag_cache_file) tracks = parse_mpd_tag_cache(self._tag_cache_file, self._media_dir)