diff --git a/mopidy/backends/spotify/__init__.py b/mopidy/backends/spotify/__init__.py index 0e32d4cd..b03849eb 100644 --- a/mopidy/backends/spotify/__init__.py +++ b/mopidy/backends/spotify/__init__.py @@ -28,7 +28,7 @@ timeout = 10 cache_path = $XDG_CACHE_DIR/mopidy/spotify # Connect to Spotify through a proxy -proxy_host = +proxy_hostname = proxy_username = proxy_password = """ @@ -81,7 +81,7 @@ class Extension(ext.Extension): schema['bitrate'] = config.Integer(choices=(96, 160, 320)) schema['timeout'] = config.Integer(minimum=0) schema['cache_path'] = config.String() - schema['proxy_host'] = config.Hostname(optional=True) + schema['proxy_hostname'] = config.Hostname(optional=True) schema['proxy_username'] = config.String(optional=True) schema['proxy_password'] = config.String(optional=True, secret=True) return schema diff --git a/mopidy/backends/spotify/actor.py b/mopidy/backends/spotify/actor.py index 67b4acdc..1f90ba51 100644 --- a/mopidy/backends/spotify/actor.py +++ b/mopidy/backends/spotify/actor.py @@ -4,23 +4,20 @@ import logging import pykka -from mopidy import settings from mopidy.backends import base +from mopidy.backends.spotify.library import SpotifyLibraryProvider +from mopidy.backends.spotify.playback import SpotifyPlaybackProvider +from mopidy.backends.spotify.session_manager import SpotifySessionManager +from mopidy.backends.spotify.playlists import SpotifyPlaylistsProvider logger = logging.getLogger('mopidy.backends.spotify') class SpotifyBackend(pykka.ThreadingActor, base.Backend): - # Imports inside methods are to prevent loading of __init__.py to fail on - # missing spotify dependencies. - def __init__(self, config, audio): super(SpotifyBackend, self).__init__() - from .library import SpotifyLibraryProvider - from .playback import SpotifyPlaybackProvider - from .session_manager import SpotifySessionManager - from .playlists import SpotifyPlaylistsProvider + self.config = config self.library = SpotifyLibraryProvider(backend=self) self.playback = SpotifyPlaybackProvider(audio=audio, backend=self) @@ -28,17 +25,8 @@ class SpotifyBackend(pykka.ThreadingActor, base.Backend): self.uri_schemes = ['spotify'] - # Fail early if settings are not present - username = settings.SPOTIFY_USERNAME - password = settings.SPOTIFY_PASSWORD - proxy = settings.SPOTIFY_PROXY_HOST - proxy_username = settings.SPOTIFY_PROXY_USERNAME - proxy_password = settings.SPOTIFY_PROXY_PASSWORD - self.spotify = SpotifySessionManager( - username, password, audio=audio, backend_ref=self.actor_ref, - proxy=proxy, proxy_username=proxy_username, - proxy_password=proxy_password) + config, audio=audio, backend_ref=self.actor_ref) def on_start(self): logger.info('Mopidy uses SPOTIFY(R) CORE') diff --git a/mopidy/backends/spotify/library.py b/mopidy/backends/spotify/library.py index 7afde913..b8192dad 100644 --- a/mopidy/backends/spotify/library.py +++ b/mopidy/backends/spotify/library.py @@ -7,7 +7,6 @@ import urllib import pykka from spotify import Link, SpotifyError -from mopidy import settings from mopidy.backends import base from mopidy.models import Track, SearchResult @@ -62,6 +61,10 @@ class SpotifyTrack(Track): class SpotifyLibraryProvider(base.BaseLibraryProvider): + def __init__(self, *args, **kwargs): + super(SpotifyLibraryProvider).__init__(*args, **kwargs) + self._timeout = self.backend.config['spotify']['timeout'] + def find_exact(self, query=None, uris=None): return self.search(query=query, uris=uris) @@ -116,10 +119,11 @@ class SpotifyLibraryProvider(base.BaseLibraryProvider): SpotifyTrack(track=t) for t in playlist if t.availability() == TRACK_AVAILABLE] - def _wait_for_object_to_load( - self, spotify_obj, timeout=settings.SPOTIFY_TIMEOUT): + def _wait_for_object_to_load(self, spotify_obj, timeout=None): # XXX Sleeping to wait for the Spotify object to load is an ugly hack, # but it works. We should look into other solutions for this. + if timeout is None: + timeout = self._timeout wait_until = time.time() + timeout while not spotify_obj.is_loaded(): time.sleep(0.1) @@ -166,7 +170,7 @@ class SpotifyLibraryProvider(base.BaseLibraryProvider): future.set(search_result) # Wait always returns None on python 2.6 :/ - self.backend.spotify.connected.wait(settings.SPOTIFY_TIMEOUT) + self.backend.spotify.connected.wait(self._timeout) if not self.backend.spotify.connected.is_set(): logger.debug('Not connected: Spotify search cancelled') return SearchResult(uri='spotify:search') @@ -176,11 +180,10 @@ class SpotifyLibraryProvider(base.BaseLibraryProvider): album_count=200, artist_count=200, track_count=200) try: - return future.get(timeout=settings.SPOTIFY_TIMEOUT) + return future.get(timeout=self._timeout) except pykka.Timeout: logger.debug( - 'Timeout: Spotify search did not return in %ds', - settings.SPOTIFY_TIMEOUT) + 'Timeout: Spotify search did not return in %ds', self._timeout) return SearchResult(uri='spotify:search') def _get_all_tracks(self): diff --git a/mopidy/backends/spotify/session_manager.py b/mopidy/backends/spotify/session_manager.py index cedff8d1..1281050b 100644 --- a/mopidy/backends/spotify/session_manager.py +++ b/mopidy/backends/spotify/session_manager.py @@ -6,7 +6,7 @@ import threading from spotify.manager import SpotifySessionManager as PyspotifySessionManager -from mopidy import audio, settings +from mopidy import audio from mopidy.backends.listener import BackendListener from mopidy.utils import process, versioning @@ -23,17 +23,21 @@ BITRATES = {96: 2, 160: 0, 320: 1} class SpotifySessionManager(process.BaseThread, PyspotifySessionManager): - cache_location = settings.SPOTIFY_CACHE_PATH + cache_location = None settings_location = cache_location appkey_file = os.path.join(os.path.dirname(__file__), 'spotify_appkey.key') user_agent = 'Mopidy %s' % versioning.get_version() - def __init__(self, username, password, audio, backend_ref, proxy=None, - proxy_username=None, proxy_password=None): + def __init__(self, config, audio, backend_ref): + + self.cache_location = config['spotify']['cache_path'] + PyspotifySessionManager.__init__( - self, username, password, proxy=proxy, - proxy_username=proxy_username, - proxy_password=proxy_password) + self, config['spotify']['username'], config['spotify']['password'], + proxy=config['spotify']['proxy_hostname'], + proxy_username=config['spotify']['proxy_username'], + proxy_password=config['spotify']['proxy_password']) + process.BaseThread.__init__(self) self.name = 'SpotifyThread' @@ -41,6 +45,8 @@ class SpotifySessionManager(process.BaseThread, PyspotifySessionManager): self.backend = None self.backend_ref = backend_ref + self.bitrate = config['spotify']['bitrate'] + self.connected = threading.Event() self.push_audio_data = True self.buffer_timestamp = 0 @@ -66,10 +72,8 @@ class SpotifySessionManager(process.BaseThread, PyspotifySessionManager): if not hasattr(self, 'session'): self.session = session - logger.debug( - 'Preferred Spotify bitrate is %s kbps', - settings.SPOTIFY_BITRATE) - session.set_preferred_bitrate(BITRATES[settings.SPOTIFY_BITRATE]) + logger.debug('Preferred Spotify bitrate is %d kbps', self.bitrate) + session.set_preferred_bitrate(BITRATES[self.bitrate]) self.container_manager = SpotifyContainerManager(self) self.playlist_manager = SpotifyPlaylistManager(self) @@ -167,9 +171,14 @@ class SpotifySessionManager(process.BaseThread, PyspotifySessionManager): if not self._initial_data_receive_completed: logger.debug('Still getting data; skipped refresh of playlists') return - playlists = map( - translator.to_mopidy_playlist, self.session.playlist_container()) - playlists.append(translator.to_mopidy_playlist(self.session.starred())) + playlists = [] + for spotify_playlist in self.session.playlist_container(): + playlists.append(translator.to_mopidy_playlist( + spotify_playlist, + bitrate=self.bitrate, username=self.username)) + playlists.append(translator.to_mopidy_playlist( + self.session.starred(), + bitrate=self.bitrate, username=self.username)) playlists = filter(None, playlists) self.backend.playlists.playlists = playlists logger.info('Loaded %d Spotify playlist(s)', len(playlists)) diff --git a/mopidy/backends/spotify/translator.py b/mopidy/backends/spotify/translator.py index dfd9d99a..1a96e27c 100644 --- a/mopidy/backends/spotify/translator.py +++ b/mopidy/backends/spotify/translator.py @@ -2,7 +2,6 @@ from __future__ import unicode_literals from spotify import Link -from mopidy import settings from mopidy.models import Artist, Album, Track, Playlist @@ -39,7 +38,7 @@ def to_mopidy_album(spotify_album): return album_cache[uri] -def to_mopidy_track(spotify_track): +def to_mopidy_track(spotify_track, bitrate=None): if spotify_track is None: return uri = str(Link.from_track(spotify_track, 0)) @@ -60,11 +59,11 @@ def to_mopidy_track(spotify_track): track_no=spotify_track.index(), date=date, length=spotify_track.duration(), - bitrate=settings.SPOTIFY_BITRATE) + bitrate=bitrate) return track_cache[uri] -def to_mopidy_playlist(spotify_playlist): +def to_mopidy_playlist(spotify_playlist, bitrate=None, username=None): if spotify_playlist is None or spotify_playlist.type() != 'playlist': return uri = str(Link.from_playlist(spotify_playlist)) @@ -72,7 +71,7 @@ def to_mopidy_playlist(spotify_playlist): return Playlist(uri=uri, name='[loading...]') name = spotify_playlist.name() tracks = [ - to_mopidy_track(spotify_track) + to_mopidy_track(spotify_track, bitrate=bitrate) for spotify_track in spotify_playlist if not spotify_track.is_local() ] @@ -81,9 +80,6 @@ def to_mopidy_playlist(spotify_playlist): # Tracks in the Starred playlist are in reverse order from the official # client. tracks.reverse() - if spotify_playlist.owner().canonical_name() != settings.SPOTIFY_USERNAME: + if spotify_playlist.owner().canonical_name() != username: name += ' by ' + spotify_playlist.owner().canonical_name() - return Playlist( - uri=uri, - name=name, - tracks=tracks) + return Playlist(uri=uri, name=name, tracks=tracks)