Merge branch 'develop' into feature/improved-lookup
Conflicts: docs/changes.rst mopidy/backends/spotify/library.py
This commit is contained in:
commit
4a29e2549a
@ -10,6 +10,9 @@ v0.11.0 (in development)
|
||||
|
||||
**Spotify backend**
|
||||
|
||||
- Add :attr:`mopidy.settings.SPOTIFY_TIMEOUT` setting which allows you to
|
||||
control how long we should wait before giving up on Spotify searches, etc.
|
||||
|
||||
- Add support for looking up albums, artists, and playlists by URI in addition
|
||||
to tracks. (Fixes: :issue:`67`)
|
||||
|
||||
|
||||
@ -41,6 +41,7 @@ class Audio(pykka.ThreadingActor):
|
||||
self._playbin = None
|
||||
self._mixer = None
|
||||
self._mixer_track = None
|
||||
self._mixer_scale = None
|
||||
self._software_mixing = False
|
||||
self._appsrc = None
|
||||
self._volume_set = None
|
||||
@ -150,6 +151,8 @@ class Audio(pykka.ThreadingActor):
|
||||
|
||||
self._mixer = mixer
|
||||
self._mixer_track = track
|
||||
self._mixer_scale = (
|
||||
self._mixer_track.min_volume, self._mixer_track.max_volume)
|
||||
logger.info(
|
||||
'Audio mixer set to "%s" using track "%s"',
|
||||
mixer.get_factory().get_name(), track.label)
|
||||
@ -390,15 +393,18 @@ class Audio(pykka.ThreadingActor):
|
||||
avg_volume = float(sum(volumes)) / len(volumes)
|
||||
|
||||
internal_scale = (0, 100)
|
||||
mixer_scale = (
|
||||
self._mixer_track.min_volume, self._mixer_track.max_volume)
|
||||
|
||||
if self._volume_set is not None and self._rescale(self._volume_set,
|
||||
old=internal_scale, new=mixer_scale) == avg_volume:
|
||||
if self._volume_set is not None:
|
||||
volume_set_on_mixer_scale = self._rescale(
|
||||
self._volume_set, old=internal_scale, new=self._mixer_scale)
|
||||
else:
|
||||
volume_set_on_mixer_scale = None
|
||||
|
||||
if volume_set_on_mixer_scale == avg_volume:
|
||||
return self._volume_set
|
||||
else:
|
||||
return self._rescale(
|
||||
avg_volume, old=mixer_scale, new=internal_scale)
|
||||
avg_volume, old=self._mixer_scale, new=internal_scale)
|
||||
|
||||
def set_volume(self, volume):
|
||||
"""
|
||||
@ -418,10 +424,9 @@ class Audio(pykka.ThreadingActor):
|
||||
self._volume_set = volume
|
||||
|
||||
internal_scale = (0, 100)
|
||||
mixer_scale = (
|
||||
self._mixer_track.min_volume, self._mixer_track.max_volume)
|
||||
|
||||
volume = self._rescale(volume, old=internal_scale, new=mixer_scale)
|
||||
volume = self._rescale(
|
||||
volume, old=internal_scale, new=self._mixer_scale)
|
||||
|
||||
volumes = (volume,) * self._mixer_track.num_channels
|
||||
self._mixer.set_volume(self._mixer_track, volumes)
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import logging
|
||||
import Queue
|
||||
import time
|
||||
|
||||
import pykka
|
||||
from spotify import Link, SpotifyError
|
||||
|
||||
from mopidy import settings
|
||||
from mopidy.backends import base
|
||||
from mopidy.models import Track
|
||||
|
||||
@ -114,14 +115,48 @@ class SpotifyLibraryProvider(base.BaseLibraryProvider):
|
||||
|
||||
def search(self, **query):
|
||||
if not query:
|
||||
# Since we can't search for the entire Spotify library, we return
|
||||
# all tracks in the playlists when the query is empty.
|
||||
tracks = []
|
||||
for playlist in self.backend.playlists.playlists:
|
||||
tracks += playlist.tracks
|
||||
return tracks
|
||||
return self._get_all_tracks()
|
||||
|
||||
spotify_query = self._translate_search_query(query)
|
||||
logger.debug('Spotify search query: %s' % spotify_query)
|
||||
|
||||
future = pykka.ThreadingFuture()
|
||||
|
||||
def callback(results, userdata=None):
|
||||
# TODO Include results from results.albums(), etc. too
|
||||
# TODO Consider launching a second search if results.total_tracks()
|
||||
# is larger than len(results.tracks())
|
||||
tracks = [
|
||||
translator.to_mopidy_track(t) for t in results.tracks()]
|
||||
future.set(tracks)
|
||||
|
||||
if not self.backend.spotify.connected.wait(settings.SPOTIFY_TIMEOUT):
|
||||
logger.debug('Not connected: Spotify search cancelled')
|
||||
return []
|
||||
|
||||
self.backend.spotify.session.search(
|
||||
spotify_query, callback,
|
||||
track_count=100, album_count=0, artist_count=0)
|
||||
|
||||
try:
|
||||
return future.get(timeout=settings.SPOTIFY_TIMEOUT)
|
||||
except pykka.Timeout:
|
||||
logger.debug(
|
||||
'Timeout: Spotify search did not return in %ds',
|
||||
settings.SPOTIFY_TIMEOUT)
|
||||
return []
|
||||
|
||||
def _get_all_tracks(self):
|
||||
# Since we can't search for the entire Spotify library, we return
|
||||
# all tracks in the playlists when the query is empty.
|
||||
tracks = []
|
||||
for playlist in self.backend.playlists.playlists:
|
||||
tracks += playlist.tracks
|
||||
return tracks
|
||||
|
||||
def _translate_search_query(self, mopidy_query):
|
||||
spotify_query = []
|
||||
for (field, values) in query.iteritems():
|
||||
for (field, values) in mopidy_query.iteritems():
|
||||
if field == 'uri':
|
||||
tracks = []
|
||||
for value in values:
|
||||
@ -144,10 +179,4 @@ class SpotifyLibraryProvider(base.BaseLibraryProvider):
|
||||
else:
|
||||
spotify_query.append('%s:"%s"' % (field, value))
|
||||
spotify_query = ' '.join(spotify_query)
|
||||
logger.debug('Spotify search query: %s' % spotify_query)
|
||||
queue = Queue.Queue()
|
||||
self.backend.spotify.search(spotify_query, queue)
|
||||
try:
|
||||
return queue.get(timeout=3) # XXX What is an reasonable timeout?
|
||||
except Queue.Empty:
|
||||
return []
|
||||
return spotify_query
|
||||
|
||||
@ -165,19 +165,6 @@ class SpotifySessionManager(process.BaseThread, PyspotifySessionManager):
|
||||
logger.info('Loaded %d Spotify playlist(s)', len(playlists))
|
||||
BackendListener.send('playlists_loaded')
|
||||
|
||||
def search(self, query, queue):
|
||||
"""Search method used by Mopidy backend"""
|
||||
def callback(results, userdata=None):
|
||||
# TODO Include results from results.albums(), etc. too
|
||||
# TODO Consider launching a second search if results.total_tracks()
|
||||
# is larger than len(results.tracks())
|
||||
tracks = [
|
||||
translator.to_mopidy_track(t) for t in results.tracks()]
|
||||
queue.put(tracks)
|
||||
self.connected.wait()
|
||||
self.session.search(
|
||||
query, callback, track_count=100, album_count=0, artist_count=0)
|
||||
|
||||
def logout(self):
|
||||
"""Log out from spotify"""
|
||||
logger.debug('Logging out from Spotify')
|
||||
|
||||
@ -282,3 +282,12 @@ SPOTIFY_PROXY_USERNAME = None
|
||||
#:
|
||||
#: SPOTIFY_PROXY_PASSWORD = None
|
||||
SPOTIFY_PROXY_PASSWORD = None
|
||||
|
||||
#: Max number of seconds to wait for Spotify operations to complete.
|
||||
#:
|
||||
#: Used by :mod:`mopidy.backends.spotify`
|
||||
#:
|
||||
#: Default::
|
||||
#:
|
||||
#: SPOTIFY_TIMEOUT = 10
|
||||
SPOTIFY_TIMEOUT = 10
|
||||
|
||||
Loading…
Reference in New Issue
Block a user