diff --git a/mopidy_iris/core.py b/mopidy_iris/core.py index 7dd705b1..473af375 100755 --- a/mopidy_iris/core.py +++ b/mopidy_iris/core.py @@ -374,39 +374,53 @@ class IrisCore(object): # We only want to play the first batch added = self.core.tracklist.add(uris = uris[0:3]) + if (not added.get()): + logger.error("No recommendations added to queue") + + self.radio['enabled'] = 0; + error = { + 'message': 'No recommendations added to queue', + 'radio': self.radio + } + if (callback): + callback(False, error) + else: + return error + # Save results (minus first batch) for later use self.radio['results'] = uris[3:] - if added.get(): - if starting: - self.core.playback.play() - self.broadcast( - data={ - 'type': 'radio_started', - 'radio': self.radio - } - ) - else: - self.broadcast( - data={ - 'type': 'radio_changed', - 'radio': self.radio - } - ) + if starting: + self.core.playback.play() + self.broadcast( + data={ + 'type': 'radio_started', + 'radio': self.radio + } + ) + else: + self.broadcast( + data={ + 'type': 'radio_changed', + 'radio': self.radio + } + ) - self.get_radio(callback=callback) - return + self.get_radio(callback=callback) + return - # failed fetching/adding tracks, so no-go - self.radio['enabled'] = 0; - error = { - 'message': 'Could not start radio', - 'radio': self.radio - } - if (callback): - callback(False, error) + # Failed fetching/adding tracks, so no-go else: - return error + logger.error("No recommendations returned by Spotify") + self.radio['enabled'] = 0; + error = { + 'message': 'Could not start radio', + 'radio': self.radio + } + if (callback): + callback(False, error) + else: + return error def stop_radio(self, *args, **kwargs): @@ -442,13 +456,10 @@ class IrisCore(object): def load_more_tracks(self, *args, **kwargs): - # this is crude, but it means we don't need to handle expired tokens - # TODO: address this when it's clear what Jodal and the team want to do with Pyspotify - self.refresh_spotify_token() - try: - token = self.spotify_token - token = token['access_token'] + self.get_spotify_token() + spotify_token = self.spotify_token + access_token = spotify_token['access_token'] except: error = 'IrisFrontend: access_token missing or invalid' logger.error(error) @@ -462,7 +473,7 @@ class IrisCore(object): url = url+'&limit=50' req = urllib2.Request(url) - req.add_header('Authorization', 'Bearer '+self.spotify_token['access_token']) + req.add_header('Authorization', 'Bearer '+access_token) response = urllib2.urlopen(req, timeout=30).read() response_dict = json.loads(response) @@ -556,21 +567,6 @@ class IrisCore(object): self.queue_metadata = cleaned_queue_metadata - self.broadcast( - data={ - 'type': 'queue_metadata_changed', - 'queue_metadata': self.queue_metadata - } - ) - - response = { - 'message': 'Cleaned queue metadata' - } - if (callback): - callback(response) - else: - return response - ## # Spotify authentication @@ -582,6 +578,11 @@ class IrisCore(object): def get_spotify_token(self, *args, **kwargs): callback = kwargs.get('callback', False) + + # Expired, so go get a new one + if (not self.spotify_token or self.spotify_token['expires_at'] <= time.time()): + self.refresh_spotify_token() + response = { 'spotify_token': self.spotify_token } @@ -608,6 +609,10 @@ class IrisCore(object): request = tornado.httpclient.HTTPRequest(url, method='POST', body=urllib.urlencode(data)) response = http_client.fetch(request) + token = json.loads(response.body) + token['expires_at'] = time.time() + token['expires_in'] + self.spotify_token = token + self.broadcast( data={ 'type': 'spotify_token_changed', @@ -615,12 +620,9 @@ class IrisCore(object): } ) - token = json.loads(response.body) - self.spotify_token = token response = { 'spotify_token': token } - if (callback): callback(response) else: diff --git a/mopidy_iris/frontend.py b/mopidy_iris/frontend.py index 1d35f2e0..6e4e0b90 100755 --- a/mopidy_iris/frontend.py +++ b/mopidy_iris/frontend.py @@ -19,9 +19,9 @@ class IrisFrontend(pykka.ThreadingActor, CoreListener): def on_start(self): logger.info('Starting Iris '+mem.iris.version) - def track_playback_ended( self, tl_track, time_position ): + def track_playback_ended(self, tl_track, time_position): mem.iris.check_for_radio_update() - def tracklist_changed( self ): + def tracklist_changed(self): mem.iris.clean_queue_metadata() \ No newline at end of file diff --git a/src/js/components/Modal/EditRadioModal.js b/src/js/components/Modal/EditRadioModal.js index 9bea050e..8ad914c6 100755 --- a/src/js/components/Modal/EditRadioModal.js +++ b/src/js/components/Modal/EditRadioModal.js @@ -26,15 +26,43 @@ export default class EditRadioModal extends React.Component{ } handleStart(e){ - e.preventDefault() - this.props.pusherActions.startRadio(this.state.seeds) - this.props.uiActions.closeModal() + e.preventDefault(); + + var valid_seeds = true; + var seeds = this.mapSeeds(); + for (var i = 0; i < seeds.length; i++){ + if (seeds[i].unresolved !== undefined){ + valid_seeds = false; + continue; + } + } + + if (valid_seeds){ + this.props.pusherActions.startRadio(this.state.seeds); + this.props.uiActions.closeModal(); + } else { + this.setState({error_message: "Invalid seed URI(s)"}); + } } handleUpdate(e){ - e.preventDefault() - this.props.pusherActions.updateRadio(this.state.seeds) - this.props.uiActions.closeModal() + e.preventDefault(); + + var valid_seeds = true; + var seeds = this.mapSeeds(); + for (var i = 0; i < seeds.length; i++){ + if (seeds[i].unresolved !== undefined){ + valid_seeds = false; + continue; + } + } + + if (valid_seeds){ + this.props.pusherActions.updateRadio(this.state.seeds); + this.props.uiActions.closeModal(); + } else { + this.setState({error_message: "Invalid seed URI(s)"}); + } } handleStop(e){ @@ -46,7 +74,7 @@ export default class EditRadioModal extends React.Component{ addSeed(){ if (this.state.uri == ''){ this.setState({error_message: 'Cannot be empty'}); - return null; + return; } var seeds = Object.assign([],this.state.seeds); @@ -62,7 +90,18 @@ export default class EditRadioModal extends React.Component{ } else { seeds.push(uris[i]); this.setState({error_message: null}); - } + } + + // Resolve + switch (helpers.uriType(uris[i])){ + case 'track': + this.props.spotifyActions.getTrack(uris[i]); + break; + + case 'artist': + this.props.spotifyActions.getArtist(uris[i]); + break; + } } // commit to state @@ -79,10 +118,10 @@ export default class EditRadioModal extends React.Component{ seeds.push(this.state.seeds[i]) } } - this.setState({seeds: seeds}) + this.setState({seeds: seeds}); } - renderSeeds(){ + mapSeeds(){ var seeds = [] if (this.state.seeds){ @@ -91,20 +130,18 @@ export default class EditRadioModal extends React.Component{ if (uri){ if (helpers.uriType(uri) == 'artist'){ if (this.props.artists && this.props.artists.hasOwnProperty(uri)){ - seeds.push(this.props.artists[uri]) + seeds.push(this.props.artists[uri]); } else { seeds.push({ - type: 'artist', unresolved: true, uri: uri }) } } else if (helpers.uriType(uri) == 'track'){ if (this.props.tracks && this.props.tracks.hasOwnProperty(uri)){ - seeds.push(this.props.tracks[uri]) + seeds.push(this.props.tracks[uri]); } else { seeds.push({ - type: 'track', unresolved: true, uri: uri }) @@ -114,6 +151,12 @@ export default class EditRadioModal extends React.Component{ } } + return seeds; + } + + renderSeeds(){ + var seeds = this.mapSeeds(); + if (seeds.length > 0){ return (