Preliminary radio

This commit is contained in:
James Barnsley
2016-12-20 16:58:20 +13:00
parent e3c224e1df
commit bf3b2bfba1
8 changed files with 92 additions and 4 deletions

View File

@ -51,6 +51,33 @@ class IrisFrontend(pykka.ThreadingActor, CoreListener):
# get a fresh spotify authentication token and store for future use
# self.refresh_spotify_token()
##
# Get a new spotify authentication token for server-side use
#
# Uses the Client Credentials Flow, so is invisible to the user. We need this token for
# any backend spotify requests (we don't tap in to Mopidy-Spotify, yet). Also used for
# passing token to frontend for javascript requests without use of the Authorization Code Flow.
##
def refresh_spotify_token( self ):
url = 'https://accounts.spotify.com/api/token'
authorization = 'YTg3ZmI0ZGJlZDMwNDc1YjhjZWMzODUyM2RmZjUzZTI6ZDdjODlkMDc1M2VmNDA2OGJiYTE2NzhjNmNmMjZlZDY='
headers = {'Authorization' : 'Basic ' + authorization}
data = {'grant_type': 'client_credentials'}
data_encoded = urllib.urlencode( data )
req = urllib2.Request(url, data_encoded, headers)
try:
response = urllib2.urlopen(req, timeout=30).read()
response_dict = json.loads(response)
self.spotify_token = response_dict
return response_dict
except urllib2.HTTPError as e:
return e
##
# Listen for core events, and update our frontend as required

View File

@ -194,7 +194,7 @@ class PusherWebsocketHandler(tornado.websocket.WebSocketHandler):
send_message(
self.connectionid,
'response',
messageJson['action'],
'radio_started',
messageJson['message_id'],
{ 'radio': radio }
)

View File

@ -12,7 +12,7 @@ const localstorageMiddleware = (function(){
// append our state to a global variable. This gives us access to debug the store at any point
window._store = store
//console.log(action)
console.log(action)
switch( action.type ){

View File

@ -43,6 +43,19 @@ export function instruct( message_type, data = null ){
}
}
export function startRadio( uris ){
return {
type: 'PUSHER_START_RADIO',
uris: uris
}
}
export function stopRadio( uris ){
return {
type: 'PUSHER_STOP_RADIO'
}
}
export function debug( call, data = null ){
return {
type: 'PUSHER_DEBUG',

View File

@ -63,6 +63,7 @@ const PusherMiddleware = (function(){
socket.onopen = () => {
store.dispatch({ type: 'PUSHER_CONNECTED', connection: connection });
store.dispatch({ type: 'PUSHER_SET_USERNAME', username: connection.username });
makeRequest({ action: 'get_radio' });
};
socket.onmessage = (message) => {
@ -149,7 +150,7 @@ const PusherMiddleware = (function(){
// make it so
var notification = new notification( title, options );
break;
break
case 'PUSHER_SEND_AUTHORIZATION':
if( window.confirm('Spotify authorization for user '+action.data.me.id+' received. Do you want to import?') ){
@ -165,6 +166,29 @@ const PusherMiddleware = (function(){
}
break
case 'PUSHER_START_RADIO':
var data = {
action: 'start_radio',
seed_artists: [],
seed_genres: [],
seed_tracks: []
}
for( var i = 0; i < action.uris.length; i++){
switch( helpers.uriType( action.uris[i] ) ){
case 'artist':
data.seed_artists.push( action.uris[i] );
break;
case 'track':
data.seed_tracks.push( action.uris[i] );
break;
}
}
makeRequest( data )
break
// This action is irrelevant to us, pass it on to the next middleware
default:
return next(action);

View File

@ -117,6 +117,10 @@ export default function reducer(ui = {}, action){
case 'FOLLOWING_LOADING':
return Object.assign({}, ui, { following_loading: true })
case 'PUSHER_RADIO':
case 'PUSHER_START_RADIO':
return Object.assign({}, ui, { radio: action.data })

View File

@ -16,7 +16,9 @@ import FollowButton from '../components/FollowButton'
import SidebarToggleButton from '../components/SidebarToggleButton'
import * as helpers from '../helpers'
import * as uiActions from '../services/ui/actions'
import * as mopidyActions from '../services/mopidy/actions'
import * as pusherActions from '../services/pusher/actions'
import * as lastfmActions from '../services/lastfm/actions'
import * as spotifyActions from '../services/spotify/actions'
@ -67,7 +69,9 @@ class Artist extends React.Component{
}
play(){
alert('Yet to be implemented')
if (!this.props.artist.uri) return
this.props.uiActions.createNotification('Starting radio...')
this.props.pusherActions.startRadio([this.props.artist.uri])
}
renderSubViewMenu(){
@ -204,7 +208,9 @@ const mapStateToProps = (state, ownProps) => {
const mapDispatchToProps = (dispatch) => {
return {
uiActions: bindActionCreators(uiActions, dispatch),
mopidyActions: bindActionCreators(mopidyActions, dispatch),
pusherActions: bindActionCreators(pusherActions, dispatch),
lastfmActions: bindActionCreators(lastfmActions, dispatch),
spotifyActions: bindActionCreators(spotifyActions, dispatch)
}

View File

@ -41,6 +41,17 @@ class Queue extends React.Component{
this.props.mopidyActions.reorderTracklist( indexes, index )
}
renderRadio(){
if (!this.props.radio) return null
return (
<div className="radio">
Playing radio
<button onClick={ e => this.props.pusherActions.instruct('stop_radio') }>Stop radio</button>
</div>
)
}
render(){
return (
<div className="view queue-view">
@ -49,6 +60,8 @@ class Queue extends React.Component{
<FullPlayer />
{ this.renderRadio() }
<section className="list-wrapper">
<TrackList
show_source_icon={true}
@ -74,6 +87,7 @@ class Queue extends React.Component{
const mapStateToProps = (state, ownProps) => {
return {
radio: state.ui.radio,
current_tracklist: state.ui.current_tracklist
}
}