Play shuffled playlist, fixes #302
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
@ -77,7 +77,7 @@
|
|||||||
|
|
||||||
// Release details
|
// Release details
|
||||||
// These are automatically injected to built HTML
|
// These are automatically injected to built HTML
|
||||||
var build = "1529257445";
|
var build = "1529956787";
|
||||||
var version = "3.21.2";
|
var version = "3.21.2";
|
||||||
|
|
||||||
// Construct the script tag
|
// Construct the script tag
|
||||||
|
|||||||
@ -215,6 +215,11 @@ class ContextMenu extends React.Component{
|
|||||||
this.props.mopidyActions.playPlaylist(this.props.menu.uris[0]);
|
this.props.mopidyActions.playPlaylist(this.props.menu.uris[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
shufflePlayPlaylist(e){
|
||||||
|
this.props.uiActions.hideContextMenu();
|
||||||
|
this.props.mopidyActions.playPlaylist(this.props.menu.uris[0], true);
|
||||||
|
}
|
||||||
|
|
||||||
playArtistTopTracks(e){
|
playArtistTopTracks(e){
|
||||||
this.props.uiActions.hideContextMenu();
|
this.props.uiActions.hideContextMenu();
|
||||||
this.props.spotifyActions.playArtistTopTracks(this.props.menu.uris[0]);
|
this.props.spotifyActions.playArtistTopTracks(this.props.menu.uris[0]);
|
||||||
@ -454,6 +459,14 @@ class ContextMenu extends React.Component{
|
|||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var shuffle_play_playlist = (
|
||||||
|
<span className="menu-item-wrapper">
|
||||||
|
<a className="menu-item" onClick={e => this.shufflePlayPlaylist(e)}>
|
||||||
|
<span className="label">Shuffle play</span>
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
|
||||||
var play_queue_item = (
|
var play_queue_item = (
|
||||||
<span className="menu-item-wrapper">
|
<span className="menu-item-wrapper">
|
||||||
<a className="menu-item" onClick={e => this.playQueueItem(e)}>
|
<a className="menu-item" onClick={e => this.playQueueItem(e)}>
|
||||||
@ -645,6 +658,7 @@ class ContextMenu extends React.Component{
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{play_playlist}
|
{play_playlist}
|
||||||
|
{shuffle_play_playlist}
|
||||||
{this.canBeInLibrary() ? <div className="divider" /> : null}
|
{this.canBeInLibrary() ? <div className="divider" /> : null}
|
||||||
{this.canBeInLibrary() ? toggle_in_library : null}
|
{this.canBeInLibrary() ? toggle_in_library : null}
|
||||||
<div className="divider" />
|
<div className="divider" />
|
||||||
@ -658,6 +672,7 @@ class ContextMenu extends React.Component{
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{play_playlist}
|
{play_playlist}
|
||||||
|
{shuffle_play_playlist}
|
||||||
{this.canBeInLibrary() ? <div className="divider" /> : null}
|
{this.canBeInLibrary() ? <div className="divider" /> : null}
|
||||||
{this.canBeInLibrary() ? toggle_in_library : null}
|
{this.canBeInLibrary() ? toggle_in_library : null}
|
||||||
<div className="divider" />
|
<div className="divider" />
|
||||||
|
|||||||
@ -715,6 +715,23 @@ export let sortItems = function (array, property, reverse = false, sort_map = nu
|
|||||||
return sorted;
|
return sorted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shuffle items in place
|
||||||
|
*
|
||||||
|
* @param Array items
|
||||||
|
* @return Array
|
||||||
|
**/
|
||||||
|
export let shuffle = function(array){
|
||||||
|
var j, x, i;
|
||||||
|
for (i = array.length - 1; i > 0; i--) {
|
||||||
|
j = Math.floor(Math.random() * (i + 1));
|
||||||
|
x = array[i];
|
||||||
|
array[i] = array[j];
|
||||||
|
array[j] = x;
|
||||||
|
}
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Figure out if a value is a number
|
* Figure out if a value is a number
|
||||||
* @param value = mixed
|
* @param value = mixed
|
||||||
|
|||||||
@ -246,10 +246,11 @@ export function enqueueURIsBatchDone(){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function playPlaylist(uri){
|
export function playPlaylist(uri, shuffle = false){
|
||||||
return {
|
return {
|
||||||
type: 'MOPIDY_PLAY_PLAYLIST',
|
type: 'MOPIDY_PLAY_PLAYLIST',
|
||||||
uri: uri
|
uri: uri,
|
||||||
|
shuffle: shuffle
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -546,14 +546,14 @@ const MopidyMiddleware = (function(){
|
|||||||
// make sure we didn't get this playlist from Mopidy-Spotify
|
// make sure we didn't get this playlist from Mopidy-Spotify
|
||||||
// if we did, we'd have a cached version on server so no need to fetch
|
// if we did, we'd have a cached version on server so no need to fetch
|
||||||
if (!store.getState().core.playlists[action.uri].is_mopidy){
|
if (!store.getState().core.playlists[action.uri].is_mopidy){
|
||||||
store.dispatch(spotifyActions.getPlaylistTracksAndPlay(action.uri))
|
store.dispatch(spotifyActions.getPlaylistTracksAndPlay(action.uri, action.shuffle))
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
// it's a spotify playlist that we haven't loaded
|
// it's a spotify playlist that we haven't loaded
|
||||||
// we need to fetch via HTTP API to avoid timeout
|
// we need to fetch via HTTP API to avoid timeout
|
||||||
} else if (helpers.uriSource(action.uri) == 'spotify' && store.getState().spotify.enabled){
|
} else if (helpers.uriSource(action.uri) == 'spotify' && store.getState().spotify.enabled){
|
||||||
store.dispatch(spotifyActions.getPlaylistTracksAndPlay(action.uri))
|
store.dispatch(spotifyActions.getPlaylistTracksAndPlay(action.uri, action.shuffle))
|
||||||
break
|
break
|
||||||
|
|
||||||
// Not in index, and Spotify HTTP not enabled, so just play it as-is
|
// Not in index, and Spotify HTTP not enabled, so just play it as-is
|
||||||
@ -567,7 +567,10 @@ const MopidyMiddleware = (function(){
|
|||||||
if (response.tracks === undefined){
|
if (response.tracks === undefined){
|
||||||
store.dispatch(uiActions.createNotification({content: 'Failed to load playlist tracks', type: 'bad'}));
|
store.dispatch(uiActions.createNotification({content: 'Failed to load playlist tracks', type: 'bad'}));
|
||||||
} else {
|
} else {
|
||||||
var tracks_uris = helpers.arrayOf('uri',response.tracks)
|
var tracks_uris = helpers.arrayOf('uri',response.tracks);
|
||||||
|
if (action.shuffle){
|
||||||
|
tracks_uris = helpers.shuffle(tracks_uris);
|
||||||
|
}
|
||||||
store.dispatch(mopidyActions.playURIs(tracks_uris, action.uri))
|
store.dispatch(mopidyActions.playURIs(tracks_uris, action.uri))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1655,14 +1655,15 @@ export function getLibraryTracksAndPlayProcessor(data){
|
|||||||
* Recursively get .next until we have all tracks
|
* Recursively get .next until we have all tracks
|
||||||
**/
|
**/
|
||||||
|
|
||||||
export function getPlaylistTracksAndPlay(uri){
|
export function getPlaylistTracksAndPlay(uri, shuffle){
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
dispatch(uiActions.startProcess(
|
dispatch(uiActions.startProcess(
|
||||||
'SPOTIFY_GET_PLAYLIST_TRACKS_AND_PLAY_PROCESSOR',
|
'SPOTIFY_GET_PLAYLIST_TRACKS_AND_PLAY_PROCESSOR',
|
||||||
'Loading playlist tracks',
|
'Loading playlist tracks',
|
||||||
{
|
{
|
||||||
uri: uri,
|
uri: uri,
|
||||||
next: 'users/'+ helpers.getFromUri('userid',uri) +'/playlists/'+ helpers.getFromUri('playlistid',uri) +'/tracks?market='+getState().spotify.country
|
next: 'users/'+ helpers.getFromUri('userid',uri) +'/playlists/'+ helpers.getFromUri('playlistid',uri) +'/tracks?market='+getState().spotify.country,
|
||||||
|
shuffle: shuffle
|
||||||
}
|
}
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -1704,17 +1705,24 @@ export function getPlaylistTracksAndPlayProcessor(data){
|
|||||||
{
|
{
|
||||||
next: response.next,
|
next: response.next,
|
||||||
total: response.total,
|
total: response.total,
|
||||||
remaining: response.total - uris.length
|
remaining: response.total - uris.length,
|
||||||
|
shuffle: data.shuffle
|
||||||
}
|
}
|
||||||
))
|
))
|
||||||
dispatch(uiActions.runProcess(
|
dispatch(uiActions.runProcess(
|
||||||
'SPOTIFY_GET_PLAYLIST_TRACKS_AND_PLAY_PROCESSOR',
|
'SPOTIFY_GET_PLAYLIST_TRACKS_AND_PLAY_PROCESSOR',
|
||||||
{
|
{
|
||||||
next: response.next,
|
next: response.next,
|
||||||
uris: uris
|
uris: uris,
|
||||||
|
shuffle: data.shuffle
|
||||||
}
|
}
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
if (data.shuffle){
|
||||||
|
uris = helpers.shuffle(uris);
|
||||||
|
}
|
||||||
|
|
||||||
dispatch(mopidyActions.playURIs(uris, data.uri))
|
dispatch(mopidyActions.playURIs(uris, data.uri))
|
||||||
dispatch(uiActions.processFinishing('SPOTIFY_GET_PLAYLIST_TRACKS_AND_PLAY_PROCESSOR'))
|
dispatch(uiActions.processFinishing('SPOTIFY_GET_PLAYLIST_TRACKS_AND_PLAY_PROCESSOR'))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -60,7 +60,7 @@ class LibraryAlbums extends React.Component{
|
|||||||
if (newProps.mopidy_uri_schemes.includes('spotify:') && (newProps.source == 'all' || newProps.source == 'spotify')){
|
if (newProps.mopidy_uri_schemes.includes('spotify:') && (newProps.source == 'all' || newProps.source == 'spotify')){
|
||||||
|
|
||||||
// Filter changed, but we haven't got this provider's library yet
|
// Filter changed, but we haven't got this provider's library yet
|
||||||
if (this.props.source != 'all' && this.props.source != 'spotify' && newProps.spotify_library_albums_status != 'finished' && newProps.spotify_library_albums_status != 'started'){
|
if (newProps.spotify_library_albums_status != 'finished' && newProps.spotify_library_albums_status != 'started'){
|
||||||
this.props.spotifyActions.getLibraryAlbums();
|
this.props.spotifyActions.getLibraryAlbums();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -56,7 +56,7 @@ class LibraryArtists extends React.Component{
|
|||||||
if (newProps.mopidy_uri_schemes.includes('spotify:') && (newProps.source == 'all' || newProps.source == 'spotify')){
|
if (newProps.mopidy_uri_schemes.includes('spotify:') && (newProps.source == 'all' || newProps.source == 'spotify')){
|
||||||
|
|
||||||
// Filter changed, but we haven't got this provider's library yet
|
// Filter changed, but we haven't got this provider's library yet
|
||||||
if (this.props.source != 'all' && this.props.source != 'spotify' && newProps.spotify_library_artists_status != 'finished'){
|
if (newProps.spotify_library_artists_status != 'finished' && newProps.spotify_library_artists_status != 'started'){
|
||||||
this.props.spotifyActions.getLibraryArtists();
|
this.props.spotifyActions.getLibraryArtists();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -58,7 +58,7 @@ class LibraryPlaylists extends React.Component{
|
|||||||
if (newProps.mopidy_uri_schemes.includes('spotify:') && (newProps.source == 'all' || newProps.source == 'spotify')){
|
if (newProps.mopidy_uri_schemes.includes('spotify:') && (newProps.source == 'all' || newProps.source == 'spotify')){
|
||||||
|
|
||||||
// Filter changed, but we haven't got this provider's library yet
|
// Filter changed, but we haven't got this provider's library yet
|
||||||
if (this.props.source != 'all' && this.props.source != 'spotify' && newProps.spotify_library_playlists_status !== 'finished'){
|
if (newProps.spotify_library_playlists_status != 'finished' && newProps.spotify_library_playlists_status != 'started'){
|
||||||
this.props.spotifyActions.getLibraryPlaylists()
|
this.props.spotifyActions.getLibraryPlaylists()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user