Merge branch 'develop' into feature/spotify-playlist-loading
This commit is contained in:
commit
95e83a0842
@ -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 *
|
||||
|
||||
@ -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:
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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))],
|
||||
|
||||
@ -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():
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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):
|
||||
"""
|
||||
|
||||
@ -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):
|
||||
|
||||
Loading…
Reference in New Issue
Block a user