From 45a79df0a88ec07d6a79c6391c4ffb1dae2f2e97 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Thu, 25 Oct 2012 22:47:20 +0200 Subject: [PATCH] Split local backend into multiple files and flatten logging hierarchy --- mopidy/backends/local/__init__.py | 213 +--------------------- mopidy/backends/local/actor.py | 33 ++++ mopidy/backends/local/library.py | 110 +++++++++++ mopidy/backends/local/stored_playlists.py | 85 +++++++++ mopidy/backends/local/translator.py | 4 +- 5 files changed, 232 insertions(+), 213 deletions(-) create mode 100644 mopidy/backends/local/actor.py create mode 100644 mopidy/backends/local/library.py create mode 100644 mopidy/backends/local/stored_playlists.py diff --git a/mopidy/backends/local/__init__.py b/mopidy/backends/local/__init__.py index b34c3da5..6f0f3770 100644 --- a/mopidy/backends/local/__init__.py +++ b/mopidy/backends/local/__init__.py @@ -1,211 +1,2 @@ -import glob -import logging -import os -import shutil - -from pykka.actor import ThreadingActor - -from mopidy import settings -from mopidy.backends import base -from mopidy.models import Playlist, Album - -from .translator import parse_m3u, parse_mpd_tag_cache - -logger = logging.getLogger(u'mopidy.backends.local') - - -class LocalBackend(ThreadingActor, base.Backend): - """ - A backend for playing music from a local music archive. - - **Dependencies:** - - - None - - **Settings:** - - - :attr:`mopidy.settings.LOCAL_MUSIC_PATH` - - :attr:`mopidy.settings.LOCAL_PLAYLIST_PATH` - - :attr:`mopidy.settings.LOCAL_TAG_CACHE_FILE` - """ - - def __init__(self, audio): - self.library = LocalLibraryProvider(backend=self) - self.playback = base.BasePlaybackProvider(audio=audio, backend=self) - self.stored_playlists = LocalStoredPlaylistsProvider(backend=self) - - self.uri_schemes = [u'file'] - - -class LocalStoredPlaylistsProvider(base.BaseStoredPlaylistsProvider): - def __init__(self, *args, **kwargs): - super(LocalStoredPlaylistsProvider, self).__init__(*args, **kwargs) - self._folder = settings.LOCAL_PLAYLIST_PATH - self.refresh() - - def lookup(self, uri): - pass # TODO - - def refresh(self): - playlists = [] - - logger.info('Loading playlists from %s', self._folder) - - for m3u in glob.glob(os.path.join(self._folder, '*.m3u')): - name = os.path.basename(m3u)[:-len('.m3u')] - tracks = [] - for uri in parse_m3u(m3u, settings.LOCAL_MUSIC_PATH): - try: - tracks.append(self.backend.library.lookup(uri)) - except LookupError, e: - logger.error('Playlist item could not be added: %s', e) - playlist = Playlist(tracks=tracks, name=name) - - # FIXME playlist name needs better handling - # FIXME tracks should come from lib. lookup - - playlists.append(playlist) - - self.playlists = playlists - - def create(self, name): - playlist = Playlist(name=name) - self.save(playlist) - return playlist - - def delete(self, playlist): - if playlist not in self._playlists: - return - - self._playlists.remove(playlist) - filename = os.path.join(self._folder, playlist.name + '.m3u') - - if os.path.exists(filename): - os.remove(filename) - - def rename(self, playlist, name): - if playlist not in self._playlists: - return - - src = os.path.join(self._folder, playlist.name + '.m3u') - dst = os.path.join(self._folder, name + '.m3u') - - renamed = playlist.copy(name=name) - index = self._playlists.index(playlist) - self._playlists[index] = renamed - - shutil.move(src, dst) - - def save(self, playlist): - file_path = os.path.join(self._folder, playlist.name + '.m3u') - - # FIXME this should be a save_m3u function, not inside save - with open(file_path, 'w') as file_handle: - for track in playlist.tracks: - if track.uri.startswith('file://'): - file_handle.write(track.uri[len('file://'):] + '\n') - else: - file_handle.write(track.uri + '\n') - - self._playlists.append(playlist) - - -class LocalLibraryProvider(base.BaseLibraryProvider): - def __init__(self, *args, **kwargs): - super(LocalLibraryProvider, self).__init__(*args, **kwargs) - self._uri_mapping = {} - self.refresh() - - def refresh(self, uri=None): - tracks = parse_mpd_tag_cache( - settings.LOCAL_TAG_CACHE_FILE, settings.LOCAL_MUSIC_PATH) - - logger.info( - 'Loading tracks in %s from %s', - settings.LOCAL_MUSIC_PATH, settings.LOCAL_TAG_CACHE_FILE) - - for track in tracks: - self._uri_mapping[track.uri] = track - - def lookup(self, uri): - try: - return self._uri_mapping[uri] - except KeyError: - logger.debug(u'Failed to lookup "%s"', uri) - return None - - def find_exact(self, **query): - self._validate_query(query) - result_tracks = self._uri_mapping.values() - - for (field, values) in query.iteritems(): - if not hasattr(values, '__iter__'): - values = [values] - # FIXME this is bound to be slow for large libraries - for value in values: - q = value.strip() - - track_filter = lambda t: q == t.name - album_filter = lambda t: q == getattr(t, 'album', Album()).name - artist_filter = lambda t: filter( - lambda a: q == a.name, t.artists) - uri_filter = lambda t: q == t.uri - any_filter = lambda t: ( - track_filter(t) or album_filter(t) or - artist_filter(t) or uri_filter(t)) - - if field == 'track': - result_tracks = filter(track_filter, result_tracks) - elif field == 'album': - result_tracks = filter(album_filter, result_tracks) - elif field == 'artist': - result_tracks = filter(artist_filter, result_tracks) - elif field == 'uri': - result_tracks = filter(uri_filter, result_tracks) - elif field == 'any': - result_tracks = filter(any_filter, result_tracks) - else: - raise LookupError('Invalid lookup field: %s' % field) - return Playlist(tracks=result_tracks) - - def search(self, **query): - self._validate_query(query) - result_tracks = self._uri_mapping.values() - - for (field, values) in query.iteritems(): - if not hasattr(values, '__iter__'): - values = [values] - # FIXME this is bound to be slow for large libraries - for value in values: - q = value.strip().lower() - - track_filter = lambda t: q in t.name.lower() - album_filter = lambda t: q in getattr( - t, 'album', Album()).name.lower() - artist_filter = lambda t: filter( - lambda a: q in a.name.lower(), t.artists) - uri_filter = lambda t: q in t.uri.lower() - any_filter = lambda t: track_filter(t) or album_filter(t) or \ - artist_filter(t) or uri_filter(t) - - if field == 'track': - result_tracks = filter(track_filter, result_tracks) - elif field == 'album': - result_tracks = filter(album_filter, result_tracks) - elif field == 'artist': - result_tracks = filter(artist_filter, result_tracks) - elif field == 'uri': - result_tracks = filter(uri_filter, result_tracks) - elif field == 'any': - result_tracks = filter(any_filter, result_tracks) - else: - raise LookupError('Invalid lookup field: %s' % field) - return Playlist(tracks=result_tracks) - - def _validate_query(self, query): - for (_, values) in query.iteritems(): - if not values: - raise LookupError('Missing query') - for value in values: - if not value: - raise LookupError('Missing query') +# flake8: noqa +from .actor import LocalBackend diff --git a/mopidy/backends/local/actor.py b/mopidy/backends/local/actor.py new file mode 100644 index 00000000..fe31a5fc --- /dev/null +++ b/mopidy/backends/local/actor.py @@ -0,0 +1,33 @@ +import logging + +from pykka.actor import ThreadingActor + +from mopidy.backends import base + +from .library import LocalLibraryProvider +from .stored_playlists import LocalStoredPlaylistsProvider + +logger = logging.getLogger(u'mopidy.backends.local') + + +class LocalBackend(ThreadingActor, base.Backend): + """ + A backend for playing music from a local music archive. + + **Dependencies:** + + - None + + **Settings:** + + - :attr:`mopidy.settings.LOCAL_MUSIC_PATH` + - :attr:`mopidy.settings.LOCAL_PLAYLIST_PATH` + - :attr:`mopidy.settings.LOCAL_TAG_CACHE_FILE` + """ + + def __init__(self, audio): + self.library = LocalLibraryProvider(backend=self) + self.playback = base.BasePlaybackProvider(audio=audio, backend=self) + self.stored_playlists = LocalStoredPlaylistsProvider(backend=self) + + self.uri_schemes = [u'file'] diff --git a/mopidy/backends/local/library.py b/mopidy/backends/local/library.py new file mode 100644 index 00000000..78178196 --- /dev/null +++ b/mopidy/backends/local/library.py @@ -0,0 +1,110 @@ +import logging + +from mopidy import settings +from mopidy.backends import base +from mopidy.models import Playlist, Album + +from .translator import parse_mpd_tag_cache + +logger = logging.getLogger(u'mopidy.backends.local') + + +class LocalLibraryProvider(base.BaseLibraryProvider): + def __init__(self, *args, **kwargs): + super(LocalLibraryProvider, self).__init__(*args, **kwargs) + self._uri_mapping = {} + self.refresh() + + def refresh(self, uri=None): + tracks = parse_mpd_tag_cache( + settings.LOCAL_TAG_CACHE_FILE, settings.LOCAL_MUSIC_PATH) + + logger.info( + 'Loading tracks in %s from %s', + settings.LOCAL_MUSIC_PATH, settings.LOCAL_TAG_CACHE_FILE) + + for track in tracks: + self._uri_mapping[track.uri] = track + + def lookup(self, uri): + try: + return self._uri_mapping[uri] + except KeyError: + logger.debug(u'Failed to lookup "%s"', uri) + return None + + def find_exact(self, **query): + self._validate_query(query) + result_tracks = self._uri_mapping.values() + + for (field, values) in query.iteritems(): + if not hasattr(values, '__iter__'): + values = [values] + # FIXME this is bound to be slow for large libraries + for value in values: + q = value.strip() + + track_filter = lambda t: q == t.name + album_filter = lambda t: q == getattr(t, 'album', Album()).name + artist_filter = lambda t: filter( + lambda a: q == a.name, t.artists) + uri_filter = lambda t: q == t.uri + any_filter = lambda t: ( + track_filter(t) or album_filter(t) or + artist_filter(t) or uri_filter(t)) + + if field == 'track': + result_tracks = filter(track_filter, result_tracks) + elif field == 'album': + result_tracks = filter(album_filter, result_tracks) + elif field == 'artist': + result_tracks = filter(artist_filter, result_tracks) + elif field == 'uri': + result_tracks = filter(uri_filter, result_tracks) + elif field == 'any': + result_tracks = filter(any_filter, result_tracks) + else: + raise LookupError('Invalid lookup field: %s' % field) + return Playlist(tracks=result_tracks) + + def search(self, **query): + self._validate_query(query) + result_tracks = self._uri_mapping.values() + + for (field, values) in query.iteritems(): + if not hasattr(values, '__iter__'): + values = [values] + # FIXME this is bound to be slow for large libraries + for value in values: + q = value.strip().lower() + + track_filter = lambda t: q in t.name.lower() + album_filter = lambda t: q in getattr( + t, 'album', Album()).name.lower() + artist_filter = lambda t: filter( + lambda a: q in a.name.lower(), t.artists) + uri_filter = lambda t: q in t.uri.lower() + any_filter = lambda t: track_filter(t) or album_filter(t) or \ + artist_filter(t) or uri_filter(t) + + if field == 'track': + result_tracks = filter(track_filter, result_tracks) + elif field == 'album': + result_tracks = filter(album_filter, result_tracks) + elif field == 'artist': + result_tracks = filter(artist_filter, result_tracks) + elif field == 'uri': + result_tracks = filter(uri_filter, result_tracks) + elif field == 'any': + result_tracks = filter(any_filter, result_tracks) + else: + raise LookupError('Invalid lookup field: %s' % field) + return Playlist(tracks=result_tracks) + + def _validate_query(self, query): + for (_, values) in query.iteritems(): + if not values: + raise LookupError('Missing query') + for value in values: + if not value: + raise LookupError('Missing query') diff --git a/mopidy/backends/local/stored_playlists.py b/mopidy/backends/local/stored_playlists.py new file mode 100644 index 00000000..1cb03425 --- /dev/null +++ b/mopidy/backends/local/stored_playlists.py @@ -0,0 +1,85 @@ +import glob +import logging +import os +import shutil + +from mopidy import settings +from mopidy.backends import base +from mopidy.models import Playlist + +from .translator import parse_m3u + +logger = logging.getLogger(u'mopidy.backends.local') + + +class LocalStoredPlaylistsProvider(base.BaseStoredPlaylistsProvider): + def __init__(self, *args, **kwargs): + super(LocalStoredPlaylistsProvider, self).__init__(*args, **kwargs) + self._folder = settings.LOCAL_PLAYLIST_PATH + self.refresh() + + def lookup(self, uri): + pass # TODO + + def refresh(self): + playlists = [] + + logger.info('Loading playlists from %s', self._folder) + + for m3u in glob.glob(os.path.join(self._folder, '*.m3u')): + name = os.path.basename(m3u)[:-len('.m3u')] + tracks = [] + for uri in parse_m3u(m3u, settings.LOCAL_MUSIC_PATH): + try: + tracks.append(self.backend.library.lookup(uri)) + except LookupError, e: + logger.error('Playlist item could not be added: %s', e) + playlist = Playlist(tracks=tracks, name=name) + + # FIXME playlist name needs better handling + # FIXME tracks should come from lib. lookup + + playlists.append(playlist) + + self.playlists = playlists + + def create(self, name): + playlist = Playlist(name=name) + self.save(playlist) + return playlist + + def delete(self, playlist): + if playlist not in self._playlists: + return + + self._playlists.remove(playlist) + filename = os.path.join(self._folder, playlist.name + '.m3u') + + if os.path.exists(filename): + os.remove(filename) + + def rename(self, playlist, name): + if playlist not in self._playlists: + return + + src = os.path.join(self._folder, playlist.name + '.m3u') + dst = os.path.join(self._folder, name + '.m3u') + + renamed = playlist.copy(name=name) + index = self._playlists.index(playlist) + self._playlists[index] = renamed + + shutil.move(src, dst) + + def save(self, playlist): + file_path = os.path.join(self._folder, playlist.name + '.m3u') + + # FIXME this should be a save_m3u function, not inside save + with open(file_path, 'w') as file_handle: + for track in playlist.tracks: + if track.uri.startswith('file://'): + file_handle.write(track.uri[len('file://'):] + '\n') + else: + file_handle.write(track.uri + '\n') + + self._playlists.append(playlist) diff --git a/mopidy/backends/local/translator.py b/mopidy/backends/local/translator.py index 5a4a238b..01aad440 100644 --- a/mopidy/backends/local/translator.py +++ b/mopidy/backends/local/translator.py @@ -1,11 +1,11 @@ import logging -logger = logging.getLogger('mopidy.backends.local.translator') - from mopidy.models import Track, Artist, Album from mopidy.utils.encoding import locale_decode from mopidy.utils.path import path_to_uri +logger = logging.getLogger('mopidy.backends.local') + def parse_m3u(file_path, music_folder): r"""