Merge branch 'develop' into feature/improved-lookup

Conflicts:
	docs/changes.rst
	mopidy/backends/spotify/library.py
This commit is contained in:
Stein Magnus Jodal 2012-12-19 23:32:32 +01:00
commit 4a29e2549a
5 changed files with 69 additions and 36 deletions

View File

@ -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`)

View File

@ -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)

View File

@ -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

View File

@ -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')

View File

@ -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