libspotify: Rewrite backend to use new API

This commit is contained in:
Stein Magnus Jodal 2010-02-10 13:14:13 +01:00
parent 580fca2cb6
commit 10534793ae

View File

@ -1,4 +1,3 @@
from copy import deepcopy
import datetime as dt import datetime as dt
import logging import logging
import threading import threading
@ -8,7 +7,9 @@ from spotify.manager import SpotifySessionManager
from spotify.alsahelper import AlsaController from spotify.alsahelper import AlsaController
from mopidy import config from mopidy import config
from mopidy.backends import BaseBackend from mopidy.backends import (BaseBackend, BaseCurrentPlaylistController,
BaseLibraryController, BasePlaybackController,
BaseStoredPlaylistsController)
from mopidy.models import Artist, Album, Track, Playlist from mopidy.models import Artist, Album, Track, Playlist
logger = logging.getLogger(u'backends.libspotify') logger = logging.getLogger(u'backends.libspotify')
@ -16,107 +17,50 @@ logger = logging.getLogger(u'backends.libspotify')
ENCODING = 'utf-8' ENCODING = 'utf-8'
class LibspotifyBackend(BaseBackend): class LibspotifyBackend(BaseBackend):
def __init__(self, *args, **kwargs): def __init__(self):
super(LibspotifyBackend, self).__init__(*args, **kwargs) self.current_playlist = LibspotifyCurrentPlaylistController(
self._next_id = 0 backend=self)
self._id_to_uri_map = {} self.library = LibspotifyLibraryController(backend=self)
self._uri_to_id_map = {} self.playback = LibspotifyPlaybackController(backend=self)
self.stored_playlists = LibspotifyStoredPlaylistsController(
backend=self)
self.uri_handlers = [u'spotify:', u'http://open.spotify.com/']
self.translate = LibspotifyTranslator()
self.spotify = self._connect()
self.stored_playlists.refresh()
def _connect(self):
logger.info(u'Connecting to Spotify') logger.info(u'Connecting to Spotify')
self.spotify = LibspotifySessionManager( spotify = LibspotifySessionManager(
config.SPOTIFY_USERNAME, config.SPOTIFY_PASSWORD, backend=self) config.SPOTIFY_USERNAME, config.SPOTIFY_PASSWORD, backend=self)
self.spotify.start() spotify.start()
return spotify
def update_stored_playlists(self):
logger.info(u'Updating stored playlists')
playlists = []
for spotify_playlist in self.spotify.playlists:
playlists.append(self._to_mopidy_playlist(spotify_playlist))
self._playlists = playlists
logger.debug(u'Available playlists: %s',
u', '.join([u'<%s>' % p.name for p in self._playlists]))
# Model translation class LibspotifyCurrentPlaylistController(BaseCurrentPlaylistController):
pass
def _to_mopidy_id(self, spotify_uri):
if spotify_uri in self._uri_to_id_map:
return self._uri_to_id_map[spotify_uri]
else:
id = self._next_id
self._next_id += 1
self._id_to_uri_map[id] = spotify_uri
self._uri_to_id_map[spotify_uri] = id
return id
def _to_mopidy_artist(self, spotify_artist): class LibspotifyLibraryController(BaseLibraryController):
return Artist( pass
uri=str(Link.from_artist(spotify_artist)),
name=spotify_artist.name().decode(ENCODING),
)
def _to_mopidy_album(self, spotify_album):
# TODO pyspotify got much more data on albums than this
return Album(name=spotify_album.name().decode(ENCODING))
def _to_mopidy_track(self, spotify_track): class LibspotifyPlaybackController(BasePlaybackController):
return Track( def _next(self, track):
uri=str(Link.from_track(spotify_track, 0)), return self._play(track)
title=spotify_track.name().decode(ENCODING),
artists=[self._to_mopidy_artist(a)
for a in spotify_track.artists()],
album=self._to_mopidy_album(spotify_track.album()),
track_no=spotify_track.index(),
date=dt.date(spotify_track.album().year(), 1, 1),
length=spotify_track.duration(),
id=self._to_mopidy_id(str(Link.from_track(spotify_track, 0))),
)
def _to_mopidy_playlist(self, spotify_playlist):
return Playlist(
uri=str(Link.from_playlist(spotify_playlist)),
name=spotify_playlist.name().decode(ENCODING),
tracks=[self._to_mopidy_track(t) for t in spotify_playlist],
)
# Playback control
def _play_current_track(self):
self.spotify.session.load(
Link.from_string(self._current_track.uri).as_track())
self.spotify.session.play(1)
def _next(self):
self._current_song_pos += 1
self._play_current_track()
return True
def _pause(self): def _pause(self):
# TODO # TODO
return False return False
def _play(self): def _play(self, track):
if self._current_track is not None: self.backend.spotify.session.load(
self._play_current_track() Link.from_string(self._current_track.uri).as_track())
return True self.backend.spotify.session.play(1)
else:
return False
def _play_id(self, songid):
matches = filter(lambda t: t.id == songid, self._current_playlist)
if matches:
self._current_song_pos = self._current_playlist.index(matches[0])
self._play_current_track()
return True
else:
return False
def _play_pos(self, songpos):
self._current_song_pos = songpos
self._play_current_track()
return True return True
def _previous(self): def _previous(self, track):
self._current_song_pos -= 1 return self._play(track)
self._play_current_track()
return True
def _resume(self): def _resume(self):
# TODO # TODO
@ -126,13 +70,60 @@ class LibspotifyBackend(BaseBackend):
self.spotify.session.play(0) self.spotify.session.play(0)
return True return True
# Status querying
def status_bitrate(self): class LibspotifyStoredPlaylistsController(BaseStoredPlaylistsController):
return 320 def refresh(self):
logger.info(u'Refreshing stored playlists')
playlists = []
for spotify_playlist in self.backend.spotify.playlists:
playlists.append(
self.backend.translate.to_mopidy_playlist(spotify_playlist))
self._playlists = playlists
logger.debug(u'Available playlists: %s',
u', '.join([u'<%s>' % p.name for p in self.playlists]))
def url_handlers(self):
return [u'spotify:', u'http://open.spotify.com/'] class LibspotifyTranslator(object):
uri_to_id_map = {}
next_id = 0
def to_mopidy_id(self, spotify_uri):
if spotify_uri not in self.uri_to_id_map:
this_id = self.next_id
self.next_id += 1
self.uri_to_id_map[spotify_uri] = this_id
return self._uri_to_id_map[spotify_uri]
def to_mopidy_artist(self, spotify_artist):
return Artist(
uri=str(Link.from_artist(spotify_artist)),
name=spotify_artist.name().decode(ENCODING),
)
def to_mopidy_album(self, spotify_album):
# TODO pyspotify got much more data on albums than this
return Album(name=spotify_album.name().decode(ENCODING))
def to_mopidy_track(self, spotify_track):
uri = str(Link.from_track(spotify_track, 0))
return Track(
uri=uri,
title=spotify_track.name().decode(ENCODING),
artists=[self.to_mopidy_artist(a) for a in spotify_track.artists()],
album=self.to_mopidy_album(spotify_track.album()),
track_no=spotify_track.index(),
date=dt.date(spotify_track.album().year(), 1, 1),
length=spotify_track.duration(),
bitrate=320,
id=self.to_mopidy_id(uri),
)
def to_mopidy_playlist(self, spotify_playlist):
return Playlist(
uri=str(Link.from_playlist(spotify_playlist)),
name=spotify_playlist.name().decode(ENCODING),
tracks=[self.to_mopidy_track(t) for t in spotify_playlist],
)
class LibspotifySessionManager(SpotifySessionManager, threading.Thread): class LibspotifySessionManager(SpotifySessionManager, threading.Thread):
@ -141,6 +132,7 @@ class LibspotifySessionManager(SpotifySessionManager, threading.Thread):
threading.Thread.__init__(self) threading.Thread.__init__(self)
self.backend = backend self.backend = backend
self.audio = AlsaController() self.audio = AlsaController()
self.playlists = []
def run(self): def run(self):
self.connect() self.connect()
@ -159,7 +151,9 @@ class LibspotifySessionManager(SpotifySessionManager, threading.Thread):
def metadata_updated(self, session): def metadata_updated(self, session):
logger.debug('Metadata updated') logger.debug('Metadata updated')
self.backend.update_stored_playlists() # XXX This changes data "owned" by another thread, and leads to
# segmentation fault. We should use locking and messaging here.
self.backend.stored_playlists.refresh()
def connection_error(self, session, error): def connection_error(self, session, error):
logger.error('Connection error: %s', error) logger.error('Connection error: %s', error)
@ -181,4 +175,3 @@ class LibspotifySessionManager(SpotifySessionManager, threading.Thread):
def end_of_track(self, session): def end_of_track(self, session):
logger.debug('End of track') logger.debug('End of track')