From 1e12f3fb29549520109eee4bbe1deab833e92583 Mon Sep 17 00:00:00 2001 From: James Barnsley Date: Sun, 6 Aug 2017 20:22:16 +1200 Subject: [PATCH] Using Mopidy-Spotify authentication for backend users --- mopidy_iris/core.py | 23 +- src/js/components/Settings/Advanced.js | 99 --------- src/js/components/Settings/Debug.js | 196 ----------------- src/js/components/Settings/System.js | 289 ------------------------- src/js/components/Sidebar.js | 8 +- src/js/views/Debug.js | 2 +- src/js/views/Settings.js | 2 +- src/scss/global/_core.scss | 7 +- src/scss/views/_settings.scss | 4 +- 9 files changed, 21 insertions(+), 609 deletions(-) delete mode 100755 src/js/components/Settings/Advanced.js delete mode 100755 src/js/components/Settings/Debug.js delete mode 100755 src/js/components/Settings/System.js diff --git a/mopidy_iris/core.py b/mopidy_iris/core.py index 89f13016..d9dcb877 100755 --- a/mopidy_iris/core.py +++ b/mopidy_iris/core.py @@ -446,27 +446,18 @@ class IrisCore(object): } def refresh_spotify_token(self, data): - - ## TODO: See if we can use the credentials used by Mopidy-Spotify 3.1.0 - # Currently this returns 'invalid_client' error # Use client_id and client_secret from config # This was introduced in Mopidy-Spotify 3.1.0 - #if 'spotify' in self.config and 'client_id' in self.config['spotify']: - # authorization = str(self.config['spotify']['client_id']+':'+self.config['spotify']['client_secret']) - # authorization = authorization.encode('base64').replace('\n', '') - - # Mopidy-Spotify is old version, so just use basic authorization code - #else: - # authorization = 'YTg3ZmI0ZGJlZDMwNDc1YjhjZWMzODUyM2RmZjUzZTI6ZDdjODlkMDc1M2VmNDA2OGJiYTE2NzhjNmNmMjZlZDY=' - - authorization = 'YTg3ZmI0ZGJlZDMwNDc1YjhjZWMzODUyM2RmZjUzZTI6ZDdjODlkMDc1M2VmNDA2OGJiYTE2NzhjNmNmMjZlZDY=' - url = 'https://accounts.spotify.com/api/token' - headers = {'Authorization' : 'Basic ' + authorization} - data = {'grant_type': 'client_credentials'} + url = 'https://auth.mopidy.com/spotify/token' + data = { + 'client_id': self.config['spotify']['client_id'], + 'client_secret': self.config['spotify']['client_secret'], + 'grant_type': 'client_credentials' + } data_encoded = urllib.urlencode( data ) - req = urllib2.Request(url, data_encoded, headers) + req = urllib2.Request(url, data_encoded) try: response = urllib2.urlopen(req, timeout=30).read() diff --git a/src/js/components/Settings/Advanced.js b/src/js/components/Settings/Advanced.js deleted file mode 100755 index 394727ea..00000000 --- a/src/js/components/Settings/Advanced.js +++ /dev/null @@ -1,99 +0,0 @@ - -import React, { PropTypes } from 'react' -import { connect } from 'react-redux' -import { Link, hashHistory } from 'react-router' -import { bindActionCreators } from 'redux' -import FontAwesome from 'react-fontawesome' - -import ConfirmationButton from '../ConfirmationButton' -import PusherConnectionList from '../PusherConnectionList' -import URISchemesList from '../URISchemesList' -import VersionManager from '../VersionManager' - -import * as uiActions from '../../services/ui/actions' -import * as pusherActions from '../../services/pusher/actions' -import * as mopidyActions from '../../services/mopidy/actions' -import * as spotifyActions from '../../services/spotify/actions' - -class Advanced extends React.Component{ - - constructor(props) { - super(props) - } - - render(){ - return ( -
- -

Pusher

- -
-
Connections
-
- - - -
-
- -

Mopidy extensions

- - -

Core

