From f311dd1e77b0aa16433effc1ff2f3f7046117a37 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Fri, 10 Jun 2011 15:14:34 +0200 Subject: [PATCH 1/8] Do not refresh playlists on every metadata update, but just when the playlist container is loaded --- mopidy/backends/spotify/session_manager.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mopidy/backends/spotify/session_manager.py b/mopidy/backends/spotify/session_manager.py index 4b6abe85..04398751 100644 --- a/mopidy/backends/spotify/session_manager.py +++ b/mopidy/backends/spotify/session_manager.py @@ -74,7 +74,6 @@ class SpotifySessionManager(BaseThread, PyspotifySessionManager): def metadata_updated(self, session): """Callback used by pyspotify""" logger.debug(u'Metadata updated') - self.refresh_stored_playlists() def connection_error(self, session, error): """Callback used by pyspotify""" From cd5886cc7a3f042365077ac0d218be3dcf688fa1 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Mon, 13 Jun 2011 16:50:17 +0200 Subject: [PATCH 2/8] Rename Spotify thread to simply 'SpotifyThread' --- 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 04398751..ba7782b2 100644 --- a/mopidy/backends/spotify/session_manager.py +++ b/mopidy/backends/spotify/session_manager.py @@ -29,7 +29,7 @@ class SpotifySessionManager(BaseThread, PyspotifySessionManager): def __init__(self, username, password): PyspotifySessionManager.__init__(self, username, password) BaseThread.__init__(self) - self.name = 'SpotifySMThread' + self.name = 'SpotifyThread' self.gstreamer = None self.backend = None From 174e0082689c8461483f4ebfeabff97b1c78a3b1 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Mon, 13 Jun 2011 16:51:02 +0200 Subject: [PATCH 3/8] Change wording of Spotify's 'no error' error so it makes sense without a preceeding error message --- 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 ba7782b2..552fa2a2 100644 --- a/mopidy/backends/spotify/session_manager.py +++ b/mopidy/backends/spotify/session_manager.py @@ -78,7 +78,7 @@ class SpotifySessionManager(BaseThread, PyspotifySessionManager): def connection_error(self, session, error): """Callback used by pyspotify""" if error is None: - logger.info(u'Spotify connection error resolved') + logger.info(u'Spotify connection OK') else: logger.error(u'Spotify connection error: %s', error) self.backend.playback.pause() From 6f4117729ab83e7a254c3515e0d9b7f5dd98a2fe Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Tue, 14 Jun 2011 00:00:38 +0200 Subject: [PATCH 4/8] Formatting --- mopidy/backends/spotify/container_manager.py | 4 ++-- mopidy/backends/spotify/session_manager.py | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/mopidy/backends/spotify/container_manager.py b/mopidy/backends/spotify/container_manager.py index 29360d79..9ae524e1 100644 --- a/mopidy/backends/spotify/container_manager.py +++ b/mopidy/backends/spotify/container_manager.py @@ -1,11 +1,11 @@ import logging -from spotify.manager import SpotifyContainerManager as PyspotifyContainerManager +from spotify.manager import SpotifyContainerManager as \ + PyspotifyContainerManager logger = logging.getLogger('mopidy.backends.spotify.container_manager') class SpotifyContainerManager(PyspotifyContainerManager): - def __init__(self, session_manager): PyspotifyContainerManager.__init__(self) self.session_manager = session_manager diff --git a/mopidy/backends/spotify/session_manager.py b/mopidy/backends/spotify/session_manager.py index 552fa2a2..d581c7c1 100644 --- a/mopidy/backends/spotify/session_manager.py +++ b/mopidy/backends/spotify/session_manager.py @@ -45,7 +45,8 @@ class SpotifySessionManager(BaseThread, PyspotifySessionManager): def setup(self): gstreamer_refs = ActorRegistry.get_by_class(GStreamer) - assert len(gstreamer_refs) == 1, 'Expected exactly one running gstreamer.' + assert len(gstreamer_refs) == 1, \ + 'Expected exactly one running gstreamer.' self.gstreamer = gstreamer_refs[0].proxy() backend_refs = ActorRegistry.get_by_class(Backend) @@ -57,14 +58,17 @@ class SpotifySessionManager(BaseThread, PyspotifySessionManager): if error: logger.error(u'Spotify login error: %s', error) return + logger.info(u'Connected to Spotify') self.session = session - logger.debug(u'Preferred Spotify bitrate is %s kbps.', settings.SPOTIFY_BITRATE) + logger.debug(u'Preferred Spotify bitrate is %s kbps', + settings.SPOTIFY_BITRATE) self.session.set_preferred_bitrate(BITRATES[settings.SPOTIFY_BITRATE]) self.container_manager = SpotifyContainerManager(self) self.container_manager.watch(self.session.playlist_container()) + self.connected.set() def logged_out(self, session): From 371fb9b90d452f8893446e4968659d2e1ff58676 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Tue, 14 Jun 2011 00:03:23 +0200 Subject: [PATCH 5/8] Add missing container callbacks with debug log statements --- mopidy/backends/spotify/container_manager.py | 28 ++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/mopidy/backends/spotify/container_manager.py b/mopidy/backends/spotify/container_manager.py index 9ae524e1..bd9035aa 100644 --- a/mopidy/backends/spotify/container_manager.py +++ b/mopidy/backends/spotify/container_manager.py @@ -11,6 +11,30 @@ class SpotifyContainerManager(PyspotifyContainerManager): self.session_manager = session_manager def container_loaded(self, container, userdata): - """Callback used by pyspotify.""" - logger.debug(u'Container loaded') + """Callback used by pyspotify""" + logger.debug(u'Callback called: playlist container loaded') self.session_manager.refresh_stored_playlists() + + def playlist_added(self, container, playlist, position, userdata): + """Callback used by pyspotify""" + logger.debug(u'Callback called: playlist "%s" added at position %d', + playlist.name(), position) + # container_loaded() is called after this callback, so we do not need + # to handle this callback. + + def playlist_moved(self, container, playlist, old_position, new_position, + userdata): + """Callback used by pyspotify""" + logger.debug( + u'Callback called: playlist "%s" moved from position %d to %d', + playlist.name(), old_position, new_position) + # container_loaded() is called after this callback, so we do not need + # to handle this callback. + + def playlist_removed(self, container, playlist, position, userdata): + """Callback used by pyspotify""" + logger.debug( + u'Callback called: playlist "%s" removed from position %d', + playlist.name(), position) + # container_loaded() is called after this callback, so we do not need + # to handle this callback. From 82ba04408c606c915f8df5795ee7cc3f4451adcb Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Tue, 14 Jun 2011 00:04:08 +0200 Subject: [PATCH 6/8] Add playlist callbacks with debug log statements --- mopidy/backends/spotify/container_manager.py | 5 ++ mopidy/backends/spotify/playlist_manager.py | 94 ++++++++++++++++++++ mopidy/backends/spotify/session_manager.py | 8 +- 3 files changed, 105 insertions(+), 2 deletions(-) create mode 100644 mopidy/backends/spotify/playlist_manager.py diff --git a/mopidy/backends/spotify/container_manager.py b/mopidy/backends/spotify/container_manager.py index bd9035aa..5166cacb 100644 --- a/mopidy/backends/spotify/container_manager.py +++ b/mopidy/backends/spotify/container_manager.py @@ -15,6 +15,11 @@ class SpotifyContainerManager(PyspotifyContainerManager): logger.debug(u'Callback called: playlist container loaded') self.session_manager.refresh_stored_playlists() + playlist_container = self.session_manager.session.playlist_container() + for playlist in playlist_container: + self.session_manager.playlist_manager.watch(playlist) + logger.debug(u'Watching %d playlist(s) for changes', len(playlist_container)) + def playlist_added(self, container, playlist, position, userdata): """Callback used by pyspotify""" logger.debug(u'Callback called: playlist "%s" added at position %d', diff --git a/mopidy/backends/spotify/playlist_manager.py b/mopidy/backends/spotify/playlist_manager.py new file mode 100644 index 00000000..5f4f1fd7 --- /dev/null +++ b/mopidy/backends/spotify/playlist_manager.py @@ -0,0 +1,94 @@ +import datetime +import logging + +from spotify.manager import SpotifyPlaylistManager as PyspotifyPlaylistManager + +logger = logging.getLogger('mopidy.backends.spotify.playlist_manager') + +class SpotifyPlaylistManager(PyspotifyPlaylistManager): + def __init__(self, session_manager): + PyspotifyPlaylistManager.__init__(self) + self.session_manager = session_manager + + def tracks_added(self, playlist, tracks, position, userdata): + """Callback used by pyspotify""" + logger.debug(u'Callback called: ' + u'%d track(s) added to position %d in playlist "%s"', + len(tracks), position, playlist.name()) + # TODO Partially update stored playlists? + + def tracks_moved(self, playlist, tracks, new_position, userdata): + """Callback used by pyspotify""" + logger.debug(u'Callback called: ' + u'%d track(s) moved to position %d in playlist "%s"', + len(tracks), new_position, playlist.name()) + # TODO Partially update stored playlists? + + def tracks_removed(self, playlist, tracks, userdata): + """Callback used by pyspotify""" + logger.debug(u'Callback called: ' + u'%d track(s) removed from playlist "%s"', len(tracks), playlist.name()) + # TODO Partially update stored playlists? + + def playlist_renamed(self, playlist, userdata): + """Callback used by pyspotify""" + logger.debug(u'Callback called: Playlist renamed to "%s"', + playlist.name()) + # TODO Partially update stored playlists? + + def playlist_state_changed(self, playlist, userdata): + """Callback used by pyspotify""" + logger.debug(u'Callback called: The state of playlist "%s" changed', + playlist.name()) + + def playlist_update_in_progress(self, playlist, done, userdata): + """Callback used by pyspotify""" + if done: + logger.debug(u'Callback called: ' + u'Update of playlist "%s" done', playlist.name()) + else: + logger.debug(u'Callback called: ' + u'Update of playlist "%s" in progress', playlist.name()) + + def playlist_metadata_updated(self, playlist, userdata): + """Callback used by pyspotify""" + logger.debug(u'Callback called: Metadata updated for playlist "%s"', + playlist.name()) + # TODO Update stored playlists? + + def track_created_changed(self, playlist, position, user, when, userdata): + """Callback used by pyspotify""" + when = datetime.datetime.fromtimestamp(when) + logger.debug( + u'Callback called: Created by/when for track %d in playlist ' + u'"%s" changed to user "N/A" and time "%s"', + position, playlist.name(), when) + + def track_message_changed(self, playlist, position, message, userdata): + """Callback used by pyspotify""" + logger.debug( + u'Callback called: Message for track %d in playlist ' + u'"%s" changed to "%s"', position, playlist.name(), message) + + def track_seen_changed(self, playlist, position, seen, userdata): + """Callback used by pyspotify""" + logger.debug( + u'Callback called: Seen attribute for track %d in playlist ' + u'"%s" changed to "%s"', position, playlist.name(), seen) + + def description_changed(self, playlist, description, userdata): + """Callback used by pyspotify""" + logger.debug( + u'Callback called: Description changed for playlist "%s" to "%s"', + playlist.name(), description) + + def subscribers_changed(self, playlist, userdata): + """Callback used by pyspotify""" + logger.debug( + u'Callback called: Subscribers changed for playlist "%s"', + playlist.name()) + + def image_changed(self, playlist, image, userdata): + """Callback used by pyspotify""" + logger.debug(u'Callback called: Image changed for playlist "%s"', + playlist.name()) diff --git a/mopidy/backends/spotify/session_manager.py b/mopidy/backends/spotify/session_manager.py index d581c7c1..fd71d861 100644 --- a/mopidy/backends/spotify/session_manager.py +++ b/mopidy/backends/spotify/session_manager.py @@ -9,11 +9,12 @@ from pykka.registry import ActorRegistry from mopidy import get_version, settings from mopidy.backends.base import Backend from mopidy.backends.spotify import BITRATES +from mopidy.backends.spotify.container_manager import SpotifyContainerManager +from mopidy.backends.spotify.playlist_manager import SpotifyPlaylistManager from mopidy.backends.spotify.translator import SpotifyTranslator from mopidy.models import Playlist from mopidy.gstreamer import GStreamer from mopidy.utils.process import BaseThread -from mopidy.backends.spotify.container_manager import SpotifyContainerManager logger = logging.getLogger('mopidy.backends.spotify.session_manager') @@ -38,6 +39,7 @@ class SpotifySessionManager(BaseThread, PyspotifySessionManager): self.session = None self.container_manager = None + self.playlist_manager = None def run_inside_try(self): self.setup() @@ -67,6 +69,8 @@ class SpotifySessionManager(BaseThread, PyspotifySessionManager): self.session.set_preferred_bitrate(BITRATES[settings.SPOTIFY_BITRATE]) self.container_manager = SpotifyContainerManager(self) + self.playlist_manager = SpotifyPlaylistManager(self) + self.container_manager.watch(self.session.playlist_container()) self.connected.set() @@ -77,7 +81,7 @@ class SpotifySessionManager(BaseThread, PyspotifySessionManager): def metadata_updated(self, session): """Callback used by pyspotify""" - logger.debug(u'Metadata updated') + logger.debug(u'Callback called: Metadata updated') def connection_error(self, session, error): """Callback used by pyspotify""" From 3c68c8f9ead9a8c04e7cefa022c2ce453efa2b26 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Tue, 14 Jun 2011 00:19:59 +0200 Subject: [PATCH 7/8] The playlist name is not available when playlist_added is called --- mopidy/backends/spotify/container_manager.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/mopidy/backends/spotify/container_manager.py b/mopidy/backends/spotify/container_manager.py index 5166cacb..520cfb68 100644 --- a/mopidy/backends/spotify/container_manager.py +++ b/mopidy/backends/spotify/container_manager.py @@ -18,12 +18,13 @@ class SpotifyContainerManager(PyspotifyContainerManager): playlist_container = self.session_manager.session.playlist_container() for playlist in playlist_container: self.session_manager.playlist_manager.watch(playlist) - logger.debug(u'Watching %d playlist(s) for changes', len(playlist_container)) + logger.debug(u'Watching %d playlist(s) for changes', + len(playlist_container)) def playlist_added(self, container, playlist, position, userdata): """Callback used by pyspotify""" - logger.debug(u'Callback called: playlist "%s" added at position %d', - playlist.name(), position) + logger.debug(u'Callback called: playlist added at position %d', + position) # container_loaded() is called after this callback, so we do not need # to handle this callback. From 4516767372f9026d84c1a9b8a562a5d0f9f61f47 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Tue, 14 Jun 2011 00:24:54 +0200 Subject: [PATCH 8/8] Refresh stored playlists when tracks are added to, moved in, or removed from playlists --- mopidy/backends/spotify/playlist_manager.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/mopidy/backends/spotify/playlist_manager.py b/mopidy/backends/spotify/playlist_manager.py index 5f4f1fd7..f72ac4ca 100644 --- a/mopidy/backends/spotify/playlist_manager.py +++ b/mopidy/backends/spotify/playlist_manager.py @@ -15,26 +15,26 @@ class SpotifyPlaylistManager(PyspotifyPlaylistManager): logger.debug(u'Callback called: ' u'%d track(s) added to position %d in playlist "%s"', len(tracks), position, playlist.name()) - # TODO Partially update stored playlists? + self.session_manager.refresh_stored_playlists() def tracks_moved(self, playlist, tracks, new_position, userdata): """Callback used by pyspotify""" logger.debug(u'Callback called: ' u'%d track(s) moved to position %d in playlist "%s"', len(tracks), new_position, playlist.name()) - # TODO Partially update stored playlists? + self.session_manager.refresh_stored_playlists() def tracks_removed(self, playlist, tracks, userdata): """Callback used by pyspotify""" logger.debug(u'Callback called: ' u'%d track(s) removed from playlist "%s"', len(tracks), playlist.name()) - # TODO Partially update stored playlists? + self.session_manager.refresh_stored_playlists() def playlist_renamed(self, playlist, userdata): """Callback used by pyspotify""" logger.debug(u'Callback called: Playlist renamed to "%s"', playlist.name()) - # TODO Partially update stored playlists? + self.session_manager.refresh_stored_playlists() def playlist_state_changed(self, playlist, userdata): """Callback used by pyspotify""" @@ -54,7 +54,6 @@ class SpotifyPlaylistManager(PyspotifyPlaylistManager): """Callback used by pyspotify""" logger.debug(u'Callback called: Metadata updated for playlist "%s"', playlist.name()) - # TODO Update stored playlists? def track_created_changed(self, playlist, position, user, when, userdata): """Callback used by pyspotify"""