diff --git a/mopidy_iris/handlers.py b/mopidy_iris/handlers.py index a7fcc16a..248dccba 100755 --- a/mopidy_iris/handlers.py +++ b/mopidy_iris/handlers.py @@ -201,10 +201,18 @@ class HttpHandler(tornado.web.RequestHandler): # when our request calls subsequent external requests (eg Spotify, Genius). # We don't need to wrap non-HTTPResponse responses as these are dicts if isinstance(response, tornado.httpclient.HTTPResponse): + + # Digest JSON resposes into JSON + content_type = response.headers.get('Content-Type') + if content_type.startswith('application/json') or content_type.startswith('text/json'): + body = json.loads(response.body) + else: + body = response.body + response = { 'response_code': response.code, 'response_message': response.reason, - 'response': response.body + 'response': body } self.write(response) diff --git a/src/js/services/genius/actions.js b/src/js/services/genius/actions.js index 0a4020c2..7ca13915 100755 --- a/src/js/services/genius/actions.js +++ b/src/js/services/genius/actions.js @@ -16,15 +16,20 @@ const sendRequest = (dispatch, getState, endpoint) => { var loader_key = helpers.generateGuid(); dispatch(uiActions.startLoading(loader_key, 'genius_'+endpoint)); + var url = endpoint; + if (!url.startsWith('http')){ + url = 'https://api.genius.com/'+url; + } + var config = { method: 'POST', cache: false, timeout: 30000, headers: { - Authorization: 'Bearer 2AGP9sfzKQcxfKSZuGa_3lqsDIpuOiTGT7-vhJYcKaaDjHIIA2HICsxXCiC30Xxi' + Authorization: 'Bearer nBNNEFekix8BOsfPyfK7LtX-CaUz7L7ak92qC3GfMAIQi8eWjuwb4P8SUxK1K-iY' }, data: JSON.stringify({ - url: 'https://genius.com/'+endpoint + url: url }), url: '//'+getState().mopidy.host+':'+getState().mopidy.port+'/iris/http/proxy_request' }; @@ -47,17 +52,26 @@ const sendRequest = (dispatch, getState, endpoint) => { }) } -export function getTrackLyrics(track){ +/** + * Extract lyrics from a page + * We don't get the lyrics in the API, so we need to 'scrape' the HTML page instead + * + * @param uri = track uri + * @param result = lyrics result (title, url) + **/ +export function getTrackLyrics(uri, url){ return (dispatch, getState) => { - var endpoint = ''; - for (var i = 0; i < track.artists.length; i++){ - endpoint += track.artists[i].name+' '; - } - endpoint += track.name+' lyrics'; - endpoint = endpoint.replace(/\s+/g, '-').toLowerCase(); + dispatch({ + type: 'TRACK_LOADED', + key: uri, + track: { + lyrics: null, + lyrics_url: null + } + }); - sendRequest(dispatch, getState, endpoint) + sendRequest(dispatch, getState, url) .then( response => { var html = $(response); @@ -75,16 +89,17 @@ export function getTrackLyrics(track){ dispatch({ type: 'TRACK_LOADED', - key: track.uri, + key: uri, track: { - lyrics: lyrics_html + lyrics: lyrics_html, + lyrics_url: url } }); } }, error => { dispatch(coreActions.handleException( - 'Could not get track lyrics', + 'Could not extract track lyrics', error )); } @@ -92,26 +107,36 @@ export function getTrackLyrics(track){ } } -export function getTrackInfo(track){ +export function findTrackLyrics(track){ return (dispatch, getState) => { var query = ''; - for (var i = 0; i < track.artists.length; i++){ - query += track.artists[i].name+' '; - } + query += track.artists[0].name+' '; query += track.name; + query = query.toLowerCase(); + query = query.replace(/\([^)]*\) */g, ''); // anything in circle-braces + query = query.replace(/\([^[]*\] */g, ''); // anything in square-braces + query = query.replace(/[^A-Za-z0-9\s]/g, ''); // non-alphanumeric sendRequest(dispatch, getState, 'search?q='+encodeURIComponent(query)) .then( response => { if (response.response.hits && response.response.hits.length > 0){ + var lyrics_results = []; + for (var i = 0; i < response.response.hits.length; i++){ + lyrics_results.push({ + title: response.response.hits[i].result.full_title, + url: response.response.hits[i].result.url + }); + } dispatch({ type: 'TRACK_LOADED', key: track.uri, track: { - annotations: response.response.hits[0].result + lyrics_results: lyrics_results } }); + dispatch(getTrackLyrics(track.uri, lyrics_results[0].url)); } }, error => { diff --git a/src/js/services/spotify/actions.js b/src/js/services/spotify/actions.js index e239e579..d52baadb 100755 --- a/src/js/services/spotify/actions.js +++ b/src/js/services/spotify/actions.js @@ -158,7 +158,7 @@ function refreshToken(dispatch, getState){ .then( response => { if (response.response_code == 200){ - var token = JSON.parse(response.response); + var token = response.response; token.token_expiry = new Date().getTime() + (token.expires_in * 1000 ); token.source = 'mopidy'; dispatch({ diff --git a/src/js/views/Track.js b/src/js/views/Track.js index 3e081c9f..d987eb58 100755 --- a/src/js/views/Track.js +++ b/src/js/views/Track.js @@ -52,7 +52,7 @@ class Track extends React.Component{ // We don't have lyrics, and we have just received our artists if (!nextProps.track.lyrics && !this.props.track.artists && nextProps.track.artists){ - this.props.geniusActions.getTrackLyrics(nextProps.track); + this.props.geniusActions.findTrackLyrics(nextProps.track); } } @@ -93,6 +93,38 @@ class Track extends React.Component{ this.props.mopidyActions.playURIs([this.props.params.uri], this.props.params.uri) } + renderLyricsSelector(){ + if (!this.props.track.lyrics_results){ + return null; + } + + return ( +
+
+ +
+ Switch to another lyrics seach result +
+
+
+ ); + } + renderLyrics(){ if (helpers.isLoading(this.props.load_queue,['genius_'])){ return ( @@ -101,9 +133,22 @@ class Track extends React.Component{ ); } else if (!this.props.track.lyrics){ - return

No lyrics available

+ return ( +
+
+ No lyrics available +
+
+ ) } else { - return
+ return ( +
+
+
+ Origin: {this.props.track.lyrics_url} +
+
+ ) } } @@ -161,6 +206,7 @@ class Track extends React.Component{ {this.props.slim_mode ? null : this.handleContextMenu(e)} />} + {this.renderLyricsSelector()} {this.renderLyrics()} diff --git a/src/scss/global/_forms.scss b/src/scss/global/_forms.scss index 5a621929..a2aa3edc 100755 --- a/src/scss/global/_forms.scss +++ b/src/scss/global/_forms.scss @@ -29,8 +29,13 @@ select { } select { + max-width: 100%; + option { + background: $white; color: $darkest_grey; + max-width: 100%; + box-sizing: border-box; } } diff --git a/src/scss/views/_track.scss b/src/scss/views/_track.scss index c2202642..fa3381f1 100755 --- a/src/scss/views/_track.scss +++ b/src/scss/views/_track.scss @@ -37,12 +37,23 @@ main .track-view { } } - .lyrics { - p { - line-height: 0.8em; + .lyrics { + .content { + padding-bottom: 20px; + + *, + & * { + font-size: 18px; + line-height: normal; + white-space: normal; + } } } + .lyrics-selector { + padding-bottom: 20px; + } + @include responsive($bp_medium){ padding-top: 0; padding-left: 10px;