- -
-
Customise behavior
-
- -
-
- -
-
Version
-
- -
-
- -
-
Reset
-
- this.resetAllSettings()} /> -
-
- -
- ); - } -} - - -/** - * Export our component - * - * We also integrate our global store, using connect() - **/ - -const mapStateToProps = (state, ownProps) => { - return { - ui: state.ui - } -} - -const mapDispatchToProps = (dispatch) => { - return { - uiActions: bindActionCreators(uiActions, dispatch), - pusherActions: bindActionCreators(pusherActions, dispatch), - mopidyActions: bindActionCreators(mopidyActions, dispatch), - spotifyActions: bindActionCreators(spotifyActions, dispatch) - } -} - -export default connect(mapStateToProps, mapDispatchToProps)(Advanced) \ No newline at end of file diff --git a/src/js/components/Settings/Debug.js b/src/js/components/Settings/Debug.js deleted file mode 100755 index c85c7225..00000000 --- a/src/js/components/Settings/Debug.js +++ /dev/null @@ -1,196 +0,0 @@ - -import React, { PropTypes } from 'react' -import { connect } from 'react-redux' -import { Link, hashHistory } from 'react-router' -import { bindActionCreators } from 'redux' -import FontAwesome from 'react-fontawesome' - -import SpotifyAuthenticationFrame from '../SpotifyAuthenticationFrame' - -import * as uiActions from '../../services/ui/actions' -import * as pusherActions from '../../services/pusher/actions' -import * as mopidyActions from '../../services/mopidy/actions' -import * as spotifyActions from '../../services/spotify/actions' - -class Debug extends React.Component{ - - constructor(props) { - super(props); - this.state = { - mopidy_call: 'playlists.asList', - mopidy_data: '{}', - pusher_data: '{"method":"get_config"}', - access_token: (this.props.access_token ? this.props.access_token : '') - } - } - - callMopidy(e){ - e.preventDefault() - this.props.mopidyActions.debug( this.state.mopidy_call, JSON.parse(this.state.mopidy_data) ) - } - - callPusher(e){ - e.preventDefault() - this.props.pusherActions.debug( JSON.parse(this.state.pusher_data) ) - } - - render(){ - return ( -
-

User interface

-
-
-
Debug
-
- -
-
-
-
Logging
-
- - - -
-
-
-
-
- this.props.uiActions.createNotification('Test notification')}>Create notification - this.props.uiActions.startProcess('test_process', 'Test process')}>Start process - this.props.uiActions.stopProcess('test_process')}>Stop process -
-
-
- -

Mopidy

-
this.callMopidy(e)}> -
-
Call
-
- this.setState({ mopidy_call: e.target.value })} - value={ this.state.mopidy_call } /> -
-
-
-
Data
-
- -
-
-
-
-
- -
-
-
- -

Pusher

-
this.callPusher(e)}> -
-
Examples
-
- -
-
-
-
Data
-
- -
-
-
-
-
- -
-
-
- -

Response

