Moving tracks into items (need to consider performance knock-ons)
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@ -106,7 +106,7 @@
|
|||||||
|
|
||||||
// Release details
|
// Release details
|
||||||
// These are automatically injected to built HTML
|
// These are automatically injected to built HTML
|
||||||
var build = "1599289916";
|
var build = "1599362472";
|
||||||
var version = "3.52.4";
|
var version = "3.52.4";
|
||||||
|
|
||||||
// Construct the script tag
|
// Construct the script tag
|
||||||
|
|||||||
@ -333,22 +333,30 @@ class PlaybackControls extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = (state) => ({
|
const mapStateToProps = (state) => {
|
||||||
snapcast_enabled: state.pusher.config.snapcast_enabled,
|
const {
|
||||||
current_track: (state.core.current_track && state.core.tracks[state.core.current_track.uri] !== undefined ? state.core.tracks[state.core.current_track.uri] : null),
|
items,
|
||||||
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),
|
next_track_uri,
|
||||||
radio_enabled: (!!(state.ui.radio && state.ui.radio.enabled)),
|
current_track,
|
||||||
play_state: state.mopidy.play_state,
|
} = state.core;
|
||||||
time_position: state.mopidy.time_position,
|
|
||||||
consume: state.mopidy.consume,
|
return {
|
||||||
repeat: state.mopidy.repeat,
|
snapcast_enabled: state.pusher.config.snapcast_enabled,
|
||||||
random: state.mopidy.random,
|
current_track: current_track ? items[current_track.uri] || current_track : null,
|
||||||
volume: state.mopidy.volume,
|
next_track: items[next_track_uri],
|
||||||
mute: state.mopidy.mute,
|
radio_enabled: (!!(state.ui.radio && state.ui.radio.enabled)),
|
||||||
sidebar_open: state.ui.sidebar_open,
|
play_state: state.mopidy.play_state,
|
||||||
slim_mode: state.ui.slim_mode,
|
time_position: state.mopidy.time_position,
|
||||||
touch_enabled: state.ui.playback_controls_touch_enabled,
|
consume: state.mopidy.consume,
|
||||||
});
|
repeat: state.mopidy.repeat,
|
||||||
|
random: state.mopidy.random,
|
||||||
|
volume: state.mopidy.volume,
|
||||||
|
mute: state.mopidy.mute,
|
||||||
|
sidebar_open: state.ui.sidebar_open,
|
||||||
|
slim_mode: state.ui.slim_mode,
|
||||||
|
touch_enabled: state.ui.playback_controls_touch_enabled,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
coreActions: bindActionCreators(coreActions, dispatch),
|
coreActions: bindActionCreators(coreActions, dispatch),
|
||||||
|
|||||||
@ -107,71 +107,74 @@ export function updateColdStore(items) {
|
|||||||
* relevant service to load the record - all from one neat package.
|
* relevant service to load the record - all from one neat package.
|
||||||
* */
|
* */
|
||||||
|
|
||||||
export function loadItems(uris, force_reload = false) {
|
export function loadItems(uris, forceReload = false) {
|
||||||
return {
|
return {
|
||||||
type: 'LOAD_ITEMS',
|
type: 'LOAD_ITEMS',
|
||||||
uris,
|
uris,
|
||||||
force_reload,
|
forceReload,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function loadItem(uri, force_reload = false) {
|
export function loadItem(uri, forceReload = false) {
|
||||||
return loadItems([uri], force_reload);
|
return loadItems([uri], forceReload);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function loadTrack(uri, force_reload = false) {
|
export function loadTrack(uri, forceReload = false) {
|
||||||
return {
|
return {
|
||||||
type: 'LOAD_TRACK',
|
type: 'LOAD_TRACK',
|
||||||
uri,
|
uri,
|
||||||
force_reload,
|
forceReload,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function loadAlbum(uri, force_reload = false) {
|
export function loadAlbum(uri, forceReload = false, callbackAction) {
|
||||||
return {
|
return {
|
||||||
type: 'LOAD_ALBUM',
|
type: 'LOAD_ALBUM',
|
||||||
uri,
|
uri,
|
||||||
force_reload,
|
forceReload,
|
||||||
|
callbackAction,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function loadArtist(uri, force_reload = false) {
|
export function loadArtist(uri, forceReload = false, callbackAction) {
|
||||||
return {
|
return {
|
||||||
type: 'LOAD_ARTIST',
|
type: 'LOAD_ARTIST',
|
||||||
uri,
|
uri,
|
||||||
force_reload,
|
forceReload,
|
||||||
|
callbackAction,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function loadPlaylist(uri, force_reload = false) {
|
export function loadPlaylist(uri, forceReload = false, callbackAction) {
|
||||||
return {
|
return {
|
||||||
type: 'LOAD_PLAYLIST',
|
type: 'LOAD_PLAYLIST',
|
||||||
uri,
|
uri,
|
||||||
force_reload,
|
forceReload,
|
||||||
|
callbackAction,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function loadUser(uri, force_reload = false) {
|
export function loadUser(uri, forceReload = false) {
|
||||||
return {
|
return {
|
||||||
type: 'LOAD_USER',
|
type: 'LOAD_USER',
|
||||||
uri,
|
uri,
|
||||||
force_reload,
|
forceReload,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function loadUserPlaylists(uri, force_reload = false) {
|
export function loadUserPlaylists(uri, forceReload = false) {
|
||||||
return {
|
return {
|
||||||
type: 'LOAD_USER_PLAYLISTS',
|
type: 'LOAD_USER_PLAYLISTS',
|
||||||
uri,
|
uri,
|
||||||
force_reload,
|
forceReload,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function loadLibrary(uri, force_reload = false) {
|
export function loadLibrary(uri, forceReload = false) {
|
||||||
return {
|
return {
|
||||||
type: 'LOAD_LIBRARY',
|
type: 'LOAD_LIBRARY',
|
||||||
uri,
|
uri,
|
||||||
force_reload,
|
forceReload,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -318,35 +318,50 @@ const CoreMiddleware = (function () {
|
|||||||
store.dispatch({
|
store.dispatch({
|
||||||
type: `LOAD_${uriType(uri).toUpperCase()}`,
|
type: `LOAD_${uriType(uri).toUpperCase()}`,
|
||||||
uri,
|
uri,
|
||||||
force_reload: action.force_reload,
|
forceReload: action.forceReload,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'LOAD_TRACK':
|
case 'LOAD_TRACK': {
|
||||||
if (
|
const fetchTrack = () => {
|
||||||
!action.force_reload
|
switch (uriSource(action.uri)) {
|
||||||
&& store.getState().core.tracks[action.uri]) {
|
case 'spotify':
|
||||||
console.info(`Loading "${action.uri}" from index`);
|
store.dispatch(spotifyActions.getTrack(action.uri));
|
||||||
|
|
||||||
|
if (spotify.me) {
|
||||||
|
store.dispatch(spotifyActions.following(action.uri));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
store.dispatch(mopidyActions.getTrack(action.uri));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (action.forceReload) {
|
||||||
|
console.info(`Force-reloading "${action.uri}"`);
|
||||||
|
fetchTrack();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (store.getState().core.items[action.uri]) {
|
||||||
|
console.info(`Using "${action.uri}" from index`);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (uriSource(action.uri)) {
|
localForage.getItem(action.uri).then((result) => {
|
||||||
case 'spotify':
|
if (result) {
|
||||||
store.dispatch(spotifyActions.getTrack(action.uri));
|
console.info(`Loading "${action.uri}" from database`);
|
||||||
|
store.dispatch(coreActions.restoreItemsFromColdStore([result]));
|
||||||
if (spotify.me) {
|
} else {
|
||||||
store.dispatch(spotifyActions.following(action.uri));
|
fetchTrack();
|
||||||
}
|
}
|
||||||
break;
|
});
|
||||||
|
|
||||||
default:
|
|
||||||
store.dispatch(mopidyActions.getTrack(action.uri));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
next(action);
|
next(action);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case 'LOAD_ALBUM':
|
case 'LOAD_ALBUM':
|
||||||
const fetchAlbum = () => {
|
const fetchAlbum = () => {
|
||||||
@ -365,7 +380,7 @@ const CoreMiddleware = (function () {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
if (action.force_reload) {
|
if (action.forceReload) {
|
||||||
console.info(`Force-reloading "${action.uri}"`);
|
console.info(`Force-reloading "${action.uri}"`);
|
||||||
fetchAlbum();
|
fetchAlbum();
|
||||||
break;
|
break;
|
||||||
@ -412,7 +427,7 @@ const CoreMiddleware = (function () {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (action.force_reload) {
|
if (action.forceReload) {
|
||||||
console.info(`Force-reloading "${action.uri}"`);
|
console.info(`Force-reloading "${action.uri}"`);
|
||||||
fetchArtist();
|
fetchArtist();
|
||||||
break;
|
break;
|
||||||
@ -460,7 +475,7 @@ const CoreMiddleware = (function () {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (action.force_reload) {
|
if (action.forceReload) {
|
||||||
console.info(`Force-reloading "${action.uri}"`);
|
console.info(`Force-reloading "${action.uri}"`);
|
||||||
fetchPlaylist();
|
fetchPlaylist();
|
||||||
break;
|
break;
|
||||||
@ -477,7 +492,6 @@ const CoreMiddleware = (function () {
|
|||||||
if (result) {
|
if (result) {
|
||||||
console.info(`Restoring "${action.uri}" from database`);
|
console.info(`Restoring "${action.uri}" from database`);
|
||||||
store.dispatch(coreActions.restoreItemsFromColdStore([result]));
|
store.dispatch(coreActions.restoreItemsFromColdStore([result]));
|
||||||
|
|
||||||
if (!result.tracks) {
|
if (!result.tracks) {
|
||||||
fetchPlaylist();
|
fetchPlaylist();
|
||||||
}
|
}
|
||||||
@ -491,7 +505,7 @@ const CoreMiddleware = (function () {
|
|||||||
|
|
||||||
case 'LOAD_USER':
|
case 'LOAD_USER':
|
||||||
if (
|
if (
|
||||||
!action.force_reload
|
!action.forceReload
|
||||||
&& store.getState().core.users[action.uri]
|
&& store.getState().core.users[action.uri]
|
||||||
&& store.getState().core.users[action.uri].playlists_uris) {
|
&& store.getState().core.users[action.uri].playlists_uris) {
|
||||||
console.info(`Loading "${action.uri}" from index`);
|
console.info(`Loading "${action.uri}" from index`);
|
||||||
@ -517,7 +531,7 @@ const CoreMiddleware = (function () {
|
|||||||
|
|
||||||
case 'LOAD_USER_PLAYLISTS':
|
case 'LOAD_USER_PLAYLISTS':
|
||||||
if (
|
if (
|
||||||
!action.force_reload
|
!action.forceReload
|
||||||
&& store.getState().core.users[action.uri]
|
&& store.getState().core.users[action.uri]
|
||||||
&& store.getState().core.users[action.uri].playlists_uris) {
|
&& store.getState().core.users[action.uri].playlists_uris) {
|
||||||
console.info(`Loading "${action.uri}" playlists from index`);
|
console.info(`Loading "${action.uri}" playlists from index`);
|
||||||
@ -558,7 +572,7 @@ const CoreMiddleware = (function () {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (action.force_reload) {
|
if (action.forceReload) {
|
||||||
console.info(`Force-reloading "${action.uri}"`);
|
console.info(`Force-reloading "${action.uri}"`);
|
||||||
fetchLibrary();
|
fetchLibrary();
|
||||||
break;
|
break;
|
||||||
@ -592,11 +606,15 @@ const CoreMiddleware = (function () {
|
|||||||
* These modify our asset indexes, which are used globally
|
* These modify our asset indexes, which are used globally
|
||||||
* */
|
* */
|
||||||
|
|
||||||
case 'CURRENT_TRACK_LOADED':
|
case 'CURRENT_TRACK_LOADED': {
|
||||||
store.dispatch(coreActions.trackLoaded(action.track));
|
const track = formatTrack(action.track);
|
||||||
action.track = formatTrack(action.track);
|
store.dispatch(coreActions.itemLoaded(track));
|
||||||
next(action);
|
next({
|
||||||
|
...action,
|
||||||
|
track,
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case 'QUEUE_LOADED':
|
case 'QUEUE_LOADED':
|
||||||
store.dispatch(coreActions.tracksLoaded(action.tracks));
|
store.dispatch(coreActions.tracksLoaded(action.tracks));
|
||||||
|
|||||||
@ -651,109 +651,65 @@ const MopidyMiddleware = (function () {
|
|||||||
* Advanced playback events
|
* Advanced playback events
|
||||||
* */
|
* */
|
||||||
|
|
||||||
case 'MOPIDY_PLAY_PLAYLIST':
|
case 'MOPIDY_PLAY_PLAYLIST': {
|
||||||
var playlist = store.getState().core.items[action.uri];
|
const playlist = store.getState().core.items[action.uri];
|
||||||
|
if (playlist && playlist.tracks) {
|
||||||
// We have the playlist loaded already, and we've got at least 1 track to start playing
|
store.dispatch(
|
||||||
if (playlist && playlist.tracks_uris && playlist.tracks_uris.length > 0) {
|
mopidyActions.playURIs(
|
||||||
// We've got all of the tracks, so just play those; no further action required
|
arrayOf('uri', playlist.tracks),
|
||||||
if (playlist.tracks_total == playlist.tracks_uris.length) {
|
action.uri,
|
||||||
store.dispatch(mopidyActions.playURIs(playlist.tracks_uris, action.uri, action.shuffle));
|
action.shuffle,
|
||||||
break;
|
),
|
||||||
}
|
);
|
||||||
|
|
||||||
// Spotify-provided playlists need to be handled by the Spotify service
|
|
||||||
if (playlist.provider == 'spotify') {
|
|
||||||
store.dispatch(spotifyActions.getAllPlaylistTracks(action.uri, action.shuffle, 'play'));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// It's a Spotify playlist that we haven't loaded yet, so Spotify HTTP API needs to go get it
|
|
||||||
} else if (uriSource(action.uri) == 'spotify' && store.getState().spotify.enabled) {
|
|
||||||
store.dispatch(spotifyActions.getAllPlaylistTracks(action.uri, action.shuffle, 'play'));
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
store.dispatch(
|
||||||
// Not in index, and Spotify HTTP not enabled, so just play it as-is
|
coreActions.loadPlaylist(
|
||||||
// Fetch the playlist tracks via backend and add each track by URI
|
action.uri,
|
||||||
request(store, 'playlists.lookup', { uri: action.uri })
|
false,
|
||||||
.then(
|
{
|
||||||
(response) => {
|
name: 'play',
|
||||||
if (!response || response.tracks === undefined || !response.tracks) {
|
shuffle: action.shuffle,
|
||||||
store.dispatch(uiActions.createNotification({ content: 'Failed to load playlist tracks', level: 'error' }));
|
|
||||||
} else {
|
|
||||||
let tracks_uris = arrayOf('uri', response.tracks);
|
|
||||||
if (action.shuffle) {
|
|
||||||
tracks_uris = shuffle(tracks_uris);
|
|
||||||
}
|
|
||||||
store.dispatch(mopidyActions.playURIs(tracks_uris, action.uri));
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
(error) => {
|
),
|
||||||
store.dispatch(coreActions.handleException(
|
);
|
||||||
`Mopidy: ${error.message ? error.message : 'Lookup failed'}`,
|
|
||||||
error,
|
|
||||||
));
|
|
||||||
},
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case 'MOPIDY_ENQUEUE_PLAYLIST':
|
case 'MOPIDY_ENQUEUE_PLAYLIST': {
|
||||||
|
const playlist = store.getState().core.items[action.uri];
|
||||||
var playlist = store.getState().core.items[action.uri];
|
if (playlist && playlist.tracks) {
|
||||||
|
store.dispatch(
|
||||||
// We have the playlist loaded already, and we've got at least 1 track to start playing
|
mopidyActions.enqueueURIs(
|
||||||
if (playlist && playlist.tracks_uris && playlist.tracks_uris.length > 0) {
|
arrayOf('uri', playlist.tracks),
|
||||||
// We've got all of the tracks, so just play those; no further action required
|
action.uri,
|
||||||
if (playlist.tracks_total == playlist.tracks_uris.length) {
|
action.shuffle,
|
||||||
let tracks_uris = Object.assign([], playlist.tracks_uris);
|
),
|
||||||
if (action.shuffle) {
|
);
|
||||||
tracks_uris = shuffle(tracks_uris);
|
|
||||||
}
|
|
||||||
store.dispatch(mopidyActions.enqueueURIs(tracks_uris, action.uri, action.play_next, action.at_position, action.offset));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Spotify-provided playlists need to be handled by the Spotify service
|
|
||||||
if (playlist.provider == 'spotify') {
|
|
||||||
store.dispatch(spotifyActions.getAllPlaylistTracks(action.uri, action.shuffle, 'enqueue', action.play_next));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// It's a Spotify playlist that we haven't loaded yet, so Spotify HTTP API needs to go get it
|
|
||||||
} else if (uriSource(action.uri) == 'spotify' && store.getState().spotify.enabled) {
|
|
||||||
store.dispatch(spotifyActions.getAllPlaylistTracks(action.uri, action.shuffle, 'enqueue', action.play_next));
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
store.dispatch(
|
||||||
// Not in index, and Spotify HTTP not enabled, so just play it as-is
|
coreActions.loadPlaylist(
|
||||||
// Fetch the playlist tracks via backend and add each track by URI
|
action.uri,
|
||||||
request(store, 'playlists.lookup', { uri: action.uri })
|
false,
|
||||||
.then(
|
{
|
||||||
(response) => {
|
name: 'enqueue',
|
||||||
if (response.tracks === undefined) {
|
shuffle: action.shuffle,
|
||||||
store.dispatch(uiActions.createNotification({ content: 'Failed to load playlist tracks', level: 'error' }));
|
play_next: action.play_next,
|
||||||
} else {
|
at_position: action.at_position,
|
||||||
let tracks_uris = arrayOf('uri', response.tracks);
|
offset: action.offset,
|
||||||
if (action.shuffle) {
|
|
||||||
tracks_uris = shuffle(tracks_uris);
|
|
||||||
}
|
|
||||||
store.dispatch(mopidyActions.enqueueURIs(tracks_uris, action.uri, action.play_next, action.at_position, action.offset));
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
(error) => {
|
),
|
||||||
store.dispatch(coreActions.handleException(
|
);
|
||||||
`Mopidy: ${error.message ? error.message : 'Lookup failed'}`,
|
|
||||||
error,
|
|
||||||
));
|
|
||||||
},
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case 'MOPIDY_ENQUEUE_URIS':
|
case 'MOPIDY_ENQUEUE_URIS': {
|
||||||
|
|
||||||
if (!action.uris || action.uris.length <= 0) {
|
if (!action.uris || action.uris.length <= 0) {
|
||||||
this.props.uiActions.createNotification({ content: 'No URIs to enqueue', level: 'warning' });
|
this.props.uiActions.createNotification({
|
||||||
|
content: 'No URIs to enqueue',
|
||||||
|
level: 'warning',
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -762,15 +718,18 @@ const MopidyMiddleware = (function () {
|
|||||||
{
|
{
|
||||||
notification: {
|
notification: {
|
||||||
content: `${store.getState().pusher.username} is adding ${action.uris.length} URIs to queue`,
|
content: `${store.getState().pusher.username} is adding ${action.uris.length} URIs to queue`,
|
||||||
icon: (store.getState().core.current_track ? getTrackIcon(store.getState().core.current_track, store.getState().core) : false),
|
icon: (
|
||||||
|
store.getState().core.current_track
|
||||||
|
? getTrackIcon(store.getState().core.current_track, store.getState().core)
|
||||||
|
: false
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
|
|
||||||
// split into batches
|
const uris = Object.assign([], action.uris);
|
||||||
var uris = Object.assign([], action.uris);
|
const batches = [];
|
||||||
var batches = [];
|
const batch_size = 5;
|
||||||
var batch_size = 5;
|
|
||||||
while (uris.length > 0) {
|
while (uris.length > 0) {
|
||||||
batches.push({
|
batches.push({
|
||||||
uris: uris.splice(0, batch_size),
|
uris: uris.splice(0, batch_size),
|
||||||
@ -781,11 +740,11 @@ const MopidyMiddleware = (function () {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// pass this modified action to the reducer (and other middleware)
|
next({
|
||||||
action.batches = batches;
|
...action,
|
||||||
next(action);
|
batches,
|
||||||
|
});
|
||||||
|
|
||||||
// start our processor
|
|
||||||
store.dispatch(uiActions.startProcess(
|
store.dispatch(uiActions.startProcess(
|
||||||
'MOPIDY_ENQUEUE_URIS_PROCESSOR',
|
'MOPIDY_ENQUEUE_URIS_PROCESSOR',
|
||||||
i18n('services.mopidy.adding_uris', { count: action.uris.length }),
|
i18n('services.mopidy.adding_uris', { count: action.uris.length }),
|
||||||
@ -796,17 +755,14 @@ const MopidyMiddleware = (function () {
|
|||||||
},
|
},
|
||||||
));
|
));
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case 'MOPIDY_ENQUEUE_URIS_PROCESSOR':
|
case 'MOPIDY_ENQUEUE_URIS_PROCESSOR': {
|
||||||
|
const last_run = store.getState().ui.processes.MOPIDY_ENQUEUE_URIS_PROCESSOR;
|
||||||
|
|
||||||
var last_run = store.getState().ui.processes.MOPIDY_ENQUEUE_URIS_PROCESSOR;
|
|
||||||
|
|
||||||
// Cancelling
|
|
||||||
if (last_run && last_run.status == 'cancelling') {
|
if (last_run && last_run.status == 'cancelling') {
|
||||||
store.dispatch(uiActions.processCancelled('MOPIDY_ENQUEUE_URIS_PROCESSOR'));
|
store.dispatch(uiActions.processCancelled('MOPIDY_ENQUEUE_URIS_PROCESSOR'));
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// make sure we have some uris in the queue
|
|
||||||
} if (action.data.batches && action.data.batches.length > 0) {
|
} if (action.data.batches && action.data.batches.length > 0) {
|
||||||
var batches = Object.assign([], action.data.batches);
|
var batches = Object.assign([], action.data.batches);
|
||||||
var batch = batches[0];
|
var batch = batches[0];
|
||||||
@ -829,9 +785,11 @@ const MopidyMiddleware = (function () {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
var { current_track } = store.getState().core;
|
const {
|
||||||
var { queue } = store.getState().core;
|
current_track,
|
||||||
var current_track_index = -1;
|
queue,
|
||||||
|
} = store.getState().core;
|
||||||
|
let current_track_index = -1;
|
||||||
|
|
||||||
if (current_track) {
|
if (current_track) {
|
||||||
for (var i = 0; i < queue.length; i++) {
|
for (var i = 0; i < queue.length; i++) {
|
||||||
@ -888,8 +846,8 @@ const MopidyMiddleware = (function () {
|
|||||||
));
|
));
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case 'MOPIDY_PLAY_URIS':
|
case 'MOPIDY_PLAY_URIS':
|
||||||
const { from_uri } = action;
|
const { from_uri } = action;
|
||||||
@ -2039,7 +1997,7 @@ const MopidyMiddleware = (function () {
|
|||||||
|
|
||||||
// Some other source, rely on Mopidy backends to do their work
|
// Some other source, rely on Mopidy backends to do their work
|
||||||
} else {
|
} else {
|
||||||
store.dispatch(mopidyActions.getImages('tracks', [track.uri]));
|
store.dispatch(mopidyActions.getImages([track.uri]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2060,17 +2018,7 @@ const MopidyMiddleware = (function () {
|
|||||||
uri: track.uri,
|
uri: track.uri,
|
||||||
});
|
});
|
||||||
|
|
||||||
// We don't have the track (including images) already in our index
|
store.dispatch(coreActions.loadItem(track.uri));
|
||||||
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 && 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]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2088,7 +2036,7 @@ const MopidyMiddleware = (function () {
|
|||||||
store.dispatch(coreActions.tracksLoaded(tracks));
|
store.dispatch(coreActions.tracksLoaded(tracks));
|
||||||
|
|
||||||
if (action.get_images) {
|
if (action.get_images) {
|
||||||
store.dispatch(mopidyActions.getImages('tracks', arrayOf('uri', tracks)));
|
store.dispatch(mopidyActions.getImages(arrayOf('uri', tracks)));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
|
|||||||
@ -946,7 +946,7 @@ export function getRecommendations(uris = [], limit = 20, tunabilities = null) {
|
|||||||
request(dispatch, getState, endpoint)
|
request(dispatch, getState, endpoint)
|
||||||
.then(
|
.then(
|
||||||
(response) => {
|
(response) => {
|
||||||
const tracks = Object.assign([], response.tracks);
|
const tracks = Object.assign([], formatTracks(response.tracks));
|
||||||
|
|
||||||
// We only get simple artist objects, so we need to
|
// We only get simple artist objects, so we need to
|
||||||
// get the full object. We'll add URIs to our recommendations
|
// get the full object. We'll add URIs to our recommendations
|
||||||
@ -983,11 +983,11 @@ export function getRecommendations(uris = [], limit = 20, tunabilities = null) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (albums.length > 0) {
|
if (albums.length > 0) {
|
||||||
dispatch(coreActions.albumsLoaded(albums));
|
dispatch(coreActions.itemsLoaded(albums));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tracks.length > 0) {
|
if (tracks.length > 0) {
|
||||||
dispatch(coreActions.tracksLoaded(tracks));
|
dispatch(coreActions.itemsLoaded(tracks));
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
@ -1104,7 +1104,6 @@ export function getArtistImages(artist) {
|
|||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
request(dispatch, getState, `search?q=${artist.name}&type=artist`)
|
request(dispatch, getState, `search?q=${artist.name}&type=artist`)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
console.log('images', response);
|
|
||||||
if (response.artists.items.length > 0) {
|
if (response.artists.items.length > 0) {
|
||||||
dispatch(coreActions.itemLoaded({
|
dispatch(coreActions.itemLoaded({
|
||||||
uri: artist.uri,
|
uri: artist.uri,
|
||||||
@ -1117,22 +1116,27 @@ export function getArtistImages(artist) {
|
|||||||
|
|
||||||
export function playArtistTopTracks(uri) {
|
export function playArtistTopTracks(uri) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
const { artists } = getState().core;
|
const {
|
||||||
|
items: {
|
||||||
|
[uri]: artist,
|
||||||
|
},
|
||||||
|
} = getState().core;
|
||||||
|
|
||||||
// Do we have this artist (and their tracks) in our index already?
|
// Do we have this artist (and their tracks) in our index already?
|
||||||
if (typeof (artists[uri]) !== 'undefined' && typeof (artists[uri].tracks) !== 'undefined') {
|
if (artist && artist.tracks) {
|
||||||
const uris = arrayOf('uri', artists[uri].tracks);
|
const uris = arrayOf('uri', artist.tracks);
|
||||||
dispatch(mopidyActions.playURIs(uris, uri));
|
dispatch(mopidyActions.playURIs(uris, uri));
|
||||||
|
|
||||||
// We need to load the artist's top tracks first
|
|
||||||
} else {
|
} else {
|
||||||
request(dispatch, getState, `artists/${getFromUri('artistid', uri)}/top-tracks?country=${getState().spotify.country}`)
|
request(
|
||||||
.then(
|
dispatch,
|
||||||
(response) => {
|
getState,
|
||||||
const uris = arrayOf('uri', response.tracks);
|
`artists/${getFromUri('artistid', uri)}/top-tracks?country=${getState().spotify.country}`,
|
||||||
dispatch(mopidyActions.playURIs(uris, uri));
|
).then(
|
||||||
},
|
(response) => {
|
||||||
);
|
const uris = arrayOf('uri', response.tracks);
|
||||||
|
dispatch(mopidyActions.playURIs(uris, uri));
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -1359,9 +1363,8 @@ export function savePlaylist(uri, name, description, is_public, is_collaborative
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPlaylist(uri) {
|
export function getPlaylist(uri, callbackAction = null) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
// get the main playlist object
|
|
||||||
request(dispatch, getState, `playlists/${getFromUri('playlistid', uri)}?market=${getState().spotify.country}`)
|
request(dispatch, getState, `playlists/${getFromUri('playlistid', uri)}?market=${getState().spotify.country}`)
|
||||||
.then(
|
.then(
|
||||||
(response) => {
|
(response) => {
|
||||||
@ -1393,6 +1396,29 @@ export function getPlaylist(uri) {
|
|||||||
uri,
|
uri,
|
||||||
tracks,
|
tracks,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
if (callbackAction) {
|
||||||
|
switch (callbackAction.name) {
|
||||||
|
case 'enqueue':
|
||||||
|
dispatch(mopidyActions.enqueueURIs(
|
||||||
|
arrayOf('uri', tracks),
|
||||||
|
uri,
|
||||||
|
callbackAction.play_next,
|
||||||
|
callbackAction.at_position,
|
||||||
|
callbackAction.offset,
|
||||||
|
));
|
||||||
|
break;
|
||||||
|
case 'play':
|
||||||
|
dispatch(mopidyActions.playURIs(
|
||||||
|
arrayOf('uri', tracks),
|
||||||
|
uri,
|
||||||
|
callbackAction.shuffle,
|
||||||
|
));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1498,8 +1524,26 @@ export function getLibraryTracksAndPlayProcessor(data) {
|
|||||||
* Recursively get .next until we have all tracks
|
* Recursively get .next until we have all tracks
|
||||||
* */
|
* */
|
||||||
|
|
||||||
export function getAllPlaylistTracks(uri, shuffle = false, callback_action = null, play_next = false, at_position = null, offset = 0) {
|
export function getAllPlaylistTracks(
|
||||||
|
uri,
|
||||||
|
shuffle = false,
|
||||||
|
callback_action = null,
|
||||||
|
play_next = false,
|
||||||
|
at_position = null,
|
||||||
|
offset = 0,
|
||||||
|
) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (data.callback_action == 'enqueue') {
|
||||||
|
dispatch(mopidyActions.enqueueURIs(uris, data.uri, data.play_next, data.at_position, data.offset));
|
||||||
|
} else {
|
||||||
|
dispatch(mopidyActions.playURIs(uris, data.uri));
|
||||||
|
}
|
||||||
|
|
||||||
dispatch(uiActions.startProcess(
|
dispatch(uiActions.startProcess(
|
||||||
'SPOTIFY_GET_ALL_PLAYLIST_TRACKS_PROCESSOR',
|
'SPOTIFY_GET_ALL_PLAYLIST_TRACKS_PROCESSOR',
|
||||||
'Loading playlist tracks',
|
'Loading playlist tracks',
|
||||||
@ -1690,7 +1734,6 @@ export function getLibraryPlaylists() {
|
|||||||
let libraryPlaylists = [];
|
let libraryPlaylists = [];
|
||||||
const fetchLibraryPlaylists = (endpoint) => request(dispatch, getState, endpoint)
|
const fetchLibraryPlaylists = (endpoint) => request(dispatch, getState, endpoint)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
console.log('spot', response);
|
|
||||||
libraryPlaylists = [...libraryPlaylists, ...formatPlaylists(response.items)];
|
libraryPlaylists = [...libraryPlaylists, ...formatPlaylists(response.items)];
|
||||||
if (response.next) {
|
if (response.next) {
|
||||||
fetchLibraryPlaylists(response.next);
|
fetchLibraryPlaylists(response.next);
|
||||||
|
|||||||
@ -172,6 +172,7 @@ const uiPersistConfig = {
|
|||||||
blacklist: [
|
blacklist: [
|
||||||
'load_queue',
|
'load_queue',
|
||||||
'notifications',
|
'notifications',
|
||||||
|
'processes',
|
||||||
'context_menu',
|
'context_menu',
|
||||||
'current_track_transition',
|
'current_track_transition',
|
||||||
'dragger',
|
'dragger',
|
||||||
|
|||||||
@ -12,9 +12,9 @@ const indexToArray = (index, keys) => {
|
|||||||
if (!index) return [];
|
if (!index) return [];
|
||||||
|
|
||||||
if (keys) {
|
if (keys) {
|
||||||
return keys.map((key) => index[key]);
|
return compact(keys.map((key) => index[key]));
|
||||||
}
|
}
|
||||||
return Object.keys(index).map((key) => index[key]);
|
return compact(Object.keys(index).map((key) => index[key]));
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -31,7 +31,7 @@ import {
|
|||||||
titleCase,
|
titleCase,
|
||||||
} from '../util/helpers';
|
} from '../util/helpers';
|
||||||
import { collate } from '../util/format';
|
import { collate } from '../util/format';
|
||||||
import { sortItems, applyFilter } from '../util/arrays';
|
import { sortItems, applyFilter, arrayOf } from '../util/arrays';
|
||||||
import { i18n, I18n } from '../locale';
|
import { i18n, I18n } from '../locale';
|
||||||
import Button from '../components/Button';
|
import Button from '../components/Button';
|
||||||
import { trackEvent } from '../components/Trackable';
|
import { trackEvent } from '../components/Trackable';
|
||||||
@ -102,12 +102,14 @@ class Artist extends React.Component {
|
|||||||
const {
|
const {
|
||||||
artist: {
|
artist: {
|
||||||
uri,
|
uri,
|
||||||
tracks_uris,
|
tracks,
|
||||||
albums_uris,
|
albums_uris,
|
||||||
},
|
},
|
||||||
mopidyActions: { playURIs },
|
mopidyActions: {
|
||||||
|
playURIs,
|
||||||
|
},
|
||||||
} = this.props;
|
} = this.props;
|
||||||
playURIs(tracks_uris || albums_uris, uri);
|
playURIs(arrayOf('uri', tracks) || albums_uris, uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
setWindowTitle = (artist = this.props.artist) => {
|
setWindowTitle = (artist = this.props.artist) => {
|
||||||
@ -532,8 +534,8 @@ const mapStateToProps = (state, ownProps) => {
|
|||||||
load_queue: state.ui.load_queue,
|
load_queue: state.ui.load_queue,
|
||||||
artist: (state.core.items[uri] || null),
|
artist: (state.core.items[uri] || null),
|
||||||
items: state.core.items,
|
items: state.core.items,
|
||||||
spotify_library_artists: state.core.items['spotify:library:artists'] || { items_uris: [] },
|
spotify_library_artists: state.core.libraries['spotify:library:artists'] || { items_uris: [] },
|
||||||
mopidy_library_artists: state.core.items['mopidy:library:artists'] || { items_uris: [] },
|
mopidy_library_artists: state.core.libraries['mopidy:library:artists'] || { items_uris: [] },
|
||||||
albums: (state.core.albums ? state.core.albums : []),
|
albums: (state.core.albums ? state.core.albums : []),
|
||||||
filter: (state.ui.artist_albums_filter ? state.ui.artist_albums_filter : null),
|
filter: (state.ui.artist_albums_filter ? state.ui.artist_albums_filter : null),
|
||||||
sort: (state.ui.artist_albums_sort ? state.ui.artist_albums_sort : null),
|
sort: (state.ui.artist_albums_sort ? state.ui.artist_albums_sort : null),
|
||||||
|
|||||||
@ -22,6 +22,7 @@ import {
|
|||||||
} from '../util/helpers';
|
} from '../util/helpers';
|
||||||
import { i18n, I18n } from '../locale';
|
import { i18n, I18n } from '../locale';
|
||||||
import Button from '../components/Button';
|
import Button from '../components/Button';
|
||||||
|
import { indexToArray } from '../util/arrays';
|
||||||
|
|
||||||
const Artwork = ({
|
const Artwork = ({
|
||||||
image,
|
image,
|
||||||
@ -79,28 +80,13 @@ class Queue extends React.Component {
|
|||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
coreActions: {
|
coreActions: {
|
||||||
loadAlbum,
|
loadItem,
|
||||||
loadArtist,
|
|
||||||
loadPlaylist,
|
|
||||||
},
|
},
|
||||||
added_from_uri,
|
added_from_uri,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
if (added_from_uri && added_from_uri !== prev_added_from_uri) {
|
if (added_from_uri && added_from_uri !== prev_added_from_uri) {
|
||||||
const item_type = uriType(added_from_uri);
|
loadItem(added_from_uri);
|
||||||
switch (item_type) {
|
|
||||||
case 'album':
|
|
||||||
loadAlbum(added_from_uri);
|
|
||||||
break;
|
|
||||||
case 'artist':
|
|
||||||
loadArtist(added_from_uri);
|
|
||||||
break;
|
|
||||||
case 'playlist':
|
|
||||||
loadPlaylist(added_from_uri);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,7 +106,12 @@ class Queue extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
removeTracks = (track_indexes) => {
|
removeTracks = (track_indexes) => {
|
||||||
const { queue_tracks, mopidyActions } = this.props;
|
const {
|
||||||
|
queue_tracks,
|
||||||
|
mopidyActions: {
|
||||||
|
removeTracks: doRemoveTracks,
|
||||||
|
},
|
||||||
|
} = this.props;
|
||||||
const tlids = [];
|
const tlids = [];
|
||||||
for (let i = 0; i < track_indexes.length; i++) {
|
for (let i = 0; i < track_indexes.length; i++) {
|
||||||
const track = queue_tracks[track_indexes[i]];
|
const track = queue_tracks[track_indexes[i]];
|
||||||
@ -130,7 +121,7 @@ class Queue extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (tlids.length > 0) {
|
if (tlids.length > 0) {
|
||||||
mopidyActions.removeTracks(tlids);
|
doRemoveTracks(tlids);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,61 +141,51 @@ class Queue extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderAddedFrom = () => {
|
renderAddedFrom = () => {
|
||||||
const { added_from_uri } = this.props;
|
const {
|
||||||
|
items,
|
||||||
|
added_from_uri,
|
||||||
|
} = this.props;
|
||||||
if (!added_from_uri) return null;
|
if (!added_from_uri) return null;
|
||||||
|
|
||||||
const uri_type = uriType(added_from_uri);
|
const uri_type = uriType(added_from_uri);
|
||||||
const items = [];
|
let addedFromItems = [];
|
||||||
|
|
||||||
// Radio nests it's seed URIs in an encoded URI format
|
// Radio nests it's seed URIs in an encoded URI format
|
||||||
switch (uri_type){
|
switch (uri_type){
|
||||||
case 'radio':
|
case 'radio':
|
||||||
const radio_seeds = getFromUri('seeds', added_from_uri);
|
addedFromItems = indexToArray(items, getFromUri('seeds', added_from_uri));
|
||||||
|
|
||||||
for (let seed of radio_seeds) {
|
|
||||||
let item_type = uriType(seed);
|
|
||||||
let item_library = this.props[`${item_type}s`];
|
|
||||||
if (item_library && item_library[seed]) {
|
|
||||||
items.push(item_library[seed]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'search':
|
case 'search':
|
||||||
items.push({
|
addedFromItems = [{
|
||||||
uri: added_from_uri,
|
uri: added_from_uri,
|
||||||
name: `"${getFromUri('searchterm', added_from_uri)}" search`,
|
name: `"${getFromUri('searchterm', added_from_uri)}" search`,
|
||||||
})
|
}];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
const item_library = this.props[`${uri_type}s`];
|
addedFromItems = indexToArray(items, [added_from_uri]);
|
||||||
if (item_library && item_library[added_from_uri]) {
|
|
||||||
items.push(item_library[added_from_uri]);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (items.length <= 0) return null;
|
if (!addedFromItems.length) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="current-track__added-from">
|
<div className="current-track__added-from">
|
||||||
{items[0].images && (
|
{addedFromItems[0].images && (
|
||||||
<URILink
|
<URILink
|
||||||
uri={items[0].uri}
|
uri={addedFromItems[0].uri}
|
||||||
className="current-track__added-from__thumbnail"
|
className="current-track__added-from__thumbnail"
|
||||||
>
|
>
|
||||||
<Thumbnail
|
<Thumbnail
|
||||||
images={items[0].images}
|
images={addedFromItems[0].images}
|
||||||
size="small"
|
size="small"
|
||||||
circle={uriType(items[0].uri) === 'artist'}
|
circle={uriType(addedFromItems[0].uri) === 'artist'}
|
||||||
type="artist"
|
type="artist"
|
||||||
/>
|
/>
|
||||||
</URILink>
|
</URILink>
|
||||||
)}
|
)}
|
||||||
<div className="current-track__added-from__text">
|
<div className="current-track__added-from__text">
|
||||||
{'Playing from '}
|
{'Playing from '}
|
||||||
<LinksSentence items={items} />
|
<LinksSentence items={addedFromItems} />
|
||||||
{uri_type === 'radio' && (
|
{uri_type === 'radio' && (
|
||||||
<span className="flag flag--blue">
|
<span className="flag flag--blue">
|
||||||
{i18n('now_playing.current_track.radio')}
|
{i18n('now_playing.current_track.radio')}
|
||||||
@ -221,6 +202,12 @@ class Queue extends React.Component {
|
|||||||
queue_tracks,
|
queue_tracks,
|
||||||
theme,
|
theme,
|
||||||
current_track_uri,
|
current_track_uri,
|
||||||
|
spotify_enabled,
|
||||||
|
uiActions,
|
||||||
|
mopidyActions: {
|
||||||
|
clearTracklist,
|
||||||
|
shuffleTracklist,
|
||||||
|
},
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const { limit } = this.state;
|
const { limit } = this.state;
|
||||||
const total_queue_tracks = queue_tracks.length;
|
const total_queue_tracks = queue_tracks.length;
|
||||||
@ -235,7 +222,7 @@ class Queue extends React.Component {
|
|||||||
|
|
||||||
const options = (
|
const options = (
|
||||||
<span>
|
<span>
|
||||||
{this.props.spotify_enabled && (
|
{spotify_enabled && (
|
||||||
<Button noHover discrete to="/queue/radio">
|
<Button noHover discrete to="/queue/radio">
|
||||||
<Icon name="radio" />
|
<Icon name="radio" />
|
||||||
<I18n path="now_playing.context_actions.radio" />
|
<I18n path="now_playing.context_actions.radio" />
|
||||||
@ -254,7 +241,7 @@ class Queue extends React.Component {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="view queue-view preserve-3d">
|
<div className="view queue-view preserve-3d">
|
||||||
<Header options={options} uiActions={this.props.uiActions}>
|
<Header options={options} uiActions={uiActions}>
|
||||||
<Icon name="play_arrow" type="material" />
|
<Icon name="play_arrow" type="material" />
|
||||||
<I18n path="now_playing.title" />
|
<I18n path="now_playing.title" />
|
||||||
</Header>
|
</Header>
|
||||||
@ -293,7 +280,7 @@ class Queue extends React.Component {
|
|||||||
<li><Dater type="total-time" data={queue_tracks} /></li>
|
<li><Dater type="total-time" data={queue_tracks} /></li>
|
||||||
{queue_tracks.length > 0 && (
|
{queue_tracks.length > 0 && (
|
||||||
<li>
|
<li>
|
||||||
<a onClick={this.props.mopidyActions.shuffleTracklist}>
|
<a onClick={shuffleTracklist}>
|
||||||
<Icon name="shuffle" />
|
<Icon name="shuffle" />
|
||||||
<I18n path="now_playing.current_track.shuffle" />
|
<I18n path="now_playing.current_track.shuffle" />
|
||||||
</a>
|
</a>
|
||||||
@ -301,7 +288,7 @@ class Queue extends React.Component {
|
|||||||
)}
|
)}
|
||||||
{queue_tracks.length > 0 && (
|
{queue_tracks.length > 0 && (
|
||||||
<li>
|
<li>
|
||||||
<a onClick={this.props.mopidyActions.clearTracklist}>
|
<a onClick={clearTracklist}>
|
||||||
<Icon name="delete_sweep" />
|
<Icon name="delete_sweep" />
|
||||||
<I18n path="now_playing.current_track.clear" />
|
<I18n path="now_playing.current_track.clear" />
|
||||||
</a>
|
</a>
|
||||||
@ -341,12 +328,17 @@ class Queue extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = (state, ownProps) => {
|
const mapStateToProps = (state) => {
|
||||||
let { current_track } = state.core;
|
let { current_track } = state.core;
|
||||||
|
const {
|
||||||
|
items,
|
||||||
|
queue,
|
||||||
|
queue_metadata,
|
||||||
|
} = state.core;
|
||||||
const queue_tracks = [];
|
const queue_tracks = [];
|
||||||
|
|
||||||
if (state.core.queue && state.core.tracks) {
|
if (queue && items) {
|
||||||
for (const queue_track of state.core.queue) {
|
for (const queue_track of queue) {
|
||||||
let track = {
|
let track = {
|
||||||
...queue_track,
|
...queue_track,
|
||||||
playing: current_track && current_track.tlid == queue_track.tlid,
|
playing: current_track && current_track.tlid == queue_track.tlid,
|
||||||
@ -355,18 +347,18 @@ const mapStateToProps = (state, ownProps) => {
|
|||||||
// If we have the track in our index, merge it in.
|
// If we have the track in our index, merge it in.
|
||||||
// We prioritise queue track over index track as queue has unique data, like which track
|
// We prioritise queue track over index track as queue has unique data, like which track
|
||||||
// is playing and tlids.
|
// is playing and tlids.
|
||||||
if (state.core.tracks.hasOwnProperty(track.uri)) {
|
if (items[track.uri]) {
|
||||||
track = {
|
track = {
|
||||||
...state.core.tracks[track.uri],
|
...items[track.uri],
|
||||||
...track,
|
...track,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now merge in our queue metadata
|
// Now merge in our queue metadata
|
||||||
if (state.core.queue_metadata[`tlid_${track.tlid}`] !== undefined) {
|
if (queue_metadata[`tlid_${track.tlid}`] !== undefined) {
|
||||||
track = {
|
track = {
|
||||||
...track,
|
...track,
|
||||||
...state.core.queue_metadata[`tlid_${track.tlid}`],
|
...queue_metadata[`tlid_${track.tlid}`],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -389,6 +381,7 @@ const mapStateToProps = (state, ownProps) => {
|
|||||||
albums: state.core.albums,
|
albums: state.core.albums,
|
||||||
playlists: state.core.playlists,
|
playlists: state.core.playlists,
|
||||||
tracks: state.core.tracks,
|
tracks: state.core.tracks,
|
||||||
|
items,
|
||||||
queue_tracks,
|
queue_tracks,
|
||||||
current_track_uri: state.core.current_track_uri,
|
current_track_uri: state.core.current_track_uri,
|
||||||
current_track,
|
current_track,
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { bindActionCreators } from 'redux';
|
import { bindActionCreators } from 'redux';
|
||||||
|
import { compact } from 'lodash';
|
||||||
import InputRange from 'react-input-range';
|
import InputRange from 'react-input-range';
|
||||||
import AlbumGrid from '../../components/AlbumGrid';
|
import AlbumGrid from '../../components/AlbumGrid';
|
||||||
import TrackList from '../../components/TrackList';
|
import TrackList from '../../components/TrackList';
|
||||||
@ -16,6 +16,7 @@ import Icon from '../../components/Icon';
|
|||||||
import * as uiActions from '../../services/ui/actions';
|
import * as uiActions from '../../services/ui/actions';
|
||||||
import * as mopidyActions from '../../services/mopidy/actions';
|
import * as mopidyActions from '../../services/mopidy/actions';
|
||||||
import * as spotifyActions from '../../services/spotify/actions';
|
import * as spotifyActions from '../../services/spotify/actions';
|
||||||
|
import * as coreActions from '../../services/core/actions';
|
||||||
import {
|
import {
|
||||||
isLoading,
|
isLoading,
|
||||||
getFromUri,
|
getFromUri,
|
||||||
@ -195,10 +196,10 @@ class Discover extends React.Component {
|
|||||||
uiActions: {
|
uiActions: {
|
||||||
showContextMenu,
|
showContextMenu,
|
||||||
},
|
},
|
||||||
tracks: tracksProp,
|
items: itemsProp,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const tracks = indexToArray(tracksProp, tracks_uris);
|
const tracks = indexToArray(itemsProp, tracks_uris);
|
||||||
|
|
||||||
showContextMenu({
|
showContextMenu({
|
||||||
e,
|
e,
|
||||||
@ -211,9 +212,8 @@ class Discover extends React.Component {
|
|||||||
|
|
||||||
handleURLSeeds = () => {
|
handleURLSeeds = () => {
|
||||||
const {
|
const {
|
||||||
spotifyActions: {
|
coreActions: {
|
||||||
getArtist,
|
loadItems,
|
||||||
getTrack,
|
|
||||||
},
|
},
|
||||||
match: {
|
match: {
|
||||||
params: {
|
params: {
|
||||||
@ -226,19 +226,7 @@ class Discover extends React.Component {
|
|||||||
// We'd need to do this if our URL has been encoded so the whole URL can become
|
// We'd need to do this if our URL has been encoded so the whole URL can become
|
||||||
// it's own URI (eg iris:discover:spotify_artist_1234) where we can't use ":"
|
// it's own URI (eg iris:discover:spotify_artist_1234) where we can't use ":"
|
||||||
const seeds = seedsProp.split('_').join(':').split(',');
|
const seeds = seedsProp.split('_').join(':').split(',');
|
||||||
|
loadItems(seeds);
|
||||||
for (let i = 0; i < seeds.length; i++) {
|
|
||||||
switch (uriType(seeds[i])) {
|
|
||||||
case 'artist':
|
|
||||||
getArtist(seeds[i]);
|
|
||||||
break;
|
|
||||||
case 'track':
|
|
||||||
getTrack(seeds[i]);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState(
|
this.setState(
|
||||||
{ seeds },
|
{ seeds },
|
||||||
@ -251,7 +239,7 @@ class Discover extends React.Component {
|
|||||||
seeds,
|
seeds,
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
var uri = 'iris:discover';
|
let uri = 'iris:discover';
|
||||||
if (seeds) {
|
if (seeds) {
|
||||||
uri += ':';
|
uri += ':';
|
||||||
for (var i = 0; i < seeds.length; i++) {
|
for (var i = 0; i < seeds.length; i++) {
|
||||||
@ -325,8 +313,7 @@ class Discover extends React.Component {
|
|||||||
|
|
||||||
renderSeeds = () => {
|
renderSeeds = () => {
|
||||||
const {
|
const {
|
||||||
tracks,
|
items,
|
||||||
artists,
|
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const {
|
const {
|
||||||
seeds,
|
seeds,
|
||||||
@ -338,34 +325,22 @@ class Discover extends React.Component {
|
|||||||
const uri = seeds[i];
|
const uri = seeds[i];
|
||||||
|
|
||||||
switch (uriType(uri)) {
|
switch (uriType(uri)) {
|
||||||
case 'track':
|
|
||||||
if (typeof (tracks[uri]) !== 'undefined') {
|
|
||||||
seeds_objects.push(tracks[uri]);
|
|
||||||
} else {
|
|
||||||
seeds_objects.push({
|
|
||||||
name: 'Loading...',
|
|
||||||
uri,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'artist':
|
|
||||||
if (typeof (artists[uri]) !== 'undefined') {
|
|
||||||
seeds_objects.push(artists[uri]);
|
|
||||||
} else {
|
|
||||||
seeds_objects.push({
|
|
||||||
name: 'Loading...',
|
|
||||||
uri,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'genre':
|
case 'genre':
|
||||||
var name = getFromUri('genreid', uri);
|
const name = getFromUri('genreid', uri);
|
||||||
seeds_objects.push({
|
seeds_objects.push({
|
||||||
name: (name.charAt(0).toUpperCase() + name.slice(1)).replace('-', ' '),
|
name: (name.charAt(0).toUpperCase() + name.slice(1)).replace('-', ' '),
|
||||||
uri,
|
uri,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
if (typeof (items[uri]) !== 'undefined') {
|
||||||
|
seeds_objects.push(items[uri]);
|
||||||
|
} else {
|
||||||
|
seeds_objects.push({
|
||||||
|
name: 'Loading...',
|
||||||
|
uri,
|
||||||
|
});
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -475,44 +450,16 @@ class Discover extends React.Component {
|
|||||||
albums_uris,
|
albums_uris,
|
||||||
artists_uris,
|
artists_uris,
|
||||||
} = {},
|
} = {},
|
||||||
tracks: tracksProp,
|
items: itemsProp,
|
||||||
artists: artistsProp,
|
|
||||||
albums: albumsProp,
|
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
if (!albums_uris === undefined || artists_uris === undefined) {
|
if (!albums_uris === undefined || artists_uris === undefined) {
|
||||||
return <div className="content-wrapper recommendations-results" />;
|
return <div className="content-wrapper recommendations-results" />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const tracks = [];
|
const tracks = compact(tracks_uris.map((uri) => itemsProp[uri]));
|
||||||
if (tracks_uris && tracksProp) {
|
const artists = compact(artists_uris.map((uri) => itemsProp[uri]));
|
||||||
for (var i = 0; i < tracks_uris.length; i++) {
|
const albums = compact(albums_uris.map((uri) => itemsProp[uri]));
|
||||||
var uri = tracks_uris[i];
|
|
||||||
if (tracksProp.hasOwnProperty(uri)) {
|
|
||||||
tracks.push(tracksProp[uri]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const artists = [];
|
|
||||||
if (artists_uris && artistsProp) {
|
|
||||||
for (var i = 0; i < artists_uris.length; i++) {
|
|
||||||
var uri = artists_uris[i];
|
|
||||||
if (artistsProp.hasOwnProperty(uri)) {
|
|
||||||
artists.push(artistsProp[uri]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const albums = [];
|
|
||||||
if (albums_uris && albumsProp) {
|
|
||||||
for (var i = 0; i < albums_uris.length; i++) {
|
|
||||||
var uri = albums_uris[i];
|
|
||||||
if (albumsProp.hasOwnProperty(uri)) {
|
|
||||||
albums.push(albumsProp[uri]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Complete records not yet in our index
|
// Complete records not yet in our index
|
||||||
if (tracks.length <= 0 && artists.length <= 0 && albums.length <= 0) {
|
if (tracks.length <= 0 && artists.length <= 0 && albums.length <= 0) {
|
||||||
@ -640,9 +587,8 @@ class Discover extends React.Component {
|
|||||||
|
|
||||||
const mapStateToProps = (state) => ({
|
const mapStateToProps = (state) => ({
|
||||||
theme: state.ui.theme,
|
theme: state.ui.theme,
|
||||||
albums: state.core.albums,
|
|
||||||
artists: state.core.artists,
|
|
||||||
tracks: state.core.tracks,
|
tracks: state.core.tracks,
|
||||||
|
items: state.core.items,
|
||||||
genres: (state.core.genres ? state.core.genres : []),
|
genres: (state.core.genres ? state.core.genres : []),
|
||||||
authorized: state.spotify.authorization,
|
authorized: state.spotify.authorization,
|
||||||
load_queue: state.ui.load_queue,
|
load_queue: state.ui.load_queue,
|
||||||
@ -654,6 +600,7 @@ const mapDispatchToProps = (dispatch) => ({
|
|||||||
uiActions: bindActionCreators(uiActions, dispatch),
|
uiActions: bindActionCreators(uiActions, dispatch),
|
||||||
mopidyActions: bindActionCreators(mopidyActions, dispatch),
|
mopidyActions: bindActionCreators(mopidyActions, dispatch),
|
||||||
spotifyActions: bindActionCreators(spotifyActions, dispatch),
|
spotifyActions: bindActionCreators(spotifyActions, dispatch),
|
||||||
|
coreActions: bindActionCreators(coreActions, dispatch),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(Discover);
|
export default connect(mapStateToProps, mapDispatchToProps)(Discover);
|
||||||
|
|||||||
Reference in New Issue
Block a user