Merge branch 'develop' into feature/spotify-playlist-loading

This commit is contained in:
Stein Magnus Jodal 2011-06-13 16:49:07 +02:00
commit 95e83a0842
8 changed files with 42 additions and 19 deletions

View File

@ -1,5 +1,10 @@
include LICENSE pylintrc *.rst *.ini data/mopidy.desktop
include *.ini
include *.rst
include LICENSE
include MANIFEST.in
include data/mopidy.desktop
include mopidy/backends/spotify/spotify_appkey.key
include pylintrc
recursive-include docs *
prune docs/_build
recursive-include requirements *

View File

@ -18,7 +18,7 @@ Please note that 0.5.0 requires some updated dependencies, as listed under
**Important changes**
- If you use the Spotify backend, you *must* upgrade to libspotify 0.0.8 and
pyspotify 1.2. If you install from APT, libspotify and pyspotify will
pyspotify 1.3. If you install from APT, libspotify and pyspotify will
automatically be upgraded. If you are not installing from APT, follow the
instructions at :doc:`/installation/libspotify/`.
@ -45,8 +45,9 @@ Please note that 0.5.0 requires some updated dependencies, as listed under
workaround of searching and reconnecting to make the playlists appear are
no longer necessary. (Fixes: :issue:`59`)
- Replace not decodable characters returned from Spotify instead of throwing
an exception, as we won't try to figure out the encoding of non-UTF-8-data.
- Track's that are no longer available in Spotify's archives are now
"autolinked" to corresponding tracks in other albums, just like the
official Spotify clients do. (Fixes: :issue:`34`)
- MPD frontend:

View File

@ -33,7 +33,7 @@ class SpotifyBackend(ThreadingActor, Backend):
**Dependencies:**
- libspotify == 0.0.8 (libspotify8 package from apt.mopidy.com)
- pyspotify == 1.2 (python-spotify package from apt.mopidy.com)
- pyspotify == 1.3 (python-spotify package from apt.mopidy.com)
**Settings:**
@ -72,19 +72,22 @@ class SpotifyBackend(ThreadingActor, Backend):
self.gstreamer = None
self.spotify = None
# Fail early if settings are not present
self.username = settings.SPOTIFY_USERNAME
self.password = settings.SPOTIFY_PASSWORD
def on_start(self):
gstreamer_refs = ActorRegistry.get_by_class(GStreamer)
assert len(gstreamer_refs) == 1, 'Expected exactly one running gstreamer.'
self.gstreamer = gstreamer_refs[0].proxy()
logger.info(u'Mopidy uses SPOTIFY(R) CORE')
self.spotify = self._connect()
def _connect(self):
from .session_manager import SpotifySessionManager
logger.info(u'Mopidy uses SPOTIFY(R) CORE')
logger.debug(u'Connecting to Spotify')
spotify = SpotifySessionManager(
settings.SPOTIFY_USERNAME, settings.SPOTIFY_PASSWORD)
spotify = SpotifySessionManager(self.username, self.password)
spotify.start()
return spotify

View File

@ -16,7 +16,7 @@ class SpotifyTranslator(object):
return Artist(name=u'[loading...]')
return Artist(
uri=str(Link.from_artist(spotify_artist)),
name=spotify_artist.name().decode(ENCODING, 'replace'),
name=spotify_artist.name()
)
@classmethod
@ -24,7 +24,7 @@ class SpotifyTranslator(object):
if spotify_album is None or not spotify_album.is_loaded():
return Album(name=u'[loading...]')
# TODO pyspotify got much more data on albums than this
return Album(name=spotify_album.name().decode(ENCODING, 'replace'))
return Album(name=spotify_album.name())
@classmethod
def to_mopidy_track(cls, spotify_track):
@ -38,7 +38,7 @@ class SpotifyTranslator(object):
date = None
return Track(
uri=uri,
name=spotify_track.name().decode(ENCODING, 'replace'),
name=spotify_track.name(),
artists=[cls.to_mopidy_artist(a) for a in spotify_track.artists()],
album=cls.to_mopidy_album(spotify_track.album()),
track_no=spotify_track.index(),
@ -57,7 +57,7 @@ class SpotifyTranslator(object):
try:
return Playlist(
uri=str(Link.from_playlist(spotify_playlist)),
name=spotify_playlist.name().decode(ENCODING, 'replace'),
name=spotify_playlist.name(),
# FIXME if check on link is a hackish workaround for is_local
tracks=[cls.to_mopidy_track(t) for t in spotify_playlist
if str(Link.from_track(t, 0))],

View File

@ -40,11 +40,15 @@ def main():
setup_mixer()
setup_backend()
setup_frontends()
while ActorRegistry.get_all():
while True:
time.sleep(1)
logger.info(u'No actors left. Exiting...')
except SettingsError as e:
logger.error(e.message)
except KeyboardInterrupt:
logger.info(u'User interrupt. Exiting...')
logger.info(u'Interrupted. Exiting...')
except Exception as e:
logger.exception(e)
finally:
stop_all_actors()
def parse_options():

View File

@ -49,7 +49,7 @@ class MpdSession(asynchat.async_chat):
Format a response from the MPD command handlers and send it to the
client.
"""
if response is not None:
if response:
response = LINE_TERMINATOR.join(response)
logger.debug(u'Response to [%s]:%s: %s', self.client_address,
self.client_port, indent(response))

View File

@ -298,7 +298,7 @@ class GStreamer(ThreadingActor):
output.sync_state_with_parent() # Required to add to running pipe
gst.element_link_many(self._tee, output)
self._outputs.append(output)
logger.info('Added %s', output.get_name())
logger.debug('GStreamer added %s', output.get_name())
def list_outputs(self):
"""

View File

@ -1,5 +1,6 @@
import logging
import signal
import thread
import threading
import gobject
@ -12,19 +13,28 @@ from mopidy import SettingsError
logger = logging.getLogger('mopidy.utils.process')
def exit_process():
logger.debug(u'Interrupting main...')
thread.interrupt_main()
logger.debug(u'Interrupted main')
def exit_handler(signum, frame):
"""A :mod:`signal` handler which will exit the program on signal."""
signals = dict((k, v) for v, k in signal.__dict__.iteritems()
if v.startswith('SIG') and not v.startswith('SIG_'))
logger.info(u'Got %s. Exiting...', signals[signum])
stop_all_actors()
logger.info(u'Got %s signal', signals[signum])
exit_process()
def stop_all_actors():
num_actors = len(ActorRegistry.get_all())
while num_actors:
logger.debug(u'Seeing %d actor and %d non-actor thread(s): %s',
num_actors, threading.active_count() - num_actors,
', '.join([t.name for t in threading.enumerate()]))
logger.debug(u'Stopping %d actor(s)...', num_actors)
ActorRegistry.stop_all()
num_actors = len(ActorRegistry.get_all())
logger.debug(u'All actors stopped.')
class BaseThread(threading.Thread):
def __init__(self):