Storing next track and pre-fetching artwork for snappier UI
This commit is contained in:
@ -11,7 +11,7 @@ export default class ArtistSentence extends React.Component{
|
||||
|
||||
render(){
|
||||
if (!this.props.artists){
|
||||
return <span>-</span>;
|
||||
return <span className={this.props.className ? this.props.className+" artist-sentence" : "artist-sentence" }>-</span>;
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@ -12,6 +12,7 @@ import ArtistSentence from './ArtistSentence'
|
||||
import Thumbnail from './Thumbnail'
|
||||
import Icon from './Icon'
|
||||
|
||||
import * as helpers from '../helpers'
|
||||
import * as uiActions from '../services/ui/actions'
|
||||
import * as coreActions from '../services/core/actions'
|
||||
import * as mopidyActions from '../services/mopidy/actions'
|
||||
@ -75,6 +76,8 @@ class PlaybackControls extends React.Component{
|
||||
{this.props.http_streaming_enabled && this.props.http_streaming_active && this.props.play_state == 'playing' ? <audio id="http-streamer" autoPlay preload="none">
|
||||
<source src={this.props.http_streaming_url} type={"audio/"+this.props.http_streaming_encoding} />
|
||||
</audio> : null}
|
||||
|
||||
{this.props.next_track && this.props.next_track.images ? <Thumbnail className="hide" size="large" images={this.props.next_track.images} /> : null}
|
||||
|
||||
<div className="current-track">
|
||||
<div className="thumbnail-wrapper" onClick={e => this.handleThumbnailClick(e)}>
|
||||
@ -150,6 +153,7 @@ const mapStateToProps = (state, ownProps) => {
|
||||
http_streaming_encoding: state.core.http_streaming_encoding,
|
||||
http_streaming_url: state.core.http_streaming_url,
|
||||
current_track: (state.core.current_track && state.core.tracks[state.core.current_track.uri] !== undefined ? state.core.tracks[state.core.current_track.uri] : null),
|
||||
next_track: (state.core.next_track_uri && state.core.tracks[state.core.next_track_uri] !== undefined ? state.core.tracks[state.core.next_track_uri] : null),
|
||||
radio_enabled: (state.ui.radio && state.ui.radio.enabled ? true : false),
|
||||
play_state: state.mopidy.play_state,
|
||||
time_position: state.mopidy.time_position,
|
||||
|
||||
@ -44,24 +44,29 @@ class Thumbnail extends React.Component{
|
||||
|
||||
// no images
|
||||
if (!this.props.image && (!this.props.images || this.props.images.length <= 0)){
|
||||
return require('../../assets/no-image.svg')
|
||||
return require('../../assets/no-image.svg');
|
||||
|
||||
// single image
|
||||
} else if (this.props.image){
|
||||
return this.props.image
|
||||
return this.props.image;
|
||||
|
||||
// multiple images
|
||||
} else if (this.props.images && this.props.images.length > 0){
|
||||
var images = helpers.sizedImages(this.props.images )
|
||||
var size = 'medium'
|
||||
if (this.props.size ) size = this.props.size
|
||||
return images[size]
|
||||
var images = helpers.sizedImages(this.props.images);
|
||||
|
||||
// Default to medium-sized image, but accept size property as override
|
||||
var size = 'medium';
|
||||
if (this.props.size){
|
||||
size = this.props.size;
|
||||
}
|
||||
|
||||
return images[size];
|
||||
}
|
||||
}
|
||||
|
||||
zoom(e, image){
|
||||
e.preventDefault()
|
||||
this.props.uiActions.openModal('image_zoom', {url: image} )
|
||||
e.preventDefault();
|
||||
this.props.uiActions.openModal('image_zoom', {url: image});
|
||||
}
|
||||
|
||||
render(){
|
||||
|
||||
@ -347,6 +347,9 @@ export let formatTracks = function(tracks){
|
||||
* @param uri = string
|
||||
**/
|
||||
export let uriSource = function(uri){
|
||||
if (!uri){
|
||||
return false;
|
||||
}
|
||||
var exploded = uri.split(':');
|
||||
return exploded[0]
|
||||
}
|
||||
@ -586,18 +589,23 @@ export let removeDuplicates = function(array){
|
||||
* @param field = string (the field we're to search)
|
||||
* @param value = string (the value to find)
|
||||
* @param array = array of objects to search
|
||||
* @param singular = boolean (just return the first result)
|
||||
* @return array
|
||||
**/
|
||||
export let applyFilter = function(field, value, array){
|
||||
var results = []
|
||||
export let applyFilter = function(field, value, array, singular = false){
|
||||
var results = [];
|
||||
|
||||
for (var i = 0; i < array.length; i++){
|
||||
if (array[i][field] && array[i][field].toLowerCase().includes(value.toLowerCase())){
|
||||
results.push(array[i])
|
||||
if (array[i][field] && String(array[i][field]).toLowerCase().includes(String(value).toLowerCase())){
|
||||
if (singular){
|
||||
return array[i];
|
||||
} else {
|
||||
results.push(array[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return results
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -316,7 +316,7 @@ const CoreMiddleware = (function(){
|
||||
case 'CURRENT_TRACK_LOADED':
|
||||
store.dispatch({
|
||||
type: 'TRACKS_LOADED',
|
||||
tracks: [action.current_track]
|
||||
tracks: [action.track]
|
||||
});
|
||||
|
||||
next(action);
|
||||
|
||||
@ -13,8 +13,13 @@ export default function reducer(core = {}, action){
|
||||
|
||||
case 'CURRENT_TRACK_LOADED':
|
||||
return Object.assign({}, core, {
|
||||
current_track: action.current_track,
|
||||
current_track_uri: action.current_track_uri
|
||||
current_track: action.track,
|
||||
current_track_uri: action.uri
|
||||
});
|
||||
|
||||
case 'NEXT_TRACK_LOADED':
|
||||
return Object.assign({}, core, {
|
||||
next_track_uri: action.uri
|
||||
});
|
||||
|
||||
case 'QUEUE_LOADED':
|
||||
|
||||
@ -171,14 +171,6 @@ export function getCurrentTrack(){
|
||||
}
|
||||
}
|
||||
|
||||
export function clearCurrentTrack(){
|
||||
return {
|
||||
type: 'CURRENT_TRACK_LOADED',
|
||||
current_track: null,
|
||||
current_track_uri: null
|
||||
}
|
||||
}
|
||||
|
||||
export function currentTrackLoaded(tl_track){
|
||||
return {
|
||||
type: 'MOPIDY_CURRENT_TRACK_LOADED',
|
||||
@ -186,6 +178,20 @@ export function currentTrackLoaded(tl_track){
|
||||
}
|
||||
}
|
||||
|
||||
export function getNextTrack(){
|
||||
return {
|
||||
type: 'MOPIDY_GET_NEXT_TRACK'
|
||||
}
|
||||
}
|
||||
|
||||
export function clearCurrentTrack(){
|
||||
return {
|
||||
type: 'CURRENT_TRACK_LOADED',
|
||||
track: null,
|
||||
uri: null
|
||||
}
|
||||
}
|
||||
|
||||
export function getQueue(){
|
||||
return {
|
||||
type: 'MOPIDY_GET_QUEUE'
|
||||
|
||||
@ -72,7 +72,8 @@ const MopidyMiddleware = (function(){
|
||||
break;
|
||||
|
||||
case 'event:tracklistChanged':
|
||||
store.dispatch(mopidyActions.getQueue())
|
||||
store.dispatch(mopidyActions.getQueue());
|
||||
store.dispatch(mopidyActions.getNextTrack());
|
||||
break;
|
||||
|
||||
case 'event:playbackStateChanged':
|
||||
@ -97,6 +98,7 @@ const MopidyMiddleware = (function(){
|
||||
|
||||
case 'event:trackPlaybackStarted':
|
||||
store.dispatch(mopidyActions.currentTrackLoaded(data.tl_track));
|
||||
store.dispatch(mopidyActions.getNextTrack());
|
||||
break;
|
||||
|
||||
case 'event:volumeChanged':
|
||||
@ -2016,23 +2018,60 @@ const MopidyMiddleware = (function(){
|
||||
case 'MOPIDY_CURRENT_TRACK_LOADED':
|
||||
var track = helpers.formatTracks(action.tl_track);
|
||||
|
||||
// We've got Spotify running, and it's a spotify track - go straight to the source!
|
||||
if (store.getState().spotify.enabled && helpers.uriSource(track.uri) == 'spotify'){
|
||||
store.dispatch(spotifyActions.getTrack(track.uri))
|
||||
// We don't have the track already in our index
|
||||
if (store.getState().core.tracks[track.uri] === undefined || store.getState().core.tracks[track.uri].images === undefined){
|
||||
|
||||
// Some other source, rely on Mopidy backends to do their work
|
||||
} else {
|
||||
store.dispatch(mopidyActions.getImages('tracks',[track.uri]))
|
||||
// We've got Spotify running, and it's a spotify track - go straight to the source!
|
||||
if (store.getState().spotify.enabled && helpers.uriSource(track.uri) == 'spotify'){
|
||||
store.dispatch(spotifyActions.getTrack(track.uri));
|
||||
|
||||
// Some other source, rely on Mopidy backends to do their work
|
||||
} else {
|
||||
store.dispatch(mopidyActions.getImages('tracks',[track.uri]));
|
||||
}
|
||||
}
|
||||
|
||||
// Set our window title to the track title
|
||||
helpers.setWindowTitle(track, store.getState().mopidy.play_state);
|
||||
store.dispatch({
|
||||
type: 'CURRENT_TRACK_LOADED',
|
||||
current_track: track
|
||||
track: track
|
||||
});
|
||||
break;
|
||||
|
||||
case 'MOPIDY_GET_NEXT_TRACK':
|
||||
request(socket, store, 'tracklist.getNextTlid')
|
||||
.then(
|
||||
response => {
|
||||
if (response && response >= 0){
|
||||
var track = helpers.applyFilter('tlid', response, store.getState().core.queue, true);
|
||||
|
||||
if (track){
|
||||
store.dispatch({
|
||||
type: 'NEXT_TRACK_LOADED',
|
||||
uri: track.uri
|
||||
});
|
||||
|
||||
console.log(track.name);
|
||||
|
||||
// We don't have the track already in our index
|
||||
if (store.getState().core.tracks[track.uri] === undefined || store.getState().core.tracks[track.uri].images === undefined){
|
||||
|
||||
// We've got Spotify running, and it's a spotify track - go straight to the source!
|
||||
if (store.getState().spotify.enabled && helpers.uriSource(track.uri) == 'spotify'){
|
||||
store.dispatch(spotifyActions.getTrack(track.uri));
|
||||
|
||||
// Some other source, rely on Mopidy backends to do their work
|
||||
} else {
|
||||
store.dispatch(mopidyActions.getImages('tracks',[track.uri]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
break;
|
||||
|
||||
case 'MOPIDY_GET_TRACK':
|
||||
request(socket, store, 'library.lookup', action.data )
|
||||
.then(
|
||||
|
||||
@ -91,9 +91,9 @@ class Queue extends React.Component{
|
||||
}
|
||||
|
||||
render(){
|
||||
|
||||
var current_track = null;
|
||||
var tracks = [];
|
||||
|
||||
if (this.props.queue && this.props.tracks){
|
||||
for (var i = 0; i < this.props.queue.length; i++){
|
||||
var track = this.props.queue[i];
|
||||
@ -131,11 +131,10 @@ class Queue extends React.Component{
|
||||
}
|
||||
}
|
||||
|
||||
var image = null
|
||||
var current_track_image = null;
|
||||
if (current_track){
|
||||
if (current_track.images !== undefined && current_track.images){
|
||||
image = helpers.sizedImages(current_track.images)
|
||||
image = image.large
|
||||
current_track_image = helpers.sizedImages(current_track.images).large;
|
||||
}
|
||||
}
|
||||
|
||||
@ -164,11 +163,11 @@ class Queue extends React.Component{
|
||||
return (
|
||||
<div className="view queue-view">
|
||||
<Header icon="play" className="overlay" title="Now playing" options={options} uiActions={this.props.uiActions} />
|
||||
<Parallax blur image={image} />
|
||||
<Parallax blur image={current_track_image} />
|
||||
<div className="content-wrapper">
|
||||
|
||||
<div className="current-track">
|
||||
{this.renderArtwork(image)}
|
||||
{this.renderArtwork(current_track_image)}
|
||||
<div className="title">
|
||||
{current_track ? <URILink type="track" uri={current_track.uri}>{current_track.name}</URILink> : <span>-</span>}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user