-
-					{ this.props.debug_response ? JSON.stringify(this.props.debug_response, null, 2) : null }
-				
- -
- ); - } -} - - -/** - * Export our component - * - * We also integrate our global store, using connect() - **/ - -const mapStateToProps = (state, ownProps) => { - return { - connection_id: state.pusher.connection_id, - access_token: (state.spotify.access_token ? state.spotify.access_token : ''), - log_actions: (state.ui.log_actions ? state.ui.log_actions : false), - log_pusher: (state.ui.log_pusher ? state.ui.log_pusher : false), - log_mopidy: (state.ui.log_mopidy ? state.ui.log_mopidy : false), - debug_info: (state.ui.debug_info ? state.ui.debug_info : false), - debug_response: state.ui.debug_response - } -} - -const mapDispatchToProps = (dispatch) => { - return { - uiActions: bindActionCreators(uiActions, dispatch), - pusherActions: bindActionCreators(pusherActions, dispatch), - mopidyActions: bindActionCreators(mopidyActions, dispatch), - spotifyActions: bindActionCreators(spotifyActions, dispatch) - } -} - -export default connect(mapStateToProps, mapDispatchToProps)(Debug) \ No newline at end of file diff --git a/src/js/components/Settings/System.js b/src/js/components/Settings/System.js deleted file mode 100755 index ecfd7188..00000000 --- a/src/js/components/Settings/System.js +++ /dev/null @@ -1,289 +0,0 @@ - -import React, { PropTypes } from 'react' -import { connect } from 'react-redux' -import { Link, hashHistory } from 'react-router' -import { bindActionCreators } from 'redux' -import FontAwesome from 'react-fontawesome' - -import SpotifyAuthenticationFrame from '../SpotifyAuthenticationFrame' -import Thumbnail from '../Thumbnail' - -import * as coreActions from '../../services/core/actions' -import * as uiActions from '../../services/ui/actions' -import * as pusherActions from '../../services/pusher/actions' -import * as mopidyActions from '../../services/mopidy/actions' -import * as spotifyActions from '../../services/spotify/actions' - -class System extends React.Component{ - - constructor(props) { - super(props); - this.state = { - country: this.props.core.country, - locale: this.props.core.locale, - mopidy_host: this.props.mopidy.host, - mopidy_port: this.props.mopidy.port, - pusher_username: this.props.pusher.username, - input_in_focus: null - } - } - - componentWillReceiveProps(newProps){ - var changed = false - var state = this.state - - if (newProps.pusher.username != this.state.pusher_username && this.state.input_in_focus != 'pusher_username'){ - state.pusher_username = newProps.pusher.username - changed = true - } - - if (changed){ - this.setState(state) - } - } - - resetAllSettings(){ - localStorage.clear(); - window.location = '#' - window.location.reload(true) - return false; - } - - setMopidyConfig(e){ - this.setState({input_in_focus: null}) - e.preventDefault(); - this.props.mopidyActions.setConfig({ host: this.state.mopidy_host, port: this.state.mopidy_port }); - window.location.reload(true); - return false; - } - - handleBlur(name, value){ - this.setState({input_in_focus: null}) - var data = {} - data[name] = value - this.props.coreActions.set(data) - } - - handleUsernameChange(username){ - this.setState({pusher_username: username.replace(/\W/g, '')}) - } - - handleUsernameBlur(e){ - this.setState({input_in_focus: null}) - this.props.pusherActions.setUsername(this.state.pusher_username) - } - - renderApplyButton(){ - if (this.props.mopidy.host == this.state.mopidy_host && this.props.mopidy.port == this.state.mopidy_port) return null - - return ( -
-
-
- -
-
- ) - } - - renderStatus(server){ - if (this.props[server].connecting){ - return - } else if (this.props[server].connected){ - return - } else { - return - } - } - - renderSpotifyUser(){ - - var user = null - if (this.props.spotify.me && this.props.spotify.authorization){ - user = this.props.spotify.me - } else if (this.props.spotify.backend_username){ - if (this.props.core.users && this.props.core.users['spotify:user:'+this.props.spotify.backend_username] !== undefined){ - user = this.props.core.users['spotify:user:'+this.props.spotify.backend_username] - } - } - - if (user){ - return ( - - - - {user.display_name ? user.display_name : user.id} - - - ) - } else if (this.props.spotify.backend_username){ - return ( - - - - {this.props.spotify.backend_username} - - - ) - } else { - return ( - - - - Unknown - - - ) - } - } - - renderSendAuthorizationButton(){ - if (!this.props.spotify.authorization) return null - - return ( - - ) - } - - render(){ - return ( -
- -

Servers

- -
-
Status
-
-
- {this.renderStatus('mopidy')}  Mopidy    - {this.renderStatus('pusher')}  Pusher -
-
-
- -
-
Username
-
- this.handleUsernameChange(e.target.value)} - onFocus={e => this.setState({input_in_focus: 'pusher_username'})} - onBlur={e => this.handleUsernameBlur(e)} - value={this.state.pusher_username } /> -
-
- -
this.setMopidyConfig(e)}> -
-
Host
-
- this.setState({mopidy_host: e.target.value})} - onFocus={e => this.setState({input_in_focus: 'mopidy_host'})} - onBlur={e => this.setState({input_in_focus: null})} - value={ this.state.mopidy_host } /> -
-
-
-
Port
-
- this.setState({mopidy_port: e.target.value})} - onFocus={e => this.setState({input_in_focus: 'mopidy_port'})} - onBlur={e => this.setState({input_in_focus: null})} - value={ this.state.mopidy_port } /> -
-
- {this.renderApplyButton()} -
- -

Localization

