2016-10-14 22:42:45 +13:00
|
|
|
|
|
|
|
|
import Mopidy from 'mopidy'
|
2016-11-16 16:30:49 +13:00
|
|
|
import * as helpers from '../../helpers'
|
2016-11-07 21:21:15 +13:00
|
|
|
|
|
|
|
|
var mopidyActions = require('./actions.js')
|
2017-01-27 16:13:11 +13:00
|
|
|
var uiActions = require('../ui/actions.js')
|
2016-11-07 21:21:15 +13:00
|
|
|
var lastfmActions = require('../lastfm/actions.js')
|
2016-12-20 07:56:42 +13:00
|
|
|
var pusherActions = require('../pusher/actions.js')
|
2016-10-14 22:42:45 +13:00
|
|
|
|
|
|
|
|
const MopidyMiddleware = (function(){
|
|
|
|
|
|
2016-10-16 12:37:37 +13:00
|
|
|
// container for the actual Mopidy socket
|
2016-10-14 22:42:45 +13:00
|
|
|
var socket = null;
|
|
|
|
|
|
2016-10-16 12:37:37 +13:00
|
|
|
// handle all manner of socket messages
|
2016-10-14 22:42:45 +13:00
|
|
|
const handleMessage = (ws, store, type, data) => {
|
2017-02-22 08:54:47 +13:00
|
|
|
|
|
|
|
|
// if debug enabled
|
|
|
|
|
if (store.getState().ui.log_mopidy) console.log('Mopidy', type, data)
|
|
|
|
|
|
2016-10-14 22:42:45 +13:00
|
|
|
switch( type ){
|
|
|
|
|
|
|
|
|
|
case 'state:online':
|
2016-10-16 12:37:37 +13:00
|
|
|
store.dispatch({ type: 'MOPIDY_CONNECTED' });
|
2016-10-14 22:42:45 +13:00
|
|
|
instruct( ws, store, 'playback.getState' );
|
|
|
|
|
instruct( ws, store, 'playback.getVolume' );
|
|
|
|
|
instruct( ws, store, 'tracklist.getConsume' );
|
|
|
|
|
instruct( ws, store, 'tracklist.getRandom' );
|
|
|
|
|
instruct( ws, store, 'tracklist.getRepeat' );
|
|
|
|
|
instruct( ws, store, 'tracklist.getTlTracks' );
|
2016-10-22 19:13:06 +13:00
|
|
|
instruct( ws, store, 'playback.getCurrentTlTrack' );
|
2016-11-03 18:22:26 +13:00
|
|
|
instruct( ws, store, 'playback.getTimePosition' );
|
2016-11-06 18:29:49 +13:00
|
|
|
instruct( ws, store, 'getUriSchemes' );
|
2016-10-14 22:42:45 +13:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'state:offline':
|
2016-10-16 12:37:37 +13:00
|
|
|
store.dispatch({ type: 'MOPIDY_DISCONNECTED' });
|
2016-10-14 22:42:45 +13:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'event:tracklistChanged':
|
2016-10-22 19:13:06 +13:00
|
|
|
instruct( ws, store, 'tracklist.getTlTracks' );
|
2016-10-14 22:42:45 +13:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'event:playbackStateChanged':
|
|
|
|
|
instruct( ws, store, 'playback.getState' );
|
2016-11-09 22:02:01 +13:00
|
|
|
instruct( ws, store, 'playback.getTimePosition' );
|
2016-10-24 21:26:01 +13:00
|
|
|
break;
|
|
|
|
|
|
2016-11-03 18:22:26 +13:00
|
|
|
case 'event:seeked':
|
|
|
|
|
store.dispatch({ type: 'MOPIDY_TIMEPOSITION', data: data.time_position });
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'event:trackPlaybackEnded':
|
|
|
|
|
instruct( ws, store, 'playback.getTimePosition' );
|
|
|
|
|
break;
|
|
|
|
|
|
2016-10-24 21:26:01 +13:00
|
|
|
case 'event:trackPlaybackStarted':
|
2016-10-22 19:13:06 +13:00
|
|
|
instruct( ws, store, 'playback.getCurrentTlTrack' );
|
2016-10-14 22:42:45 +13:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'event:volumeChanged':
|
2016-10-20 21:38:01 +13:00
|
|
|
store.dispatch({ type: 'MOPIDY_VOLUME', data: data.volume });
|
2016-10-14 22:42:45 +13:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'event:optionsChanged':
|
|
|
|
|
instruct( ws, store, 'tracklist.getConsume' );
|
|
|
|
|
instruct( ws, store, 'tracklist.getRandom' );
|
|
|
|
|
instruct( ws, store, 'tracklist.getRepeat' );
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
2016-10-16 12:37:37 +13:00
|
|
|
//console.log( 'MopidyService: Unhandled message', type, message );
|
2016-10-14 22:42:45 +13:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2016-10-16 12:37:37 +13:00
|
|
|
* Call something with Mopidy
|
2016-10-14 22:42:45 +13:00
|
|
|
*
|
|
|
|
|
* Sends request to Mopidy server, and updates our local storage on return
|
2016-10-16 12:37:37 +13:00
|
|
|
* @param object ws = Mopidy class that wraps a Websocket
|
|
|
|
|
* @param string model = which Mopidy model (playback, tracklist, etc)
|
|
|
|
|
* @param string property = TlTracks, Consume, etc
|
|
|
|
|
* @param string value (optional) = value of the property to pass
|
2016-10-20 21:38:01 +13:00
|
|
|
* @return promise
|
2016-10-14 22:42:45 +13:00
|
|
|
**/
|
2016-10-20 21:38:01 +13:00
|
|
|
const instruct = ( ws, store, call, value = {} ) => {
|
2016-10-16 12:37:37 +13:00
|
|
|
|
2016-11-09 15:49:25 +13:00
|
|
|
if( !store.getState().mopidy.connected ) return false
|
|
|
|
|
|
2016-10-14 22:42:45 +13:00
|
|
|
var callParts = call.split('.');
|
|
|
|
|
var model = callParts[0];
|
|
|
|
|
var method = callParts[1];
|
|
|
|
|
|
2016-10-20 21:38:01 +13:00
|
|
|
return new Promise( (resolve, reject) => {
|
2016-11-14 11:20:02 +13:00
|
|
|
|
|
|
|
|
if( model in ws ){
|
|
|
|
|
if( method in ws[model] ){
|
|
|
|
|
var mopidyObject = ws[model][method]
|
|
|
|
|
var property = method;
|
|
|
|
|
}else{
|
|
|
|
|
var mopidyObject = ws[model]
|
|
|
|
|
var property = model;
|
|
|
|
|
}
|
|
|
|
|
}else{
|
|
|
|
|
var error = {
|
|
|
|
|
message: 'Call to an invalid object. Check your are calling a valid Mopidy endpoint.',
|
|
|
|
|
call: call,
|
|
|
|
|
value: value
|
|
|
|
|
}
|
|
|
|
|
console.error(error)
|
|
|
|
|
reject(error)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
property = property.replace('get','');
|
|
|
|
|
property = property.replace('set','');
|
|
|
|
|
|
2016-11-06 18:29:49 +13:00
|
|
|
mopidyObject( value )
|
2016-10-14 22:42:45 +13:00
|
|
|
.then(
|
2016-10-20 21:38:01 +13:00
|
|
|
response => {
|
2016-11-06 18:29:49 +13:00
|
|
|
store.dispatch({ type: 'MOPIDY_'+property.toUpperCase(), call: call, data: response });
|
2016-10-20 21:38:01 +13:00
|
|
|
resolve(response);
|
|
|
|
|
},
|
|
|
|
|
error => {
|
|
|
|
|
console.error(error)
|
|
|
|
|
reject(error);
|
|
|
|
|
}
|
2016-10-14 22:42:45 +13:00
|
|
|
);
|
2016-10-20 21:38:01 +13:00
|
|
|
});
|
2016-10-14 22:42:45 +13:00
|
|
|
}
|
|
|
|
|
|
2016-10-16 12:37:37 +13:00
|
|
|
/**
|
|
|
|
|
* Middleware
|
|
|
|
|
*
|
|
|
|
|
* This behaves like an action interceptor. We listen for specific actions
|
|
|
|
|
* and handle special functionality. If the action is not in our switch, then
|
|
|
|
|
* it just proceeds to the next middleware, or default functionality
|
|
|
|
|
**/
|
2016-10-14 22:42:45 +13:00
|
|
|
return store => next => action => {
|
2016-10-20 21:38:01 +13:00
|
|
|
|
2016-10-14 22:42:45 +13:00
|
|
|
switch(action.type) {
|
|
|
|
|
|
2016-10-16 12:37:37 +13:00
|
|
|
case 'MOPIDY_CONNECT':
|
2016-10-14 22:42:45 +13:00
|
|
|
|
|
|
|
|
if(socket != null) socket.close();
|
2016-10-16 12:37:37 +13:00
|
|
|
store.dispatch({ type: 'MOPIDY_CONNECTING' });
|
2016-10-14 22:42:45 +13:00
|
|
|
|
2016-10-18 08:56:53 +13:00
|
|
|
var state = store.getState();
|
|
|
|
|
|
2016-10-14 22:42:45 +13:00
|
|
|
socket = new Mopidy({
|
2016-10-18 08:56:53 +13:00
|
|
|
webSocketUrl: 'ws://'+state.mopidy.host+':'+state.mopidy.port+'/mopidy/ws',
|
2016-10-14 22:42:45 +13:00
|
|
|
callingConvention: 'by-position-or-by-name'
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
socket.on( (type, data) => handleMessage( socket, store, type, data ) );
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
2016-10-16 12:37:37 +13:00
|
|
|
case 'MOPIDY_DISCONNECT':
|
2016-10-14 22:42:45 +13:00
|
|
|
if(socket != null) socket.close();
|
|
|
|
|
socket = null;
|
2016-10-16 12:37:37 +13:00
|
|
|
store.dispatch({ type: 'MOPIDY_DISCONNECTED' });
|
2016-10-14 22:42:45 +13:00
|
|
|
break;
|
|
|
|
|
|
2016-10-16 12:37:37 +13:00
|
|
|
// send an instruction to the websocket
|
|
|
|
|
case 'MOPIDY_INSTRUCT':
|
2016-10-14 22:42:45 +13:00
|
|
|
instruct( socket, store, action.call, action.value )
|
|
|
|
|
break;
|
|
|
|
|
|
2016-12-05 10:20:01 +13:00
|
|
|
case 'MOPIDY_DEBUG':
|
|
|
|
|
instruct( socket, store, action.call, action.value )
|
|
|
|
|
.then( response => {
|
|
|
|
|
store.dispatch({ type: 'DEBUG', response: response })
|
|
|
|
|
})
|
|
|
|
|
break;
|
|
|
|
|
|
2017-02-10 09:06:41 +13:00
|
|
|
case 'MOPIDY_URISCHEMES':
|
|
|
|
|
var uri_schemes = action.data
|
|
|
|
|
var remove = ['http','https','mms','rtmp','rtmps','rtsp','sc','spotify']
|
|
|
|
|
|
|
|
|
|
// remove all our ignored types
|
|
|
|
|
for( var i = 0; i < remove.length; i++ ){
|
|
|
|
|
var index = uri_schemes.indexOf(remove[i])
|
|
|
|
|
if( index > -1 ) uri_schemes.splice(index, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// append with ':' to make them a mopidy URI
|
|
|
|
|
for( var i = 0; i < uri_schemes.length; i++ ){
|
|
|
|
|
uri_schemes[i] = uri_schemes[i] +':'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
store.dispatch({ type: 'MOPIDY_URISCHEMES_FILTERED', data: uri_schemes });
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* General playback
|
|
|
|
|
**/
|
|
|
|
|
|
2016-11-29 15:36:01 +13:00
|
|
|
case 'MOPIDY_NEXT':
|
2017-03-09 08:04:23 +13:00
|
|
|
var data = {
|
|
|
|
|
type: 'browser_notification',
|
|
|
|
|
title: 'Track skipped',
|
|
|
|
|
body: store.getState().pusher.username +' skipped this track',
|
|
|
|
|
icon: (store.getState().ui.current_track ? helpers.getTrackIcon( store.getState().ui.current_track ) : false)
|
2016-11-29 15:36:01 +13:00
|
|
|
}
|
2017-03-09 08:04:23 +13:00
|
|
|
store.dispatch( pusherActions.deliverBroadcast(data) )
|
2016-11-29 15:36:01 +13:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'MOPIDY_STOP':
|
2017-03-09 08:04:23 +13:00
|
|
|
var data = {
|
|
|
|
|
type: 'browser_notification',
|
|
|
|
|
title: 'Playback stopped',
|
|
|
|
|
body: store.getState().pusher.username +' stopped playback',
|
|
|
|
|
icon: (store.getState().ui.current_track ? helpers.getTrackIcon( store.getState().ui.current_track ) : false)
|
2016-11-29 15:36:01 +13:00
|
|
|
}
|
2017-03-09 08:04:23 +13:00
|
|
|
store.dispatch( pusherActions.deliverBroadcast(data) )
|
2016-11-29 15:36:01 +13:00
|
|
|
break;
|
|
|
|
|
|
2017-02-14 20:24:35 +13:00
|
|
|
case 'MOPIDY_ENQUEUE_URIS':
|
|
|
|
|
var value = { uris: action.uris }
|
|
|
|
|
if (action.at_position){
|
|
|
|
|
value.at_position = action.at_position
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
instruct( socket, store, 'tracklist.add', value )
|
|
|
|
|
.then( response => {
|
|
|
|
|
var tlids = []
|
|
|
|
|
for (var i = 0; i < response.length; i++){
|
|
|
|
|
tlids.push(response[i].tlid)
|
|
|
|
|
}
|
|
|
|
|
store.dispatch( pusherActions.addQueueMetadata(tlids, action.from_uri) )
|
2017-03-14 08:00:39 +13:00
|
|
|
store.dispatch( uiActions.createNotification('Added '+tlids.length+' URI(s) to queue') )
|
2017-02-14 20:24:35 +13:00
|
|
|
})
|
|
|
|
|
break
|
|
|
|
|
|
2017-01-27 09:55:19 +13:00
|
|
|
case 'MOPIDY_ENQUEUE_URIS_NEXT':
|
2016-12-13 15:55:30 +13:00
|
|
|
|
|
|
|
|
var current_track = store.getState().ui.current_track
|
|
|
|
|
var current_tracklist = store.getState().ui.current_tracklist
|
|
|
|
|
var current_track_index = -1
|
2017-02-14 12:13:07 +13:00
|
|
|
|
|
|
|
|
if (typeof(current_track) !== 'undefined'){
|
|
|
|
|
for( var i = 0; i < current_tracklist.length; i++ ){
|
|
|
|
|
if( current_tracklist[i].tlid == current_track.tlid ){
|
|
|
|
|
current_track_index = i
|
|
|
|
|
break
|
|
|
|
|
}
|
2016-12-13 15:55:30 +13:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var at_position = null
|
|
|
|
|
if( current_track_index > -1 ) at_position = current_track_index + 1
|
|
|
|
|
|
|
|
|
|
instruct( socket, store, 'tracklist.add', { uris: action.uris, at_position: at_position } )
|
2017-02-14 12:13:07 +13:00
|
|
|
.then( response => {
|
|
|
|
|
var tlids = []
|
|
|
|
|
for (var i = 0; i < response.length; i++){
|
|
|
|
|
tlids.push(response[i].tlid)
|
|
|
|
|
}
|
|
|
|
|
store.dispatch( pusherActions.addQueueMetadata(tlids, action.from_uri) )
|
2017-03-14 08:00:39 +13:00
|
|
|
store.dispatch( uiActions.createNotification('Added '+tlids.length+' URI(s) to queue') )
|
2017-02-14 12:13:07 +13:00
|
|
|
})
|
2016-12-13 15:55:30 +13:00
|
|
|
break
|
|
|
|
|
|
2016-11-18 08:29:13 +13:00
|
|
|
case 'MOPIDY_PLAY_URIS':
|
2016-10-20 21:38:01 +13:00
|
|
|
|
2016-12-21 10:22:43 +13:00
|
|
|
// stop the radio
|
|
|
|
|
if (store.getState().ui.radio && store.getState().ui.radio.enabled){
|
|
|
|
|
store.dispatch( pusherActions.stopRadio() )
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-20 21:38:01 +13:00
|
|
|
// add our first track
|
|
|
|
|
instruct( socket, store, 'tracklist.add', { uri: action.uris[0], at_position: 0 } )
|
|
|
|
|
.then( response => {
|
|
|
|
|
|
2017-03-14 08:00:39 +13:00
|
|
|
// treat empty response as a failed lookup
|
2016-11-18 08:29:13 +13:00
|
|
|
if( !response || response.length <= 0 ){
|
2017-02-14 20:24:35 +13:00
|
|
|
store.dispatch( uiActions.createNotification('Failed to load URI(s)', 'bad') )
|
2017-03-14 08:00:39 +13:00
|
|
|
|
2016-11-18 08:29:13 +13:00
|
|
|
}else{
|
|
|
|
|
// play it
|
2017-02-14 20:24:35 +13:00
|
|
|
store.dispatch( mopidyActions.changeTrack( response[0].tlid ) );
|
|
|
|
|
|
|
|
|
|
var tlids = []
|
|
|
|
|
for (var i = 0; i < response.length; i++){
|
|
|
|
|
tlids.push(response[i].tlid)
|
|
|
|
|
}
|
|
|
|
|
store.dispatch( pusherActions.addQueueMetadata(tlids, action.from_uri) )
|
2016-10-21 16:34:16 +13:00
|
|
|
|
2017-03-14 08:00:39 +13:00
|
|
|
// add the rest of our uris (if any)
|
|
|
|
|
action.uris.shift();
|
|
|
|
|
if( action.uris.length > 0 ){
|
|
|
|
|
store.dispatch( mopidyActions.enqueueURIs( action.uris, action.from_uri, 1 ) )
|
|
|
|
|
}
|
2016-10-20 21:38:01 +13:00
|
|
|
}
|
|
|
|
|
})
|
2016-10-19 14:09:34 +13:00
|
|
|
break;
|
|
|
|
|
|
2016-11-16 16:30:49 +13:00
|
|
|
case 'MOPIDY_REORDER_TRACKLIST':
|
|
|
|
|
|
|
|
|
|
// add our first track
|
|
|
|
|
instruct( socket, store, 'tracklist.move', { start: action.range_start, end: action.range_start + action.range_length, to_position: action.insert_before } )
|
|
|
|
|
.then( response => {
|
2016-11-25 19:04:27 +13:00
|
|
|
// TODO: when complete, send event to confirm success/failure
|
2016-11-16 16:30:49 +13:00
|
|
|
})
|
|
|
|
|
break;
|
|
|
|
|
|
2017-01-18 09:09:24 +13:00
|
|
|
case 'MOPIDY_GET_SEARCH_RESULTS':
|
|
|
|
|
var queryObject = {};
|
|
|
|
|
for( var i = 0; i < action.fields.length; i++ ){
|
|
|
|
|
queryObject[action.fields[i]] = [action.query];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
instruct( socket, store, 'library.search', {query: queryObject, uris: action.uris})
|
|
|
|
|
.then( response => {
|
|
|
|
|
|
|
|
|
|
// collate all our different sources into one array
|
|
|
|
|
var tracks = []
|
|
|
|
|
for( var i = 0; i < response.length; i++ ){
|
|
|
|
|
if( response[i].tracks ) tracks = [...tracks, ...response[i].tracks]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
store.dispatch({ type: 'SEARCH_RESULTS_LOADED', tracks: tracks });
|
|
|
|
|
})
|
|
|
|
|
break;
|
|
|
|
|
|
2017-01-13 13:17:47 +13:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* =============================================================== PLAYLIST(S) ==========
|
|
|
|
|
* ======================================================================================
|
|
|
|
|
**/
|
|
|
|
|
|
|
|
|
|
case 'MOPIDY_GET_LIBRARY_PLAYLISTS':
|
2016-10-21 23:27:50 +13:00
|
|
|
instruct( socket, store, 'playlists.asList' )
|
|
|
|
|
.then( response => {
|
2016-11-21 21:46:53 +13:00
|
|
|
|
2017-01-13 13:17:47 +13:00
|
|
|
// drop in our URI list
|
|
|
|
|
var playlist_uris = helpers.asURIs(response)
|
|
|
|
|
|
|
|
|
|
store.dispatch({ type: 'LIBRARY_PLAYLISTS_LOADED', uris: playlist_uris });
|
|
|
|
|
|
|
|
|
|
// get the full playlist objects
|
|
|
|
|
for (var i = 0; i < response.length; i++ ){
|
|
|
|
|
instruct( socket, store, 'playlists.lookup', { uri: response[i].uri })
|
|
|
|
|
.then( response => {
|
2017-02-23 22:06:43 +13:00
|
|
|
var source = helpers.uriSource(response.uri)
|
2017-01-13 13:17:47 +13:00
|
|
|
var playlist = Object.assign(
|
|
|
|
|
{},
|
|
|
|
|
{
|
2017-01-14 01:01:48 +13:00
|
|
|
type: 'playlist',
|
|
|
|
|
name: response.name,
|
|
|
|
|
uri: response.uri,
|
2017-02-23 22:06:43 +13:00
|
|
|
source: (source == 'spotify' ? 'local' : source),
|
2017-01-14 01:01:48 +13:00
|
|
|
last_modified: response.last_modified,
|
2017-01-13 13:17:47 +13:00
|
|
|
tracks_total: ( response.tracks ? response.tracks.length : 0 )
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|
2017-01-15 11:40:08 +13:00
|
|
|
store.dispatch({
|
|
|
|
|
type: 'PLAYLIST_LOADED',
|
2017-01-19 11:47:15 +13:00
|
|
|
key: playlist.uri,
|
2017-01-15 11:40:08 +13:00
|
|
|
playlist: playlist
|
|
|
|
|
})
|
2017-01-13 13:17:47 +13:00
|
|
|
})
|
|
|
|
|
}
|
2016-10-21 23:27:50 +13:00
|
|
|
})
|
|
|
|
|
break;
|
|
|
|
|
|
2017-01-13 13:17:47 +13:00
|
|
|
case 'MOPIDY_GET_PLAYLIST':
|
2016-11-01 11:49:04 +13:00
|
|
|
instruct( socket, store, 'playlists.lookup', action.data )
|
|
|
|
|
.then( response => {
|
2016-11-09 21:13:40 +13:00
|
|
|
var playlist = Object.assign(
|
|
|
|
|
{},
|
|
|
|
|
response,
|
|
|
|
|
{
|
2017-01-14 01:01:48 +13:00
|
|
|
type: 'playlist',
|
2017-02-03 09:04:47 +13:00
|
|
|
tracks: ( response.tracks ? response.tracks : [] ),
|
|
|
|
|
tracks_total: ( response.tracks ? response.tracks.length : [] )
|
2016-11-09 21:13:40 +13:00
|
|
|
}
|
|
|
|
|
)
|
2016-11-01 11:49:04 +13:00
|
|
|
|
2016-11-16 13:13:35 +13:00
|
|
|
// tracks? get the full track objects
|
2017-01-19 11:47:15 +13:00
|
|
|
if( playlist.tracks.length > 0 ) store.dispatch({
|
|
|
|
|
type: 'MOPIDY_RESOLVE_PLAYLIST_TRACKS',
|
|
|
|
|
tracks: playlist.tracks,
|
|
|
|
|
key: response.uri
|
|
|
|
|
})
|
2016-11-14 11:20:02 +13:00
|
|
|
|
2017-01-15 11:40:08 +13:00
|
|
|
store.dispatch({
|
|
|
|
|
type: 'PLAYLIST_LOADED',
|
2017-01-19 11:47:15 +13:00
|
|
|
key: playlist.uri,
|
2017-01-15 11:40:08 +13:00
|
|
|
playlist: playlist
|
|
|
|
|
})
|
2016-11-16 13:13:35 +13:00
|
|
|
})
|
|
|
|
|
break;
|
2016-11-01 11:49:04 +13:00
|
|
|
|
2017-01-13 13:17:47 +13:00
|
|
|
case 'MOPIDY_RESOLVE_PLAYLIST_TRACKS':
|
2016-11-16 13:13:35 +13:00
|
|
|
var tracks = Object.assign([], action.tracks)
|
2017-01-13 13:17:47 +13:00
|
|
|
var uris = helpers.asURIs(tracks)
|
2016-11-16 13:13:35 +13:00
|
|
|
|
|
|
|
|
instruct( socket, store, 'library.lookup', { uris: uris } )
|
|
|
|
|
.then( response => {
|
|
|
|
|
for(var uri in response){
|
|
|
|
|
if (response.hasOwnProperty(uri)) {
|
|
|
|
|
|
|
|
|
|
var track = response[uri][0]
|
|
|
|
|
if( track ){
|
|
|
|
|
|
|
|
|
|
// find the track reference, and drop in the full track data
|
|
|
|
|
function getByURI( trackReference ){
|
|
|
|
|
return track.uri == trackReference.uri
|
|
|
|
|
}
|
|
|
|
|
var trackReferences = tracks.filter(getByURI);
|
|
|
|
|
|
|
|
|
|
// there could be multiple instances of this track, so accommodate this
|
|
|
|
|
for( var j = 0; j < trackReferences.length; j++){
|
|
|
|
|
var key = tracks.indexOf( trackReferences[j] );
|
|
|
|
|
tracks[ key ] = track;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-11-14 11:20:02 +13:00
|
|
|
}
|
2016-11-16 13:13:35 +13:00
|
|
|
|
2017-01-15 11:40:08 +13:00
|
|
|
store.dispatch({
|
|
|
|
|
type: 'PLAYLIST_TRACKS',
|
|
|
|
|
tracks: tracks,
|
2017-01-19 11:47:15 +13:00
|
|
|
key: action.key
|
2017-01-15 11:40:08 +13:00
|
|
|
})
|
2016-11-01 11:49:04 +13:00
|
|
|
})
|
2016-11-16 13:13:35 +13:00
|
|
|
break
|
2016-11-01 11:49:04 +13:00
|
|
|
|
2016-11-13 14:41:08 +13:00
|
|
|
case 'MOPIDY_ADD_PLAYLIST_TRACKS':
|
|
|
|
|
|
2017-01-19 13:19:11 +13:00
|
|
|
instruct( socket, store, 'playlists.lookup', { uri: action.key })
|
2016-11-13 14:41:08 +13:00
|
|
|
.then( response => {
|
|
|
|
|
var tracks = [];
|
|
|
|
|
for( var i = 0; i < action.tracks_uris.length; i++ ){
|
|
|
|
|
tracks.push({
|
|
|
|
|
__model__: "Track",
|
|
|
|
|
uri: action.tracks_uris[i]
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var playlist = Object.assign({}, response)
|
2016-11-14 17:32:36 +13:00
|
|
|
if( playlist.tracks ){
|
|
|
|
|
playlist.tracks = [...playlist.tracks, ...tracks]
|
|
|
|
|
}else{
|
|
|
|
|
playlist.tracks = tracks
|
|
|
|
|
}
|
2016-11-13 14:41:08 +13:00
|
|
|
|
|
|
|
|
instruct( socket, store, 'playlists.save', { playlist: playlist } )
|
|
|
|
|
.then( response => {
|
2017-01-19 11:47:15 +13:00
|
|
|
store.dispatch({
|
|
|
|
|
type: 'PLAYLIST_TRACKS_ADDED',
|
2017-01-19 13:19:11 +13:00
|
|
|
key: action.key,
|
2017-01-19 11:47:15 +13:00
|
|
|
tracks_uris: action.tracks_uris
|
|
|
|
|
});
|
2016-11-13 14:41:08 +13:00
|
|
|
})
|
|
|
|
|
});
|
|
|
|
|
break
|
|
|
|
|
|
2016-11-13 12:55:00 +13:00
|
|
|
case 'MOPIDY_REMOVE_PLAYLIST_TRACKS':
|
|
|
|
|
|
|
|
|
|
// reverse order our indexes (otherwise removing from top will affect the keys following)
|
|
|
|
|
function descending(a,b){
|
|
|
|
|
return b-a;
|
|
|
|
|
}
|
|
|
|
|
var indexes = Object.assign([], action.tracks_indexes)
|
|
|
|
|
indexes.sort(descending);
|
|
|
|
|
|
2017-01-19 13:19:11 +13:00
|
|
|
instruct( socket, store, 'playlists.lookup', { uri: action.key })
|
2016-11-13 12:55:00 +13:00
|
|
|
.then( response => {
|
|
|
|
|
var playlist = Object.assign({}, response)
|
|
|
|
|
for( var i = 0; i < indexes.length; i++ ){
|
|
|
|
|
playlist.tracks.splice(indexes[i], 1);
|
|
|
|
|
}
|
|
|
|
|
instruct( socket, store, 'playlists.save', { playlist: playlist } )
|
|
|
|
|
.then( response => {
|
2017-01-19 11:47:15 +13:00
|
|
|
store.dispatch({
|
|
|
|
|
type: 'PLAYLIST_TRACKS_REMOVED',
|
2017-01-19 13:19:11 +13:00
|
|
|
key: action.key,
|
2017-01-19 11:47:15 +13:00
|
|
|
tracks_indexes: action.tracks_indexes
|
|
|
|
|
});
|
2016-11-13 12:55:00 +13:00
|
|
|
})
|
|
|
|
|
});
|
|
|
|
|
break
|
|
|
|
|
|
2016-11-14 17:32:36 +13:00
|
|
|
case 'MOPIDY_SAVE_PLAYLIST':
|
2017-01-19 13:19:11 +13:00
|
|
|
instruct( socket, store, 'playlists.lookup', { uri: action.key })
|
2016-11-14 17:32:36 +13:00
|
|
|
.then( response => {
|
|
|
|
|
var playlist = Object.assign({}, response, { name: action.name })
|
|
|
|
|
instruct( socket, store, 'playlists.save', { playlist: playlist } )
|
|
|
|
|
.then( response => {
|
2017-01-15 11:40:08 +13:00
|
|
|
store.dispatch({
|
|
|
|
|
type: 'PLAYLIST_UPDATED',
|
2017-01-19 13:19:11 +13:00
|
|
|
key: action.key,
|
2017-01-15 11:40:08 +13:00
|
|
|
playlist: playlist
|
|
|
|
|
})
|
2016-11-14 17:32:36 +13:00
|
|
|
})
|
|
|
|
|
});
|
|
|
|
|
break
|
|
|
|
|
|
2016-11-16 13:13:35 +13:00
|
|
|
case 'MOPIDY_REORDER_PLAYLIST_TRACKS':
|
2017-01-19 13:19:11 +13:00
|
|
|
instruct( socket, store, 'playlists.lookup', { uri: action.key })
|
2016-11-16 13:13:35 +13:00
|
|
|
.then( response => {
|
|
|
|
|
|
|
|
|
|
var playlist = Object.assign({}, response)
|
|
|
|
|
var tracks = Object.assign([], playlist.tracks)
|
|
|
|
|
var tracks_to_move = []
|
|
|
|
|
|
|
|
|
|
// calculate destination index: if dragging down, accommodate the offset created by the tracks we're moving
|
2016-11-16 16:30:49 +13:00
|
|
|
var range_start = action.range_start
|
|
|
|
|
var range_length = action.range_length
|
|
|
|
|
var insert_before = action.insert_before
|
|
|
|
|
if( insert_before > range_start ) insert_before = insert_before - range_length
|
2016-11-16 13:13:35 +13:00
|
|
|
|
|
|
|
|
// collate our tracks to be moved
|
2016-11-16 16:30:49 +13:00
|
|
|
for( var i = 0; i < range_length; i++ ){
|
2016-11-16 13:13:35 +13:00
|
|
|
|
|
|
|
|
// add to FRONT: we work backwards to avoid screwing up our indexes
|
2016-11-16 16:30:49 +13:00
|
|
|
tracks_to_move.unshift( tracks[range_start + i] )
|
2016-11-16 13:13:35 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// remove tracks from their old location
|
|
|
|
|
tracks.splice( range_start, range_length )
|
|
|
|
|
|
|
|
|
|
// now plug them back in, in their new location
|
|
|
|
|
for( var i = 0; i < tracks_to_move.length; i++ ){
|
2016-11-16 16:30:49 +13:00
|
|
|
tracks.splice( insert_before, 0, tracks_to_move[i] )
|
2016-11-16 13:13:35 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// update playlist
|
|
|
|
|
playlist = Object.assign({}, playlist, { tracks: tracks })
|
|
|
|
|
instruct( socket, store, 'playlists.save', { playlist: playlist } )
|
|
|
|
|
.then( response => {
|
|
|
|
|
|
2017-01-19 11:47:15 +13:00
|
|
|
store.dispatch({
|
|
|
|
|
type: 'MOPIDY_RESOLVE_PLAYLIST_TRACKS',
|
|
|
|
|
tracks: playlist.tracks,
|
|
|
|
|
key: playlist.uri
|
|
|
|
|
})
|
2016-11-16 13:13:35 +13:00
|
|
|
})
|
|
|
|
|
});
|
|
|
|
|
break
|
|
|
|
|
|
2017-01-13 13:17:47 +13:00
|
|
|
case 'MOPIDY_CREATE_PLAYLIST':
|
|
|
|
|
instruct( socket, store, 'playlists.create', { name: action.name, uri_scheme: action.scheme })
|
|
|
|
|
.then( response => {
|
|
|
|
|
|
|
|
|
|
// re-load our global playlists
|
|
|
|
|
//store.dispatch({ type: 'MOPIDY_GET_PLAYLISTS' });
|
|
|
|
|
});
|
|
|
|
|
break
|
|
|
|
|
|
2016-11-14 11:20:02 +13:00
|
|
|
case 'MOPIDY_DELETE_PLAYLIST':
|
2017-01-19 13:19:11 +13:00
|
|
|
instruct( socket, store, 'playlists.delete', { uri: action.key })
|
2016-11-14 11:20:02 +13:00
|
|
|
.then( response => {
|
|
|
|
|
|
|
|
|
|
// re-load our global playlists
|
2017-01-13 13:17:47 +13:00
|
|
|
// store.dispatch({ type: 'MOPIDY_PLAYLISTS' });
|
2016-11-14 11:20:02 +13:00
|
|
|
});
|
|
|
|
|
break
|
2017-01-13 13:17:47 +13:00
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* =============================================================== ALBUM(S) =============
|
|
|
|
|
* ======================================================================================
|
|
|
|
|
**/
|
|
|
|
|
|
2017-01-26 09:15:54 +13:00
|
|
|
case 'MOPIDY_GET_LIBRARY_ALBUMS':
|
2017-01-13 13:17:47 +13:00
|
|
|
instruct( socket, store, 'library.browse', { uri: 'local:directory?type=album' } )
|
2017-01-15 21:06:24 +13:00
|
|
|
.then( response => {
|
2017-01-25 08:55:18 +13:00
|
|
|
|
2017-01-26 08:51:35 +13:00
|
|
|
var uris = helpers.asURIs(response)
|
|
|
|
|
|
2017-01-15 21:06:24 +13:00
|
|
|
store.dispatch({
|
2017-01-26 08:51:35 +13:00
|
|
|
type: 'MOPIDY_GET_ALBUMS',
|
|
|
|
|
uris: uris.slice(0,50)
|
2017-01-15 21:06:24 +13:00
|
|
|
});
|
2017-01-25 08:55:18 +13:00
|
|
|
|
2017-01-15 21:06:24 +13:00
|
|
|
store.dispatch({
|
|
|
|
|
type: 'LOCAL_ALBUMS_LOADED',
|
2017-01-26 08:51:35 +13:00
|
|
|
uris: uris
|
2017-01-15 21:06:24 +13:00
|
|
|
});
|
2017-01-26 08:51:35 +13:00
|
|
|
})
|
|
|
|
|
break;
|
2017-01-25 08:55:18 +13:00
|
|
|
|
2017-01-26 08:51:35 +13:00
|
|
|
case 'MOPIDY_GET_ALBUMS':
|
|
|
|
|
instruct( socket, store, 'library.lookup', { uris: action.uris } )
|
|
|
|
|
.then( response => {
|
2017-01-25 08:55:18 +13:00
|
|
|
|
2017-01-26 08:51:35 +13:00
|
|
|
var albums = []
|
2017-01-25 08:55:18 +13:00
|
|
|
|
2017-01-26 08:51:35 +13:00
|
|
|
for (var uri in response){
|
|
|
|
|
if (response.hasOwnProperty(uri) && response[uri].length > 0 && response[uri][0] && response[uri][0].album ){
|
|
|
|
|
var album = Object.assign(
|
|
|
|
|
{},
|
|
|
|
|
{
|
|
|
|
|
artists: response[uri][0].artists,
|
|
|
|
|
tracks: response[uri],
|
|
|
|
|
tracks_total: response[uri].length
|
|
|
|
|
},
|
|
|
|
|
response[uri][0].album
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
albums.push(album)
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-01-25 08:55:18 +13:00
|
|
|
|
2017-01-26 08:51:35 +13:00
|
|
|
store.dispatch({
|
|
|
|
|
type: 'ALBUMS_LOADED',
|
|
|
|
|
albums: albums
|
|
|
|
|
});
|
2017-01-13 13:17:47 +13:00
|
|
|
})
|
|
|
|
|
break;
|
2016-11-14 11:20:02 +13:00
|
|
|
|
2017-01-15 21:06:24 +13:00
|
|
|
case 'MOPIDY_GET_ALBUM':
|
2016-11-01 13:19:51 +13:00
|
|
|
instruct( socket, store, 'library.lookup', action.data )
|
|
|
|
|
.then( response => {
|
2016-11-09 20:38:08 +13:00
|
|
|
var album = Object.assign(
|
|
|
|
|
{},
|
|
|
|
|
{ images: [] },
|
|
|
|
|
response[0].album,
|
|
|
|
|
{
|
|
|
|
|
artists: response[0].artists,
|
|
|
|
|
tracks: response,
|
|
|
|
|
tracks_total: response.length
|
|
|
|
|
}
|
|
|
|
|
)
|
2016-11-01 13:19:51 +13:00
|
|
|
|
|
|
|
|
var uris = [];
|
2016-11-09 20:38:08 +13:00
|
|
|
for( var i = 0; i < album.tracks.length; i++ ){
|
|
|
|
|
uris.push( album.tracks[i].uri );
|
2016-11-01 13:19:51 +13:00
|
|
|
}
|
|
|
|
|
|
2016-11-07 21:21:15 +13:00
|
|
|
// load artwork from LastFM
|
|
|
|
|
if( album.images.length <= 0 ){
|
|
|
|
|
|
|
|
|
|
var mbid = helpers.getFromUri('mbid',album.uri)
|
|
|
|
|
if( mbid ){
|
|
|
|
|
store.dispatch( lastfmActions.getAlbum( false, false, mbid ) )
|
|
|
|
|
}else{
|
|
|
|
|
store.dispatch( lastfmActions.getAlbum( album.artists[0].name, album.name ) )
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-01 13:19:51 +13:00
|
|
|
instruct( socket, store, 'library.lookup', { uris: uris } )
|
|
|
|
|
.then( response => {
|
|
|
|
|
|
|
|
|
|
for(var uri in response){
|
|
|
|
|
if (response.hasOwnProperty(uri)) {
|
|
|
|
|
|
|
|
|
|
var track = response[uri][0];
|
|
|
|
|
|
|
|
|
|
// find the track reference, and drop in the full track data
|
|
|
|
|
function getByURI( trackReference ){
|
|
|
|
|
return track.uri == trackReference.uri
|
|
|
|
|
}
|
2016-11-09 20:38:08 +13:00
|
|
|
var trackReferences = album.tracks.filter(getByURI);
|
2016-11-01 13:19:51 +13:00
|
|
|
|
|
|
|
|
// there could be multiple instances of this track, so accommodate this
|
|
|
|
|
for( var j = 0; j < trackReferences.length; j++){
|
2016-11-09 20:38:08 +13:00
|
|
|
var key = album.tracks.indexOf( trackReferences[j] );
|
|
|
|
|
album.tracks[ key ] = track;
|
2016-11-01 13:19:51 +13:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-15 21:06:24 +13:00
|
|
|
store.dispatch({
|
|
|
|
|
type: 'ALBUM_LOADED',
|
2017-01-19 11:47:15 +13:00
|
|
|
key: album.uri,
|
2017-01-15 21:06:24 +13:00
|
|
|
album: album
|
|
|
|
|
});
|
2016-11-01 13:19:51 +13:00
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
break;
|
2017-01-13 13:17:47 +13:00
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* =============================================================== ARTIST(S) ============
|
|
|
|
|
* ======================================================================================
|
|
|
|
|
**/
|
|
|
|
|
|
2017-01-26 09:15:54 +13:00
|
|
|
case 'MOPIDY_GET_LIBRARY_ARTISTS':
|
2017-01-15 21:06:24 +13:00
|
|
|
store.dispatch({ type: 'LOCAL_ARTISTS_LOADED', data: false });
|
2017-01-13 13:17:47 +13:00
|
|
|
instruct( socket, store, 'library.browse', { uri: 'local:directory?type=artist' } )
|
|
|
|
|
.then( response => {
|
2017-01-15 21:06:24 +13:00
|
|
|
store.dispatch({
|
|
|
|
|
type: 'ARTISTS_LOADED',
|
|
|
|
|
artists: response
|
|
|
|
|
});
|
|
|
|
|
store.dispatch({
|
|
|
|
|
type: 'LOCAL_ARTISTS_LOADED',
|
|
|
|
|
uris: helpers.asURIs(response)
|
|
|
|
|
});
|
2017-01-13 13:17:47 +13:00
|
|
|
})
|
|
|
|
|
break;
|
2016-11-01 13:19:51 +13:00
|
|
|
|
2017-01-15 21:06:24 +13:00
|
|
|
case 'MOPIDY_GET_ARTIST':
|
2016-11-01 16:41:04 +13:00
|
|
|
instruct( socket, store, 'library.lookup', action.data )
|
2017-01-16 08:11:32 +13:00
|
|
|
.then( response => {
|
|
|
|
|
|
2017-01-15 21:06:24 +13:00
|
|
|
var albums = []
|
2016-11-01 16:41:04 +13:00
|
|
|
for( var i = 0; i < response.length; i++ ){
|
|
|
|
|
var album = response[i].album;
|
2017-01-16 08:11:32 +13:00
|
|
|
if (album){
|
|
|
|
|
function getByURI( albumToCheck ){
|
|
|
|
|
return album.uri == albumToCheck.uri
|
|
|
|
|
}
|
|
|
|
|
var existingAlbum = albums.find(getByURI);
|
|
|
|
|
if( !existingAlbum ){
|
|
|
|
|
albums.push(album)
|
2017-01-26 08:51:35 +13:00
|
|
|
}
|
2016-11-01 16:41:04 +13:00
|
|
|
}
|
|
|
|
|
}
|
2017-01-16 08:11:32 +13:00
|
|
|
if (albums){
|
|
|
|
|
store.dispatch({
|
|
|
|
|
type: 'ALBUMS_LOADED',
|
|
|
|
|
albums: albums
|
|
|
|
|
})
|
|
|
|
|
}
|
2017-01-15 21:06:24 +13:00
|
|
|
|
|
|
|
|
var artist = Object.assign(
|
|
|
|
|
{},
|
2017-02-23 22:06:43 +13:00
|
|
|
(response ? response[0].artists[0] : {}),
|
2017-01-15 21:06:24 +13:00
|
|
|
{
|
|
|
|
|
albums_uris: helpers.asURIs(albums),
|
|
|
|
|
tracks: response.slice(0,10)
|
|
|
|
|
}
|
|
|
|
|
)
|
2017-01-16 08:11:32 +13:00
|
|
|
|
|
|
|
|
store.dispatch({
|
|
|
|
|
type: 'ARTIST_LOADED',
|
2017-01-19 11:47:15 +13:00
|
|
|
key: artist.uri,
|
2017-01-16 08:11:32 +13:00
|
|
|
artist: artist
|
|
|
|
|
});
|
2016-11-07 21:21:15 +13:00
|
|
|
|
|
|
|
|
// load artwork from LastFM
|
2017-01-16 08:11:32 +13:00
|
|
|
if( !artist.images || artist.images.length <= 0 ){
|
2016-11-07 21:21:15 +13:00
|
|
|
if( artist.musicbrainz_id ){
|
2017-01-15 21:06:24 +13:00
|
|
|
store.dispatch( lastfmActions.getArtist( artist.uri, false, artist.musicbrainz_id ) )
|
2016-11-07 21:21:15 +13:00
|
|
|
}else{
|
2017-01-15 21:06:24 +13:00
|
|
|
store.dispatch( lastfmActions.getArtist( artist.uri, artist.name.replace('&','and') ) )
|
2016-11-07 21:21:15 +13:00
|
|
|
}
|
|
|
|
|
}
|
2016-11-01 16:41:04 +13:00
|
|
|
})
|
|
|
|
|
break;
|
2017-01-13 13:17:47 +13:00
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* =============================================================== LOCAL ================
|
|
|
|
|
* ======================================================================================
|
|
|
|
|
**/
|
2016-11-01 16:41:04 +13:00
|
|
|
|
2017-01-17 12:03:15 +13:00
|
|
|
case 'MOPIDY_GET_DIRECTORY':
|
2016-11-02 16:51:00 +13:00
|
|
|
store.dispatch({ type: 'MOPIDY_DIRECTORY_LOADED', data: false });
|
|
|
|
|
instruct( socket, store, 'library.browse', action.data )
|
|
|
|
|
.then( response => {
|
2017-01-19 11:47:15 +13:00
|
|
|
store.dispatch({
|
|
|
|
|
type: 'MOPIDY_DIRECTORY_LOADED',
|
|
|
|
|
data: response
|
|
|
|
|
});
|
2016-11-02 16:51:00 +13:00
|
|
|
})
|
|
|
|
|
break;
|
|
|
|
|
|
2016-10-16 12:37:37 +13:00
|
|
|
// This action is irrelevant to us, pass it on to the next middleware
|
2016-10-14 22:42:45 +13:00
|
|
|
default:
|
|
|
|
|
return next(action);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
})();
|
|
|
|
|
|
|
|
|
|
export default MopidyMiddleware
|