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 logging
import threading
@ -8,7 +7,9 @@ from spotify.manager import SpotifySessionManager
from spotify.alsahelper import AlsaController
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
logger = logging.getLogger(u'backends.libspotify')
@ -16,107 +17,50 @@ logger = logging.getLogger(u'backends.libspotify')
ENCODING = 'utf-8'
class LibspotifyBackend(BaseBackend):
def __init__(self, *args, **kwargs):
super(LibspotifyBackend, self).__init__(*args, **kwargs)
self._next_id = 0
self._id_to_uri_map = {}
self._uri_to_id_map = {}
def __init__(self):
self.current_playlist = LibspotifyCurrentPlaylistController(
backend=self)
self.library = LibspotifyLibraryController(backend=self)
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')
self.spotify = LibspotifySessionManager(
spotify = LibspotifySessionManager(
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):
return Artist(
uri=str(Link.from_artist(spotify_artist)),
name=spotify_artist.name().decode(ENCODING),
)
class LibspotifyLibraryController(BaseLibraryController):
pass
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):
return Track(
uri=str(Link.from_track(spotify_track, 0)),
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
class LibspotifyPlaybackController(BasePlaybackController):
def _next(self, track):
return self._play(track)
def _pause(self):
# TODO
return False
def _play(self):
if self._current_track is not None:
self._play_current_track()
return True
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()
def _play(self, track):
self.backend.spotify.session.load(
Link.from_string(self._current_track.uri).as_track())
self.backend.spotify.session.play(1)
return True
def _previous(self):
self._current_song_pos -= 1
self._play_current_track()
return True
def _previous(self, track):
return self._play(track)
def _resume(self):
# TODO
@ -126,13 +70,60 @@ class LibspotifyBackend(BaseBackend):
self.spotify.session.play(0)
return True
# Status querying
def status_bitrate(self):
return 320
class LibspotifyStoredPlaylistsController(BaseStoredPlaylistsController):
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):
@ -141,6 +132,7 @@ class LibspotifySessionManager(SpotifySessionManager, threading.Thread):
threading.Thread.__init__(self)
self.backend = backend
self.audio = AlsaController()
self.playlists = []
def run(self):
self.connect()
@ -159,7 +151,9 @@ class LibspotifySessionManager(SpotifySessionManager, threading.Thread):
def metadata_updated(self, session):
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):
logger.error('Connection error: %s', error)
@ -181,4 +175,3 @@ class LibspotifySessionManager(SpotifySessionManager, threading.Thread):
def end_of_track(self, session):
logger.debug('End of track')