diff --git a/src/components/playback/playbackmanager.js b/src/components/playback/playbackmanager.js index 73f07a05f2..ad1b156d67 100644 --- a/src/components/playback/playbackmanager.js +++ b/src/components/playback/playbackmanager.js @@ -2097,6 +2097,7 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla state.PlayState.IsMuted = player.isMuted(); state.PlayState.IsPaused = player.paused(); state.PlayState.RepeatMode = self.getRepeatMode(player); + state.PlayState.ShuffleMode = self.getPlaylistShuffleMode(player); state.PlayState.MaxStreamingBitrate = self.getMaxStreamingBitrate(player); state.PlayState.PositionTicks = getCurrentTicks(player); @@ -3304,6 +3305,11 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla sendProgressUpdate(player, 'repeatmodechange'); } + function onShufflePlaylistModeChange() { + var player = this; + sendProgressUpdate(player, 'shuffleplaylistmodechange'); + } + function onPlaylistItemMove(e) { var player = this; sendProgressUpdate(player, 'playlistitemmove', true); @@ -3358,6 +3364,7 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla events.on(player, 'unpause', onPlaybackUnpause); events.on(player, 'volumechange', onPlaybackVolumeChange); events.on(player, 'repeatmodechange', onRepeatModeChange); + events.on(player, 'shuffleplaylistmodechange', onShufflePlaylistModeChange); events.on(player, 'playlistitemmove', onPlaylistItemMove); events.on(player, 'playlistitemremove', onPlaylistItemRemove); events.on(player, 'playlistitemadd', onPlaylistItemAdd); @@ -3370,6 +3377,7 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla events.on(player, 'unpause', onPlaybackUnpause); events.on(player, 'volumechange', onPlaybackVolumeChange); events.on(player, 'repeatmodechange', onRepeatModeChange); + events.on(player, 'shuffleplaylistmodechange', onShufflePlaylistModeChange); events.on(player, 'playlistitemmove', onPlaylistItemMove); events.on(player, 'playlistitemremove', onPlaylistItemRemove); events.on(player, 'playlistitemadd', onPlaylistItemAdd); @@ -3811,7 +3819,7 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla }); }; - PlaybackManager.prototype.shuffle = function (shuffleItem, player, queryOptions) { + PlaybackManager.prototype.shuffle = function (shuffleItem, player) { player = player || this._currentPlayer; if (player && player.shuffle) { @@ -3878,6 +3886,7 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla 'GoToSearch', 'DisplayMessage', 'SetRepeatMode', + 'SetPlaylistShuffleMode', 'PlayMediaSource', 'PlayTrailers' ]; @@ -3932,6 +3941,27 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla return this._playQueueManager.getRepeatMode(); }; + PlaybackManager.prototype.setPlaylistShuffleMode = function (value, player) { + + player = player || this._currentPlayer; + if (player && !enableLocalPlaylistManagement(player)) { + return player.setShuffleMode(value); + } + + this._playQueueManager.setShuffleMode(value); + events.trigger(player, 'shuffleplaylistmodechange'); + }; + + PlaybackManager.prototype.getPlaylistShuffleMode = function (player) { + + player = player || this._currentPlayer; + if (player && !enableLocalPlaylistManagement(player)) { + return player.getPlaylistShuffleMode(); + } + + return this._playQueueManager.getShuffleMode(); + }; + PlaybackManager.prototype.trySetActiveDeviceName = function (name) { name = normalizeName(name); @@ -4000,6 +4030,9 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla case 'SetRepeatMode': this.setRepeatMode(cmd.Arguments.RepeatMode, player); break; + case 'SetPlaylistShuffleMode': + this.setPlaylistShuffleMode(cmd.Arguments.ShuffleMode, player); + break; case 'VolumeUp': this.volumeUp(player); break; diff --git a/src/components/playback/playqueuemanager.js b/src/components/playback/playqueuemanager.js index 565cb6993e..e26a738a02 100644 --- a/src/components/playback/playqueuemanager.js +++ b/src/components/playback/playqueuemanager.js @@ -24,8 +24,10 @@ define([], function () { function PlayQueueManager() { + this._sortedPlaylist = []; this._playlist = []; this._repeatMode = 'RepeatNone'; + this._shuffleMode = 'Sorted'; } PlayQueueManager.prototype.getPlaylist = function () { @@ -56,6 +58,30 @@ define([], function () { } }; + PlayQueueManager.prototype.shufflePlaylist = function () { + this._sortedPlaylist = []; + for (let item of this._playlist) { + this._sortedPlaylist.push(item); + } + + for (let i = this._playlist.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * i); + const temp = this._playlist[i]; + this._playlist[i] = this._playlist[j]; + this._playlist[j] = temp; + } + this._shuffleMode = 'Shuffle'; + }; + + PlayQueueManager.prototype.sortShuffledPlaylist = function () { + this._playlist = []; + for (let item of this._sortedPlaylist) { + this._playlist.push(item); + } + this._sortedPlaylist = []; + this._shuffleMode = 'Sorted'; + }; + function arrayInsertAt(destArray, pos, arrayToInsert) { var args = []; args.push(pos); // where to insert @@ -176,21 +202,39 @@ define([], function () { PlayQueueManager.prototype.reset = function () { + this._sortedPlaylist = []; this._playlist = []; this._currentPlaylistItemId = null; this._repeatMode = 'RepeatNone'; + this._shuffleMode = 'Sorted'; }; PlayQueueManager.prototype.setRepeatMode = function (value) { - - this._repeatMode = value; + if (value === 'RepeatOne' || value === 'RepeatAll' || value === 'RepeatNone') { + this._repeatMode = value; + } else { + throw new TypeError('invalid value provided for setRepeatMode'); + } }; PlayQueueManager.prototype.getRepeatMode = function () { - return this._repeatMode; }; + PlayQueueManager.prototype.setShuffleMode = function (value) { + if (value === 'Sorted') { + this.sortShuffledPlaylist(); + } else if (value === 'Shuffle') { + this.shufflePlaylist(); + } else { + throw new TypeError('invalid value provided for setShuffleMode'); + } + }; + + PlayQueueManager.prototype.getShuffleMode = function () { + return this._shuffleMode; + }; + PlayQueueManager.prototype.getNextItemInfo = function () { var newIndex; diff --git a/src/components/remotecontrol/remotecontrol.css b/src/components/remotecontrol/remotecontrol.css index de7e689ac3..34a65f61e3 100644 --- a/src/components/remotecontrol/remotecontrol.css +++ b/src/components/remotecontrol/remotecontrol.css @@ -298,7 +298,7 @@ font-size: smaller; } - .nowPlayingInfoButtons .btnFastForward { + .nowPlayingInfoButtons .btnShuffleMobile { position: absolute; right: 0; margin-right: 0; diff --git a/src/components/remotecontrol/remotecontrol.js b/src/components/remotecontrol/remotecontrol.js index d850d1be60..8755fb491c 100644 --- a/src/components/remotecontrol/remotecontrol.js +++ b/src/components/remotecontrol/remotecontrol.js @@ -297,6 +297,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL buttonVisible(context.querySelector('.btnRewind'), false); buttonVisible(context.querySelector('.btnFastForward'), false); buttonVisible(context.querySelector('.nowPlayingInfoButtons').querySelector('.repeatToggleButton'), true); + buttonVisible(context.querySelector('.nowPlayingInfoButtons').querySelector('.btnShuffleMobile'), true); } else { buttonVisible(context.querySelector('.btnRewind'), null != item); buttonVisible(context.querySelector('.btnFastForward'), null != item); @@ -495,6 +496,18 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL updateRepeatModeDisplay(playbackManager.getRepeatMode(player)); } + function onShufflePlaylistModeChange() { + let shuffleMode = playbackManager.getPlaylistShuffleMode(this); + let context = dlg; + let shuffleButton = context.querySelector('.btnShuffle'); + if ('Sorted' === shuffleMode) { + shuffleButton.classList.remove('shuffleButton-active'); + } else if ('Shuffle' === shuffleMode) { + shuffleButton.classList.add('shuffleButton-active'); + } + onPlaylistUpdate(); + } + function onPlaylistUpdate(e) { loadPlaylist(dlg, this); } @@ -556,7 +569,8 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL events.off(player, 'playbackstart', onPlaybackStart); events.off(player, 'statechange', onStateChanged); events.off(player, 'repeatmodechange', onRepeatModeChange); - events.off(player, 'playlistitemremove', onPlaylistUpdate); + events.off(player, 'shuffleplaylistmodechange', onShufflePlaylistModeChange); + events.off(player, 'playlistitemremove', onPlaylistItemRemoved); events.off(player, 'playlistitemmove', onPlaylistUpdate); events.off(player, 'playbackstop', onPlaybackStopped); events.off(player, 'volumechange', onVolumeChanged); @@ -576,6 +590,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL events.on(player, 'playbackstart', onPlaybackStart); events.on(player, 'statechange', onStateChanged); events.on(player, 'repeatmodechange', onRepeatModeChange); + events.on(player, 'shuffleplaylistmodechange', onShufflePlaylistModeChange); events.on(player, 'playlistitemremove', onPlaylistItemRemoved); events.on(player, 'playlistitemmove', onPlaylistUpdate); events.on(player, 'playbackstop', onPlaybackStopped); @@ -676,6 +691,16 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL playbackManager.fastForward(currentPlayer); } }); + context.querySelector('.btnShuffle').addEventListener('click', function () { + if (currentPlayer) { + if (playbackManager.getPlaylistShuffleMode(currentPlayer) === 'Sorted') { + playbackManager.setPlaylistShuffleMode('Shuffle', currentPlayer); + } else { + playbackManager.setPlaylistShuffleMode('Sorted', currentPlayer); + } + } + }); + context.querySelector('.btnPreviousTrack').addEventListener('click', function () { console.log(currentPlayer); if (currentPlayer) { diff --git a/src/nowplaying.html b/src/nowplaying.html index 4266b01f01..70f8e298ef 100644 --- a/src/nowplaying.html +++ b/src/nowplaying.html @@ -58,7 +58,10 @@ - + +