Merge branch 'develop'

This commit is contained in:
James Barnsley
2017-11-22 08:03:09 +13:00
8 changed files with 149 additions and 90 deletions

View File

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

View File

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

View File

@ -39,12 +39,14 @@ class List extends React.Component{
return (
<div className="list-item header cf">
{
this.props.columns.map((col, col_index) => {
var className = 'col '+col.name.replace('.','_')
return <div className={className} key={col_index}>{ col.label ? col.label : col.name }</div>
})
}
<div className="liner">
{
this.props.columns.map((col, col_index) => {
var className = 'col '+col.name.replace('.','_')
return <div className={className} key={col_index}>{ col.label ? col.label : col.name }</div>
})
}
</div>
</div>
)
}
@ -54,7 +56,7 @@ class List extends React.Component{
var value = row
for (var i = 0; i < key.length; i++){
if (typeof(value[key[i]]) === 'undefined'){
if (value[key[i]] === undefined){
return <span>-</span>
} else if (typeof(value[key[i]]) === 'string' && value[key[i]].replace(' ','') == ''){
return <span>-</span>

View File

@ -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 (
<div>
@ -123,7 +166,7 @@ export default class EditRadioModal extends React.Component{
return (
<div className="list-item" key={seed.uri}>
{seed.unresolved ? <span className="grey-text">{seed.uri}</span> : <span>{seed.name}</span> }
<span className="grey-text">&nbsp;({seed.type})</span>
{!seed.unresolved ? <span className="grey-text">&nbsp;({seed.type})</span> : null}
<button className="discrete remove-uri no-hover" onClick={e => this.removeSeed(seed.uri)}>
<FontAwesome name="close" />&nbsp;Remove
</button>

View File

@ -262,7 +262,9 @@ export default class Track extends React.Component{
return (
<div className={className}>
{track_actions}
{track_columns}
<div className="liner">
{track_columns}
</div>
</div>
)
} else {

View File

@ -83,7 +83,7 @@ const PusherMiddleware = (function(){
store.dispatch(uiActions.stopLoading(request_id));
reject({message: "Request timed out", method: method, data: data});
},
5000 // 30000
30000
);
// add query to our deferred responses
@ -379,12 +379,13 @@ const PusherMiddleware = (function(){
request(store, 'change_radio', data)
.then(
response => {
store.dispatch(uiActions.processFinished('PUSHER_RADIO_PROCESS'));
if (response.status == 0){
store.dispatch(uiActions.createNotification(response.message, 'bad'))
store.dispatch(uiActions.createNotification(response.message, 'bad'));
}
store.dispatch(uiActions.processFinished('PUSHER_RADIO_PROCESS'))
},
error => {
error => {
store.dispatch(uiActions.processFinished('PUSHER_RADIO_PROCESS'));
store.dispatch(coreActions.handleException(
'Could not change radio',
error

View File

@ -466,7 +466,7 @@ class Settings extends React.Component {
<FontAwesome name="github" />&nbsp;GitHub
</a>
&nbsp;&nbsp;
<a rel="license" href="http://creativecommons.org/licenses/by-nc/4.0/" target="_blank" style={{display: 'inline-block', verticalAlign: 'middle'}}><img alt="Creative Commons License" src="https://i.creativecommons.org/l/by-nc/4.0/88x31.png" /></a>
<a className="button" href="http://creativecommons.org/licenses/by-nc/4.0/" target="_blank"><FontAwesome name="creative-commons" />&nbsp;Licence</a>
</div>
</div>

View File

@ -1,7 +1,6 @@
.list {
.list-item {
@include clearfix;
-webkit-touch-callout: none;
-webkit-user-select: none;
@ -12,13 +11,17 @@
display: block;
position: relative;
padding: 14px 30px 13px 10px;
margin: 0 -10px -1px -10px;
cursor: pointer;
border-radius: 3px;
border-bottom: 1px solid rgba(255,255,255,0.05);
border-top: 1px solid rgba(255,255,255,0.05);
.liner {
@include clearfix;
padding: 14px 30px 13px 10px;
}
&.selected {
background: rgba(255,255,255,0.08) !important;
@ -229,11 +232,11 @@
.list-item {
&.can-sort {
padding-left: 70px !important;
padding-left: 60px !important;
}
&:not(.can-sort){
padding-left: 42px !important;
padding-left: 35px !important;
}
.select-zone {
@ -247,8 +250,9 @@
.fa {
position: absolute;
top: 27px;
top: 50%;
left: 17px;
margin-top: -3px;
pointer-events: none;
color: $white;
z-index: 1;
@ -262,8 +266,9 @@
width: 14px;
height: 14px;
position: absolute;
top: 23px;
top: 50%;
left: 14px;
margin-top: -7px;
}
}
@ -279,8 +284,9 @@
.fa {
position: absolute;
top: 24px;
top: 50%;
left: 6px;
margin-top: -6px;
pointer-events: none;
}
}
@ -290,7 +296,10 @@
@include responsive($bp_medium){
.list-item {
padding: 12px !important;
.liner {
padding: 12px !important;
}
.source {
position: static;