- -
-
Country
-
- this.setState({country: e.target.value})} - onFocus={e => this.setState({input_in_focus: 'country'})} - onBlur={e => this.handleBlur('country',e.target.value)} - value={ this.state.country } /> -
-
-
-
Locale
-
- this.setState({locale: e.target.value})} - onFocus={e => this.setState({input_in_focus: 'locale'})} - onBlur={e => this.handleBlur('locale',e.target.value)} - value={this.state.locale} /> -
-
- -

Spotify

- -
-
Status
-
-
- {!this.props.mopidy.uri_schemes || !this.props.mopidy.uri_schemes.includes('spotify:') ?
  Mopidy-Spotify not available
: null} - - {this.props.spotify.authorization ? Authorized  All Spotify functionality available : Limited access  Authorize Iris for full functionality} -
-
-
-
-
Authorization
-
- - { this.renderSendAuthorizationButton() } -
-
-
-
Current user
-
-
- { this.renderSpotifyUser() } -
-
-
- -
- ); - } -} - - -/** - * Export our component - * - * We also integrate our global store, using connect() - **/ - -const mapStateToProps = (state, ownProps) => { - return { - core: state.core, - ui: state.ui, - mopidy: state.mopidy, - pusher: state.pusher, - spotify: state.spotify - } -} - -const mapDispatchToProps = (dispatch) => { - return { - coreActions: bindActionCreators(coreActions, dispatch), - uiActions: bindActionCreators(uiActions, dispatch), - pusherActions: bindActionCreators(pusherActions, dispatch), - mopidyActions: bindActionCreators(mopidyActions, dispatch), - spotifyActions: bindActionCreators(spotifyActions, dispatch) - } -} - -export default connect(mapStateToProps, mapDispatchToProps)(System) \ No newline at end of file diff --git a/src/js/components/Sidebar.js b/src/js/components/Sidebar.js index 595ac8fb..5f24881f 100755 --- a/src/js/components/Sidebar.js +++ b/src/js/components/Sidebar.js @@ -68,14 +68,14 @@ class Sidebar extends React.Component{ Playlists - {this.props.spotify_enabled && this.props.spotify_authorized ? + Artists - : null} - {this.props.spotify_enabled && this.props.spotify_authorized ? + + Albums - : null} + Browse diff --git a/src/js/views/Debug.js b/src/js/views/Debug.js index d6998416..4c05b43d 100755 --- a/src/js/views/Debug.js +++ b/src/js/views/Debug.js @@ -117,7 +117,7 @@ class Debug extends React.Component{
this.props.spotifyActions.authorizationGranted({access_token: e.target.value})} + onChange={e => this.props.spotifyActions.authorizationGranted({access_token: e.target.value})} value={this.state.access_token} />
diff --git a/src/js/views/Settings.js b/src/js/views/Settings.js index 536e0098..4061015e 100755 --- a/src/js/views/Settings.js +++ b/src/js/views/Settings.js @@ -212,7 +212,7 @@ class Settings extends React.Component { return (
-
+
diff --git a/src/scss/global/_core.scss b/src/scss/global/_core.scss index def1d122..816b9405 100755 --- a/src/scss/global/_core.scss +++ b/src/scss/global/_core.scss @@ -264,12 +264,17 @@ button.placeholder { .pulse { @include pulse(); } .one-liner { @include one_line_text; } +.centred-text { text-align: center; } .grey-text { color: $mid_grey !important; } .red-text { color: $red !important; } .green-text { color: $green !important; } .orange-text { color: $orange !important; } .dark-text { color: $dark_grey !important; } -.centred-text { text-align: center; } +.grey-border { border-color: $mid_grey !important; } +.red-border { border-color: $red !important; } +.green-border { border-color: $green !important; } +.orange-border { border-color: $orange !important; } +.dark-border { border-color: $dark_grey !important; } footer { display: block; diff --git a/src/scss/views/_settings.scss b/src/scss/views/_settings.scss index b60cac06..814f7f0a 100755 --- a/src/scss/views/_settings.scss +++ b/src/scss/views/_settings.scss @@ -53,8 +53,8 @@ top: 2px; left: 0; width: 34px; - height: 26px; - padding-top: 8px; + height: 25px; + padding-top: 9px; border: 2px solid $grey; text-align: center; border-radius: 50%;