diff --git a/src/js/App.js b/src/js/App.js
index 0a4b00bf..c71266b5 100755
--- a/src/js/App.js
+++ b/src/js/App.js
@@ -101,7 +101,6 @@ class App extends React.Component{
// Listen for standalone key codes
let keyCodes = [27,32,191]
if (keyCodes.indexOf(e.keyCode) > -1){
- if (keyCode == 37)
e.preventDefault()
return true
}
diff --git a/src/js/components/Modal/KioskModeModal.js b/src/js/components/Modal/KioskModeModal.js
index f5d38bf5..f868ff33 100755
--- a/src/js/components/Modal/KioskModeModal.js
+++ b/src/js/components/Modal/KioskModeModal.js
@@ -24,8 +24,8 @@ export default class KioskModeModal extends React.Component{
}
render(){
- if (this.props.current_track && this.props.current_track.album && this.props.current_track.album.images){
- var images = this.props.current_track.album.images
+ if (this.props.current_track && this.props.current_track.images){
+ var images = this.props.current_track.images
} else {
var images = []
}
diff --git a/src/js/components/PlaybackControls.js b/src/js/components/PlaybackControls.js
index d251af8a..7328adc3 100755
--- a/src/js/components/PlaybackControls.js
+++ b/src/js/components/PlaybackControls.js
@@ -75,8 +75,8 @@ class PlaybackControls extends React.Component{
render(){
var images = false
- if (this.props.current_track && this.props.current_track.album && this.props.current_track.album.images){
- images = this.props.current_track.album.images
+ if (this.props.current_track && this.props.current_track.images){
+ images = this.props.current_track.images
}
return (
diff --git a/src/js/helpers.js b/src/js/helpers.js
index 8acd92d5..5977ce85 100755
--- a/src/js/helpers.js
+++ b/src/js/helpers.js
@@ -13,50 +13,67 @@ export let sizedImages = function( images ){
huge: false
}
- if( images.length <= 0 ) return sizes;
+ if (images.length <= 0) return sizes;
+
+ for (var i = 0; i < images.length; i++){
+ let image = images[i]
- for( var i = 0; i < images.length; i++ ){
-
// Mopidy image object
- if (typeof(images[i].__model__) !== 'undefined' && images[i].__model__ == 'Image'){
- sizes.small = images[i].uri
+ if (image.__model__ && image.__model__ == 'Image'){
+
+ if (image.width < 400){
+ sizes.small = image.url;
+ }else if (image.width < 800){
+ sizes.medium = image.url;
+ }else if (image.width < 1000){
+ sizes.large = image.url;
+ }else{
+ sizes.huge = image.url;
+ }
// Mopidy image string
- } else if (typeof(images[i]) == 'string'){
- sizes.small = images[i]
-
+ } else if (typeof(image) == 'string'){
+ sizes.small = image
+
// spotify-styled images
- } else if (typeof(images[i].width) !== 'undefined'){
- if( images[i].width < 400 ){
- sizes.small = images[i].url;
- }else if( images[i].width < 800 ){
- sizes.medium = images[i].url;
- }else if( images[i].width < 1000 ){
- sizes.large = images[i].url;
+ } else if (image.width !== undefined){
+
+ if (image.width < 400){
+ sizes.small = image.url;
+ }else if (image.width < 800){
+ sizes.medium = image.url;
+ }else if (image.width < 1000){
+ sizes.large = image.url;
}else{
- sizes.huge = images[i].url;
+ sizes.huge = image.url;
}
// lastfm-styled images
- } else if (typeof(images[i].size) !== 'undefined'){
- switch( images[i].size ){
+ } else if (image.size !== undefined){
+ switch( image.size ){
case 'mega':
case 'extralarge':
- sizes.huge = images[i]['#text']
+ sizes.huge = image['#text']
break
case 'large':
- sizes.large = images[i]['#text']
+ sizes.large = image['#text']
break
case 'medium':
- sizes.medium = images[i]['#text']
+ sizes.medium = image['#text']
break
case 'small':
- sizes.small = images[i]['#text']
+ sizes.small = image['#text']
break
}
}
}
+ if (!sizes.small){
+ if (sizes.medium) sizes.small = sizes.medium
+ else if (sizes.large) sizes.small = sizes.large
+ else if (sizes.huge) sizes.small = sizes.huge
+ else sizes.small = null
+ }
if (!sizes.medium){
if (sizes.large) sizes.medium = sizes.large
else if (sizes.huge) sizes.medium = sizes.huge
@@ -68,6 +85,41 @@ export let sizedImages = function( images ){
return sizes;
}
+
+/**
+ * Digest an array of Mopidy image objects into a universal format
+ *
+ * @param mopidy = obj (mopidy store object)
+ * @param images = array
+ * @return array
+ **/
+export let digestMopidyImages = function(mopidy, images){
+ let digested = []
+
+ for (let i = 0; i < images.length; i++){
+
+ // Accommodate backends that provide URIs vs URLs
+ let url = images[i].url
+ if (!url && images[i].uri){
+ url = images[i].uri
+ }
+/*
+ // Replace local images to point directly to our Mopidy server
+ if (url.startsWith('/images/')){
+ url = '//'+mopidy.host+':'+mopidy.port+url
+ }
+ */
+
+ // Amend our URL
+ images[i].url = url
+
+ digested.push(images[i])
+ }
+
+ return digested
+}
+
+
export let generateGuid = function(format = 'xxxxxxxxxxxx'){
return format.replace(/[xy]/g, function(c) {
var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
@@ -75,7 +127,6 @@ export let generateGuid = function(format = 'xxxxxxxxxxxx'){
});
}
-
export let getCurrentPusherConnection = function( connections, connectionid ){
function isCurrentConnection(connection){
return connection.connectionid == newProps.pusher.connectionid;
@@ -93,15 +144,14 @@ export let getCurrentPusherConnection = function( connections, connectionid ){
* @param track object
* @return string
**/
-export let getTrackIcon = function(current_track = false, ui = false){
- if (!ui) return false
+export let getTrackIcon = function(current_track = false, core = false){
+ if (!core) return false
if (!current_track) return false
if (typeof(current_track.uri) == 'undefined') return false
if (typeof(core.tracks[current_track.uri]) === 'undefined') return false
var track = core.tracks[current_track.uri]
- if (!track.album) return false
- if (!track.album.images) return false
- return sizedImages(track.album.images).small
+ if (!track.images) return false
+ return sizedImages(track.images).small
}
diff --git a/src/js/services/core/middleware.js b/src/js/services/core/middleware.js
index 5e377476..f6d12636 100755
--- a/src/js/services/core/middleware.js
+++ b/src/js/services/core/middleware.js
@@ -24,16 +24,6 @@ const CoreMiddleware = (function(){
next(action)
break
- case 'MOPIDY_CONNECTED':
- ReactGA.event({ category: 'Mopidy', action: 'Connected', label: window.location.hostname })
- next(action)
- break
-
- case 'PUSHER_CONNECTED':
- ReactGA.event({ category: 'Pusher', action: 'Connected', label: action.username })
- next(action)
- break
-
case 'ALBUM_LOADED':
if (action.data) ReactGA.event({ category: 'Album', action: 'Load', label: action.album.uri })
@@ -75,11 +65,6 @@ const CoreMiddleware = (function(){
next(action)
break
- case 'MOPIDY_DIRECTORY':
- if (action.data) ReactGA.event({ category: 'Directory', action: 'Load', label: action.data.uri })
- next(action)
- break
-
case 'PLAY_PLAYLIST':
ReactGA.event({ category: 'Playlist', action: 'Play', label: action.uri })
next(action)
diff --git a/src/js/services/mopidy/actions.js b/src/js/services/mopidy/actions.js
index c65e25b7..8e3337bc 100755
--- a/src/js/services/mopidy/actions.js
+++ b/src/js/services/mopidy/actions.js
@@ -164,6 +164,14 @@ export function setTimePosition(time_position){
* Asset-oriented actions
**/
+export function getImages( context, uris ){
+ return {
+ type: 'MOPIDY_GET_IMAGES',
+ context: context,
+ uris: uris
+ }
+}
+
export function deletePlaylist( uri ){
return {
type: 'MOPIDY_DELETE_PLAYLIST',
diff --git a/src/js/services/mopidy/middleware.js b/src/js/services/mopidy/middleware.js
index 33686817..9b07c2e9 100755
--- a/src/js/services/mopidy/middleware.js
+++ b/src/js/services/mopidy/middleware.js
@@ -1,4 +1,5 @@
+import ReactGA from 'react-ga'
import Mopidy from 'mopidy'
import { hashHistory } from 'react-router'
import * as helpers from '../../helpers'
@@ -192,6 +193,11 @@ const MopidyMiddleware = (function(){
socket.on( (type, data) => handleMessage( socket, store, type, data ) )
break
+ case 'MOPIDY_CONNECTED':
+ ReactGA.event({ category: 'Mopidy', action: 'Connected', label: window.location.hostname })
+ next(action)
+ break
+
case 'MOPIDY_DISCONNECT':
if(socket != null) socket.close()
socket = null
@@ -238,7 +244,7 @@ const MopidyMiddleware = (function(){
type: 'browser_notification',
title: 'Track skipped',
body: store.getState().pusher.username +' skipped this track',
- icon: (store.getState().core.current_track ? helpers.getTrackIcon(store.getState().core.current_track, store.getState().ui) : false)
+ icon: (store.getState().core.current_track ? helpers.getTrackIcon(store.getState().core.current_track, store.getState().core) : false)
}
store.dispatch( pusherActions.deliverBroadcast(data) )
break
@@ -248,7 +254,7 @@ const MopidyMiddleware = (function(){
type: 'browser_notification',
title: 'Playback stopped',
body: store.getState().pusher.username +' stopped playback',
- icon: (store.getState().core.current_track ? helpers.getTrackIcon(store.getState().core.current_track, store.getState().ui) : false)
+ icon: (store.getState().core.current_track ? helpers.getTrackIcon(store.getState().core.current_track, store.getState().core) : false)
}
store.dispatch( pusherActions.deliverBroadcast(data) )
break
@@ -400,13 +406,6 @@ const MopidyMiddleware = (function(){
break
- case 'CANCEL_PROCESS':
- if (action.key == 'MOPIDY_ENQUEUE_URIS'){
- store.dispatch(mopidyActions.enqueueURIsCancel())
- }
- next(action)
- break
-
case 'MOPIDY_PLAY_URIS':
// Stop the radio
@@ -1033,17 +1032,49 @@ const MopidyMiddleware = (function(){
// Fire off our universal track index loader
store.dispatch({ type: 'TRACK_LOADED', key: action.data.track.uri, track: action.data.track })
- // When current track is Spotify track, go get the full object
- // This is because Mopidy doesn't give us full artist/album objects, without artwork
- if (action.data.track.uri.substring(0,14) == 'spotify:track:'){
- store.dispatch( spotifyActions.getTrack( action.data.track.uri ) )
- }
+ // Get me some images
+ store.dispatch(mopidyActions.getImages('tracks',[action.data.track.uri]))
}
next(action)
break
+ /**
+ * =============================================================== IMAGES ===============
+ * ======================================================================================
+ **/
+
+ case 'MOPIDY_GET_IMAGES':
+
+ instruct( socket, store, 'library.getImages', {uris: action.uris})
+ .then( response => {
+
+ let records = []
+ for (var uri in response){
+ if (response.hasOwnProperty(uri)){
+
+ let images = response[uri]
+ images = helpers.digestMopidyImages(store.getState().mopidy, images)
+
+ records.push({
+ uri: uri,
+ images: images
+ })
+ }
+ }
+
+ let action_data = {
+ type: (action.context+'_LOADED').toUpperCase()
+ }
+ action_data[action.context] = records
+ store.dispatch(action_data)
+ })
+
+ next(action)
+ break
+
+
/**
* =============================================================== LOCAL ================
* ======================================================================================
@@ -1060,6 +1091,11 @@ const MopidyMiddleware = (function(){
})
break
+ case 'MOPIDY_DIRECTORY':
+ if (action.data) ReactGA.event({ category: 'Directory', action: 'Load', label: action.data.uri })
+ next(action)
+ break
+
// This action is irrelevant to us, pass it on to the next middleware
default:
return next(action)
diff --git a/src/js/services/pusher/middleware.js b/src/js/services/pusher/middleware.js
index 8c21883c..ec245e06 100755
--- a/src/js/services/pusher/middleware.js
+++ b/src/js/services/pusher/middleware.js
@@ -98,6 +98,7 @@ const PusherMiddleware = (function(){
break;
case 'PUSHER_CONNECTED':
+ ReactGA.event({ category: 'Pusher', action: 'Connected', label: action.username })
request(store, 'get_config')
.then(
response => {
diff --git a/src/js/services/ui/middleware.js b/src/js/services/ui/middleware.js
index 2a36af7b..ac21c4a3 100755
--- a/src/js/services/ui/middleware.js
+++ b/src/js/services/ui/middleware.js
@@ -123,6 +123,13 @@ const UIMiddleware = (function(){
next(action)
break
+ case 'CANCEL_PROCESS':
+ if (action.key == 'MOPIDY_ENQUEUE_URIS'){
+ store.dispatch(mopidyActions.enqueueURIsCancel())
+ }
+ next(action)
+ break
+
// This action is irrelevant to us, pass it on to the next middleware
default:
return next(action)
diff --git a/src/js/views/Queue.js b/src/js/views/Queue.js
index 6bda7fb7..0dbab57d 100755
--- a/src/js/views/Queue.js
+++ b/src/js/views/Queue.js
@@ -64,7 +64,7 @@ class Queue extends React.Component{
return (
{this.props.radio_enabled ?
: null}
-
+
)
}
@@ -74,15 +74,16 @@ class Queue extends React.Component{
return (
{this.props.radio_enabled ?
: null}
-
+
)
}
render(){
var image = null
- if (this.props.current_track && this.props.current_track.album !== undefined && this.props.current_track.album.images){
- image = helpers.sizedImages(this.props.current_track.album.images).huge
+ if (this.props.current_track && this.props.current_track.images !== undefined){
+ image = helpers.sizedImages(this.props.current_track.images)
+ image = image.large
}
var options = (
diff --git a/src/js/views/Search.js b/src/js/views/Search.js
index d2ea4fe6..3a596760 100755
--- a/src/js/views/Search.js
+++ b/src/js/views/Search.js
@@ -16,6 +16,7 @@ import LazyLoadListener from '../components/LazyLoadListener'
import SearchForm from '../components/SearchForm'
import * as helpers from '../helpers'
+import * as coreActions from '../services/core/actions'
import * as uiActions from '../services/ui/actions'
import * as mopidyActions from '../services/mopidy/actions'
import * as spotifyActions from '../services/spotify/actions'
@@ -30,7 +31,7 @@ class Search extends React.Component{
// Make sure we have search parameters to start with
if (this.props.params && this.props.params.type && this.props.params.query){
- this.props.uiActions.startSearch(this.props.params.type, this.props.params.query)
+ this.props.coreActions.startSearch(this.props.params.type, this.props.params.query)
}
// Auto-focus on the input field
@@ -43,12 +44,12 @@ class Search extends React.Component{
if (newProps.params && newProps.params.type && newProps.params.query){
if (this.props.params.query != newProps.params.query || this.props.params.type != newProps.params.type){
- this.props.uiActions.startSearch(newProps.params.type, newProps.params.query)
+ this.props.coreActions.startSearch(newProps.params.type, newProps.params.query)
}
// mopidy comes online
if (!this.props.mopidy_connected && newProps.mopidy_connected){
- this.props.uiActions.startSearch(newProps.params.type, newProps.params.query, true)
+ this.props.coreActions.startSearch(newProps.params.type, newProps.params.query, true)
}
}
}
@@ -260,22 +261,23 @@ const mapStateToProps = (state, ownProps) => {
return {
mopidy_connected: state.mopidy.connected,
search_settings: (state.ui.search_settings ? state.ui.search_settings : null),
- tracks: (state.ui.search_results ? state.ui.search_results.tracks : []),
- tracks_more: (state.ui.search_results && state.ui.search_results.tracks_more ? state.ui.search_results.tracks_more : null),
+ tracks: (state.core.search_results ? state.core.search_results.tracks : []),
+ tracks_more: (state.core.search_results && state.core.search_results.tracks_more ? state.core.search_results.tracks_more : null),
artists: (state.core.artists ? state.core.artists : []),
- artists_uris: (state.ui.search_results ? state.ui.search_results.artists_uris : []),
- artists_more: (state.ui.search_results ? state.ui.search_results.artists_more : null),
+ artists_uris: (state.core.search_results ? state.core.search_results.artists_uris : []),
+ artists_more: (state.core.search_results ? state.core.search_results.artists_more : null),
albums: (state.core.albums ? state.core.albums : []),
- albums_uris: (state.ui.search_results ? state.ui.search_results.albums_uris : []),
- albums_more: (state.ui.search_results ? state.ui.search_results.albums_more : null),
+ albums_uris: (state.core.search_results ? state.core.search_results.albums_uris : []),
+ albums_more: (state.core.search_results ? state.core.search_results.albums_more : null),
playlists: (state.core.playlists ? state.core.playlists : []),
- playlists_uris: (state.ui.search_results ? state.ui.search_results.playlists_uris : []),
- playlists_more: (state.ui.search_results ? state.ui.search_results.playlists_more : null)
+ playlists_uris: (state.core.search_results ? state.core.search_results.playlists_uris : []),
+ playlists_more: (state.core.search_results ? state.core.search_results.playlists_more : null)
}
}
const mapDispatchToProps = (dispatch) => {
return {
+ coreActions: bindActionCreators(coreActions, dispatch),
uiActions: bindActionCreators(uiActions, dispatch),
mopidyActions: bindActionCreators(mopidyActions, dispatch),
spotifyActions: bindActionCreators(spotifyActions, dispatch)