POC for playlist queueing
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
@ -98,7 +98,7 @@
|
||||
|
||||
// Release details
|
||||
// These are automatically injected to built HTML
|
||||
var build = "1552850454";
|
||||
var build = "1552871534";
|
||||
var version = "3.33.0";
|
||||
|
||||
// Construct the script tag
|
||||
|
||||
@ -218,16 +218,12 @@ class ContextMenu extends React.Component{
|
||||
|
||||
playPlaylist(e){
|
||||
this.props.uiActions.hideContextMenu();
|
||||
|
||||
// THis is the ideal setup: one action to play whatever type
|
||||
// then the action detects playlist vs album vs artist vs track etc and
|
||||
// loads as is required... then enqueue works the same
|
||||
this.props.mopidyActions.playURIs(this.props.menu.uris[0], this.props.menu.uris[0]);
|
||||
this.props.mopidyActions.playPlaylist(this.props.menu.uris[0]);
|
||||
}
|
||||
|
||||
enqueuePlaylist(e){
|
||||
this.props.uiActions.hideContextMenu();
|
||||
this.props.mopidyActions.playPlaylist(this.props.menu.uris[0]);
|
||||
this.props.mopidyActions.enqueuePlaylist(this.props.menu.uris[0]);
|
||||
}
|
||||
|
||||
shufflePlayPlaylist(e){
|
||||
@ -240,9 +236,9 @@ class ContextMenu extends React.Component{
|
||||
this.props.spotifyActions.playArtistTopTracks(this.props.menu.uris[0]);
|
||||
}
|
||||
|
||||
addToQueue(e, next = false){
|
||||
addToQueue(e, play_next = false){
|
||||
this.props.uiActions.hideContextMenu();
|
||||
this.props.mopidyActions.enqueueURIs(this.props.menu.uris, this.props.menu.tracklist_uri, next);
|
||||
this.props.mopidyActions.enqueueURIs(this.props.menu.uris, this.props.menu.tracklist_uri, play_next);
|
||||
}
|
||||
|
||||
addTracksToPlaylist(e, playlist_uri){
|
||||
@ -516,6 +512,14 @@ class ContextMenu extends React.Component{
|
||||
</div>
|
||||
)
|
||||
|
||||
var add_playlist_to_queue = (
|
||||
<div className="context-menu__item">
|
||||
<a className="context-menu__item__link" onClick={e => this.enqueuePlaylist(e)}>
|
||||
<span className="context-menu__item__label">Add to queue</span>
|
||||
</a>
|
||||
</div>
|
||||
)
|
||||
|
||||
var add_to_playlist = (
|
||||
<div className="context-menu__item context-menu__item--has-submenu">
|
||||
<a className="context-menu__item__link" onClick={e => this.setSubmenu('add-to-playlist')}>
|
||||
@ -699,6 +703,7 @@ class ContextMenu extends React.Component{
|
||||
<div>
|
||||
{play_playlist}
|
||||
{shuffle_play_playlist}
|
||||
{add_playlist_to_queue}
|
||||
{this.canBeInLibrary() ? <div className="context-menu__divider" /> : null}
|
||||
{this.canBeInLibrary() ? toggle_in_library : null}
|
||||
<div className="context-menu__divider" />
|
||||
|
||||
@ -231,48 +231,6 @@ export function removeFromIndex(index_name, key, new_key = null){
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Playing of assetse
|
||||
**/
|
||||
|
||||
/**
|
||||
* Play one or many URIs
|
||||
*
|
||||
* @param uri = String (when playing just one URI, eg playlist)
|
||||
* @param uris = Array (when playing several URIs, eg tracks)
|
||||
* @param origin = String (URI from which these URIs came)
|
||||
* @param shuffle = Boolean (shuffle the order of URIs)
|
||||
**/
|
||||
export function playURIs(uri = null, uris = [], origin = null, shuffle = false){
|
||||
return {
|
||||
type: 'PLAY_URIS',
|
||||
uri: uri,
|
||||
uris: uris,
|
||||
origin: origin,
|
||||
shuffle: shuffle
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue one or many URIs
|
||||
*
|
||||
* @param uris = Array
|
||||
* @param position = Int (position in track_list in which to insert these URIs)
|
||||
* @param origin = String (URI from which these URIs came)
|
||||
* @param shuffle = Boolean (shuffle the order of URIs)
|
||||
**/
|
||||
export function enqueueURIs(uris = [], position = null, origin = null, shuffle = false){
|
||||
return {
|
||||
type: 'ENQUEUE_URIS',
|
||||
position: position,
|
||||
uris: uris,
|
||||
origin: origin,
|
||||
shuffle: shuffle
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Playlist manipulation
|
||||
**/
|
||||
|
||||
@ -176,21 +176,6 @@ const CoreMiddleware = (function(){
|
||||
|
||||
break
|
||||
|
||||
case 'PLAYLIST_TRACKS_ADDED':
|
||||
store.dispatch(uiActions.createNotification({type: 'info', content: 'Added '+action.tracks_uris.length+' tracks to playlist'}))
|
||||
switch(helpers.uriSource(action.key)){
|
||||
|
||||
case 'spotify':
|
||||
store.dispatch(spotifyActions.getPlaylist(action.key));
|
||||
break
|
||||
|
||||
case 'm3u':
|
||||
if (store.getState().mopidy.connected ) store.dispatch(mopidyActions.getPlaylist(action.key));
|
||||
break
|
||||
}
|
||||
next(action)
|
||||
break
|
||||
|
||||
// Get assets from all of our providers
|
||||
case 'GET_LIBRARY_PLAYLISTS':
|
||||
if (store.getState().spotify.connected){
|
||||
@ -245,6 +230,21 @@ const CoreMiddleware = (function(){
|
||||
next(action);
|
||||
break;
|
||||
|
||||
case 'PLAYLIST_TRACKS_ADDED':
|
||||
store.dispatch(uiActions.createNotification({type: 'info', content: 'Added '+action.tracks_uris.length+' tracks to playlist'}))
|
||||
switch(helpers.uriSource(action.key)){
|
||||
|
||||
case 'spotify':
|
||||
store.dispatch(spotifyActions.getPlaylist(action.key));
|
||||
break
|
||||
|
||||
case 'm3u':
|
||||
if (store.getState().mopidy.connected ) store.dispatch(mopidyActions.getPlaylist(action.key));
|
||||
break
|
||||
}
|
||||
next(action)
|
||||
break
|
||||
|
||||
case 'PLAYLIST_TRACKS_REORDERED':
|
||||
var playlists = Object.assign({}, core.playlists);
|
||||
var playlist = Object.assign({}, playlists[action.key]);
|
||||
|
||||
@ -255,7 +255,11 @@ export function changeTrack(tlid){
|
||||
}
|
||||
}
|
||||
|
||||
export function playURIs(uris, from_uri = null, shuffle = false){
|
||||
/**
|
||||
* Playing assets
|
||||
**/
|
||||
|
||||
export function playURIs(uris = [], from_uri = null, shuffle = false){
|
||||
return {
|
||||
type: 'MOPIDY_PLAY_URIS',
|
||||
uris: uris,
|
||||
@ -263,23 +267,6 @@ export function playURIs(uris, from_uri = null, shuffle = false){
|
||||
}
|
||||
}
|
||||
|
||||
export function enqueueURIs(uris, from_uri = null, next = false, at_position = null, offset = 0){
|
||||
return {
|
||||
type: 'MOPIDY_ENQUEUE_URIS',
|
||||
uris: uris,
|
||||
at_position: at_position,
|
||||
next: next,
|
||||
from_uri: from_uri,
|
||||
offset: offset
|
||||
}
|
||||
}
|
||||
|
||||
export function enqueueURIsBatchDone(){
|
||||
return {
|
||||
type: 'MOPIDY_ENQUEUE_URIS_BATCH_DONE'
|
||||
}
|
||||
}
|
||||
|
||||
export function playAlbum(uri){
|
||||
return {
|
||||
type: 'MOPIDY_PLAY_ALBUM',
|
||||
@ -287,6 +274,53 @@ export function playAlbum(uri){
|
||||
}
|
||||
}
|
||||
|
||||
export function playPlaylist(uri){
|
||||
return {
|
||||
type: 'MOPIDY_PLAY_PLAYLIST',
|
||||
uri: uri
|
||||
}
|
||||
}
|
||||
|
||||
export function enqueueURIs(uris = [], from_uri = null, play_next = false, at_position = null, offset = 0){
|
||||
return {
|
||||
type: 'MOPIDY_ENQUEUE_URIS',
|
||||
uris: uris,
|
||||
from_uri: from_uri,
|
||||
at_position: at_position,
|
||||
play_next: play_next,
|
||||
offset: offset
|
||||
}
|
||||
}
|
||||
|
||||
export function enqueueAlbum(uri, next = false, at_position = null){
|
||||
return {
|
||||
type: 'MOPIDY_ENQUEUE_ALBUM',
|
||||
uri: uri,
|
||||
next: next,
|
||||
at_position: at_position
|
||||
}
|
||||
}
|
||||
|
||||
export function enqueuePlaylist(uri, play_next = false, at_position = null){
|
||||
return {
|
||||
type: 'MOPIDY_ENQUEUE_PLAYLIST',
|
||||
uri: uri,
|
||||
play_next: play_next,
|
||||
at_position: at_position
|
||||
}
|
||||
}
|
||||
|
||||
export function enqueueURIsBatchDone(){
|
||||
return {
|
||||
type: 'MOPIDY_ENQUEUE_URIS_BATCH_DONE'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Playlist maniopulation
|
||||
**/
|
||||
|
||||
export function removeTracks(tlids){
|
||||
return {
|
||||
type: 'MOPIDY_REMOVE_TRACKS',
|
||||
|
||||
@ -597,31 +597,26 @@ const MopidyMiddleware = (function(){
|
||||
|
||||
case 'MOPIDY_PLAY_PLAYLIST':
|
||||
|
||||
// Clear tracklist (if set)
|
||||
if (store.getState().ui.clear_tracklist_on_play){
|
||||
store.dispatch(mopidyActions.clearTracklist())
|
||||
}
|
||||
var playlist = store.getState().core.playlists[action.uri];
|
||||
|
||||
// playlist already in index
|
||||
if (store.getState().core.playlists.hasOwnProperty(action.uri)){
|
||||
// We have the playlist loaded already, and we've got at least 1 track to start playing
|
||||
if (playlist && playlist.tracks_uris && playlist.tracks_uris.length > 0){
|
||||
|
||||
// Spotify-provied playlists need to be handled by the Spotify service
|
||||
if (store.getState().core.playlists[action.uri].provider == 'spotify'){
|
||||
store.dispatch(spotifyActions.getPlaylistTracksAndPlay(action.uri, action.shuffle))
|
||||
break
|
||||
// We only need to load them if we haven't already got all the tracks
|
||||
if (playlist.provider == 'spotify' && playlist.tracks_total == playlist.tracks_uris.length){
|
||||
store.dispatch(spotifyActions.getAllPlaylistTracks(action.uri, action.shuffle, 'play'));
|
||||
break;
|
||||
}
|
||||
|
||||
// it's a spotify playlist that we haven't loaded
|
||||
// we need to fetch via HTTP API to avoid timeout
|
||||
// It's a Spotify playlist that we haven't loaded yet, so Spotify HTTP API needs to go get it
|
||||
} else if (helpers.uriSource(action.uri) == 'spotify' && store.getState().spotify.enabled){
|
||||
store.dispatch(spotifyActions.getPlaylistTracksAndPlay(action.uri, action.shuffle))
|
||||
store.dispatch(spotifyActions.getAllPlaylistTracks(action.uri, action.shuffle, 'play'));
|
||||
break
|
||||
|
||||
// Not in index, and Spotify HTTP not enabled, so just play it as-is
|
||||
}
|
||||
|
||||
// fetch the playlist tracks via backend
|
||||
// add each track by URI
|
||||
// Not in index, and Spotify HTTP not enabled, so just play it as-is
|
||||
// Fetch the playlist tracks via backend and add each track by URI
|
||||
request(socket, store, 'playlists.lookup', {uri: action.uri})
|
||||
.then(
|
||||
response => {
|
||||
@ -644,6 +639,51 @@ const MopidyMiddleware = (function(){
|
||||
);
|
||||
break;
|
||||
|
||||
case 'MOPIDY_ENQUEUE_PLAYLIST':
|
||||
console.log(action);
|
||||
|
||||
var playlist = store.getState().core.playlists[action.uri];
|
||||
|
||||
// We have the playlist loaded already, and we've got at least 1 track to start playing
|
||||
if (playlist && playlist.tracks_uris && playlist.tracks_uris.length > 0){
|
||||
|
||||
// Spotify-provied playlists need to be handled by the Spotify service
|
||||
// We only need to load them if we haven't already got all the tracks
|
||||
if (playlist.provider == 'spotify' && playlist.tracks_total == playlist.tracks_uris.length){
|
||||
store.dispatch(spotifyActions.getAllPlaylistTracks(action.uri, action.shuffle, 'enqueue'));
|
||||
break;
|
||||
}
|
||||
|
||||
// It's a Spotify playlist that we haven't loaded yet, so Spotify HTTP API needs to go get it
|
||||
} else if (helpers.uriSource(action.uri) == 'spotify' && store.getState().spotify.enabled){
|
||||
store.dispatch(spotifyActions.getAllPlaylistTracks(action.uri, action.shuffle, 'enqueue'));
|
||||
break
|
||||
}
|
||||
|
||||
// Not in index, and Spotify HTTP not enabled, so just play it as-is
|
||||
// Fetch the playlist tracks via backend and add each track by URI
|
||||
request(socket, store, 'playlists.lookup', {uri: action.uri})
|
||||
.then(
|
||||
response => {
|
||||
if (response.tracks === undefined){
|
||||
store.dispatch(uiActions.createNotification({content: 'Failed to load playlist tracks', type: 'bad'}));
|
||||
} else {
|
||||
var tracks_uris = helpers.arrayOf('uri',response.tracks);
|
||||
if (action.shuffle){
|
||||
tracks_uris = helpers.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;
|
||||
|
||||
case 'MOPIDY_ENQUEUE_URIS':
|
||||
|
||||
if (!action.uris || action.uris.length <= 0){
|
||||
@ -669,15 +709,15 @@ const MopidyMiddleware = (function(){
|
||||
batches.push({
|
||||
uris: uris.splice(0,batch_size),
|
||||
at_position: action.at_position,
|
||||
next: action.next,
|
||||
play_next: action.play_next,
|
||||
offset: action.offset + (batch_size * batches.length),
|
||||
from_uri: action.from_uri
|
||||
})
|
||||
}
|
||||
|
||||
// pass this modified action to the reducer (and other middleware)
|
||||
action.batches = batches
|
||||
next(action)
|
||||
action.batches = batches;
|
||||
next(action);
|
||||
|
||||
// start our processor
|
||||
store.dispatch(uiActions.startProcess(
|
||||
@ -703,7 +743,7 @@ const MopidyMiddleware = (function(){
|
||||
// make sure we have some uris in the queue
|
||||
} else 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 total_uris = 0
|
||||
for (var i = 0; i < batches.length; i++){
|
||||
@ -740,7 +780,7 @@ const MopidyMiddleware = (function(){
|
||||
var params = {uris: batch.uris}
|
||||
|
||||
// Play this batch next
|
||||
if (batch.next){
|
||||
if (batch.play_next){
|
||||
|
||||
// Make sure we're playing something first
|
||||
if (current_track_index > -1){
|
||||
|
||||
@ -1634,32 +1634,36 @@ export function getLibraryTracksAndPlayProcessor(data){
|
||||
* Recursively get .next until we have all tracks
|
||||
**/
|
||||
|
||||
export function getPlaylistTracksAndPlay(uri, shuffle){
|
||||
export function getAllPlaylistTracks(uri, shuffle = false, callback_action = null, play_next = false, at_position = null, offset = 0){
|
||||
return (dispatch, getState) => {
|
||||
dispatch(uiActions.startProcess(
|
||||
'SPOTIFY_GET_PLAYLIST_TRACKS_AND_PLAY_PROCESSOR',
|
||||
'SPOTIFY_GET_ALL_PLAYLIST_TRACKS_PROCESSOR',
|
||||
'Loading playlist tracks',
|
||||
{
|
||||
uri: uri,
|
||||
next: 'playlists/'+ helpers.getFromUri('playlistid',uri) +'/tracks?market='+getState().spotify.country,
|
||||
shuffle: shuffle
|
||||
shuffle: shuffle,
|
||||
play_next: play_next,
|
||||
at_position: at_position,
|
||||
offset: offset,
|
||||
callback_action: callback_action
|
||||
}
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
export function getPlaylistTracksAndPlayProcessor(data){
|
||||
export function getAllPlaylistTracksProcessor(data){
|
||||
return (dispatch, getState) => {
|
||||
request(dispatch, getState, data.next)
|
||||
.then(
|
||||
response => {
|
||||
|
||||
// Check to see if we've been cancelled
|
||||
if (getState().ui.processes['SPOTIFY_GET_PLAYLIST_TRACKS_AND_PLAY_PROCESSOR'] !== undefined){
|
||||
var processor = getState().ui.processes['SPOTIFY_GET_PLAYLIST_TRACKS_AND_PLAY_PROCESSOR']
|
||||
if (getState().ui.processes['SPOTIFY_GET_ALL_PLAYLIST_TRACKS_PROCESSOR'] !== undefined){
|
||||
var processor = getState().ui.processes['SPOTIFY_GET_ALL_PLAYLIST_TRACKS_PROCESSOR']
|
||||
|
||||
if (processor.status == 'cancelling'){
|
||||
dispatch(uiActions.processCancelled('SPOTIFY_GET_PLAYLIST_TRACKS_AND_PLAY_PROCESSOR'))
|
||||
dispatch(uiActions.processCancelled('SPOTIFY_GET_ALL_PLAYLIST_TRACKS_PROCESSOR'))
|
||||
return false
|
||||
}
|
||||
}
|
||||
@ -1679,31 +1683,41 @@ export function getPlaylistTracksAndPlayProcessor(data){
|
||||
// We got a next link, so we've got more work to be done
|
||||
if (response.next){
|
||||
dispatch(uiActions.updateProcess(
|
||||
'SPOTIFY_GET_PLAYLIST_TRACKS_AND_PLAY_PROCESSOR',
|
||||
'SPOTIFY_GET_ALL_PLAYLIST_TRACKS_PROCESSOR',
|
||||
'Loading '+(response.total-uris.length)+' playlist tracks',
|
||||
{
|
||||
next: response.next,
|
||||
total: response.total,
|
||||
remaining: response.total - uris.length,
|
||||
shuffle: data.shuffle
|
||||
}
|
||||
))
|
||||
Object.assign(
|
||||
{},
|
||||
data,
|
||||
{
|
||||
next: response.next,
|
||||
total: response.total,
|
||||
remaining: response.total - uris.length
|
||||
}
|
||||
)
|
||||
));
|
||||
dispatch(uiActions.runProcess(
|
||||
'SPOTIFY_GET_PLAYLIST_TRACKS_AND_PLAY_PROCESSOR',
|
||||
{
|
||||
next: response.next,
|
||||
uris: uris,
|
||||
shuffle: data.shuffle
|
||||
}
|
||||
))
|
||||
'SPOTIFY_GET_ALL_PLAYLIST_TRACKS_PROCESSOR',
|
||||
Object.assign(
|
||||
{},
|
||||
data,
|
||||
{
|
||||
next: response.next,
|
||||
uris: uris,
|
||||
}
|
||||
)
|
||||
));
|
||||
} else {
|
||||
|
||||
if (data.shuffle){
|
||||
uris = helpers.shuffle(uris);
|
||||
}
|
||||
|
||||
dispatch(mopidyActions.playURIs(uris, data.uri))
|
||||
dispatch(uiActions.processFinishing('SPOTIFY_GET_PLAYLIST_TRACKS_AND_PLAY_PROCESSOR'))
|
||||
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.processFinishing('SPOTIFY_GET_ALL_PLAYLIST_TRACKS_PROCESSOR'))
|
||||
}
|
||||
},
|
||||
error => {
|
||||
|
||||
@ -305,14 +305,14 @@ const SpotifyMiddleware = (function(){
|
||||
store.dispatch(spotifyActions.getLibraryAlbumsProcessor(action.data))
|
||||
break
|
||||
|
||||
case 'SPOTIFY_GET_PLAYLIST_TRACKS_AND_PLAY_PROCESSOR':
|
||||
store.dispatch(spotifyActions.getPlaylistTracksAndPlayProcessor(action.data))
|
||||
break
|
||||
|
||||
case 'SPOTIFY_GET_LIBRARY_TRACKS_AND_PLAY_PROCESSOR':
|
||||
store.dispatch(spotifyActions.getLibraryTracksAndPlayProcessor(action.data))
|
||||
break
|
||||
|
||||
case 'SPOTIFY_GET_ALL_PLAYLIST_TRACKS_PROCESSOR':
|
||||
store.dispatch(spotifyActions.getAllPlaylistTracksProcessor(action.data))
|
||||
break
|
||||
|
||||
case 'SPOTIFY_LIBRARY_ALBUMS_LOADED':
|
||||
var albums = []
|
||||
for (var i = 0; i < action.albums.length; i++){
|
||||
|
||||
Reference in New Issue
Block a user