From dd4cc0328a54ffc34251aee1e10e56d2250daeb1 Mon Sep 17 00:00:00 2001 From: ferferga Date: Wed, 17 Jun 2020 20:28:42 +0200 Subject: [PATCH 01/42] Attempt to fix miniplayer context menu --- src/components/nowPlayingBar/nowPlayingBar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/nowPlayingBar/nowPlayingBar.js b/src/components/nowPlayingBar/nowPlayingBar.js index bc9c3c1a8..9732951f5 100644 --- a/src/components/nowPlayingBar/nowPlayingBar.js +++ b/src/components/nowPlayingBar/nowPlayingBar.js @@ -545,7 +545,7 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', apiClient.getItem(apiClient.getCurrentUserId(), nowPlayingItem.Id).then(function (item) { var userData = item.UserData || {}; var likes = userData.Likes == null ? '' : userData.Likes; - var contextButton = document.querySelector('.btnToggleContextMenu'); + var contextButton = document.querySelector('.nowPlayingBar').querySelector('.btnToggleContextMenu'); var options = { play: false, queue: false, From e2f61c67b9c50ad815d4d778a451a70ffb9599b3 Mon Sep 17 00:00:00 2001 From: ferferga Date: Wed, 17 Jun 2020 20:29:08 +0200 Subject: [PATCH 02/42] Show all artists of an item and set bottom bar transparent on mobile devices --- .../remotecontrol/remotecontrol.css | 3 ++ src/components/remotecontrol/remotecontrol.js | 30 ++++++++++++------- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/components/remotecontrol/remotecontrol.css b/src/components/remotecontrol/remotecontrol.css index 073c92533..115fc2c24 100644 --- a/src/components/remotecontrol/remotecontrol.css +++ b/src/components/remotecontrol/remotecontrol.css @@ -364,6 +364,9 @@ padding-left: 7.3%; padding-right: 7.3%; } + .playlistSectionButtonTransparent { + background: rgba(0, 0, 0, 0) !important; + } .playlistSectionButton .btnTogglePlaylist { font-size: larger; diff --git a/src/components/remotecontrol/remotecontrol.js b/src/components/remotecontrol/remotecontrol.js index 089915a83..f5de415a3 100644 --- a/src/components/remotecontrol/remotecontrol.js +++ b/src/components/remotecontrol/remotecontrol.js @@ -1,4 +1,4 @@ -define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageLoader', 'playbackManager', 'nowPlayingHelper', 'events', 'connectionManager', 'apphost', 'globalize', 'layoutManager', 'userSettings', 'cardBuilder', 'cardStyle', 'emby-itemscontainer', 'css!./remotecontrol.css', 'emby-ratingbutton'], function (browser, datetime, backdrop, libraryBrowser, listView, imageLoader, playbackManager, nowPlayingHelper, events, connectionManager, appHost, globalize, layoutManager, userSettings, cardBuilder) { +define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageLoader', 'playbackManager', 'nowPlayingHelper', 'events', 'connectionManager', 'apphost', 'globalize', 'layoutManager', 'userSettings', 'cardBuilder', 'itemContextMenu', 'cardStyle', 'emby-itemscontainer', 'css!./remotecontrol.css', 'emby-ratingbutton'], function (browser, datetime, backdrop, libraryBrowser, listView, imageLoader, playbackManager, nowPlayingHelper, events, connectionManager, appHost, globalize, layoutManager, userSettings, cardBuilder, itemContextMenu) { 'use strict'; function showAudioMenu(context, player, button, item) { @@ -119,18 +119,21 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL var songName = item.Name; if (item.Album != null && item.Artists != null) { var albumName = item.Album; - var artistName; if (item.ArtistItems != null) { - artistName = item.ArtistItems[0].Name; - context.querySelector('.nowPlayingAlbum').innerHTML = '${albumName}`; - context.querySelector('.nowPlayingArtist').innerHTML = '${artistName}`; - context.querySelector('.contextMenuAlbum').innerHTML = ' ` + globalize.translate('ViewAlbum') + ''; + var artistsSeries = ''; + for (let artist of item.ArtistItems) { + let artistName = artist.Name; + let artistId = artist.Id; + artistsSeries += `${artistName}`; + if (artist !== item.ArtistItems.slice(-1)[0]) { + artistsSeries += ', '; + } + } + context.querySelector('.nowPlayingArtist').innerHTML = artistsSeries; context.querySelector('.contextMenuArtist').innerHTML = ' ` + globalize.translate('ViewArtist') + ''; - } else { - artistName = item.Artists; - context.querySelector('.nowPlayingAlbum').innerHTML = albumName; - context.querySelector('.nowPlayingArtist').innerHTML = artistName; } + context.querySelector('.nowPlayingAlbum').innerHTML = '${albumName}`; + context.querySelector('.contextMenuAlbum').innerHTML = ' ` + globalize.translate('ViewAlbum') + ''; } context.querySelector('.nowPlayingSongName').innerHTML = songName; } else if (item.Type == 'Episode') { @@ -703,10 +706,16 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL context.querySelector('.btnSavePlaylist').classList.remove('hide'); context.querySelector('.contextMenu').classList.add('hide'); context.querySelector('.volumecontrol').classList.add('hide'); + if (layoutManager.mobile) { + context.querySelector('.playlistSectionButton').classList.remove('playlistSectionButtonTransparent'); + } } else { context.querySelector('.playlist').classList.add('hide'); context.querySelector('.btnSavePlaylist').classList.add('hide'); context.querySelector('.volumecontrol').classList.remove('hide'); + if (layoutManager.mobile) { + context.querySelector('.playlistSectionButton').classList.add('playlistSectionButtonTransparent'); + } } }); context.querySelector('.btnToggleContextMenu').addEventListener('click', function () { @@ -774,6 +783,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL context.querySelector('.playlistSectionButton').innerHTML += contextmenuHtml; } else { context.querySelector('.playlistSectionButton').innerHTML += volumecontrolHtml + contextmenuHtml; + context.querySelector('.playlistSectionButton').classList.add('playlistSectionButtonTransparent'); } bindEvents(context); From 4159268949d433f42dc99969cbf2060c4624842f Mon Sep 17 00:00:00 2001 From: ferferga Date: Wed, 17 Jun 2020 21:02:50 +0200 Subject: [PATCH 03/42] Repeat track if going back when less than 5 seconds of the item has been playing --- src/components/nowPlayingBar/nowPlayingBar.js | 22 ++++++++++++------- src/components/remotecontrol/remotecontrol.js | 15 ++++++++++--- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/src/components/nowPlayingBar/nowPlayingBar.js b/src/components/nowPlayingBar/nowPlayingBar.js index 9732951f5..33f9aa405 100644 --- a/src/components/nowPlayingBar/nowPlayingBar.js +++ b/src/components/nowPlayingBar/nowPlayingBar.js @@ -117,8 +117,13 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', nowPlayingImageElement = elem.querySelector('.nowPlayingImage'); nowPlayingTextElement = elem.querySelector('.nowPlayingBarText'); nowPlayingUserData = elem.querySelector('.nowPlayingBarUserDataButtons'); - + positionSlider = elem.querySelector('.nowPlayingBarPositionSlider'); muteButton = elem.querySelector('.muteButton'); + playPauseButtons = elem.querySelectorAll('.playPauseButton'); + toggleRepeatButton = elem.querySelector('.toggleRepeatButton'); + volumeSlider = elem.querySelector('.nowPlayingBarVolumeSlider'); + volumeSliderContainer = elem.querySelector('.nowPlayingBarVolumeSliderContainer'); + muteButton.addEventListener('click', function () { if (currentPlayer) { @@ -134,7 +139,6 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', } }); - playPauseButtons = elem.querySelectorAll('.playPauseButton'); playPauseButtons.forEach((button) => { button.addEventListener('click', onPlayPauseClick); }); @@ -147,9 +151,15 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', }); elem.querySelector('.previousTrackButton').addEventListener('click', function () { - if (currentPlayer) { - playbackManager.previousTrack(currentPlayer); + if (currentPlayer.id === 'htmlaudioplayer' && (currentPlayer._currentTime <= 5 || !playbackManager.previousTrack(currentPlayer))) { + playbackManager.seekPercent(0, currentPlayer); + // This is done automatically by playbackManager, however, setting this here + // gives instant visual feedback + positionSlider.value = 0; + } else { + playbackManager.previousTrack(currentPlayer); + } } }); @@ -174,9 +184,6 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', toggleRepeatButtonIcon = toggleRepeatButton.querySelector('.material-icons'); - volumeSlider = elem.querySelector('.nowPlayingBarVolumeSlider'); - volumeSliderContainer = elem.querySelector('.nowPlayingBarVolumeSliderContainer'); - if (appHost.supports('physicalvolumecontrol')) { volumeSliderContainer.classList.add('hide'); } else { @@ -193,7 +200,6 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', volumeSlider.addEventListener('mousemove', setVolume); volumeSlider.addEventListener('touchmove', setVolume); - positionSlider = elem.querySelector('.nowPlayingBarPositionSlider'); positionSlider.addEventListener('change', function () { if (currentPlayer) { diff --git a/src/components/remotecontrol/remotecontrol.js b/src/components/remotecontrol/remotecontrol.js index f5de415a3..c1c5bfd6d 100644 --- a/src/components/remotecontrol/remotecontrol.js +++ b/src/components/remotecontrol/remotecontrol.js @@ -606,6 +606,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL function bindEvents(context) { var btnCommand = context.querySelectorAll('.btnCommand'); + var positionSlider = context.querySelector('.nowPlayingPositionSlider'); for (var i = 0, length = btnCommand.length; i < length; i++) { btnCommand[i].addEventListener('click', onBtnCommandClick); @@ -654,11 +655,19 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL } }); context.querySelector('.btnPreviousTrack').addEventListener('click', function () { + console.log(currentPlayer); if (currentPlayer) { - playbackManager.previousTrack(currentPlayer); + if (currentPlayer.id === 'htmlaudioplayer' && (currentPlayer._currentTime <= 5 || !playbackManager.previousTrack(currentPlayer))) { + playbackManager.seekPercent(0, currentPlayer); + // This is done automatically by playbackManager. However, setting this here + // gives instant visual feedback + positionSlider.value = 0; + } else { + playbackManager.previousTrack(currentPlayer); + } } }); - context.querySelector('.nowPlayingPositionSlider').addEventListener('change', function () { + positionSlider.addEventListener('change', function () { var value = this.value; if (currentPlayer) { @@ -667,7 +676,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL } }); - context.querySelector('.nowPlayingPositionSlider').getBubbleText = function (value) { + positionSlider.getBubbleText = function (value) { var state = lastPlayerState; if (!state || !state.NowPlayingItem || !currentRuntimeTicks) { From 8b4b67254a57b5544daa0b0cc086bfab40c9f3b3 Mon Sep 17 00:00:00 2001 From: ferferga Date: Wed, 17 Jun 2020 22:09:30 +0200 Subject: [PATCH 04/42] Show playlist on desktop layout and use native context menu --- src/assets/css/flexstyles.css | 4 + src/components/remotecontrol/remotecontrol.js | 93 +++++++++++-------- src/nowplaying.html | 10 +- 3 files changed, 59 insertions(+), 48 deletions(-) diff --git a/src/assets/css/flexstyles.css b/src/assets/css/flexstyles.css index a5a479f2f..2f3a386bf 100644 --- a/src/assets/css/flexstyles.css +++ b/src/assets/css/flexstyles.css @@ -30,6 +30,10 @@ align-items: flex-start; } +.align-items-flex-end { + align-items: flex-end; +} + .justify-content-center { justify-content: center; } diff --git a/src/components/remotecontrol/remotecontrol.js b/src/components/remotecontrol/remotecontrol.js index c1c5bfd6d..89c56090e 100644 --- a/src/components/remotecontrol/remotecontrol.js +++ b/src/components/remotecontrol/remotecontrol.js @@ -1,5 +1,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageLoader', 'playbackManager', 'nowPlayingHelper', 'events', 'connectionManager', 'apphost', 'globalize', 'layoutManager', 'userSettings', 'cardBuilder', 'itemContextMenu', 'cardStyle', 'emby-itemscontainer', 'css!./remotecontrol.css', 'emby-ratingbutton'], function (browser, datetime, backdrop, libraryBrowser, listView, imageLoader, playbackManager, nowPlayingHelper, events, connectionManager, appHost, globalize, layoutManager, userSettings, cardBuilder, itemContextMenu) { 'use strict'; + var showMuteButton = true; + var showVolumeSlider = true; function showAudioMenu(context, player, button, item) { var currentIndex = playbackManager.getAudioStreamIndex(player); @@ -130,10 +132,8 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL } } context.querySelector('.nowPlayingArtist').innerHTML = artistsSeries; - context.querySelector('.contextMenuArtist').innerHTML = ' ` + globalize.translate('ViewArtist') + ''; } context.querySelector('.nowPlayingAlbum').innerHTML = '${albumName}`; - context.querySelector('.contextMenuAlbum').innerHTML = ' ` + globalize.translate('ViewAlbum') + ''; } context.querySelector('.nowPlayingSongName').innerHTML = songName; } else if (item.Type == 'Episode') { @@ -167,10 +167,25 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL }) : null; console.debug('updateNowPlayingInfo'); + var contextButton = context.querySelector('.btnToggleContextMenu'); + var options = { + play: false, + queue: false, + openAlbum: false, + positionTo: contextButton + }; + var apiClient = connectionManager.getApiClient(item.ServerId); + apiClient.getCurrentUser().then(function (user) { + contextButton.addEventListener('click', function () { + itemContextMenu.show(Object.assign({ + item: item, + user: user + }, options)); + }); + }); setImageUrl(context, state, url); if (item) { backdrop.setBackdrops([item]); - var apiClient = connectionManager.getApiClient(item.ServerId); apiClient.getItem(apiClient.getCurrentUserId(), item.Id).then(function (fullItem) { var userData = fullItem.UserData || {}; var likes = null == userData.Likes ? '' : userData.Likes; @@ -336,8 +351,6 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL function updatePlayerVolumeState(context, isMuted, volumeLevel) { var view = context; var supportedCommands = currentPlayerSupportedCommands; - var showMuteButton = true; - var showVolumeSlider = true; if (-1 === supportedCommands.indexOf('Mute')) { showMuteButton = false; @@ -365,24 +378,28 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL buttonMuteIcon.classList.add('volume_up'); } - if (showMuteButton) { - buttonMute.classList.remove('hide'); + if (!showMuteButton && !showVolumeSlider) { + context.querySelector('.volumecontrol').classList.add('hide'); } else { - buttonMute.classList.add('hide'); - } - - var nowPlayingVolumeSlider = context.querySelector('.nowPlayingVolumeSlider'); - var nowPlayingVolumeSliderContainer = context.querySelector('.nowPlayingVolumeSliderContainer'); - - if (nowPlayingVolumeSlider) { - if (showVolumeSlider) { - nowPlayingVolumeSliderContainer.classList.remove('hide'); + if (showMuteButton) { + buttonMute.classList.remove('hide'); } else { - nowPlayingVolumeSliderContainer.classList.add('hide'); + buttonMute.classList.add('hide'); } - if (!nowPlayingVolumeSlider.dragging) { - nowPlayingVolumeSlider.value = volumeLevel || 0; + var nowPlayingVolumeSlider = context.querySelector('.nowPlayingVolumeSlider'); + var nowPlayingVolumeSliderContainer = context.querySelector('.nowPlayingVolumeSliderContainer'); + + if (nowPlayingVolumeSlider) { + if (showVolumeSlider) { + nowPlayingVolumeSliderContainer.classList.remove('hide'); + } else { + nowPlayingVolumeSliderContainer.classList.add('hide'); + } + + if (!nowPlayingVolumeSlider.dragging) { + nowPlayingVolumeSlider.value = volumeLevel || 0; + } } } } @@ -435,11 +452,12 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL }], dragHandle: true }); - - if (items.length) { - context.querySelector('.btnTogglePlaylist').classList.remove('hide'); - } else { - context.querySelector('.btnTogglePlaylist').classList.add('hide'); + if (layoutManager.mobile) { + if (items.length > 0) { + context.querySelector('.btnTogglePlaylist').classList.remove('hide'); + } else { + context.querySelector('.btnTogglePlaylist').classList.add('hide'); + } } var itemsContainer = context.querySelector('.playlist'); @@ -456,8 +474,6 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL } imageLoader.lazyChildren(itemsContainer); - context.querySelector('.playlist').classList.add('hide'); - context.querySelector('.contextMenu').classList.add('hide'); context.querySelector('.btnSavePlaylist').classList.add('hide'); }); } @@ -713,7 +729,6 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL if (context.querySelector('.playlist').classList.contains('hide')) { context.querySelector('.playlist').classList.remove('hide'); context.querySelector('.btnSavePlaylist').classList.remove('hide'); - context.querySelector('.contextMenu').classList.add('hide'); context.querySelector('.volumecontrol').classList.add('hide'); if (layoutManager.mobile) { context.querySelector('.playlistSectionButton').classList.remove('playlistSectionButtonTransparent'); @@ -721,21 +736,14 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL } else { context.querySelector('.playlist').classList.add('hide'); context.querySelector('.btnSavePlaylist').classList.add('hide'); - context.querySelector('.volumecontrol').classList.remove('hide'); + if (showMuteButton && showVolumeSlider) { + context.querySelector('.volumecontrol').classList.remove('hide'); + } if (layoutManager.mobile) { context.querySelector('.playlistSectionButton').classList.add('playlistSectionButtonTransparent'); } } }); - context.querySelector('.btnToggleContextMenu').addEventListener('click', function () { - if (context.querySelector('.contextMenu').classList.contains('hide')) { - context.querySelector('.contextMenu').classList.remove('hide'); - context.querySelector('.btnSavePlaylist').classList.add('hide'); - context.querySelector('.playlist').classList.add('hide'); - } else { - context.querySelector('.contextMenu').classList.add('hide'); - } - }); } function onPlayerChange() { @@ -787,12 +795,17 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL volumecontrolHtml += ``; volumecontrolHtml += '
'; volumecontrolHtml += ''; + let optionsSection = context.querySelector('.playlistSectionButton'); if (!layoutManager.mobile) { context.querySelector('.nowPlayingSecondaryButtons').innerHTML += volumecontrolHtml; - context.querySelector('.playlistSectionButton').innerHTML += contextmenuHtml; + optionsSection.innerHTML += contextmenuHtml; + optionsSection.classList.remove('align-items-center', 'justify-content-center'); + optionsSection.classList.add('align-items-right', 'justify-content-flex-end'); + context.querySelector('.playlist').classList.remove('hide'); } else { - context.querySelector('.playlistSectionButton').innerHTML += volumecontrolHtml + contextmenuHtml; - context.querySelector('.playlistSectionButton').classList.add('playlistSectionButtonTransparent'); + optionsSection.innerHTML += volumecontrolHtml + contextmenuHtml; + optionsSection.classList.add('playlistSectionButtonTransparent'); + context.querySelector('.btnTogglePlaylist').classList.remove('hide'); } bindEvents(context); diff --git a/src/nowplaying.html b/src/nowplaying.html index 5f235a562..efeb2526d 100644 --- a/src/nowplaying.html +++ b/src/nowplaying.html @@ -163,20 +163,14 @@
- -
-
-
-
-
-
-
From 74d32d3cbd5cbe6e2845317cc9d92ed733813570 Mon Sep 17 00:00:00 2001 From: ferferga Date: Wed, 17 Jun 2020 22:30:59 +0200 Subject: [PATCH 05/42] Add repeat button in mobile layout --- src/components/remotecontrol/remotecontrol.css | 2 +- src/components/remotecontrol/remotecontrol.js | 10 ++++++++-- src/nowplaying.html | 8 +++++++- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/components/remotecontrol/remotecontrol.css b/src/components/remotecontrol/remotecontrol.css index 115fc2c24..de7e689ac 100644 --- a/src/components/remotecontrol/remotecontrol.css +++ b/src/components/remotecontrol/remotecontrol.css @@ -290,7 +290,7 @@ border-radius: 0; } - .nowPlayingInfoButtons .btnRewind { + .nowPlayingInfoButtons .btnRepeatMobile { position: absolute; left: 0; margin-left: 0; diff --git a/src/components/remotecontrol/remotecontrol.js b/src/components/remotecontrol/remotecontrol.js index 89c56090e..d850d1be6 100644 --- a/src/components/remotecontrol/remotecontrol.js +++ b/src/components/remotecontrol/remotecontrol.js @@ -293,8 +293,14 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL buttonVisible(context.querySelector('.btnStop'), null != item); buttonVisible(context.querySelector('.btnNextTrack'), null != item); buttonVisible(context.querySelector('.btnPreviousTrack'), null != item); - buttonVisible(context.querySelector('.btnRewind'), null != item); - buttonVisible(context.querySelector('.btnFastForward'), null != item); + if (layoutManager.mobile) { + buttonVisible(context.querySelector('.btnRewind'), false); + buttonVisible(context.querySelector('.btnFastForward'), false); + buttonVisible(context.querySelector('.nowPlayingInfoButtons').querySelector('.repeatToggleButton'), true); + } else { + buttonVisible(context.querySelector('.btnRewind'), null != item); + buttonVisible(context.querySelector('.btnFastForward'), null != item); + } var positionSlider = context.querySelector('.nowPlayingPositionSlider'); if (positionSlider && item && item.RunTimeTicks) { diff --git a/src/nowplaying.html b/src/nowplaying.html index efeb2526d..4266b01f0 100644 --- a/src/nowplaying.html +++ b/src/nowplaying.html @@ -29,6 +29,11 @@
+ + - From ff6bfbf2b43ed14f4ff99aeb4afb7cffb7c6883b Mon Sep 17 00:00:00 2001 From: ferferga Date: Thu, 18 Jun 2020 01:19:59 +0200 Subject: [PATCH 06/42] Add playlist shuffling toggle to remotecontrol.js --- src/components/playback/playbackmanager.js | 35 ++++++++++++- src/components/playback/playqueuemanager.js | 50 +++++++++++++++++-- .../remotecontrol/remotecontrol.css | 2 +- src/components/remotecontrol/remotecontrol.js | 27 +++++++++- src/nowplaying.html | 5 +- src/plugins/sessionPlayer/plugin.js | 7 +++ src/themes/appletv/theme.css | 4 ++ src/themes/blueradiance/theme.css | 4 ++ src/themes/dark/theme.css | 4 ++ src/themes/light/theme.css | 4 ++ src/themes/purplehaze/theme.css | 4 ++ src/themes/wmc/theme.css | 4 ++ 12 files changed, 143 insertions(+), 7 deletions(-) diff --git a/src/components/playback/playbackmanager.js b/src/components/playback/playbackmanager.js index 73f07a05f..ad1b156d6 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 565cb6993..e26a738a0 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 de7e689ac..34a65f61e 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 d850d1be6..8755fb491 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 4266b01f0..70f8e298e 100644 --- a/src/nowplaying.html +++ b/src/nowplaying.html @@ -58,7 +58,10 @@ - + +
diff --git a/src/plugins/sessionPlayer/plugin.js b/src/plugins/sessionPlayer/plugin.js index 489b57493..241d3fba4 100644 --- a/src/plugins/sessionPlayer/plugin.js +++ b/src/plugins/sessionPlayer/plugin.js @@ -506,6 +506,13 @@ define(['playbackManager', 'events', 'serverNotifications', 'connectionManager'] }); }; + SessionPlayer.prototype.setPlaylistShuffleMode = function (mode) { + + sendCommandByName(this, 'SetPlaylistShuffleMode', { + ShuffleMode: mode + }); + }; + SessionPlayer.prototype.displayContent = function (options) { sendCommandByName(this, 'DisplayContent', options); diff --git a/src/themes/appletv/theme.css b/src/themes/appletv/theme.css index b3ce2c7e9..9d0ccdc9d 100644 --- a/src/themes/appletv/theme.css +++ b/src/themes/appletv/theme.css @@ -450,6 +450,10 @@ html { color: #4285f4; } +.shuffleButton-active { + color: #4285f4 !important; +} + .card:focus .cardBox.visualCardBox, .card:focus .cardBox:not(.visualCardBox) .cardScalable { border-color: #00a4dc !important; diff --git a/src/themes/blueradiance/theme.css b/src/themes/blueradiance/theme.css index 74a60c91c..5215f4bb6 100644 --- a/src/themes/blueradiance/theme.css +++ b/src/themes/blueradiance/theme.css @@ -450,6 +450,10 @@ html { color: #4285f4; } +.shuffleButton-active { + color: #4285f4 !important; +} + .card:focus .cardBox.visualCardBox, .card:focus .cardBox:not(.visualCardBox) .cardScalable { border-color: #00a4dc !important; diff --git a/src/themes/dark/theme.css b/src/themes/dark/theme.css index a32e60638..1e8a996c9 100644 --- a/src/themes/dark/theme.css +++ b/src/themes/dark/theme.css @@ -421,6 +421,10 @@ html { color: #4285f4; } +.shuffleButton-active { + color: #4285f4 !important; +} + .card:focus .cardBox.visualCardBox, .card:focus .cardBox:not(.visualCardBox) .cardScalable { border-color: #00a4dc !important; diff --git a/src/themes/light/theme.css b/src/themes/light/theme.css index 114ef7c3b..82b2f5075 100644 --- a/src/themes/light/theme.css +++ b/src/themes/light/theme.css @@ -432,6 +432,10 @@ html { color: #4285f4; } +.shuffleButton-active { + color: #4285f4 !important; +} + .card:focus .cardBox.visualCardBox, .card:focus .cardBox:not(.visualCardBox) .cardScalable { border-color: #00a4dc !important; diff --git a/src/themes/purplehaze/theme.css b/src/themes/purplehaze/theme.css index de69a5542..140969f65 100644 --- a/src/themes/purplehaze/theme.css +++ b/src/themes/purplehaze/theme.css @@ -547,6 +547,10 @@ a[data-role=button] { color: #4285f4; } +.shuffleButton-active { + color: #4285f4 !important; +} + .personCard .cardScalable { border-radius: 50%; border: 1px solid rgb(255, 255, 255); diff --git a/src/themes/wmc/theme.css b/src/themes/wmc/theme.css index e7d4c0371..a62d38297 100644 --- a/src/themes/wmc/theme.css +++ b/src/themes/wmc/theme.css @@ -430,6 +430,10 @@ html { color: #4285f4; } +.shuffleButton-active { + color: #4285f4 !important; +} + .card:focus .cardBox.visualCardBox, .card:focus .cardBox:not(.visualCardBox) .cardScalable { border-color: #fff !important; From 40c2ccaab3a1bfbbddf39ae29dbe6504b2e7dc90 Mon Sep 17 00:00:00 2001 From: ferferga Date: Thu, 18 Jun 2020 01:24:04 +0200 Subject: [PATCH 07/42] Minor style improvements to nowPlaying --- src/components/nowPlayingBar/nowPlayingBar.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/nowPlayingBar/nowPlayingBar.css b/src/components/nowPlayingBar/nowPlayingBar.css index b1e77715f..e83ef729d 100644 --- a/src/components/nowPlayingBar/nowPlayingBar.css +++ b/src/components/nowPlayingBar/nowPlayingBar.css @@ -56,8 +56,8 @@ text-align: left; flex-grow: 1; font-size: 92%; - margin-right: 2.4em; - margin-left: 1em; + margin-right: 1em; + margin-left: 0.2em; } .nowPlayingBarCenter { @@ -159,7 +159,7 @@ } .nowPlayingBarInfoContainer { - width: 70%; + width: 100%; } } From 2ab476f073790845e1b12b79181da1a6f30d4a5a Mon Sep 17 00:00:00 2001 From: ferferga Date: Thu, 18 Jun 2020 08:04:33 +0200 Subject: [PATCH 08/42] Fix playlist for items without images --- src/components/listview/listview.js | 90 +++++++++++++++-------------- 1 file changed, 46 insertions(+), 44 deletions(-) diff --git a/src/components/listview/listview.js b/src/components/listview/listview.js index 42f32ba79..4ce8526c9 100644 --- a/src/components/listview/listview.js +++ b/src/components/listview/listview.js @@ -270,52 +270,54 @@ define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutMan if (options.image !== false) { let imgData = options.imageSource === 'channel' ? getChannelImageUrl(item, downloadWidth) : getImageUrl(item, downloadWidth); - let imgUrl = imgData.url; - let blurhash = imgData.blurhash; - let imageClass = isLargeStyle ? 'listItemImage listItemImage-large' : 'listItemImage'; + if (imgData) { + let imgUrl = imgData.url; + let blurhash = imgData.blurhash; + let imageClass = isLargeStyle ? 'listItemImage listItemImage-large' : 'listItemImage'; - if (isLargeStyle && layoutManager.tv) { - imageClass += ' listItemImage-large-tv'; + if (isLargeStyle && layoutManager.tv) { + imageClass += ' listItemImage-large-tv'; + } + + var playOnImageClick = options.imagePlayButton && !layoutManager.tv; + + if (!clickEntireItem) { + imageClass += ' itemAction'; + } + + var imageAction = playOnImageClick ? 'resume' : action; + + let blurhashAttrib = ''; + if (blurhash && blurhash.length > 0) { + blurhashAttrib = 'data-blurhash="' + blurhash + '"'; + } + + if (imgUrl) { + html += '
'; + } else { + html += '
'; + } + + var indicatorsHtml = ''; + indicatorsHtml += indicators.getPlayedIndicatorHtml(item); + + if (indicatorsHtml) { + html += '
' + indicatorsHtml + '
'; + } + + if (playOnImageClick) { + html += ''; + } + + var progressHtml = indicators.getProgressBarHtml(item, { + containerClass: 'listItemProgressBar' + }); + + if (progressHtml) { + html += progressHtml; + } + html += '
'; } - - var playOnImageClick = options.imagePlayButton && !layoutManager.tv; - - if (!clickEntireItem) { - imageClass += ' itemAction'; - } - - var imageAction = playOnImageClick ? 'resume' : action; - - let blurhashAttrib = ''; - if (blurhash && blurhash.length > 0) { - blurhashAttrib = 'data-blurhash="' + blurhash + '"'; - } - - if (imgUrl) { - html += '
'; - } else { - html += '
'; - } - - var indicatorsHtml = ''; - indicatorsHtml += indicators.getPlayedIndicatorHtml(item); - - if (indicatorsHtml) { - html += '
' + indicatorsHtml + '
'; - } - - if (playOnImageClick) { - html += ''; - } - - var progressHtml = indicators.getProgressBarHtml(item, { - containerClass: 'listItemProgressBar' - }); - - if (progressHtml) { - html += progressHtml; - } - html += '
'; } if (options.showIndexNumberLeft) { From 2a74d53c35cfe8a9388a6f33c06f347c7eb1f93f Mon Sep 17 00:00:00 2001 From: ferferga Date: Thu, 18 Jun 2020 08:39:23 +0200 Subject: [PATCH 09/42] Finish work on shuffle mode --- src/components/nowPlayingBar/nowPlayingBar.js | 72 ++++++++++++++----- .../remotecontrol/remotecontrol.css | 4 +- src/components/remotecontrol/remotecontrol.js | 10 ++- src/nowplaying.html | 13 ---- 4 files changed, 64 insertions(+), 35 deletions(-) diff --git a/src/components/nowPlayingBar/nowPlayingBar.js b/src/components/nowPlayingBar/nowPlayingBar.js index 33f9aa405..e1d8ebba6 100644 --- a/src/components/nowPlayingBar/nowPlayingBar.js +++ b/src/components/nowPlayingBar/nowPlayingBar.js @@ -47,7 +47,9 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', html += ''; html += ''; - html += ''; + if (!layoutManager.mobile) { + html += ''; + } html += '
'; html += '
'; @@ -61,12 +63,17 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', html += '
'; html += ''; + html += ''; html += '
'; html += '
'; html += ''; - html += ''; + if (layoutManager.mobile) { + html += ''; + } else { + html += ''; + } html += '
'; html += '
'; @@ -163,6 +170,16 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', } }); + elem.querySelector('.btnShuffle').addEventListener('click', function () { + if (currentPlayer) { + if (playbackManager.getPlaylistShuffleMode(currentPlayer) === 'Sorted') { + playbackManager.setPlaylistShuffleMode('Shuffle', currentPlayer); + } else { + playbackManager.setPlaylistShuffleMode('Sorted', currentPlayer); + } + } + }); + toggleRepeatButton = elem.querySelector('.toggleRepeatButton'); toggleRepeatButton.addEventListener('click', function () { @@ -263,6 +280,10 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', parentContainer.insertAdjacentHTML('afterbegin', getNowPlayingBarHtml()); nowPlayingBarElement = parentContainer.querySelector('.nowPlayingBar'); + if (layoutManager.mobile) { + hideButton(nowPlayingBarElement.querySelector('.shuffle')); + } + if (browser.safari && browser.slow) { // Not handled well here. The wrong elements receive events, bar doesn't update quickly enough, etc. nowPlayingBarElement.classList.add('noMediaProgress'); @@ -316,6 +337,7 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', } updateRepeatModeDisplay(playState.RepeatMode); + onPlaylistShuffleModeChange(); updatePlayerVolumeState(playState.IsMuted, playState.VolumeLevel); @@ -550,22 +572,24 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', var apiClient = connectionManager.getApiClient(nowPlayingItem.ServerId); apiClient.getItem(apiClient.getCurrentUserId(), nowPlayingItem.Id).then(function (item) { var userData = item.UserData || {}; - var likes = userData.Likes == null ? '' : userData.Likes; - var contextButton = document.querySelector('.nowPlayingBar').querySelector('.btnToggleContextMenu'); - var options = { - play: false, - queue: false, - positionTo: contextButton - }; - nowPlayingUserData.innerHTML = ''; - apiClient.getCurrentUser().then(function(user) { - contextButton.addEventListener('click', function () { - itemContextMenu.show(Object.assign({ - item: item, - user: user - }, options )); + if (!layoutManager.mobile) { + var likes = userData.Likes == null ? '' : userData.Likes; + var contextButton = document.querySelector('.nowPlayingBar').querySelector('.btnToggleContextMenu'); + var options = { + play: false, + queue: false, + positionTo: contextButton + }; + apiClient.getCurrentUser().then(function (user) { + contextButton.addEventListener('click', function () { + itemContextMenu.show(Object.assign({ + item: item, + user: user + }, options)); + }); }); - }); + } + nowPlayingUserData.innerHTML = ''; }); } } else { @@ -592,6 +616,18 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', updateRepeatModeDisplay(playbackManager.getRepeatMode(player)); } + function onPlaylistShuffleModeChange() { + let shuffleMode = playbackManager.getPlaylistShuffleMode(this); + let context = nowPlayingBarElement; + let toggleShuffleButton = context.querySelector('.btnShuffle'); + + if ('Sorted' === shuffleMode) { + toggleShuffleButton.classList.remove('shuffleButton-active'); + } else if ('Shuffle' === shuffleMode) { + toggleShuffleButton.classList.add('shuffleButton-active'); + } + } + function showNowPlayingBar() { if (!isVisibilityAllowed) { @@ -697,6 +733,7 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', events.off(player, 'playbackstart', onPlaybackStart); events.off(player, 'statechange', onPlaybackStart); events.off(player, 'repeatmodechange', onRepeatModeChange); + events.off(player, 'shuffleplaylistmodechange', onPlaylistShuffleModeChange); events.off(player, 'playbackstop', onPlaybackStopped); events.off(player, 'volumechange', onVolumeChanged); events.off(player, 'pause', onPlayPauseStateChanged); @@ -745,6 +782,7 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', events.on(player, 'playbackstart', onPlaybackStart); events.on(player, 'statechange', onPlaybackStart); events.on(player, 'repeatmodechange', onRepeatModeChange); + events.on(player, 'shuffleplaylistmodechange', onPlaylistShuffleModeChange); events.on(player, 'playbackstop', onPlaybackStopped); events.on(player, 'volumechange', onVolumeChanged); events.on(player, 'pause', onPlayPauseStateChanged); diff --git a/src/components/remotecontrol/remotecontrol.css b/src/components/remotecontrol/remotecontrol.css index 34a65f61e..6e6dc3bf1 100644 --- a/src/components/remotecontrol/remotecontrol.css +++ b/src/components/remotecontrol/remotecontrol.css @@ -290,7 +290,7 @@ border-radius: 0; } - .nowPlayingInfoButtons .btnRepeatMobile { + .nowPlayingInfoButtons .btnRepeat { position: absolute; left: 0; margin-left: 0; @@ -298,7 +298,7 @@ font-size: smaller; } - .nowPlayingInfoButtons .btnShuffleMobile { + .nowPlayingInfoButtons .btnShuffle { position: absolute; right: 0; margin-right: 0; diff --git a/src/components/remotecontrol/remotecontrol.js b/src/components/remotecontrol/remotecontrol.js index 8755fb491..0b6ba6d57 100644 --- a/src/components/remotecontrol/remotecontrol.js +++ b/src/components/remotecontrol/remotecontrol.js @@ -2,6 +2,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL 'use strict'; var showMuteButton = true; var showVolumeSlider = true; + var shuffleButton; function showAudioMenu(context, player, button, item) { var currentIndex = playbackManager.getAudioStreamIndex(player); @@ -296,8 +297,6 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL if (layoutManager.mobile) { 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); @@ -326,6 +325,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL } updateRepeatModeDisplay(playState.RepeatMode); + onShufflePlaylistModeChange(); updateNowPlayingInfo(context, state); } @@ -826,9 +826,11 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL volumecontrolHtml += ``; volumecontrolHtml += '
'; volumecontrolHtml += ''; + let shuffleButtonHtml = ``; + let repeatButtonHtml = ``; let optionsSection = context.querySelector('.playlistSectionButton'); if (!layoutManager.mobile) { - context.querySelector('.nowPlayingSecondaryButtons').innerHTML += volumecontrolHtml; + context.querySelector('.nowPlayingSecondaryButtons').insertAdjacentHTML('beforeend', repeatButtonHtml + shuffleButtonHtml + volumecontrolHtml); optionsSection.innerHTML += contextmenuHtml; optionsSection.classList.remove('align-items-center', 'justify-content-center'); optionsSection.classList.add('align-items-right', 'justify-content-flex-end'); @@ -837,6 +839,8 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL optionsSection.innerHTML += volumecontrolHtml + contextmenuHtml; optionsSection.classList.add('playlistSectionButtonTransparent'); context.querySelector('.btnTogglePlaylist').classList.remove('hide'); + context.querySelector('.nowPlayingInfoButtons').insertAdjacentHTML('afterbegin', repeatButtonHtml); + context.querySelector('.nowPlayingInfoButtons').insertAdjacentHTML('beforeend', shuffleButtonHtml); } bindEvents(context); diff --git a/src/nowplaying.html b/src/nowplaying.html index 70f8e298e..0c7688246 100644 --- a/src/nowplaying.html +++ b/src/nowplaying.html @@ -29,11 +29,6 @@
- - -
@@ -80,11 +72,6 @@ - -
From e2da870f3282d13d66ea38e6d5f0aeea55fe3016 Mon Sep 17 00:00:00 2001 From: ferferga Date: Thu, 18 Jun 2020 08:56:09 +0200 Subject: [PATCH 10/42] Take out likes variable from condition --- src/components/nowPlayingBar/nowPlayingBar.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/nowPlayingBar/nowPlayingBar.js b/src/components/nowPlayingBar/nowPlayingBar.js index e1d8ebba6..958b0ebfc 100644 --- a/src/components/nowPlayingBar/nowPlayingBar.js +++ b/src/components/nowPlayingBar/nowPlayingBar.js @@ -572,10 +572,10 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', var apiClient = connectionManager.getApiClient(nowPlayingItem.ServerId); apiClient.getItem(apiClient.getCurrentUserId(), nowPlayingItem.Id).then(function (item) { var userData = item.UserData || {}; + var likes = userData.Likes == null ? '' : userData.Likes; if (!layoutManager.mobile) { - var likes = userData.Likes == null ? '' : userData.Likes; - var contextButton = document.querySelector('.nowPlayingBar').querySelector('.btnToggleContextMenu'); - var options = { + let contextButton = document.querySelector('.nowPlayingBar').querySelector('.btnToggleContextMenu'); + let options = { play: false, queue: false, positionTo: contextButton From dcb0eb8c01c5e70daf229d6b6b9f0f3999886e70 Mon Sep 17 00:00:00 2001 From: ferferga Date: Thu, 18 Jun 2020 09:06:12 +0200 Subject: [PATCH 11/42] Show .btnSavePlaylist always on desktop --- src/components/remotecontrol/remotecontrol.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/remotecontrol/remotecontrol.js b/src/components/remotecontrol/remotecontrol.js index 0b6ba6d57..e084afc8e 100644 --- a/src/components/remotecontrol/remotecontrol.js +++ b/src/components/remotecontrol/remotecontrol.js @@ -481,7 +481,6 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL } imageLoader.lazyChildren(itemsContainer); - context.querySelector('.btnSavePlaylist').classList.add('hide'); }); } @@ -835,6 +834,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL optionsSection.classList.remove('align-items-center', 'justify-content-center'); optionsSection.classList.add('align-items-right', 'justify-content-flex-end'); context.querySelector('.playlist').classList.remove('hide'); + context.querySelector('.btnSavePlaylist').classList.remove('hide'); } else { optionsSection.innerHTML += volumecontrolHtml + contextmenuHtml; optionsSection.classList.add('playlistSectionButtonTransparent'); From 1a6e0973f3399e0606f5a0ac3d8b06aa6b4c551e Mon Sep 17 00:00:00 2001 From: ferferga Date: Thu, 18 Jun 2020 09:22:17 +0200 Subject: [PATCH 12/42] Fix duplicated context menus --- src/components/nowPlayingBar/nowPlayingBar.js | 4 ++++ src/components/remotecontrol/remotecontrol.js | 6 +++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/components/nowPlayingBar/nowPlayingBar.js b/src/components/nowPlayingBar/nowPlayingBar.js index 958b0ebfc..e50c0cee5 100644 --- a/src/components/nowPlayingBar/nowPlayingBar.js +++ b/src/components/nowPlayingBar/nowPlayingBar.js @@ -575,6 +575,10 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', var likes = userData.Likes == null ? '' : userData.Likes; if (!layoutManager.mobile) { let contextButton = document.querySelector('.nowPlayingBar').querySelector('.btnToggleContextMenu'); + // We remove the previous event listener by replacing the item in each update event + let contextButtonClone = contextButton.cloneNode(true); + contextButton.parentNode.replaceChild(contextButtonClone, contextButton); + contextButton = document.querySelector('.nowPlayingBar').querySelector('.btnToggleContextMenu'); let options = { play: false, queue: false, diff --git a/src/components/remotecontrol/remotecontrol.js b/src/components/remotecontrol/remotecontrol.js index e084afc8e..f2a298ac2 100644 --- a/src/components/remotecontrol/remotecontrol.js +++ b/src/components/remotecontrol/remotecontrol.js @@ -168,7 +168,11 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL }) : null; console.debug('updateNowPlayingInfo'); - var contextButton = context.querySelector('.btnToggleContextMenu'); + let contextButton = context.querySelector('.btnToggleContextMenu'); + // We remove the previous event listener by replacing the item in each update event + let contextButtonClone = contextButton.cloneNode(true); + contextButton.parentNode.replaceChild(contextButtonClone, contextButton); + contextButton = context.querySelector('.btnToggleContextMenu'); var options = { play: false, queue: false, From 989c99521fc8e820157aedd74e95c14af66a893a Mon Sep 17 00:00:00 2001 From: ferferga Date: Thu, 18 Jun 2020 09:30:31 +0200 Subject: [PATCH 13/42] Go to previous track on double click --- src/components/nowPlayingBar/nowPlayingBar.js | 15 ++++++++++++--- src/components/remotecontrol/remotecontrol.js | 17 ++++++++++++----- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/components/nowPlayingBar/nowPlayingBar.js b/src/components/nowPlayingBar/nowPlayingBar.js index e50c0cee5..9eebdfcfc 100644 --- a/src/components/nowPlayingBar/nowPlayingBar.js +++ b/src/components/nowPlayingBar/nowPlayingBar.js @@ -157,12 +157,15 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', } }); - elem.querySelector('.previousTrackButton').addEventListener('click', function () { + elem.querySelector('.previousTrackButton').addEventListener('click', function (e) { if (currentPlayer) { if (currentPlayer.id === 'htmlaudioplayer' && (currentPlayer._currentTime <= 5 || !playbackManager.previousTrack(currentPlayer))) { + // Cancel this event if doubleclick is fired + if (e.originalEvent.detail > 1) { + return; + } playbackManager.seekPercent(0, currentPlayer); - // This is done automatically by playbackManager, however, setting this here - // gives instant visual feedback + // This is done automatically by playbackManager, however, setting this here gives instant visual feedback positionSlider.value = 0; } else { playbackManager.previousTrack(currentPlayer); @@ -170,6 +173,12 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', } }); + elem.querySelector('.previousTrackButton').addEventListener('dblclick', function () { + if (currentPlayer) { + playbackManager.previousTrack(currentPlayer); + } + }); + elem.querySelector('.btnShuffle').addEventListener('click', function () { if (currentPlayer) { if (playbackManager.getPlaylistShuffleMode(currentPlayer) === 'Sorted') { diff --git a/src/components/remotecontrol/remotecontrol.js b/src/components/remotecontrol/remotecontrol.js index f2a298ac2..3d1faadff 100644 --- a/src/components/remotecontrol/remotecontrol.js +++ b/src/components/remotecontrol/remotecontrol.js @@ -2,7 +2,6 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL 'use strict'; var showMuteButton = true; var showVolumeSlider = true; - var shuffleButton; function showAudioMenu(context, player, button, item) { var currentIndex = playbackManager.getAudioStreamIndex(player); @@ -704,19 +703,27 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL } }); - context.querySelector('.btnPreviousTrack').addEventListener('click', function () { - console.log(currentPlayer); + context.querySelector('.btnPreviousTrack').addEventListener('click', function (e) { if (currentPlayer) { if (currentPlayer.id === 'htmlaudioplayer' && (currentPlayer._currentTime <= 5 || !playbackManager.previousTrack(currentPlayer))) { + // Cancel this event if doubleclick is fired + if (e.originalEvent.detail > 1) { + return; + } playbackManager.seekPercent(0, currentPlayer); - // This is done automatically by playbackManager. However, setting this here - // gives instant visual feedback + // This is done automatically by playbackManager. However, setting this here gives instant visual feedback positionSlider.value = 0; } else { playbackManager.previousTrack(currentPlayer); } } }); + + context.querySelector('.btnPreviousTrack').addEventListener('dblclick', function () { + if (currentPlayer) { + playbackManager.previousTrack(currentPlayer); + } + }); positionSlider.addEventListener('change', function () { var value = this.value; From 0e2b960ae795ef2e6be7553bc92ca9f286d8fe23 Mon Sep 17 00:00:00 2001 From: ferferga Date: Thu, 18 Jun 2020 10:15:30 +0200 Subject: [PATCH 14/42] Repeat song on double click only if there is a previous item --- src/components/nowPlayingBar/nowPlayingBar.js | 2 +- src/components/remotecontrol/remotecontrol.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/nowPlayingBar/nowPlayingBar.js b/src/components/nowPlayingBar/nowPlayingBar.js index 9eebdfcfc..1c1fbb9f0 100644 --- a/src/components/nowPlayingBar/nowPlayingBar.js +++ b/src/components/nowPlayingBar/nowPlayingBar.js @@ -161,7 +161,7 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', if (currentPlayer) { if (currentPlayer.id === 'htmlaudioplayer' && (currentPlayer._currentTime <= 5 || !playbackManager.previousTrack(currentPlayer))) { // Cancel this event if doubleclick is fired - if (e.originalEvent.detail > 1) { + if (e.originalEvent.detail > 1 && playbackManager.previousTrack(currentPlayer)) { return; } playbackManager.seekPercent(0, currentPlayer); diff --git a/src/components/remotecontrol/remotecontrol.js b/src/components/remotecontrol/remotecontrol.js index 3d1faadff..dd4e42c0c 100644 --- a/src/components/remotecontrol/remotecontrol.js +++ b/src/components/remotecontrol/remotecontrol.js @@ -707,7 +707,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL if (currentPlayer) { if (currentPlayer.id === 'htmlaudioplayer' && (currentPlayer._currentTime <= 5 || !playbackManager.previousTrack(currentPlayer))) { // Cancel this event if doubleclick is fired - if (e.originalEvent.detail > 1) { + if (e.originalEvent.detail > 1 && playbackManager.previousTrack(currentPlayer)) { return; } playbackManager.seekPercent(0, currentPlayer); From f9119cc2f6164eb561693d729ad0dcd525e28391 Mon Sep 17 00:00:00 2001 From: ferferga Date: Thu, 18 Jun 2020 10:55:58 +0200 Subject: [PATCH 15/42] Replace context menu 'ViewArtist' with 'ViewAlbumArtist' option for players and fix texting bits --- src/components/itemContextMenu.js | 9 +++++---- src/components/nowPlayingBar/nowPlayingBar.js | 2 +- src/components/remotecontrol/remotecontrol.js | 2 +- src/strings/en-us.json | 3 +-- src/strings/es.json | 5 +++-- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/components/itemContextMenu.js b/src/components/itemContextMenu.js index f258f5fe4..4f0e4e506 100644 --- a/src/components/itemContextMenu.js +++ b/src/components/itemContextMenu.js @@ -288,10 +288,11 @@ define(['apphost', 'globalize', 'connectionManager', 'itemHelper', 'appRouter', icon: 'album' }); } - - if (options.openArtist !== false && item.ArtistItems && item.ArtistItems.length) { + // Show Album Artist by default, as a song can have multiple artists, which specific one would this option refer to? + // Although some albums can have multiple artists, it's not as common as songs. + if (options.openArtist !== false && item.AlbumArtists && item.AlbumArtists.length) { commands.push({ - name: globalize.translate('ViewArtist'), + name: globalize.translate('ViewAlbumArtist'), id: 'artist', icon: 'person' }); @@ -458,7 +459,7 @@ define(['apphost', 'globalize', 'connectionManager', 'itemHelper', 'appRouter', getResolveFunction(resolve, id)(); break; case 'artist': - appRouter.showItem(item.ArtistItems[0].Id, item.ServerId); + appRouter.showItem(item.AlbumArtists[0].Id, item.ServerId); getResolveFunction(resolve, id)(); break; case 'playallfromhere': diff --git a/src/components/nowPlayingBar/nowPlayingBar.js b/src/components/nowPlayingBar/nowPlayingBar.js index 1c1fbb9f0..50830af10 100644 --- a/src/components/nowPlayingBar/nowPlayingBar.js +++ b/src/components/nowPlayingBar/nowPlayingBar.js @@ -161,7 +161,7 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', if (currentPlayer) { if (currentPlayer.id === 'htmlaudioplayer' && (currentPlayer._currentTime <= 5 || !playbackManager.previousTrack(currentPlayer))) { // Cancel this event if doubleclick is fired - if (e.originalEvent.detail > 1 && playbackManager.previousTrack(currentPlayer)) { + if (e.detail > 1 && playbackManager.previousTrack(currentPlayer)) { return; } playbackManager.seekPercent(0, currentPlayer); diff --git a/src/components/remotecontrol/remotecontrol.js b/src/components/remotecontrol/remotecontrol.js index dd4e42c0c..3a87fff80 100644 --- a/src/components/remotecontrol/remotecontrol.js +++ b/src/components/remotecontrol/remotecontrol.js @@ -707,7 +707,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL if (currentPlayer) { if (currentPlayer.id === 'htmlaudioplayer' && (currentPlayer._currentTime <= 5 || !playbackManager.previousTrack(currentPlayer))) { // Cancel this event if doubleclick is fired - if (e.originalEvent.detail > 1 && playbackManager.previousTrack(currentPlayer)) { + if (e.detail > 1 && playbackManager.previousTrack(currentPlayer)) { return; } playbackManager.seekPercent(0, currentPlayer); diff --git a/src/strings/en-us.json b/src/strings/en-us.json index bf77f630a..fcfb3e711 100644 --- a/src/strings/en-us.json +++ b/src/strings/en-us.json @@ -1521,7 +1521,7 @@ "Vertical": "Vertical", "VideoRange": "Video range", "ViewAlbum": "View album", - "ViewArtist": "View artist", + "ViewAlbumArtist": "View album artist", "ViewPlaybackInfo": "View playback info", "Watched": "Watched", "Wednesday": "Wednesday", @@ -1554,5 +1554,4 @@ "UnsupportedPlayback": "Jellyfin cannot decrypt content protected by DRM but all content will be attempted regardless, including protected titles. Some files may appear completely black due to encryption or other unsupported features, such as interactive titles.", "EnableBlurhash": "Enable blurred placeholders for images", "EnableBlurhashHelp": "Images that are still being loaded will be displayed with a blurred placeholder" - } diff --git a/src/strings/es.json b/src/strings/es.json index eedfa3d73..09b5f2cae 100644 --- a/src/strings/es.json +++ b/src/strings/es.json @@ -1205,7 +1205,7 @@ "ValueTimeLimitSingleHour": "Tiempo límite: 1 hora", "ValueVideoCodec": "Códec de video: {0}", "ViewAlbum": "Ver album", - "ViewArtist": "Ver artista", + "ViewAlbumArtist": "Ver artista del álbum", "ViewPlaybackInfo": "Ver información de la reproducción", "Watched": "Visto", "Wednesday": "Miércoles", @@ -1560,5 +1560,6 @@ "EnableDetailsBannerHelp": "Mostrar imagen de banner en el tope de la página de detalles del elemento.", "EnableDetailsBanner": "Barra de Detalles", "ShowMore": "Mostrar más", - "ShowLess": "Mostrar menos" + "ShowLess": "Mostrar menos", + "ViewAlbumArtist": "Ver artista del álbum" } From 5a87754c2adc452ba9cdd80e538e0b3e5106482a Mon Sep 17 00:00:00 2001 From: ferferga Date: Thu, 18 Jun 2020 11:12:03 +0200 Subject: [PATCH 16/42] Switch wrong condition for the new previous track behaviour and fix lint --- src/components/nowPlayingBar/nowPlayingBar.js | 2 +- src/components/remotecontrol/remotecontrol.css | 1 + src/components/remotecontrol/remotecontrol.js | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/nowPlayingBar/nowPlayingBar.js b/src/components/nowPlayingBar/nowPlayingBar.js index 50830af10..f76055226 100644 --- a/src/components/nowPlayingBar/nowPlayingBar.js +++ b/src/components/nowPlayingBar/nowPlayingBar.js @@ -159,7 +159,7 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', elem.querySelector('.previousTrackButton').addEventListener('click', function (e) { if (currentPlayer) { - if (currentPlayer.id === 'htmlaudioplayer' && (currentPlayer._currentTime <= 5 || !playbackManager.previousTrack(currentPlayer))) { + if (currentPlayer.id === 'htmlaudioplayer' && (currentPlayer._currentTime >= 5 || !playbackManager.previousTrack(currentPlayer))) { // Cancel this event if doubleclick is fired if (e.detail > 1 && playbackManager.previousTrack(currentPlayer)) { return; diff --git a/src/components/remotecontrol/remotecontrol.css b/src/components/remotecontrol/remotecontrol.css index 6e6dc3bf1..7df8fefc3 100644 --- a/src/components/remotecontrol/remotecontrol.css +++ b/src/components/remotecontrol/remotecontrol.css @@ -364,6 +364,7 @@ padding-left: 7.3%; padding-right: 7.3%; } + .playlistSectionButtonTransparent { background: rgba(0, 0, 0, 0) !important; } diff --git a/src/components/remotecontrol/remotecontrol.js b/src/components/remotecontrol/remotecontrol.js index 3a87fff80..c31d57a74 100644 --- a/src/components/remotecontrol/remotecontrol.js +++ b/src/components/remotecontrol/remotecontrol.js @@ -705,7 +705,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL context.querySelector('.btnPreviousTrack').addEventListener('click', function (e) { if (currentPlayer) { - if (currentPlayer.id === 'htmlaudioplayer' && (currentPlayer._currentTime <= 5 || !playbackManager.previousTrack(currentPlayer))) { + if (currentPlayer.id === 'htmlaudioplayer' && (currentPlayer._currentTime >= 5 || !playbackManager.previousTrack(currentPlayer))) { // Cancel this event if doubleclick is fired if (e.detail > 1 && playbackManager.previousTrack(currentPlayer)) { return; From 6c0f3a39c745f7e818d535d45a273ec1f8dadaab Mon Sep 17 00:00:00 2001 From: ferferga Date: Thu, 18 Jun 2020 12:27:15 +0200 Subject: [PATCH 17/42] Remove blank space when item doesn't have an image --- src/components/nowPlayingBar/nowPlayingBar.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/nowPlayingBar/nowPlayingBar.js b/src/components/nowPlayingBar/nowPlayingBar.js index f76055226..6fc526489 100644 --- a/src/components/nowPlayingBar/nowPlayingBar.js +++ b/src/components/nowPlayingBar/nowPlayingBar.js @@ -570,8 +570,12 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', if (url) { imageLoader.lazyImage(nowPlayingImageElement, url); + nowPlayingImageElement.style.display = null; + nowPlayingTextElement.style.marginLeft = null; } else { nowPlayingImageElement.style.backgroundImage = ''; + nowPlayingImageElement.style.display = 'none'; + nowPlayingTextElement.style.marginLeft = '1em'; } } From 3b938e9b00ac33b6a37090a233d5ce88ad3d16a6 Mon Sep 17 00:00:00 2001 From: ferferga Date: Thu, 18 Jun 2020 13:47:57 +0200 Subject: [PATCH 18/42] Remove repeated string in es.json --- src/strings/es.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/strings/es.json b/src/strings/es.json index 09b5f2cae..126692923 100644 --- a/src/strings/es.json +++ b/src/strings/es.json @@ -1560,6 +1560,5 @@ "EnableDetailsBannerHelp": "Mostrar imagen de banner en el tope de la página de detalles del elemento.", "EnableDetailsBanner": "Barra de Detalles", "ShowMore": "Mostrar más", - "ShowLess": "Mostrar menos", - "ViewAlbumArtist": "Ver artista del álbum" + "ShowLess": "Mostrar menos" } From a62d69f81360528d057ca74daf3c4abe08f74b4a Mon Sep 17 00:00:00 2001 From: ferferga Date: Thu, 18 Jun 2020 18:31:46 +0200 Subject: [PATCH 19/42] CSS fixes when resizing the window --- .../nowPlayingBar/nowPlayingBar.css | 19 +- .../remotecontrol/remotecontrol.css | 322 ++++-------------- src/components/remotecontrol/remotecontrol.js | 4 + 3 files changed, 86 insertions(+), 259 deletions(-) diff --git a/src/components/nowPlayingBar/nowPlayingBar.css b/src/components/nowPlayingBar/nowPlayingBar.css index e83ef729d..a2d5b4479 100644 --- a/src/components/nowPlayingBar/nowPlayingBar.css +++ b/src/components/nowPlayingBar/nowPlayingBar.css @@ -57,7 +57,7 @@ flex-grow: 1; font-size: 92%; margin-right: 1em; - margin-left: 0.2em; + margin-left: 0.5em; } .nowPlayingBarCenter { @@ -135,12 +135,23 @@ } } -@media all and (max-width: 62em) { - .nowPlayingBarCenter .nowPlayingBarCurrentTime { +@media all and (max-width: 66em) { + .btnShuffle { display: none !important; } } +@media all and (max-width: 80em) { + .nowPlayingBarCenter .nowPlayingBarCurrentTime, + .nowPlayingBarCenter .stopButton { + display: none !important; + } + + .nowPlayingBarInfoContainer { + width: 45%; + } +} + @media all and (max-width: 56em) { .nowPlayingBarCenter { display: none !important; @@ -153,7 +164,7 @@ } } -@media all and (max-width: 36em) { +@media all and (max-width: 60em) { .nowPlayingBarRight .nowPlayingBarVolumeSliderContainer { display: none !important; } diff --git a/src/components/remotecontrol/remotecontrol.css b/src/components/remotecontrol/remotecontrol.css index 7df8fefc3..d80d7a3c3 100644 --- a/src/components/remotecontrol/remotecontrol.css +++ b/src/components/remotecontrol/remotecontrol.css @@ -171,6 +171,72 @@ z-index: 0; } +.mobilePlayer.playlistSectionButtonTransparent { + background: rgba(0, 0, 0, 0) !important; +} + +.mobilePlayer.playlistSection .playlist, +.mobilePlayer.playlistSection .contextMenu { + position: absolute; + top: 12.2em; + bottom: 4.2em; + overflow: scroll; + padding: 0 1em; + display: inline-block; + left: 0; + right: 0; + z-index: 1000; +} + +.mobilePlayer.playlistSectionButton { + position: fixed; + bottom: 0; + left: 0; + height: 4.2em; + right: 0; + padding-left: 7.3%; + padding-right: 7.3%; +} + +.playlistSectionButton:not(.mobilePlayer) { + background: unset !important; +} + +.nowPlayingPlaylist:not(.mobilePlayer) { + background: unset !important; +} + +.mobilePlayer.playlistSectionButton .btnTogglePlaylist { + font-size: larger; + margin: 0; + padding-left: 0; +} + +.mobilePlayer.playlistSectionButton .btnSavePlaylist { + margin: 0; + padding-right: 0; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + flex-grow: 1; + -webkit-box-pack: end; + -webkit-justify-content: flex-end; + justify-content: flex-end; + border-radius: 0; +} + +.mobilePlayer.playlistSectionButton .btnToggleContextMenu { + font-size: larger; + margin: 0; + padding-right: 0; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + flex-grow: 1; + -webkit-box-pack: end; + -webkit-justify-content: flex-end; + justify-content: flex-end; + border-radius: 0; +} + @media all and (min-width: 63em) { .nowPlayingPage { padding: 8em 0 0 0 !important; @@ -188,12 +254,6 @@ .nowPlayingPageUserDataButtonsTitle { display: none !important; } - - .playlistSectionButton, - .nowPlayingPlaylist, - .nowPlayingContextMenu { - background: unset !important; - } } @media all and (min-width: 80em) { @@ -202,7 +262,7 @@ } } -@media all and (orientation: portrait) and (max-width: 47em) { +@media all and (orientation: portrait) and (max-width: 43em) { .remoteControlContent { padding-left: 7.3% !important; padding-right: 7.3% !important; @@ -342,254 +402,6 @@ width: auto; } - #nowPlayingPage .playlistSection .playlist, - #nowPlayingPage .playlistSection .contextMenu { - position: absolute; - top: 12.2em; - bottom: 4.2em; - overflow: scroll; - padding: 0 1em; - display: inline-block; - left: 0; - right: 0; - z-index: 1000; - } - - .playlistSectionButton { - position: fixed; - bottom: 0; - left: 0; - height: 4.2em; - right: 0; - padding-left: 7.3%; - padding-right: 7.3%; - } - - .playlistSectionButtonTransparent { - background: rgba(0, 0, 0, 0) !important; - } - - .playlistSectionButton .btnTogglePlaylist { - font-size: larger; - margin: 0; - padding-left: 0; - } - - .playlistSectionButton .btnSavePlaylist { - margin: 0; - padding-right: 0; - -webkit-box-flex: 1; - -webkit-flex-grow: 1; - flex-grow: 1; - -webkit-box-pack: end; - -webkit-justify-content: flex-end; - justify-content: flex-end; - border-radius: 0; - } - - .playlistSectionButton .btnToggleContextMenu { - font-size: larger; - margin: 0; - padding-right: 0; - -webkit-box-flex: 1; - -webkit-flex-grow: 1; - flex-grow: 1; - -webkit-box-pack: end; - -webkit-justify-content: flex-end; - justify-content: flex-end; - border-radius: 0; - } - - .playlistSectionButton .volumecontrol { - width: 100%; - } - - .remoteControlSection { - margin: 0; - padding: 0 0 4.2em 0; - } - - .nowPlayingButtonsContainer { - display: flex; - height: 100%; - flex-direction: column; - } -} - -@media all and (orientation: landscape) and (max-width: 63em) { - .remoteControlContent { - padding-left: 4.3% !important; - padding-right: 4.3% !important; - display: flex; - height: 100%; - flex-direction: column; - } - - .nowPlayingInfoContainer { - -webkit-box-orient: horizontal !important; - -webkit-box-direction: normal !important; - -webkit-flex-direction: row !important; - flex-direction: row !important; - -webkit-box-align: center; - -webkit-align-items: center; - align-items: center; - width: 100%; - height: calc(100% - 4.2em); - } - - .nowPlayingPageTitle { - /* text-align: center; */ - margin: 0; - } - - .nowPlayingInfoContainerMedia { - text-align: left !important; - width: 80%; - } - - .nowPlayingPositionSliderContainer { - margin: 0.2em 1em 0.2em 1em; - } - - .nowPlayingInfoButtons { - /* margin: 1.5em 0 0 0; */ - -webkit-box-pack: center; - -webkit-justify-content: center; - justify-content: center; - font-size: x-large; - height: 100%; - } - - .nowPlayingPageImageContainer { - width: 30%; - margin: auto 1em auto auto; - } - - .nowPlayingPageImageContainerNoAlbum .cardImageContainer .cardImageIcon { - font-size: 12em; - color: inherit; - } - - .nowPlayingInfoControls { - margin: 0.5em 0 1em 0; - width: 100%; - -webkit-box-pack: start !important; - -webkit-justify-content: start !important; - justify-content: start !important; - } - - .nowPlayingSecondaryButtons { - -webkit-box-flex: 1; - -webkit-flex-grow: 1; - flex-grow: 1; - -webkit-box-pack: center; - -webkit-justify-content: center; - justify-content: center; - } - - .nowPlayingInfoControls .nowPlayingPageUserDataButtonsTitle { - width: 20%; - font-size: large; - } - - .nowPlayingInfoControls .nowPlayingPageUserDataButtonsTitle button { - padding-top: 0; - padding-right: 0; - margin-right: 0; - float: right; - border-radius: 0; - } - - .paper-icon-button-light:hover { - color: #fff !important; - background-color: transparent !important; - } - - .btnPlayPause { - padding: 0; - margin: 0; - font-size: 1.7em; - } - - .btnPlayPause:hover { - background-color: transparent !important; - } - - .nowPlayingPageImage { - /* width: inherit; */ - overflow-y: hidden; - overflow: hidden; - margin: 0 auto; - } - - .nowPlayingPageImage.nowPlayingPageImageAudio { - width: 100%; - } - - .nowPlayingPageImageContainer.nowPlayingPageImagePoster { - height: 100%; - overflow: hidden; - } - - .nowPlayingPageImageContainer.nowPlayingPageImagePoster img { - height: 100%; - width: auto; - } - - #nowPlayingPage .playlistSection .playlist, - #nowPlayingPage .playlistSection .contextMenu { - position: absolute; - top: 7.2em; - bottom: 4.2em; - overflow: scroll; - padding: 0 1em; - display: inline-block; - left: 0; - right: 0; - z-index: 1000; - } - - .playlistSectionButton { - position: fixed; - bottom: 0; - left: 0; - height: 4.2em; - right: 0; - padding-left: 4.3%; - padding-right: 4.3%; - } - - .playlistSectionButton .btnTogglePlaylist { - font-size: larger; - margin: 0; - padding-left: 0; - } - - .playlistSectionButton .btnSavePlaylist { - margin: 0; - padding-right: 0; - -webkit-box-flex: 1; - -webkit-flex-grow: 1; - flex-grow: 1; - -webkit-box-pack: end; - -webkit-justify-content: flex-end; - justify-content: flex-end; - border-radius: 0; - } - - .playlistSectionButton .btnToggleContextMenu { - font-size: larger; - margin: 0; - padding-right: 0; - -webkit-box-flex: 1; - -webkit-flex-grow: 1; - flex-grow: 1; - -webkit-box-pack: end; - -webkit-justify-content: flex-end; - justify-content: flex-end; - border-radius: 0; - } - .playlistSectionButton .volumecontrol { width: 100%; } diff --git a/src/components/remotecontrol/remotecontrol.js b/src/components/remotecontrol/remotecontrol.js index c31d57a74..0cf8e2f95 100644 --- a/src/components/remotecontrol/remotecontrol.js +++ b/src/components/remotecontrol/remotecontrol.js @@ -852,6 +852,10 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL context.querySelector('.btnTogglePlaylist').classList.remove('hide'); context.querySelector('.nowPlayingInfoButtons').insertAdjacentHTML('afterbegin', repeatButtonHtml); context.querySelector('.nowPlayingInfoButtons').insertAdjacentHTML('beforeend', shuffleButtonHtml); + let childs = context.querySelectorAll('*'); + for (let child of childs) { + child.classList.add('mobilePlayer'); + } } bindEvents(context); From ad72f590f7294e3bc3e1a00ed7bc4ea7db4e3584 Mon Sep 17 00:00:00 2001 From: ferferga Date: Mon, 22 Jun 2020 10:51:42 +0200 Subject: [PATCH 20/42] Use layout-mobile class --- .../remotecontrol/remotecontrol.css | 22 +++++++++++-------- src/components/remotecontrol/remotecontrol.js | 4 ---- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/components/remotecontrol/remotecontrol.css b/src/components/remotecontrol/remotecontrol.css index d80d7a3c3..6dbd3b716 100644 --- a/src/components/remotecontrol/remotecontrol.css +++ b/src/components/remotecontrol/remotecontrol.css @@ -171,12 +171,16 @@ z-index: 0; } -.mobilePlayer.playlistSectionButtonTransparent { +.layout-mobile .playlistSectionButtonTransparent { background: rgba(0, 0, 0, 0) !important; } -.mobilePlayer.playlistSection .playlist, -.mobilePlayer.playlistSection .contextMenu { +.layout-mobile .btnShuffle { + display: unset !important; +} + +.layout-mobile .playlistSection .playlist, +.layout-mobile .playlistSection .contextMenu { position: absolute; top: 12.2em; bottom: 4.2em; @@ -188,7 +192,7 @@ z-index: 1000; } -.mobilePlayer.playlistSectionButton { +.layout-mobile .playlistSectionButton { position: fixed; bottom: 0; left: 0; @@ -198,21 +202,21 @@ padding-right: 7.3%; } -.playlistSectionButton:not(.mobilePlayer) { +.playlistSectionButton:not(>.layout-mobile) { background: unset !important; } -.nowPlayingPlaylist:not(.mobilePlayer) { +.nowPlayingPlaylist:not(>.layout-mobile) { background: unset !important; } -.mobilePlayer.playlistSectionButton .btnTogglePlaylist { +.layout-mobile .playlistSectionButton .btnTogglePlaylist { font-size: larger; margin: 0; padding-left: 0; } -.mobilePlayer.playlistSectionButton .btnSavePlaylist { +.layout-mobile .playlistSectionButton .btnSavePlaylist { margin: 0; padding-right: 0; -webkit-box-flex: 1; @@ -224,7 +228,7 @@ border-radius: 0; } -.mobilePlayer.playlistSectionButton .btnToggleContextMenu { +.layout-mobile .playlistSectionButton .btnToggleContextMenu { font-size: larger; margin: 0; padding-right: 0; diff --git a/src/components/remotecontrol/remotecontrol.js b/src/components/remotecontrol/remotecontrol.js index 0cf8e2f95..c31d57a74 100644 --- a/src/components/remotecontrol/remotecontrol.js +++ b/src/components/remotecontrol/remotecontrol.js @@ -852,10 +852,6 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL context.querySelector('.btnTogglePlaylist').classList.remove('hide'); context.querySelector('.nowPlayingInfoButtons').insertAdjacentHTML('afterbegin', repeatButtonHtml); context.querySelector('.nowPlayingInfoButtons').insertAdjacentHTML('beforeend', shuffleButtonHtml); - let childs = context.querySelectorAll('*'); - for (let child of childs) { - child.classList.add('mobilePlayer'); - } } bindEvents(context); From 2e8e240aa0ff3de9faedf6fbb6a3aa19524a1491 Mon Sep 17 00:00:00 2001 From: ferferga Date: Mon, 22 Jun 2020 11:10:26 +0200 Subject: [PATCH 21/42] Add shuffle mode to Chromecast --- src/plugins/chromecastPlayer/plugin.js | 19 ++++++++++++++++++- src/plugins/sessionPlayer/plugin.js | 4 ++++ src/scripts/serverNotifications.js | 3 +++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/plugins/chromecastPlayer/plugin.js b/src/plugins/chromecastPlayer/plugin.js index b3f75f7a6..22242959f 100644 --- a/src/plugins/chromecastPlayer/plugin.js +++ b/src/plugins/chromecastPlayer/plugin.js @@ -548,7 +548,7 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' events.trigger(instance, 'playbackstop', [state]); - var state = instance.lastPlayerData.PlayState || {}; + state = instance.lastPlayerData.PlayState || {}; var volume = state.VolumeLevel || 0.5; var mute = state.IsMuted || false; @@ -572,6 +572,7 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' bindEventForRelay(instance, 'unpause'); bindEventForRelay(instance, 'volumechange'); bindEventForRelay(instance, 'repeatmodechange'); + bindEventForRelay(instance, 'shuffleplaylistmodechange'); events.on(instance._castPlayer, 'playstatechange', function (e, data) { @@ -651,6 +652,7 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' 'SetSubtitleStreamIndex', 'DisplayContent', 'SetRepeatMode', + 'SetPlaylistShuffleMode', 'EndSession', 'PlayMediaSource', 'PlayTrailers' @@ -864,6 +866,12 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' return state.RepeatMode; }; + ChromecastPlayer.prototype.getPlaylistShuffleMode = function () { + var state = this.lastPlayerData || {}; + state = state.PlayState || {}; + return state.ShuffleMode; + }; + ChromecastPlayer.prototype.playTrailers = function (item) { this._castPlayer.sendMessage({ @@ -884,6 +892,15 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' }); }; + ChromecastPlayer.prototype.setPlaylistShuffleMode = function (value) { + this._castPlayer.sendMessage({ + options: { + ShuffleMode: value + }, + command: 'SetPlaylistShuffleMode' + }); + }; + ChromecastPlayer.prototype.toggleMute = function () { this._castPlayer.sendMessage({ diff --git a/src/plugins/sessionPlayer/plugin.js b/src/plugins/sessionPlayer/plugin.js index 241d3fba4..b607f0441 100644 --- a/src/plugins/sessionPlayer/plugin.js +++ b/src/plugins/sessionPlayer/plugin.js @@ -513,6 +513,10 @@ define(['playbackManager', 'events', 'serverNotifications', 'connectionManager'] }); }; + SessionPlayer.prototype.getPlaylistShuffleMode = function () { + + }; + SessionPlayer.prototype.displayContent = function (options) { sendCommandByName(this, 'DisplayContent', options); diff --git a/src/scripts/serverNotifications.js b/src/scripts/serverNotifications.js index 2553c284f..77c51f33c 100644 --- a/src/scripts/serverNotifications.js +++ b/src/scripts/serverNotifications.js @@ -65,6 +65,9 @@ define(['connectionManager', 'playbackManager', 'syncPlayManager', 'events', 'in case 'SetRepeatMode': playbackManager.setRepeatMode(cmd.Arguments.RepeatMode); break; + case 'SetPlaylistShuffleMode': + playbackManager.setPlaylistShuffleMode(cmd.Arguments.ShuffleMode); + break; case 'VolumeUp': inputManager.trigger('volumeup'); return; From 6aae85e99141521fc77322563642f54571744a1e Mon Sep 17 00:00:00 2001 From: ferferga Date: Mon, 22 Jun 2020 11:25:16 +0200 Subject: [PATCH 22/42] Rename 'Playlist' term to 'Queue' --- src/components/nowPlayingBar/nowPlayingBar.js | 16 ++++++------- src/components/playback/playbackmanager.js | 24 +++++++++---------- src/components/remotecontrol/remotecontrol.js | 16 ++++++------- src/plugins/chromecastPlayer/plugin.js | 10 ++++---- src/plugins/sessionPlayer/plugin.js | 6 ++--- src/scripts/serverNotifications.js | 4 ++-- 6 files changed, 38 insertions(+), 38 deletions(-) diff --git a/src/components/nowPlayingBar/nowPlayingBar.js b/src/components/nowPlayingBar/nowPlayingBar.js index 6fc526489..eb21f9044 100644 --- a/src/components/nowPlayingBar/nowPlayingBar.js +++ b/src/components/nowPlayingBar/nowPlayingBar.js @@ -181,10 +181,10 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', elem.querySelector('.btnShuffle').addEventListener('click', function () { if (currentPlayer) { - if (playbackManager.getPlaylistShuffleMode(currentPlayer) === 'Sorted') { - playbackManager.setPlaylistShuffleMode('Shuffle', currentPlayer); + if (playbackManager.getQueueShuffleMode(currentPlayer) === 'Sorted') { + playbackManager.setQueueShuffleMode('Shuffle', currentPlayer); } else { - playbackManager.setPlaylistShuffleMode('Sorted', currentPlayer); + playbackManager.setQueueShuffleMode('Sorted', currentPlayer); } } }); @@ -346,7 +346,7 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', } updateRepeatModeDisplay(playState.RepeatMode); - onPlaylistShuffleModeChange(); + onQueueShuffleModeChange(); updatePlayerVolumeState(playState.IsMuted, playState.VolumeLevel); @@ -633,8 +633,8 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', updateRepeatModeDisplay(playbackManager.getRepeatMode(player)); } - function onPlaylistShuffleModeChange() { - let shuffleMode = playbackManager.getPlaylistShuffleMode(this); + function onQueueShuffleModeChange() { + let shuffleMode = playbackManager.getQueueShuffleMode(this); let context = nowPlayingBarElement; let toggleShuffleButton = context.querySelector('.btnShuffle'); @@ -750,7 +750,7 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', events.off(player, 'playbackstart', onPlaybackStart); events.off(player, 'statechange', onPlaybackStart); events.off(player, 'repeatmodechange', onRepeatModeChange); - events.off(player, 'shuffleplaylistmodechange', onPlaylistShuffleModeChange); + events.off(player, 'shufflequeuemodechange', onQueueShuffleModeChange); events.off(player, 'playbackstop', onPlaybackStopped); events.off(player, 'volumechange', onVolumeChanged); events.off(player, 'pause', onPlayPauseStateChanged); @@ -799,7 +799,7 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', events.on(player, 'playbackstart', onPlaybackStart); events.on(player, 'statechange', onPlaybackStart); events.on(player, 'repeatmodechange', onRepeatModeChange); - events.on(player, 'shuffleplaylistmodechange', onPlaylistShuffleModeChange); + events.on(player, 'shufflequeuemodechange', onQueueShuffleModeChange); events.on(player, 'playbackstop', onPlaybackStopped); events.on(player, 'volumechange', onVolumeChanged); events.on(player, 'pause', onPlayPauseStateChanged); diff --git a/src/components/playback/playbackmanager.js b/src/components/playback/playbackmanager.js index ad1b156d6..4d58e85cf 100644 --- a/src/components/playback/playbackmanager.js +++ b/src/components/playback/playbackmanager.js @@ -2097,7 +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.ShuffleMode = self.getQueueShuffleMode(player); state.PlayState.MaxStreamingBitrate = self.getMaxStreamingBitrate(player); state.PlayState.PositionTicks = getCurrentTicks(player); @@ -3305,9 +3305,9 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla sendProgressUpdate(player, 'repeatmodechange'); } - function onShufflePlaylistModeChange() { + function onShuffleQueueModeChange() { var player = this; - sendProgressUpdate(player, 'shuffleplaylistmodechange'); + sendProgressUpdate(player, 'shufflequeuemodechange'); } function onPlaylistItemMove(e) { @@ -3364,7 +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, 'shufflequeuemodechange', onShuffleQueueModeChange); events.on(player, 'playlistitemmove', onPlaylistItemMove); events.on(player, 'playlistitemremove', onPlaylistItemRemove); events.on(player, 'playlistitemadd', onPlaylistItemAdd); @@ -3377,7 +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, 'shufflequeuemodechange', onShuffleQueueModeChange); events.on(player, 'playlistitemmove', onPlaylistItemMove); events.on(player, 'playlistitemremove', onPlaylistItemRemove); events.on(player, 'playlistitemadd', onPlaylistItemAdd); @@ -3886,7 +3886,7 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla 'GoToSearch', 'DisplayMessage', 'SetRepeatMode', - 'SetPlaylistShuffleMode', + 'SetQueueShuffleMode', 'PlayMediaSource', 'PlayTrailers' ]; @@ -3941,7 +3941,7 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla return this._playQueueManager.getRepeatMode(); }; - PlaybackManager.prototype.setPlaylistShuffleMode = function (value, player) { + PlaybackManager.prototype.setQueueShuffleMode = function (value, player) { player = player || this._currentPlayer; if (player && !enableLocalPlaylistManagement(player)) { @@ -3949,14 +3949,14 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla } this._playQueueManager.setShuffleMode(value); - events.trigger(player, 'shuffleplaylistmodechange'); + events.trigger(player, 'shufflequeuemodechange'); }; - PlaybackManager.prototype.getPlaylistShuffleMode = function (player) { + PlaybackManager.prototype.getQueueShuffleMode = function (player) { player = player || this._currentPlayer; if (player && !enableLocalPlaylistManagement(player)) { - return player.getPlaylistShuffleMode(); + return player.getQueueShuffleMode(); } return this._playQueueManager.getShuffleMode(); @@ -4030,8 +4030,8 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla case 'SetRepeatMode': this.setRepeatMode(cmd.Arguments.RepeatMode, player); break; - case 'SetPlaylistShuffleMode': - this.setPlaylistShuffleMode(cmd.Arguments.ShuffleMode, player); + case 'SetQueueShuffleMode': + this.setQueueShuffleMode(cmd.Arguments.ShuffleMode, player); break; case 'VolumeUp': this.volumeUp(player); diff --git a/src/components/remotecontrol/remotecontrol.js b/src/components/remotecontrol/remotecontrol.js index c31d57a74..a671bf5af 100644 --- a/src/components/remotecontrol/remotecontrol.js +++ b/src/components/remotecontrol/remotecontrol.js @@ -328,7 +328,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL } updateRepeatModeDisplay(playState.RepeatMode); - onShufflePlaylistModeChange(); + onShuffleQueueModeChange(); updateNowPlayingInfo(context, state); } @@ -498,8 +498,8 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL updateRepeatModeDisplay(playbackManager.getRepeatMode(player)); } - function onShufflePlaylistModeChange() { - let shuffleMode = playbackManager.getPlaylistShuffleMode(this); + function onShuffleQueueModeChange() { + let shuffleMode = playbackManager.getQueueShuffleMode(this); let context = dlg; let shuffleButton = context.querySelector('.btnShuffle'); if ('Sorted' === shuffleMode) { @@ -571,7 +571,7 @@ 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, 'shuffleplaylistmodechange', onShufflePlaylistModeChange); + events.off(player, 'shufflequeuemodechange', onShuffleQueueModeChange); events.off(player, 'playlistitemremove', onPlaylistItemRemoved); events.off(player, 'playlistitemmove', onPlaylistUpdate); events.off(player, 'playbackstop', onPlaybackStopped); @@ -592,7 +592,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, 'shufflequeuemodechange', onShuffleQueueModeChange); events.on(player, 'playlistitemremove', onPlaylistItemRemoved); events.on(player, 'playlistitemmove', onPlaylistUpdate); events.on(player, 'playbackstop', onPlaybackStopped); @@ -695,10 +695,10 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL }); context.querySelector('.btnShuffle').addEventListener('click', function () { if (currentPlayer) { - if (playbackManager.getPlaylistShuffleMode(currentPlayer) === 'Sorted') { - playbackManager.setPlaylistShuffleMode('Shuffle', currentPlayer); + if (playbackManager.getQueueShuffleMode(currentPlayer) === 'Sorted') { + playbackManager.setQueueShuffleMode('Shuffle', currentPlayer); } else { - playbackManager.setPlaylistShuffleMode('Sorted', currentPlayer); + playbackManager.setQueueShuffleMode('Sorted', currentPlayer); } } }); diff --git a/src/plugins/chromecastPlayer/plugin.js b/src/plugins/chromecastPlayer/plugin.js index 22242959f..0809fb82c 100644 --- a/src/plugins/chromecastPlayer/plugin.js +++ b/src/plugins/chromecastPlayer/plugin.js @@ -572,7 +572,7 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' bindEventForRelay(instance, 'unpause'); bindEventForRelay(instance, 'volumechange'); bindEventForRelay(instance, 'repeatmodechange'); - bindEventForRelay(instance, 'shuffleplaylistmodechange'); + bindEventForRelay(instance, 'shufflequeuemodechange'); events.on(instance._castPlayer, 'playstatechange', function (e, data) { @@ -652,7 +652,7 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' 'SetSubtitleStreamIndex', 'DisplayContent', 'SetRepeatMode', - 'SetPlaylistShuffleMode', + 'SetQueueShuffleMode', 'EndSession', 'PlayMediaSource', 'PlayTrailers' @@ -866,7 +866,7 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' return state.RepeatMode; }; - ChromecastPlayer.prototype.getPlaylistShuffleMode = function () { + ChromecastPlayer.prototype.getQueueShuffleMode = function () { var state = this.lastPlayerData || {}; state = state.PlayState || {}; return state.ShuffleMode; @@ -892,12 +892,12 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' }); }; - ChromecastPlayer.prototype.setPlaylistShuffleMode = function (value) { + ChromecastPlayer.prototype.setQueueShuffleMode = function (value) { this._castPlayer.sendMessage({ options: { ShuffleMode: value }, - command: 'SetPlaylistShuffleMode' + command: 'SetQueueShuffleMode' }); }; diff --git a/src/plugins/sessionPlayer/plugin.js b/src/plugins/sessionPlayer/plugin.js index b607f0441..5ed07bb4f 100644 --- a/src/plugins/sessionPlayer/plugin.js +++ b/src/plugins/sessionPlayer/plugin.js @@ -506,14 +506,14 @@ define(['playbackManager', 'events', 'serverNotifications', 'connectionManager'] }); }; - SessionPlayer.prototype.setPlaylistShuffleMode = function (mode) { + SessionPlayer.prototype.setQueueShuffleMode = function (mode) { - sendCommandByName(this, 'SetPlaylistShuffleMode', { + sendCommandByName(this, 'SetQueueShuffleMode', { ShuffleMode: mode }); }; - SessionPlayer.prototype.getPlaylistShuffleMode = function () { + SessionPlayer.prototype.getQueueShuffleMode = function () { }; diff --git a/src/scripts/serverNotifications.js b/src/scripts/serverNotifications.js index 77c51f33c..c1d9c95e9 100644 --- a/src/scripts/serverNotifications.js +++ b/src/scripts/serverNotifications.js @@ -65,8 +65,8 @@ define(['connectionManager', 'playbackManager', 'syncPlayManager', 'events', 'in case 'SetRepeatMode': playbackManager.setRepeatMode(cmd.Arguments.RepeatMode); break; - case 'SetPlaylistShuffleMode': - playbackManager.setPlaylistShuffleMode(cmd.Arguments.ShuffleMode); + case 'SetQueueShuffleMode': + playbackManager.setQueueShuffleMode(cmd.Arguments.ShuffleMode); break; case 'VolumeUp': inputManager.trigger('volumeup'); From d13e1acf8de3c03d27992014a616425031fa650d Mon Sep 17 00:00:00 2001 From: ferferga Date: Mon, 22 Jun 2020 12:41:22 +0200 Subject: [PATCH 23/42] Fix 'undefined' on Chromecast and cleanup on nowPlayingBar.js --- src/components/nowPlayingBar/nowPlayingBar.js | 36 +++++++++---------- src/components/playback/playbackmanager.js | 2 +- src/components/remotecontrol/remotecontrol.js | 14 ++++++-- 3 files changed, 30 insertions(+), 22 deletions(-) diff --git a/src/components/nowPlayingBar/nowPlayingBar.js b/src/components/nowPlayingBar/nowPlayingBar.js index eb21f9044..07cdaac4f 100644 --- a/src/components/nowPlayingBar/nowPlayingBar.js +++ b/src/components/nowPlayingBar/nowPlayingBar.js @@ -457,15 +457,6 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', } } - function getTextActionButton(item, text) { - - if (!text) { - text = itemHelper.getDisplayName(item); - } - - return `${text}`; - } - function seriesImageUrl(item, options) { if (!item) { @@ -541,18 +532,25 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', if (textLines.length > 1) { textLines[1].secondary = true; } - nowPlayingTextElement.innerHTML = textLines.map(function (nowPlayingName) { - var cssClass = nowPlayingName.secondary ? ' class="nowPlayingBarSecondaryText"' : ''; - - if (nowPlayingName.item) { - var nowPlayingText = getTextActionButton(nowPlayingName.item, nowPlayingName.text); - return `
${nowPlayingText}
`; + if (textLines) { + nowPlayingTextElement.innerHTML = ''; + let itemText = document.createElement('div'); + let secondaryText = document.createElement('div'); + secondaryText.classList.add('nowPlayingBarSecondaryText'); + if (textLines[0].text) { + let text = document.createElement('a'); + text.innerHTML = textLines[0].text; + itemText.appendChild(text); } - - return `
${nowPlayingText}
`; - - }).join(''); + if (textLines[1].text) { + let text = document.createElement('a'); + text.innerHTML = textLines[1].text; + secondaryText.appendChild(text); + } + nowPlayingTextElement.appendChild(itemText); + nowPlayingTextElement.appendChild(secondaryText); + } var imgHeight = 70; diff --git a/src/components/playback/playbackmanager.js b/src/components/playback/playbackmanager.js index 4d58e85cf..38ce9bf9f 100644 --- a/src/components/playback/playbackmanager.js +++ b/src/components/playback/playbackmanager.js @@ -3945,7 +3945,7 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla player = player || this._currentPlayer; if (player && !enableLocalPlaylistManagement(player)) { - return player.setShuffleMode(value); + return player.setQueueShuffleMode(value); } this._playQueueManager.setShuffleMode(value); diff --git a/src/components/remotecontrol/remotecontrol.js b/src/components/remotecontrol/remotecontrol.js index a671bf5af..784e7ff5b 100644 --- a/src/components/remotecontrol/remotecontrol.js +++ b/src/components/remotecontrol/remotecontrol.js @@ -120,9 +120,9 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL if (item.Type == 'Audio' || item.MediaStreams[0].Type == 'Audio') { var songName = item.Name; if (item.Album != null && item.Artists != null) { + var artistsSeries = ''; var albumName = item.Album; if (item.ArtistItems != null) { - var artistsSeries = ''; for (let artist of item.ArtistItems) { let artistName = artist.Name; let artistId = artist.Id; @@ -131,8 +131,18 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL artistsSeries += ', '; } } - context.querySelector('.nowPlayingArtist').innerHTML = artistsSeries; + } else if (item.Artists) { + // For some reason, Chromecast Player doesn't return a item.ArtistItems object, so we need to fallback + // to normal item.Artists item. + // TODO: Normalise fields returned by all the players + for (let artist of item.Artists) { + artistsSeries += `${artist}`; + if (artist !== item.Artists.slice(-1)[0]) { + artistsSeries += ', '; + } + } } + context.querySelector('.nowPlayingArtist').innerHTML = artistsSeries; context.querySelector('.nowPlayingAlbum').innerHTML = '${albumName}`; } context.querySelector('.nowPlayingSongName').innerHTML = songName; From f6a9f4679ae14f81b664d63efd09918c977583d3 Mon Sep 17 00:00:00 2001 From: ferferga Date: Mon, 22 Jun 2020 17:37:06 +0200 Subject: [PATCH 24/42] Style cleanup, address suggestions and placeholders for songs in cardBuilder and listView --- src/assets/css/flexstyles.css | 4 + src/components/cardbuilder/card.css | 3 +- src/components/cardbuilder/cardBuilder.js | 3 + src/components/listview/listview.js | 96 ++++++++++--------- .../nowPlayingBar/nowPlayingBar.css | 20 ++-- src/components/nowPlayingBar/nowPlayingBar.js | 17 ++-- .../remotecontrol/remotecontrol.css | 87 ++++++++++------- src/components/remotecontrol/remotecontrol.js | 20 ++-- src/nowplaying.html | 21 ++++ src/themes/appletv/theme.css | 2 +- src/themes/blueradiance/theme.css | 2 +- src/themes/dark/theme.css | 2 +- src/themes/light/theme.css | 2 +- src/themes/purplehaze/theme.css | 2 +- src/themes/wmc/theme.css | 2 +- 15 files changed, 167 insertions(+), 116 deletions(-) diff --git a/src/assets/css/flexstyles.css b/src/assets/css/flexstyles.css index 2f3a386bf..429ed7a65 100644 --- a/src/assets/css/flexstyles.css +++ b/src/assets/css/flexstyles.css @@ -42,6 +42,10 @@ justify-content: flex-end; } +.justify-content-space-between { + justify-content: space-between; +} + .flex-wrap-wrap { flex-wrap: wrap; } diff --git a/src/components/cardbuilder/card.css b/src/components/cardbuilder/card.css index c24fcf6ba..f02f1de6b 100644 --- a/src/components/cardbuilder/card.css +++ b/src/components/cardbuilder/card.css @@ -167,8 +167,9 @@ button::-moz-focus-inner { position: relative; background-clip: content-box !important; color: inherit; +} - /* This is only needed for scalable cards */ +.cardImageContainer.cardScalable { height: 100%; contain: strict; } diff --git a/src/components/cardbuilder/cardBuilder.js b/src/components/cardbuilder/cardBuilder.js index e540a4021..37c0e2696 100644 --- a/src/components/cardbuilder/cardBuilder.js +++ b/src/components/cardbuilder/cardBuilder.js @@ -1537,8 +1537,11 @@ import 'programStyles'; case 'MusicAlbum': return ''; case 'MusicArtist': + return ''; case 'Person': return ''; + case 'Audio': + return ''; case 'Movie': return ''; case 'Series': diff --git a/src/components/listview/listview.js b/src/components/listview/listview.js index 4ce8526c9..1bbde6fdb 100644 --- a/src/components/listview/listview.js +++ b/src/components/listview/listview.js @@ -1,4 +1,4 @@ -define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutManager', 'globalize', 'datetime', 'apphost', 'css!./listview', 'emby-ratingbutton', 'emby-playstatebutton'], function (itemHelper, mediaInfo, indicators, connectionManager, layoutManager, globalize, datetime, appHost) { +define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutManager', 'globalize', 'datetime', 'cardBuilder', 'css!./listview', 'emby-ratingbutton', 'emby-playstatebutton'], function (itemHelper, mediaInfo, indicators, connectionManager, layoutManager, globalize, datetime, cardBuilder) { 'use strict'; function getIndex(item, options) { @@ -270,54 +270,56 @@ define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutMan if (options.image !== false) { let imgData = options.imageSource === 'channel' ? getChannelImageUrl(item, downloadWidth) : getImageUrl(item, downloadWidth); + let imgUrl; + let blurhash; if (imgData) { - let imgUrl = imgData.url; - let blurhash = imgData.blurhash; - let imageClass = isLargeStyle ? 'listItemImage listItemImage-large' : 'listItemImage'; - - if (isLargeStyle && layoutManager.tv) { - imageClass += ' listItemImage-large-tv'; - } - - var playOnImageClick = options.imagePlayButton && !layoutManager.tv; - - if (!clickEntireItem) { - imageClass += ' itemAction'; - } - - var imageAction = playOnImageClick ? 'resume' : action; - - let blurhashAttrib = ''; - if (blurhash && blurhash.length > 0) { - blurhashAttrib = 'data-blurhash="' + blurhash + '"'; - } - - if (imgUrl) { - html += '
'; - } else { - html += '
'; - } - - var indicatorsHtml = ''; - indicatorsHtml += indicators.getPlayedIndicatorHtml(item); - - if (indicatorsHtml) { - html += '
' + indicatorsHtml + '
'; - } - - if (playOnImageClick) { - html += ''; - } - - var progressHtml = indicators.getProgressBarHtml(item, { - containerClass: 'listItemProgressBar' - }); - - if (progressHtml) { - html += progressHtml; - } - html += '
'; + imgUrl = imgData.url; + blurhash = imgData.blurhash; } + let imageClass = isLargeStyle ? 'listItemImage listItemImage-large' : 'listItemImage'; + + if (isLargeStyle && layoutManager.tv) { + imageClass += ' listItemImage-large-tv'; + } + + var playOnImageClick = options.imagePlayButton && !layoutManager.tv; + + if (!clickEntireItem) { + imageClass += ' itemAction'; + } + + var imageAction = playOnImageClick ? 'resume' : action; + + let blurhashAttrib = ''; + if (blurhash && blurhash.length > 0) { + blurhashAttrib = 'data-blurhash="' + blurhash + '"'; + } + + if (imgUrl) { + html += '
'; + } else { + html += '
' + cardBuilder.getDefaultText(item, options); + } + + var indicatorsHtml = ''; + indicatorsHtml += indicators.getPlayedIndicatorHtml(item); + + if (indicatorsHtml) { + html += '
' + indicatorsHtml + '
'; + } + + if (playOnImageClick) { + html += ''; + } + + var progressHtml = indicators.getProgressBarHtml(item, { + containerClass: 'listItemProgressBar' + }); + + if (progressHtml) { + html += progressHtml; + } + html += '
'; } if (options.showIndexNumberLeft) { diff --git a/src/components/nowPlayingBar/nowPlayingBar.css b/src/components/nowPlayingBar/nowPlayingBar.css index a2d5b4479..c22429eed 100644 --- a/src/components/nowPlayingBar/nowPlayingBar.css +++ b/src/components/nowPlayingBar/nowPlayingBar.css @@ -133,10 +133,7 @@ .toggleRepeatButton { display: none !important; } -} - -@media all and (max-width: 66em) { - .btnShuffle { + .nowPlayingBar .btnShuffleQueue { display: none !important; } } @@ -152,18 +149,21 @@ } } +.layout-mobile .nowPlayingBarRight button:not(.playPauseButton, .nextTrackButton) { + display: none; +} + +.layout-mobile .nowPlayingBarRight input, +.layout-mobile .nowPlayingBarRight div { + display: none; +} + @media all and (max-width: 56em) { .nowPlayingBarCenter { display: none !important; } } -@media all and (min-width: 56em) { - .nowPlayingBarRight .playPauseButton { - display: none; - } -} - @media all and (max-width: 60em) { .nowPlayingBarRight .nowPlayingBarVolumeSliderContainer { display: none !important; diff --git a/src/components/nowPlayingBar/nowPlayingBar.js b/src/components/nowPlayingBar/nowPlayingBar.js index 07cdaac4f..f0b585e9d 100644 --- a/src/components/nowPlayingBar/nowPlayingBar.js +++ b/src/components/nowPlayingBar/nowPlayingBar.js @@ -63,7 +63,7 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', html += '
'; html += ''; - html += ''; + html += ''; html += '
'; html += '
'; @@ -179,7 +179,7 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', } }); - elem.querySelector('.btnShuffle').addEventListener('click', function () { + elem.querySelector('.btnShuffleQueue').addEventListener('click', function () { if (currentPlayer) { if (playbackManager.getQueueShuffleMode(currentPlayer) === 'Sorted') { playbackManager.setQueueShuffleMode('Shuffle', currentPlayer); @@ -290,7 +290,8 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', nowPlayingBarElement = parentContainer.querySelector('.nowPlayingBar'); if (layoutManager.mobile) { - hideButton(nowPlayingBarElement.querySelector('.shuffle')); + hideButton(nowPlayingBarElement.querySelector('.btnShuffleQueue')); + hideButton(nowPlayingBarElement.querySelector('.nowPlayingBarCenter')); } if (browser.safari && browser.slow) { @@ -634,12 +635,12 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', function onQueueShuffleModeChange() { let shuffleMode = playbackManager.getQueueShuffleMode(this); let context = nowPlayingBarElement; - let toggleShuffleButton = context.querySelector('.btnShuffle'); + let toggleShuffleButton = context.querySelector('.btnShuffleQueue'); - if ('Sorted' === shuffleMode) { - toggleShuffleButton.classList.remove('shuffleButton-active'); - } else if ('Shuffle' === shuffleMode) { - toggleShuffleButton.classList.add('shuffleButton-active'); + if (shuffleMode === 'Sorted') { + toggleShuffleButton.classList.remove('shuffleQueue-active'); + } else if (shuffleMode === 'Shuffle') { + toggleShuffleButton.classList.add('shuffleQueue-active'); } } diff --git a/src/components/remotecontrol/remotecontrol.css b/src/components/remotecontrol/remotecontrol.css index 6dbd3b716..3fd0ab097 100644 --- a/src/components/remotecontrol/remotecontrol.css +++ b/src/components/remotecontrol/remotecontrol.css @@ -175,10 +175,6 @@ background: rgba(0, 0, 0, 0) !important; } -.layout-mobile .btnShuffle { - display: unset !important; -} - .layout-mobile .playlistSection .playlist, .layout-mobile .playlistSection .contextMenu { position: absolute; @@ -202,62 +198,82 @@ padding-right: 7.3%; } -.playlistSectionButton:not(>.layout-mobile) { - background: unset !important; +.layout-desktop .playlistSectionButton, +.layout-tv .playlistSectionButton { + background: none; } -.nowPlayingPlaylist:not(>.layout-mobile) { - background: unset !important; +.layout-desktop .nowPlayingPlaylist, +.layout-tv .nowPlayingPlaylist { + background: none; } .layout-mobile .playlistSectionButton .btnTogglePlaylist { font-size: larger; margin: 0; - padding-left: 0; } .layout-mobile .playlistSectionButton .btnSavePlaylist { margin: 0; padding-right: 0; - -webkit-box-flex: 1; - -webkit-flex-grow: 1; - flex-grow: 1; - -webkit-box-pack: end; - -webkit-justify-content: flex-end; - justify-content: flex-end; + border-radius: 0; +} + +.layout-mobile .playlistSectionButton .volumecontrol { + margin: 0; + padding-right: 0; border-radius: 0; } .layout-mobile .playlistSectionButton .btnToggleContextMenu { font-size: larger; margin: 0; - padding-right: 0; +} + +.layout-mobile .nowPlayingSecondaryButtons .btnShuffleQueue { + display: none; +} + +.layout-mobile .nowPlayingSecondaryButtons .volumecontrol { + display: none; +} + +.layout-mobile .nowPlayingSecondaryButtons .btnRepeat { + display: none; +} + +.layout-desktop .nowPlayingInfoButtons .btnRepeat, +.layout-tv .nowPlayingInfoButtons .btnRepeat { + display: none; +} + +.layout-desktop .nowPlayingInfoButtons .btnShuffleQueue, +.layout-tv .nowPlayingInfoButtons .btnShuffleQueue{ + display: none; +} + +.layout-desktop .playlistSectionButton .volumecontrol, +.layout-tv .playlistSectionButton .volumecontrol { + display: none; +} + +.nowPlayingSecondaryButtons { -webkit-box-flex: 1; -webkit-flex-grow: 1; flex-grow: 1; -webkit-box-pack: end; -webkit-justify-content: flex-end; justify-content: flex-end; - border-radius: 0; +} + +.nowPlayingInfoControls .nowPlayingPageUserDataButtonsTitle { + display: none; } @media all and (min-width: 63em) { .nowPlayingPage { padding: 8em 0 0 0 !important; } - - .nowPlayingSecondaryButtons { - -webkit-box-flex: 1; - -webkit-flex-grow: 1; - flex-grow: 1; - -webkit-box-pack: end; - -webkit-justify-content: flex-end; - justify-content: flex-end; - } - - .nowPlayingPageUserDataButtonsTitle { - display: none !important; - } } @media all and (min-width: 80em) { @@ -275,6 +291,10 @@ flex-direction: column; } + .nowPlayingInfoControls .nowPlayingPageUserDataButtonsTitle { + display: unset; + } + .nowPlayingInfoContainer { -webkit-box-orient: vertical !important; -webkit-box-direction: normal !important; @@ -362,7 +382,7 @@ font-size: smaller; } - .nowPlayingInfoButtons .btnShuffle { + .nowPlayingInfoButtons .btnShuffleQueue { position: absolute; right: 0; margin-right: 0; @@ -447,6 +467,10 @@ background-image: url(../../assets/img/equalizer.gif) !important; } +.playlistIndexIndicatorImage > * { + display: none; +} + .hideVideoButtons .videoButton { display: none; } @@ -456,7 +480,6 @@ } @media all and (max-width: 63em) { - .nowPlayingSecondaryButtons .nowPlayingPageUserDataButtons, .nowPlayingSecondaryButtons .repeatToggleButton, .nowPlayingInfoButtons .playlist .listItemMediaInfo, .nowPlayingInfoButtons .btnStop { diff --git a/src/components/remotecontrol/remotecontrol.js b/src/components/remotecontrol/remotecontrol.js index 784e7ff5b..18a8f2005 100644 --- a/src/components/remotecontrol/remotecontrol.js +++ b/src/components/remotecontrol/remotecontrol.js @@ -511,11 +511,11 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL function onShuffleQueueModeChange() { let shuffleMode = playbackManager.getQueueShuffleMode(this); let context = dlg; - let shuffleButton = context.querySelector('.btnShuffle'); + let shuffleButton = context.querySelector('.btnShuffleQueue'); if ('Sorted' === shuffleMode) { - shuffleButton.classList.remove('shuffleButton-active'); + shuffleButton.classList.remove('shuffleQueue-active'); } else if ('Shuffle' === shuffleMode) { - shuffleButton.classList.add('shuffleButton-active'); + shuffleButton.classList.add('shuffleQueue-active'); } onPlaylistUpdate(); } @@ -703,7 +703,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL playbackManager.fastForward(currentPlayer); } }); - context.querySelector('.btnShuffle').addEventListener('click', function () { + context.querySelector('.btnShuffleQueue').addEventListener('click', function () { if (currentPlayer) { if (playbackManager.getQueueShuffleMode(currentPlayer) === 'Sorted') { playbackManager.setQueueShuffleMode('Shuffle', currentPlayer); @@ -841,27 +841,23 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL } function init(ownerView, context) { - let contextmenuHtml = ``; let volumecontrolHtml = '
'; volumecontrolHtml += ``; volumecontrolHtml += '
'; volumecontrolHtml += '
'; - let shuffleButtonHtml = ``; - let repeatButtonHtml = ``; let optionsSection = context.querySelector('.playlistSectionButton'); if (!layoutManager.mobile) { - context.querySelector('.nowPlayingSecondaryButtons').insertAdjacentHTML('beforeend', repeatButtonHtml + shuffleButtonHtml + volumecontrolHtml); - optionsSection.innerHTML += contextmenuHtml; + context.querySelector('.nowPlayingSecondaryButtons').insertAdjacentHTML('beforeend', volumecontrolHtml); optionsSection.classList.remove('align-items-center', 'justify-content-center'); optionsSection.classList.add('align-items-right', 'justify-content-flex-end'); context.querySelector('.playlist').classList.remove('hide'); context.querySelector('.btnSavePlaylist').classList.remove('hide'); } else { - optionsSection.innerHTML += volumecontrolHtml + contextmenuHtml; + optionsSection.querySelector('.btnTogglePlaylist').insertAdjacentHTML('afterend', volumecontrolHtml); optionsSection.classList.add('playlistSectionButtonTransparent'); context.querySelector('.btnTogglePlaylist').classList.remove('hide'); - context.querySelector('.nowPlayingInfoButtons').insertAdjacentHTML('afterbegin', repeatButtonHtml); - context.querySelector('.nowPlayingInfoButtons').insertAdjacentHTML('beforeend', shuffleButtonHtml); + context.querySelector('.playlistSectionButton').classList.remove('justify-content-center'); + context.querySelector('.playlistSectionButton').classList.add('justify-content-space-between'); } bindEvents(context); diff --git a/src/nowplaying.html b/src/nowplaying.html index 0c7688246..e42964707 100644 --- a/src/nowplaying.html +++ b/src/nowplaying.html @@ -29,6 +29,11 @@
+ + + +
@@ -72,6 +81,15 @@ + + + +
@@ -165,6 +183,9 @@ +
diff --git a/src/themes/appletv/theme.css b/src/themes/appletv/theme.css index 9d0ccdc9d..8b6b3e190 100644 --- a/src/themes/appletv/theme.css +++ b/src/themes/appletv/theme.css @@ -450,7 +450,7 @@ html { color: #4285f4; } -.shuffleButton-active { +.shuffleQueue-active { color: #4285f4 !important; } diff --git a/src/themes/blueradiance/theme.css b/src/themes/blueradiance/theme.css index 5215f4bb6..d8d7525d3 100644 --- a/src/themes/blueradiance/theme.css +++ b/src/themes/blueradiance/theme.css @@ -450,7 +450,7 @@ html { color: #4285f4; } -.shuffleButton-active { +.shuffleQueue-active { color: #4285f4 !important; } diff --git a/src/themes/dark/theme.css b/src/themes/dark/theme.css index 1e8a996c9..9491b2cae 100644 --- a/src/themes/dark/theme.css +++ b/src/themes/dark/theme.css @@ -421,7 +421,7 @@ html { color: #4285f4; } -.shuffleButton-active { +.shuffleQueue-active { color: #4285f4 !important; } diff --git a/src/themes/light/theme.css b/src/themes/light/theme.css index 82b2f5075..abb5a58fb 100644 --- a/src/themes/light/theme.css +++ b/src/themes/light/theme.css @@ -432,7 +432,7 @@ html { color: #4285f4; } -.shuffleButton-active { +.shuffleQueue-active { color: #4285f4 !important; } diff --git a/src/themes/purplehaze/theme.css b/src/themes/purplehaze/theme.css index 140969f65..ca8fe1fdf 100644 --- a/src/themes/purplehaze/theme.css +++ b/src/themes/purplehaze/theme.css @@ -547,7 +547,7 @@ a[data-role=button] { color: #4285f4; } -.shuffleButton-active { +.shuffleQueue-active { color: #4285f4 !important; } diff --git a/src/themes/wmc/theme.css b/src/themes/wmc/theme.css index a62d38297..7def8215f 100644 --- a/src/themes/wmc/theme.css +++ b/src/themes/wmc/theme.css @@ -430,7 +430,7 @@ html { color: #4285f4; } -.shuffleButton-active { +.shuffleQueue-active { color: #4285f4 !important; } From dba995c5f068169fc10bd6426afd7cd27a6bacab Mon Sep 17 00:00:00 2001 From: ferferga Date: Mon, 22 Jun 2020 17:55:28 +0200 Subject: [PATCH 25/42] Fix not usable buttons in desktop layout --- src/components/remotecontrol/remotecontrol.js | 53 +++++++++++-------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/src/components/remotecontrol/remotecontrol.js b/src/components/remotecontrol/remotecontrol.js index 18a8f2005..6626d83c4 100644 --- a/src/components/remotecontrol/remotecontrol.js +++ b/src/components/remotecontrol/remotecontrol.js @@ -354,17 +354,19 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL function updateRepeatModeDisplay(repeatMode) { var context = dlg; - var toggleRepeatButton = context.querySelector('.repeatToggleButton'); + let toggleRepeatButtons = context.querySelectorAll('.repeatToggleButton'); - if ('RepeatAll' == repeatMode) { - toggleRepeatButton.innerHTML = ""; - toggleRepeatButton.classList.add('repeatButton-active'); - } else if ('RepeatOne' == repeatMode) { - toggleRepeatButton.innerHTML = ""; - toggleRepeatButton.classList.add('repeatButton-active'); - } else { - toggleRepeatButton.innerHTML = ""; - toggleRepeatButton.classList.remove('repeatButton-active'); + for (let toggleRepeatButton of toggleRepeatButtons) { + if ('RepeatAll' == repeatMode) { + toggleRepeatButton.innerHTML = ""; + toggleRepeatButton.classList.add('repeatButton-active'); + } else if ('RepeatOne' == repeatMode) { + toggleRepeatButton.innerHTML = ""; + toggleRepeatButton.classList.add('repeatButton-active'); + } else { + toggleRepeatButton.innerHTML = ""; + toggleRepeatButton.classList.remove('repeatButton-active'); + } } } @@ -511,11 +513,14 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL function onShuffleQueueModeChange() { let shuffleMode = playbackManager.getQueueShuffleMode(this); let context = dlg; - let shuffleButton = context.querySelector('.btnShuffleQueue'); - if ('Sorted' === shuffleMode) { - shuffleButton.classList.remove('shuffleQueue-active'); - } else if ('Shuffle' === shuffleMode) { - shuffleButton.classList.add('shuffleQueue-active'); + let shuffleButtons = context.querySelectorAll('.btnShuffleQueue'); + + for (let shuffleButton of shuffleButtons) { + if ('Sorted' === shuffleMode) { + shuffleButton.classList.remove('shuffleQueue-active'); + } else if ('Shuffle' === shuffleMode) { + shuffleButton.classList.add('shuffleQueue-active'); + } } onPlaylistUpdate(); } @@ -703,15 +708,17 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL playbackManager.fastForward(currentPlayer); } }); - context.querySelector('.btnShuffleQueue').addEventListener('click', function () { - if (currentPlayer) { - if (playbackManager.getQueueShuffleMode(currentPlayer) === 'Sorted') { - playbackManager.setQueueShuffleMode('Shuffle', currentPlayer); - } else { - playbackManager.setQueueShuffleMode('Sorted', currentPlayer); + for (let shuffleButton of context.querySelectorAll('.btnShuffleQueue')) { + shuffleButton.addEventListener('click', function () { + if (currentPlayer) { + if (playbackManager.getQueueShuffleMode(currentPlayer) === 'Sorted') { + playbackManager.setQueueShuffleMode('Shuffle', currentPlayer); + } else { + playbackManager.setQueueShuffleMode('Sorted', currentPlayer); + } } - } - }); + }); + } context.querySelector('.btnPreviousTrack').addEventListener('click', function (e) { if (currentPlayer) { From 3e1d24d9a25a63aa2b1aa2a4ad5c142d699204a0 Mon Sep 17 00:00:00 2001 From: ferferga Date: Mon, 22 Jun 2020 18:02:45 +0200 Subject: [PATCH 26/42] Don't shuffle first item --- src/components/playback/playqueuemanager.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/components/playback/playqueuemanager.js b/src/components/playback/playqueuemanager.js index e26a738a0..4eac7d7c7 100644 --- a/src/components/playback/playqueuemanager.js +++ b/src/components/playback/playqueuemanager.js @@ -60,16 +60,22 @@ define([], function () { PlayQueueManager.prototype.shufflePlaylist = function () { this._sortedPlaylist = []; + let currentPlaylistItem = this._playlist[this.getCurrentPlaylistIndex()]; for (let item of this._playlist) { this._sortedPlaylist.push(item); } for (let i = this._playlist.length - 1; i > 0; i--) { + if (this._playlist[i] === currentPlaylistItem) { + this._playlist.splice(i, 1); + continue; + } const j = Math.floor(Math.random() * i); const temp = this._playlist[i]; this._playlist[i] = this._playlist[j]; this._playlist[j] = temp; } + this._playlist.unshift(currentPlaylistItem); this._shuffleMode = 'Shuffle'; }; From ae8d06533c9a902a120c57ab896aed0d3372187f Mon Sep 17 00:00:00 2001 From: ferferga Date: Tue, 23 Jun 2020 16:55:37 +0200 Subject: [PATCH 27/42] Add space-between in desktop and hide duplicate heart on mobile --- src/components/remotecontrol/remotecontrol.css | 4 ++++ src/nowplaying.html | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/remotecontrol/remotecontrol.css b/src/components/remotecontrol/remotecontrol.css index 0386981bf..69f7025d6 100644 --- a/src/components/remotecontrol/remotecontrol.css +++ b/src/components/remotecontrol/remotecontrol.css @@ -254,6 +254,10 @@ display: none; } +.layout-mobile .nowPlayingPageUserDataButtons { + display: none; +} + @media all and (min-width: 63em) { .nowPlayingPage { padding: 8em 0 0 0 !important; diff --git a/src/nowplaying.html b/src/nowplaying.html index e42964707..56610ca6e 100644 --- a/src/nowplaying.html +++ b/src/nowplaying.html @@ -26,7 +26,7 @@
-
+
From b7714eea14d78088dd3c86d123af1989761f1290 Mon Sep 17 00:00:00 2001 From: ferferga Date: Tue, 23 Jun 2020 19:09:22 +0200 Subject: [PATCH 28/42] Address review comments --- src/components/cardbuilder/card.css | 2 +- src/components/cardbuilder/cardBuilder.js | 1 - src/components/nowPlayingBar/nowPlayingBar.js | 81 +++++++++++-------- src/components/playback/playbackmanager.js | 32 ++++++-- src/components/playback/playqueuemanager.js | 39 ++++++--- .../remotecontrol/remotecontrol.css | 1 - src/components/remotecontrol/remotecontrol.js | 61 ++++++++------ src/plugins/chromecastPlayer/plugin.js | 4 +- src/plugins/sessionPlayer/plugin.js | 2 +- src/scripts/serverNotifications.js | 2 +- 10 files changed, 138 insertions(+), 87 deletions(-) diff --git a/src/components/cardbuilder/card.css b/src/components/cardbuilder/card.css index f02f1de6b..75943a0c4 100644 --- a/src/components/cardbuilder/card.css +++ b/src/components/cardbuilder/card.css @@ -169,7 +169,7 @@ button::-moz-focus-inner { color: inherit; } -.cardImageContainer.cardScalable { +.cardScalable .cardImageContainer { height: 100%; contain: strict; } diff --git a/src/components/cardbuilder/cardBuilder.js b/src/components/cardbuilder/cardBuilder.js index f594a3daf..b08e5024a 100644 --- a/src/components/cardbuilder/cardBuilder.js +++ b/src/components/cardbuilder/cardBuilder.js @@ -1534,7 +1534,6 @@ import 'programStyles'; case 'MusicAlbum': return ''; case 'MusicArtist': - return ''; case 'Person': return ''; case 'Audio': diff --git a/src/components/nowPlayingBar/nowPlayingBar.js b/src/components/nowPlayingBar/nowPlayingBar.js index f0b585e9d..b8a2a5082 100644 --- a/src/components/nowPlayingBar/nowPlayingBar.js +++ b/src/components/nowPlayingBar/nowPlayingBar.js @@ -165,7 +165,8 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', return; } playbackManager.seekPercent(0, currentPlayer); - // This is done automatically by playbackManager, however, setting this here gives instant visual feedback + // This is done automatically by playbackManager, however, setting this here gives instant visual feedback. + // TODO: Check why seekPercentage doesn't reflect the changes inmmediately, so we can remove this workaround. positionSlider.value = 0; } else { playbackManager.previousTrack(currentPlayer); @@ -181,11 +182,7 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', elem.querySelector('.btnShuffleQueue').addEventListener('click', function () { if (currentPlayer) { - if (playbackManager.getQueueShuffleMode(currentPlayer) === 'Sorted') { - playbackManager.setQueueShuffleMode('Shuffle', currentPlayer); - } else { - playbackManager.setQueueShuffleMode('Sorted', currentPlayer); - } + playbackManager.toggleQueueShuffleMode(currentPlayer); } }); @@ -367,16 +364,23 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', function updateRepeatModeDisplay(repeatMode) { toggleRepeatButtonIcon.classList.remove('repeat', 'repeat_one'); + const cssClass = 'repeatButton-active'; - if (repeatMode === 'RepeatAll') { - toggleRepeatButtonIcon.classList.add('repeat'); - toggleRepeatButton.classList.add('repeatButton-active'); - } else if (repeatMode === 'RepeatOne') { - toggleRepeatButtonIcon.classList.add('repeat_one'); - toggleRepeatButton.classList.add('repeatButton-active'); - } else { - toggleRepeatButtonIcon.classList.add('repeat'); - toggleRepeatButton.classList.remove('repeatButton-active'); + switch (repeatMode) { + case 'RepeatAll': + toggleRepeatButtonIcon.classList.add('repeat'); + toggleRepeatButton.classList.add(cssClass); + break; + case 'RepeatOne': + toggleRepeatButtonIcon.classList.add('repeat_one'); + toggleRepeatButton.classList.add(cssClass); + break; + case 'RepeatNone': + toggleRepeatButtonIcon.classList.add('repeat'); + toggleRepeatButton.classList.remove(cssClass); + break; + default: + throw new TypeError('invalid value for repeatMode'); } } @@ -530,24 +534,26 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', var nowPlayingItem = state.NowPlayingItem; var textLines = nowPlayingItem ? nowPlayingHelper.getNowPlayingNames(nowPlayingItem) : []; - if (textLines.length > 1) { - textLines[1].secondary = true; - } - + nowPlayingTextElement.innerHTML = ''; if (textLines) { - nowPlayingTextElement.innerHTML = ''; - let itemText = document.createElement('div'); let secondaryText = document.createElement('div'); secondaryText.classList.add('nowPlayingBarSecondaryText'); - if (textLines[0].text) { - let text = document.createElement('a'); - text.innerHTML = textLines[0].text; - itemText.appendChild(text); + let itemText = document.createElement('div'); + if (textLines.length > 1) { + textLines[1].secondary = true; + if (textLines[1].text) { + let text = document.createElement('a'); + text.innerHTML = textLines[1].text; + secondaryText.appendChild(text); + } } - if (textLines[1].text) { - let text = document.createElement('a'); - text.innerHTML = textLines[1].text; - secondaryText.appendChild(text); + + if (textLines[0].text) { + if (textLines[0].text) { + let text = document.createElement('a'); + text.innerHTML = textLines[0].text; + itemText.appendChild(text); + } } nowPlayingTextElement.appendChild(itemText); nowPlayingTextElement.appendChild(secondaryText); @@ -586,11 +592,11 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', var userData = item.UserData || {}; var likes = userData.Likes == null ? '' : userData.Likes; if (!layoutManager.mobile) { - let contextButton = document.querySelector('.nowPlayingBar').querySelector('.btnToggleContextMenu'); + let contextButton = nowPlayingBarElement.querySelector('.btnToggleContextMenu'); // We remove the previous event listener by replacing the item in each update event let contextButtonClone = contextButton.cloneNode(true); contextButton.parentNode.replaceChild(contextButtonClone, contextButton); - contextButton = document.querySelector('.nowPlayingBar').querySelector('.btnToggleContextMenu'); + contextButton = nowPlayingBarElement.querySelector('.btnToggleContextMenu'); let options = { play: false, queue: false, @@ -637,10 +643,15 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', let context = nowPlayingBarElement; let toggleShuffleButton = context.querySelector('.btnShuffleQueue'); - if (shuffleMode === 'Sorted') { - toggleShuffleButton.classList.remove('shuffleQueue-active'); - } else if (shuffleMode === 'Shuffle') { - toggleShuffleButton.classList.add('shuffleQueue-active'); + switch (shuffleMode) { + case 'Sorted': + toggleShuffleButton.classList.remove('shuffleQueue-active'); + break; + case 'Shuffle': + toggleShuffleButton.classList.add('shuffleQueue-active'); + break; + default: + throw new TypeError('invalid value for shuffleMode'); } } diff --git a/src/components/playback/playbackmanager.js b/src/components/playback/playbackmanager.js index 38ce9bf9f..3bd4be9c0 100644 --- a/src/components/playback/playbackmanager.js +++ b/src/components/playback/playbackmanager.js @@ -3886,7 +3886,7 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla 'GoToSearch', 'DisplayMessage', 'SetRepeatMode', - 'SetQueueShuffleMode', + 'SetShuffleQueue', 'PlayMediaSource', 'PlayTrailers' ]; @@ -3941,9 +3941,7 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla return this._playQueueManager.getRepeatMode(); }; - PlaybackManager.prototype.setQueueShuffleMode = function (value, player) { - - player = player || this._currentPlayer; + PlaybackManager.prototype.setQueueShuffleMode = function (value, player = this._currentPlayer) { if (player && !enableLocalPlaylistManagement(player)) { return player.setQueueShuffleMode(value); } @@ -3952,9 +3950,7 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla events.trigger(player, 'shufflequeuemodechange'); }; - PlaybackManager.prototype.getQueueShuffleMode = function (player) { - - player = player || this._currentPlayer; + PlaybackManager.prototype.getQueueShuffleMode = function (player = this._currentPlayer) { if (player && !enableLocalPlaylistManagement(player)) { return player.getQueueShuffleMode(); } @@ -3962,6 +3958,26 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla return this._playQueueManager.getShuffleMode(); }; + PlaybackManager.prototype.toggleQueueShuffleMode = function (player = this._currentPlayer) { + let currentvalue; + if (player && !enableLocalPlaylistManagement(player)) { + currentvalue = player.getQueueShuffleMode(); + switch (currentvalue) { + case 'Shuffle': + player.setQueueShuffleMode('Sorted'); + break; + case 'Sorted': + player.setQueueShuffleMode('Shuffle'); + break; + default: + throw new TypeError('current value for shufflequeue is invalid'); + } + } else { + this._playQueueManager.toggleShuffleMode(); + } + events.trigger(player, 'shufflequeuemodechange'); + }; + PlaybackManager.prototype.trySetActiveDeviceName = function (name) { name = normalizeName(name); @@ -4030,7 +4046,7 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla case 'SetRepeatMode': this.setRepeatMode(cmd.Arguments.RepeatMode, player); break; - case 'SetQueueShuffleMode': + case 'SetShuffleQueue': this.setQueueShuffleMode(cmd.Arguments.ShuffleMode, player); break; case 'VolumeUp': diff --git a/src/components/playback/playqueuemanager.js b/src/components/playback/playqueuemanager.js index 4eac7d7c7..03f5246b8 100644 --- a/src/components/playback/playqueuemanager.js +++ b/src/components/playback/playqueuemanager.js @@ -60,16 +60,12 @@ define([], function () { PlayQueueManager.prototype.shufflePlaylist = function () { this._sortedPlaylist = []; - let currentPlaylistItem = this._playlist[this.getCurrentPlaylistIndex()]; - for (let item of this._playlist) { + for (const item of this._playlist) { this._sortedPlaylist.push(item); } + const currentPlaylistItem = this._playlist.splice(this.getCurrentPlaylistIndex(), 1)[0]; for (let i = this._playlist.length - 1; i > 0; i--) { - if (this._playlist[i] === currentPlaylistItem) { - this._playlist.splice(i, 1); - continue; - } const j = Math.floor(Math.random() * i); const temp = this._playlist[i]; this._playlist[i] = this._playlist[j]; @@ -216,7 +212,8 @@ define([], function () { }; PlayQueueManager.prototype.setRepeatMode = function (value) { - if (value === 'RepeatOne' || value === 'RepeatAll' || value === 'RepeatNone') { + const repeatModes = ['RepeatOne', 'RepeatAll', 'RepeatNone']; + if (repeatModes.includes(value)) { this._repeatMode = value; } else { throw new TypeError('invalid value provided for setRepeatMode'); @@ -228,12 +225,28 @@ define([], function () { }; 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'); + switch (value) { + case 'Shuffle': + this.shufflePlaylist(); + break; + case 'Sorted': + this.sortShuffledPlaylist(); + break; + default: + throw new TypeError('invalid value provided to setShuffleMode'); + } + }; + + PlayQueueManager.prototype.toggleShuffleMode = function () { + switch (this._shuffleMode) { + case 'Shuffle': + this.setShuffleMode('Sorted'); + break; + case 'Sorted': + this.setShuffleMode('Shuffle'); + break; + default: + throw new TypeError('current value for shufflequeue is invalid'); } }; diff --git a/src/components/remotecontrol/remotecontrol.css b/src/components/remotecontrol/remotecontrol.css index 69f7025d6..d4511a9dd 100644 --- a/src/components/remotecontrol/remotecontrol.css +++ b/src/components/remotecontrol/remotecontrol.css @@ -208,7 +208,6 @@ .layout-mobile .playlistSectionButton .btnSavePlaylist { margin: 0; - padding-right: 0; border-radius: 0; } diff --git a/src/components/remotecontrol/remotecontrol.js b/src/components/remotecontrol/remotecontrol.js index 6626d83c4..89598d09a 100644 --- a/src/components/remotecontrol/remotecontrol.js +++ b/src/components/remotecontrol/remotecontrol.js @@ -123,7 +123,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL var artistsSeries = ''; var albumName = item.Album; if (item.ArtistItems != null) { - for (let artist of item.ArtistItems) { + for (const artist of item.ArtistItems) { let artistName = artist.Name; let artistId = artist.Id; artistsSeries += `${artistName}`; @@ -135,7 +135,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL // For some reason, Chromecast Player doesn't return a item.ArtistItems object, so we need to fallback // to normal item.Artists item. // TODO: Normalise fields returned by all the players - for (let artist of item.Artists) { + for (const artist of item.Artists) { artistsSeries += `${artist}`; if (artist !== item.Artists.slice(-1)[0]) { artistsSeries += ', '; @@ -176,7 +176,6 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL maxHeight: 300 * 2 }) : null; - console.debug('updateNowPlayingInfo'); let contextButton = context.querySelector('.btnToggleContextMenu'); // We remove the previous event listener by replacing the item in each update event let contextButtonClone = contextButton.cloneNode(true); @@ -355,18 +354,30 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL function updateRepeatModeDisplay(repeatMode) { var context = dlg; let toggleRepeatButtons = context.querySelectorAll('.repeatToggleButton'); + const cssClass = 'repeatButton-active'; + let innHtml = ''; + let repeatOn = undefined; - for (let toggleRepeatButton of toggleRepeatButtons) { - if ('RepeatAll' == repeatMode) { - toggleRepeatButton.innerHTML = ""; - toggleRepeatButton.classList.add('repeatButton-active'); - } else if ('RepeatOne' == repeatMode) { - toggleRepeatButton.innerHTML = ""; - toggleRepeatButton.classList.add('repeatButton-active'); + switch (repeatMode) { + case 'RepeatAll': + break; + case 'RepeatOne': + innHtml = ''; + break; + case 'RepeatNone': + repeatOn = null; + break; + default: + throw new TypeError('invalid value for repeatMode'); + } + + for (const toggleRepeatButton of toggleRepeatButtons) { + if (repeatOn === null) { + toggleRepeatButton.classList.remove(cssClass); } else { - toggleRepeatButton.innerHTML = ""; - toggleRepeatButton.classList.remove('repeatButton-active'); + toggleRepeatButton.classList.add(cssClass); } + toggleRepeatButton.innerHTML = innHtml; } } @@ -516,10 +527,15 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL let shuffleButtons = context.querySelectorAll('.btnShuffleQueue'); for (let shuffleButton of shuffleButtons) { - if ('Sorted' === shuffleMode) { - shuffleButton.classList.remove('shuffleQueue-active'); - } else if ('Shuffle' === shuffleMode) { - shuffleButton.classList.add('shuffleQueue-active'); + switch (shuffleMode) { + case 'Sorted': + shuffleButton.classList.remove('shuffleQueue-active'); + break; + case 'Shuffle': + shuffleButton.classList.add('shuffleQueue-active'); + break; + default: + throw new TypeError('invalid shuffle mode'); } } onPlaylistUpdate(); @@ -708,14 +724,10 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL playbackManager.fastForward(currentPlayer); } }); - for (let shuffleButton of context.querySelectorAll('.btnShuffleQueue')) { + for (const shuffleButton of context.querySelectorAll('.btnShuffleQueue')) { shuffleButton.addEventListener('click', function () { if (currentPlayer) { - if (playbackManager.getQueueShuffleMode(currentPlayer) === 'Sorted') { - playbackManager.setQueueShuffleMode('Shuffle', currentPlayer); - } else { - playbackManager.setQueueShuffleMode('Sorted', currentPlayer); - } + playbackManager.toggleQueueShuffleMode(currentPlayer); } }); } @@ -728,7 +740,8 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL return; } playbackManager.seekPercent(0, currentPlayer); - // This is done automatically by playbackManager. However, setting this here gives instant visual feedback + // This is done automatically by playbackManager. However, setting this here gives instant visual feedback. + // TODO: Check why seekPercentage doesn't reflect the changes inmmediately, so we can remove this workaround. positionSlider.value = 0; } else { playbackManager.previousTrack(currentPlayer); @@ -794,7 +807,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL } else { context.querySelector('.playlist').classList.add('hide'); context.querySelector('.btnSavePlaylist').classList.add('hide'); - if (showMuteButton && showVolumeSlider) { + if (showMuteButton || showVolumeSlider) { context.querySelector('.volumecontrol').classList.remove('hide'); } if (layoutManager.mobile) { diff --git a/src/plugins/chromecastPlayer/plugin.js b/src/plugins/chromecastPlayer/plugin.js index 0809fb82c..6384ce769 100644 --- a/src/plugins/chromecastPlayer/plugin.js +++ b/src/plugins/chromecastPlayer/plugin.js @@ -652,7 +652,7 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' 'SetSubtitleStreamIndex', 'DisplayContent', 'SetRepeatMode', - 'SetQueueShuffleMode', + 'SetShuffleQueue', 'EndSession', 'PlayMediaSource', 'PlayTrailers' @@ -897,7 +897,7 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' options: { ShuffleMode: value }, - command: 'SetQueueShuffleMode' + command: 'SetShuffleQueue' }); }; diff --git a/src/plugins/sessionPlayer/plugin.js b/src/plugins/sessionPlayer/plugin.js index 5ed07bb4f..6a266941d 100644 --- a/src/plugins/sessionPlayer/plugin.js +++ b/src/plugins/sessionPlayer/plugin.js @@ -508,7 +508,7 @@ define(['playbackManager', 'events', 'serverNotifications', 'connectionManager'] SessionPlayer.prototype.setQueueShuffleMode = function (mode) { - sendCommandByName(this, 'SetQueueShuffleMode', { + sendCommandByName(this, 'SetShuffleQueue', { ShuffleMode: mode }); }; diff --git a/src/scripts/serverNotifications.js b/src/scripts/serverNotifications.js index c1d9c95e9..cddd2cf79 100644 --- a/src/scripts/serverNotifications.js +++ b/src/scripts/serverNotifications.js @@ -65,7 +65,7 @@ define(['connectionManager', 'playbackManager', 'syncPlayManager', 'events', 'in case 'SetRepeatMode': playbackManager.setRepeatMode(cmd.Arguments.RepeatMode); break; - case 'SetQueueShuffleMode': + case 'SetShuffleQueue': playbackManager.setQueueShuffleMode(cmd.Arguments.ShuffleMode); break; case 'VolumeUp': From 78cdbbb14f4ab3ffb0dfafe5fbaa92101f17b53b Mon Sep 17 00:00:00 2001 From: ferferga Date: Tue, 23 Jun 2020 20:32:39 +0200 Subject: [PATCH 29/42] Remove item from sorted playlist when on shuffle mode and address review comments --- src/components/nowPlayingBar/nowPlayingBar.js | 14 ++++++++------ src/components/playback/playqueuemanager.js | 6 ++++++ src/components/remotecontrol/remotecontrol.js | 10 +++++++--- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/components/nowPlayingBar/nowPlayingBar.js b/src/components/nowPlayingBar/nowPlayingBar.js index b8a2a5082..97227ca71 100644 --- a/src/components/nowPlayingBar/nowPlayingBar.js +++ b/src/components/nowPlayingBar/nowPlayingBar.js @@ -379,6 +379,8 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', toggleRepeatButtonIcon.classList.add('repeat'); toggleRepeatButton.classList.remove(cssClass); break; + case undefined: + break; default: throw new TypeError('invalid value for repeatMode'); } @@ -536,9 +538,9 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', var textLines = nowPlayingItem ? nowPlayingHelper.getNowPlayingNames(nowPlayingItem) : []; nowPlayingTextElement.innerHTML = ''; if (textLines) { + let itemText = document.createElement('div'); let secondaryText = document.createElement('div'); secondaryText.classList.add('nowPlayingBarSecondaryText'); - let itemText = document.createElement('div'); if (textLines.length > 1) { textLines[1].secondary = true; if (textLines[1].text) { @@ -549,11 +551,9 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', } if (textLines[0].text) { - if (textLines[0].text) { - let text = document.createElement('a'); - text.innerHTML = textLines[0].text; - itemText.appendChild(text); - } + let text = document.createElement('a'); + text.innerHTML = textLines[0].text; + itemText.appendChild(text); } nowPlayingTextElement.appendChild(itemText); nowPlayingTextElement.appendChild(secondaryText); @@ -650,6 +650,8 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', case 'Shuffle': toggleShuffleButton.classList.add('shuffleQueue-active'); break; + case undefined: + break; default: throw new TypeError('invalid value for shuffleMode'); } diff --git a/src/components/playback/playqueuemanager.js b/src/components/playback/playqueuemanager.js index 03f5246b8..8ffefd6b8 100644 --- a/src/components/playback/playqueuemanager.js +++ b/src/components/playback/playqueuemanager.js @@ -155,6 +155,12 @@ define([], function () { var currentPlaylistItemId = this.getCurrentPlaylistItemId(); var isCurrentIndex = playlistItemIds.indexOf(currentPlaylistItemId) !== -1; + if (this._sortedPlaylist.length <= playlistItemIds.length) { + this._sortedPlaylist = this._sortedPlaylist.splice(0).filter(function (item) { + return playlistItemIds.indexOf(item.PlaylistItemId) === -1; + }); + } + this._playlist = playlist.filter(function (item) { return playlistItemIds.indexOf(item.PlaylistItemId) === -1; }); diff --git a/src/components/remotecontrol/remotecontrol.js b/src/components/remotecontrol/remotecontrol.js index b9c2c9eb8..36d703fd8 100644 --- a/src/components/remotecontrol/remotecontrol.js +++ b/src/components/remotecontrol/remotecontrol.js @@ -356,7 +356,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL let toggleRepeatButtons = context.querySelectorAll('.repeatToggleButton'); const cssClass = 'repeatButton-active'; let innHtml = ''; - let repeatOn = undefined; + let repeatOn = true; switch (repeatMode) { case 'RepeatAll': @@ -365,14 +365,16 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL innHtml = ''; break; case 'RepeatNone': - repeatOn = null; + repeatOn = false; + break; + case undefined: break; default: throw new TypeError('invalid value for repeatMode'); } for (const toggleRepeatButton of toggleRepeatButtons) { - if (repeatOn === null) { + if (!repeatOn) { toggleRepeatButton.classList.remove(cssClass); } else { toggleRepeatButton.classList.add(cssClass); @@ -534,6 +536,8 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL case 'Shuffle': shuffleButton.classList.add('shuffleQueue-active'); break; + case undefined: + break; default: throw new TypeError('invalid shuffle mode'); } From 4fe179214c822d74a120e2df5dad86ab17832525 Mon Sep 17 00:00:00 2001 From: ferferga Date: Tue, 23 Jun 2020 23:41:52 +0200 Subject: [PATCH 30/42] Address review comments --- src/components/nowPlayingBar/nowPlayingBar.js | 40 ++++++---------- src/components/playback/playbackmanager.js | 8 +--- src/components/playback/playqueuemanager.js | 16 +++---- src/components/remotecontrol/remotecontrol.js | 48 +++++++------------ 4 files changed, 41 insertions(+), 71 deletions(-) diff --git a/src/components/nowPlayingBar/nowPlayingBar.js b/src/components/nowPlayingBar/nowPlayingBar.js index 97227ca71..8573d9418 100644 --- a/src/components/nowPlayingBar/nowPlayingBar.js +++ b/src/components/nowPlayingBar/nowPlayingBar.js @@ -182,26 +182,22 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', elem.querySelector('.btnShuffleQueue').addEventListener('click', function () { if (currentPlayer) { - playbackManager.toggleQueueShuffleMode(currentPlayer); + playbackManager.toggleQueueShuffleMode(); } }); toggleRepeatButton = elem.querySelector('.toggleRepeatButton'); toggleRepeatButton.addEventListener('click', function () { - - if (currentPlayer) { - - switch (playbackManager.getRepeatMode(currentPlayer)) { - case 'RepeatAll': - playbackManager.setRepeatMode('RepeatOne', currentPlayer); - break; - case 'RepeatOne': - playbackManager.setRepeatMode('RepeatNone', currentPlayer); - break; - default: - playbackManager.setRepeatMode('RepeatAll', currentPlayer); - break; - } + switch (playbackManager.getRepeatMode()) { + case 'RepeatAll': + playbackManager.setRepeatMode('RepeatOne'); + break; + case 'RepeatOne': + playbackManager.setRepeatMode('RepeatNone'); + break; + default: + playbackManager.setRepeatMode('RepeatAll'); + break; } }); @@ -343,7 +339,7 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', toggleRepeatButton.classList.remove('hide'); } - updateRepeatModeDisplay(playState.RepeatMode); + updateRepeatModeDisplay(playbackManager.getRepeatMode()); onQueueShuffleModeChange(); updatePlayerVolumeState(playState.IsMuted, playState.VolumeLevel); @@ -379,8 +375,6 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', toggleRepeatButtonIcon.classList.add('repeat'); toggleRepeatButton.classList.remove(cssClass); break; - case undefined: - break; default: throw new TypeError('invalid value for repeatMode'); } @@ -627,19 +621,17 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', onStateChanged.call(player, e, state); } - function onRepeatModeChange(e) { + function onRepeatModeChange() { if (!isEnabled) { return; } - var player = this; - - updateRepeatModeDisplay(playbackManager.getRepeatMode(player)); + updateRepeatModeDisplay(playbackManager.getRepeatMode()); } function onQueueShuffleModeChange() { - let shuffleMode = playbackManager.getQueueShuffleMode(this); + let shuffleMode = playbackManager.getQueueShuffleMode(); let context = nowPlayingBarElement; let toggleShuffleButton = context.querySelector('.btnShuffleQueue'); @@ -650,8 +642,6 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', case 'Shuffle': toggleShuffleButton.classList.add('shuffleQueue-active'); break; - case undefined: - break; default: throw new TypeError('invalid value for shuffleMode'); } diff --git a/src/components/playback/playbackmanager.js b/src/components/playback/playbackmanager.js index 3bd4be9c0..b3193f750 100644 --- a/src/components/playback/playbackmanager.js +++ b/src/components/playback/playbackmanager.js @@ -3920,9 +3920,7 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla return info ? info.supportedCommands : []; }; - PlaybackManager.prototype.setRepeatMode = function (value, player) { - - player = player || this._currentPlayer; + PlaybackManager.prototype.setRepeatMode = function (value, player = this._currentPlayer) { if (player && !enableLocalPlaylistManagement(player)) { return player.setRepeatMode(value); } @@ -3931,9 +3929,7 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla events.trigger(player, 'repeatmodechange'); }; - PlaybackManager.prototype.getRepeatMode = function (player) { - - player = player || this._currentPlayer; + PlaybackManager.prototype.getRepeatMode = function (player = this._currentPlayer) { if (player && !enableLocalPlaylistManagement(player)) { return player.getRepeatMode(); } diff --git a/src/components/playback/playqueuemanager.js b/src/components/playback/playqueuemanager.js index 8ffefd6b8..7d2f941f0 100644 --- a/src/components/playback/playqueuemanager.js +++ b/src/components/playback/playqueuemanager.js @@ -144,9 +144,7 @@ define([], function () { PlayQueueManager.prototype.removeFromPlaylist = function (playlistItemIds) { - var playlist = this.getPlaylist(); - - if (playlist.length <= playlistItemIds.length) { + if (this._playlist.length <= playlistItemIds.length) { return { result: 'empty' }; @@ -155,14 +153,12 @@ define([], function () { var currentPlaylistItemId = this.getCurrentPlaylistItemId(); var isCurrentIndex = playlistItemIds.indexOf(currentPlaylistItemId) !== -1; - if (this._sortedPlaylist.length <= playlistItemIds.length) { - this._sortedPlaylist = this._sortedPlaylist.splice(0).filter(function (item) { - return playlistItemIds.indexOf(item.PlaylistItemId) === -1; - }); - } + this._sortedPlaylist = this._sortedPlaylist.filter(function (item) { + return !playlistItemIds.includes(item.PlaylistItemId); + }); - this._playlist = playlist.filter(function (item) { - return playlistItemIds.indexOf(item.PlaylistItemId) === -1; + this._playlist = this._playlist.filter(function (item) { + return !playlistItemIds.includes(item.PlaylistItemId); }); return { diff --git a/src/components/remotecontrol/remotecontrol.js b/src/components/remotecontrol/remotecontrol.js index 36d703fd8..cbc726238 100644 --- a/src/components/remotecontrol/remotecontrol.js +++ b/src/components/remotecontrol/remotecontrol.js @@ -250,20 +250,16 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL var currentImgUrl; return function () { - function toggleRepeat(player) { - if (player) { - switch (playbackManager.getRepeatMode(player)) { - case 'RepeatNone': - playbackManager.setRepeatMode('RepeatAll', player); - break; - - case 'RepeatAll': - playbackManager.setRepeatMode('RepeatOne', player); - break; - - case 'RepeatOne': - playbackManager.setRepeatMode('RepeatNone', player); - } + function toggleRepeat() { + switch (playbackManager.getRepeatMode()) { + case 'RepeatNone': + playbackManager.setRepeatMode('RepeatAll'); + break; + case 'RepeatAll': + playbackManager.setRepeatMode('RepeatOne'); + break; + case 'RepeatOne': + playbackManager.setRepeatMode('RepeatNone'); } } @@ -336,7 +332,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL context.classList.add('hideVideoButtons'); } - updateRepeatModeDisplay(playState.RepeatMode); + updateRepeatModeDisplay(playbackManager.getRepeatMode()); onShuffleQueueModeChange(); updateNowPlayingInfo(context, state); } @@ -367,18 +363,12 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL case 'RepeatNone': repeatOn = false; break; - case undefined: - break; default: throw new TypeError('invalid value for repeatMode'); } for (const toggleRepeatButton of toggleRepeatButtons) { - if (!repeatOn) { - toggleRepeatButton.classList.remove(cssClass); - } else { - toggleRepeatButton.classList.add(cssClass); - } + toggleRepeatButton.classList.toggle(cssClass, repeatOn); toggleRepeatButton.innerHTML = innHtml; } } @@ -518,25 +508,23 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL onStateChanged.call(player, e, state); } - function onRepeatModeChange(e) { - var player = this; - updateRepeatModeDisplay(playbackManager.getRepeatMode(player)); + function onRepeatModeChange() { + updateRepeatModeDisplay(playbackManager.getRepeatMode()); } function onShuffleQueueModeChange() { let shuffleMode = playbackManager.getQueueShuffleMode(this); let context = dlg; + const cssClass = 'shuffleQueue-active'; let shuffleButtons = context.querySelectorAll('.btnShuffleQueue'); for (let shuffleButton of shuffleButtons) { switch (shuffleMode) { case 'Sorted': - shuffleButton.classList.remove('shuffleQueue-active'); + shuffleButton.classList.toggle(cssClass, false); break; case 'Shuffle': - shuffleButton.classList.add('shuffleQueue-active'); - break; - case undefined: + shuffleButton.classList.toggle(cssClass, true); break; default: throw new TypeError('invalid shuffle mode'); @@ -645,7 +633,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL function onBtnCommandClick() { if (currentPlayer) { if (this.classList.contains('repeatToggleButton')) { - toggleRepeat(currentPlayer); + toggleRepeat(); } else { playbackManager.sendCommand({ Name: this.getAttribute('data-command') From 8d31d507c270ca6c97723ec720ed1634fe449947 Mon Sep 17 00:00:00 2001 From: ferferga Date: Tue, 23 Jun 2020 23:45:21 +0200 Subject: [PATCH 31/42] Show favorite icon in playlist items on desktop layout --- src/components/remotecontrol/remotecontrol.js | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/components/remotecontrol/remotecontrol.js b/src/components/remotecontrol/remotecontrol.js index cbc726238..050ab3eb1 100644 --- a/src/components/remotecontrol/remotecontrol.js +++ b/src/components/remotecontrol/remotecontrol.js @@ -465,11 +465,21 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL function loadPlaylist(context, player) { getPlaylistItems(player).then(function (items) { var html = ''; + let favoritesEnabled = true; + if (layoutManager.mobile) { + if (items.length > 0) { + context.querySelector('.btnTogglePlaylist').classList.remove('hide'); + } else { + context.querySelector('.btnTogglePlaylist').classList.add('hide'); + } + favoritesEnabled = false; + } + html += listView.getListViewHtml({ items: items, smallIcon: true, action: 'setplaylistindex', - enableUserDataButtons: false, + enableUserDataButtons: favoritesEnabled, rightButtons: [{ icon: 'remove_circle_outline', title: globalize.translate('ButtonRemove'), @@ -477,13 +487,6 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL }], dragHandle: true }); - if (layoutManager.mobile) { - if (items.length > 0) { - context.querySelector('.btnTogglePlaylist').classList.remove('hide'); - } else { - context.querySelector('.btnTogglePlaylist').classList.add('hide'); - } - } var itemsContainer = context.querySelector('.playlist'); itemsContainer.innerHTML = html; From 6325046b96ffca33e35bbe240ae2fdd1da2d9d6e Mon Sep 17 00:00:00 2001 From: ferferga Date: Tue, 23 Jun 2020 23:57:38 +0200 Subject: [PATCH 32/42] Fix breakage on remote sessions and address review comments --- .../nowPlayingBar/nowPlayingBar.css | 4 ++++ src/components/nowPlayingBar/nowPlayingBar.js | 15 +++++++-------- src/components/remotecontrol/remotecontrol.js | 19 ++++++++++--------- src/nowplaying.html | 2 +- src/plugins/sessionPlayer/plugin.js | 4 ++-- src/scripts/site.js | 2 +- 6 files changed, 25 insertions(+), 21 deletions(-) diff --git a/src/components/nowPlayingBar/nowPlayingBar.css b/src/components/nowPlayingBar/nowPlayingBar.css index 35d71f054..6c6aeb001 100644 --- a/src/components/nowPlayingBar/nowPlayingBar.css +++ b/src/components/nowPlayingBar/nowPlayingBar.css @@ -154,6 +154,10 @@ display: none; } +.layout-desktop .nowPlayingBarRight .playPauseButton { + display: none; +} + .layout-mobile .nowPlayingBarRight input, .layout-mobile .nowPlayingBarRight div { display: none; diff --git a/src/components/nowPlayingBar/nowPlayingBar.js b/src/components/nowPlayingBar/nowPlayingBar.js index 8573d9418..4158ae6f9 100644 --- a/src/components/nowPlayingBar/nowPlayingBar.js +++ b/src/components/nowPlayingBar/nowPlayingBar.js @@ -196,8 +196,8 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', playbackManager.setRepeatMode('RepeatNone'); break; default: + case 'RepeatNone': playbackManager.setRepeatMode('RepeatAll'); - break; } }); @@ -371,12 +371,11 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', toggleRepeatButtonIcon.classList.add('repeat_one'); toggleRepeatButton.classList.add(cssClass); break; + default: case 'RepeatNone': toggleRepeatButtonIcon.classList.add('repeat'); toggleRepeatButton.classList.remove(cssClass); break; - default: - throw new TypeError('invalid value for repeatMode'); } } @@ -633,17 +632,17 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', function onQueueShuffleModeChange() { let shuffleMode = playbackManager.getQueueShuffleMode(); let context = nowPlayingBarElement; + const cssClass = 'shuffleQueue-active'; let toggleShuffleButton = context.querySelector('.btnShuffleQueue'); switch (shuffleMode) { - case 'Sorted': - toggleShuffleButton.classList.remove('shuffleQueue-active'); - break; case 'Shuffle': - toggleShuffleButton.classList.add('shuffleQueue-active'); + toggleShuffleButton.classList.toggle(cssClass, true); break; default: - throw new TypeError('invalid value for shuffleMode'); + case 'Sorted': + toggleShuffleButton.classList.toggle(cssClass, false); + break; } } diff --git a/src/components/remotecontrol/remotecontrol.js b/src/components/remotecontrol/remotecontrol.js index 050ab3eb1..81ea967ec 100644 --- a/src/components/remotecontrol/remotecontrol.js +++ b/src/components/remotecontrol/remotecontrol.js @@ -252,14 +252,16 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL return function () { function toggleRepeat() { switch (playbackManager.getRepeatMode()) { - case 'RepeatNone': - playbackManager.setRepeatMode('RepeatAll'); - break; case 'RepeatAll': playbackManager.setRepeatMode('RepeatOne'); break; case 'RepeatOne': playbackManager.setRepeatMode('RepeatNone'); + break; + default: + case 'RepeatNone': + playbackManager.setRepeatMode('RepeatAll'); + break; } } @@ -360,11 +362,10 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL case 'RepeatOne': innHtml = ''; break; + default: case 'RepeatNone': repeatOn = false; break; - default: - throw new TypeError('invalid value for repeatMode'); } for (const toggleRepeatButton of toggleRepeatButtons) { @@ -523,14 +524,13 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL for (let shuffleButton of shuffleButtons) { switch (shuffleMode) { - case 'Sorted': - shuffleButton.classList.toggle(cssClass, false); - break; case 'Shuffle': shuffleButton.classList.toggle(cssClass, true); break; default: - throw new TypeError('invalid shuffle mode'); + case 'Sorted': + shuffleButton.classList.toggle(cssClass, false); + break; } } onPlaylistUpdate(); @@ -867,6 +867,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL optionsSection.classList.add('align-items-right', 'justify-content-flex-end'); context.querySelector('.playlist').classList.remove('hide'); context.querySelector('.btnSavePlaylist').classList.remove('hide'); + context.classList.add('padded-bottom'); } else { optionsSection.querySelector('.btnTogglePlaylist').insertAdjacentHTML('afterend', volumecontrolHtml); optionsSection.classList.add('playlistSectionButtonTransparent'); diff --git a/src/nowplaying.html b/src/nowplaying.html index 56610ca6e..9460cb814 100644 --- a/src/nowplaying.html +++ b/src/nowplaying.html @@ -176,7 +176,7 @@
-
+
diff --git a/src/plugins/sessionPlayer/plugin.js b/src/plugins/sessionPlayer/plugin.js index 6a266941d..095cbc696 100644 --- a/src/plugins/sessionPlayer/plugin.js +++ b/src/plugins/sessionPlayer/plugin.js @@ -257,13 +257,13 @@ define(['playbackManager', 'events', 'serverNotifications', 'connectionManager'] return { name: s.DeviceName, deviceName: s.DeviceName, - deviceType: s.DeviceType, + deviceType: 'Remote Control', id: s.Id, playerName: name, appName: s.Client, playableMediaTypes: s.PlayableMediaTypes, isLocalPlayer: false, - supportedCommands: s.SupportedCommands, + supportedCommands: s.Capabilities.SupportedCommands, user: s.UserId ? { Id: s.UserId, diff --git a/src/scripts/site.js b/src/scripts/site.js index 27818f34b..e5c188bdc 100644 --- a/src/scripts/site.js +++ b/src/scripts/site.js @@ -196,7 +196,7 @@ var Dashboard = { capabilities: function (appHost) { var capabilities = { PlayableMediaTypes: ['Audio', 'Video'], - SupportedCommands: ['MoveUp', 'MoveDown', 'MoveLeft', 'MoveRight', 'PageUp', 'PageDown', 'PreviousLetter', 'NextLetter', 'ToggleOsd', 'ToggleContextMenu', 'Select', 'Back', 'SendKey', 'SendString', 'GoHome', 'GoToSettings', 'VolumeUp', 'VolumeDown', 'Mute', 'Unmute', 'ToggleMute', 'SetVolume', 'SetAudioStreamIndex', 'SetSubtitleStreamIndex', 'DisplayContent', 'GoToSearch', 'DisplayMessage', 'SetRepeatMode', 'ChannelUp', 'ChannelDown', 'PlayMediaSource', 'PlayTrailers'], + SupportedCommands: ['MoveUp', 'MoveDown', 'MoveLeft', 'MoveRight', 'PageUp', 'PageDown', 'PreviousLetter', 'NextLetter', 'ToggleOsd', 'ToggleContextMenu', 'Select', 'Back', 'SendKey', 'SendString', 'GoHome', 'GoToSettings', 'VolumeUp', 'VolumeDown', 'Mute', 'Unmute', 'ToggleMute', 'SetVolume', 'SetAudioStreamIndex', 'SetSubtitleStreamIndex', 'DisplayContent', 'GoToSearch', 'DisplayMessage', 'SetRepeatMode', 'SetShuffleQueue', 'ChannelUp', 'ChannelDown', 'PlayMediaSource', 'PlayTrailers'], SupportsPersistentIdentifier: 'cordova' === self.appMode || 'android' === self.appMode, SupportsMediaControl: true }; From ab12e6e4ad35adb2c23288c28d364b8aba909c96 Mon Sep 17 00:00:00 2001 From: ferferga Date: Wed, 24 Jun 2020 19:50:54 +0200 Subject: [PATCH 33/42] Address review comments --- src/components/nowPlayingBar/nowPlayingBar.js | 25 ++++++++----------- src/components/remotecontrol/remotecontrol.js | 17 +++---------- 2 files changed, 14 insertions(+), 28 deletions(-) diff --git a/src/components/nowPlayingBar/nowPlayingBar.js b/src/components/nowPlayingBar/nowPlayingBar.js index 4158ae6f9..dfb73a860 100644 --- a/src/components/nowPlayingBar/nowPlayingBar.js +++ b/src/components/nowPlayingBar/nowPlayingBar.js @@ -195,7 +195,6 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', case 'RepeatOne': playbackManager.setRepeatMode('RepeatNone'); break; - default: case 'RepeatNone': playbackManager.setRepeatMode('RepeatAll'); } @@ -203,11 +202,7 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', toggleRepeatButtonIcon = toggleRepeatButton.querySelector('.material-icons'); - if (appHost.supports('physicalvolumecontrol')) { - volumeSliderContainer.classList.add('hide'); - } else { - volumeSliderContainer.classList.remove('hide'); - } + volumeSliderContainer.classList.toggle('hide', appHost.supports('physicalvolumecontrol')); function setVolume() { if (currentPlayer) { @@ -371,8 +366,8 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', toggleRepeatButtonIcon.classList.add('repeat_one'); toggleRepeatButton.classList.add(cssClass); break; - default: case 'RepeatNone': + default: toggleRepeatButtonIcon.classList.add('repeat'); toggleRepeatButton.classList.remove(cssClass); break; @@ -445,11 +440,7 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', // See bindEvents for why this is necessary if (volumeSlider) { - if (showVolumeSlider) { - volumeSliderContainer.classList.remove('hide'); - } else { - volumeSliderContainer.classList.add('hide'); - } + volumeSliderContainer.classList.toggle('hide', showVolumeSlider); if (!volumeSlider.dragging) { volumeSlider.value = volumeLevel || 0; @@ -630,6 +621,10 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', } function onQueueShuffleModeChange() { + if (!isEnabled) { + return; + } + let shuffleMode = playbackManager.getQueueShuffleMode(); let context = nowPlayingBarElement; const cssClass = 'shuffleQueue-active'; @@ -637,11 +632,11 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', switch (shuffleMode) { case 'Shuffle': - toggleShuffleButton.classList.toggle(cssClass, true); + toggleShuffleButton.classList.add(cssClass); break; - default: case 'Sorted': - toggleShuffleButton.classList.toggle(cssClass, false); + default: + toggleShuffleButton.classList.remove(cssClass); break; } } diff --git a/src/components/remotecontrol/remotecontrol.js b/src/components/remotecontrol/remotecontrol.js index 81ea967ec..3a69353b4 100644 --- a/src/components/remotecontrol/remotecontrol.js +++ b/src/components/remotecontrol/remotecontrol.js @@ -258,10 +258,8 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL case 'RepeatOne': playbackManager.setRepeatMode('RepeatNone'); break; - default: case 'RepeatNone': playbackManager.setRepeatMode('RepeatAll'); - break; } } @@ -362,8 +360,8 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL case 'RepeatOne': innHtml = ''; break; - default: case 'RepeatNone': + default: repeatOn = false; break; } @@ -407,21 +405,14 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL if (!showMuteButton && !showVolumeSlider) { context.querySelector('.volumecontrol').classList.add('hide'); } else { - if (showMuteButton) { - buttonMute.classList.remove('hide'); - } else { - buttonMute.classList.add('hide'); - } + buttonMute.classList.toggle('hide', showMuteButton); var nowPlayingVolumeSlider = context.querySelector('.nowPlayingVolumeSlider'); var nowPlayingVolumeSliderContainer = context.querySelector('.nowPlayingVolumeSliderContainer'); if (nowPlayingVolumeSlider) { - if (showVolumeSlider) { - nowPlayingVolumeSliderContainer.classList.remove('hide'); - } else { - nowPlayingVolumeSliderContainer.classList.add('hide'); - } + + nowPlayingVolumeSliderContainer.classList.toggle('hide', !showVolumeSlider); if (!nowPlayingVolumeSlider.dragging) { nowPlayingVolumeSlider.value = volumeLevel || 0; From fd6b20d88ceef8cc9df111109352f580083476ec Mon Sep 17 00:00:00 2001 From: ferferga Date: Wed, 24 Jun 2020 19:52:47 +0200 Subject: [PATCH 34/42] Add missing CSS selector for TV Layout + fix code smell --- src/components/nowPlayingBar/nowPlayingBar.css | 3 ++- src/components/remotecontrol/remotecontrol.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/nowPlayingBar/nowPlayingBar.css b/src/components/nowPlayingBar/nowPlayingBar.css index 6c6aeb001..e545d82d1 100644 --- a/src/components/nowPlayingBar/nowPlayingBar.css +++ b/src/components/nowPlayingBar/nowPlayingBar.css @@ -154,7 +154,8 @@ display: none; } -.layout-desktop .nowPlayingBarRight .playPauseButton { +.layout-desktop .nowPlayingBarRight .playPauseButton, +.layout-tv .nowPlayingBarRight .playPauseButton { display: none; } diff --git a/src/components/remotecontrol/remotecontrol.js b/src/components/remotecontrol/remotecontrol.js index 3a69353b4..a83009ad9 100644 --- a/src/components/remotecontrol/remotecontrol.js +++ b/src/components/remotecontrol/remotecontrol.js @@ -518,8 +518,8 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL case 'Shuffle': shuffleButton.classList.toggle(cssClass, true); break; - default: case 'Sorted': + default: shuffleButton.classList.toggle(cssClass, false); break; } From 82fa5f554cc604055ce303cc03459b561ea88a7b Mon Sep 17 00:00:00 2001 From: ferferga Date: Tue, 30 Jun 2020 19:28:52 +0200 Subject: [PATCH 35/42] Address review comments and keep focus on playlist update --- src/components/nowPlayingBar/nowPlayingBar.js | 4 +-- src/components/remotecontrol/remotecontrol.js | 30 +++++++++++++------ 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/components/nowPlayingBar/nowPlayingBar.js b/src/components/nowPlayingBar/nowPlayingBar.js index dfb73a860..ebae04b08 100644 --- a/src/components/nowPlayingBar/nowPlayingBar.js +++ b/src/components/nowPlayingBar/nowPlayingBar.js @@ -159,7 +159,7 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', elem.querySelector('.previousTrackButton').addEventListener('click', function (e) { if (currentPlayer) { - if (currentPlayer.id === 'htmlaudioplayer' && (currentPlayer._currentTime >= 5 || !playbackManager.previousTrack(currentPlayer))) { + if (lastPlayerState.NowPlayingItem.MediaType === 'Audio' && (currentPlayer._currentTime >= 5 || !playbackManager.previousTrack(currentPlayer))) { // Cancel this event if doubleclick is fired if (e.detail > 1 && playbackManager.previousTrack(currentPlayer)) { return; @@ -440,7 +440,7 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', // See bindEvents for why this is necessary if (volumeSlider) { - volumeSliderContainer.classList.toggle('hide', showVolumeSlider); + volumeSliderContainer.classList.toggle('hide', !showVolumeSlider); if (!volumeSlider.dragging) { volumeSlider.value = volumeLevel || 0; diff --git a/src/components/remotecontrol/remotecontrol.js b/src/components/remotecontrol/remotecontrol.js index a83009ad9..092c08680 100644 --- a/src/components/remotecontrol/remotecontrol.js +++ b/src/components/remotecontrol/remotecontrol.js @@ -333,7 +333,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL } updateRepeatModeDisplay(playbackManager.getRepeatMode()); - onShuffleQueueModeChange(); + onShuffleQueueModeChange(false); updateNowPlayingInfo(context, state); } @@ -405,7 +405,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL if (!showMuteButton && !showVolumeSlider) { context.querySelector('.volumecontrol').classList.add('hide'); } else { - buttonMute.classList.toggle('hide', showMuteButton); + buttonMute.classList.toggle('hide', !showMuteButton); var nowPlayingVolumeSlider = context.querySelector('.nowPlayingVolumeSlider'); var nowPlayingVolumeSliderContainer = context.querySelector('.nowPlayingVolumeSliderContainer'); @@ -481,7 +481,16 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL }); var itemsContainer = context.querySelector('.playlist'); + let focusedItemPlaylistId = itemsContainer.querySelector('button:focus'); itemsContainer.innerHTML = html; + if (focusedItemPlaylistId !== null) { + focusedItemPlaylistId = focusedItemPlaylistId.getAttribute('data-playlistitemid'); + const newFocusedItem = itemsContainer.querySelector(`button[data-playlistitemid=${focusedItemPlaylistId}]`); + if (newFocusedItem !== null) { + newFocusedItem.focus(); + } + } + var playlistItemId = playbackManager.getCurrentPlaylistItemId(player); if (playlistItemId) { @@ -507,7 +516,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL updateRepeatModeDisplay(playbackManager.getRepeatMode()); } - function onShuffleQueueModeChange() { + function onShuffleQueueModeChange(updateView = true) { let shuffleMode = playbackManager.getQueueShuffleMode(this); let context = dlg; const cssClass = 'shuffleQueue-active'; @@ -516,15 +525,18 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL for (let shuffleButton of shuffleButtons) { switch (shuffleMode) { case 'Shuffle': - shuffleButton.classList.toggle(cssClass, true); + shuffleButton.classList.add(cssClass); break; case 'Sorted': default: - shuffleButton.classList.toggle(cssClass, false); + shuffleButton.classList.remove(cssClass); break; } } - onPlaylistUpdate(); + + if (updateView) { + onPlaylistUpdate(); + } } function onPlaylistUpdate(e) { @@ -550,7 +562,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL if (!state.NextMediaType) { updatePlayerState(player, dlg, {}); - loadPlaylist(dlg); + //onPlaylistUpdate(); Emby.Page.back(); } } @@ -562,7 +574,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL function onStateChanged(event, state) { var player = this; updatePlayerState(player, dlg, state); - loadPlaylist(dlg, player); + onPlaylistUpdate(); } function onTimeUpdate(e) { @@ -720,7 +732,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL context.querySelector('.btnPreviousTrack').addEventListener('click', function (e) { if (currentPlayer) { - if (currentPlayer.id === 'htmlaudioplayer' && (currentPlayer._currentTime >= 5 || !playbackManager.previousTrack(currentPlayer))) { + if (lastPlayerState.NowPlayingItem.MediaType === 'Audio' && (currentPlayer._currentTime >= 5 || !playbackManager.previousTrack(currentPlayer))) { // Cancel this event if doubleclick is fired if (e.detail > 1 && playbackManager.previousTrack(currentPlayer)) { return; From 5626380d7f0326fe8943be720eab6b25d656f4b0 Mon Sep 17 00:00:00 2001 From: ferferga Date: Tue, 30 Jun 2020 20:16:14 +0200 Subject: [PATCH 36/42] Revert changes caused by faulty API changes (jellyfin/jellyfin#3435) --- src/plugins/sessionPlayer/plugin.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/sessionPlayer/plugin.js b/src/plugins/sessionPlayer/plugin.js index 095cbc696..084aa027c 100644 --- a/src/plugins/sessionPlayer/plugin.js +++ b/src/plugins/sessionPlayer/plugin.js @@ -257,7 +257,7 @@ define(['playbackManager', 'events', 'serverNotifications', 'connectionManager'] return { name: s.DeviceName, deviceName: s.DeviceName, - deviceType: 'Remote Control', + deviceType: s.DeviceType, id: s.Id, playerName: name, appName: s.Client, From 21b17e80972479072fe3886cd140f26d3db67dd7 Mon Sep 17 00:00:00 2001 From: Fernando Date: Wed, 1 Jul 2020 13:22:56 +0200 Subject: [PATCH 37/42] Focus contextbutton after update if focused beforehand Co-authored-by: Dmitry Lyzo <56478732+dmitrylyzo@users.noreply.github.com> --- src/components/remotecontrol/remotecontrol.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/remotecontrol/remotecontrol.js b/src/components/remotecontrol/remotecontrol.js index 655761d93..e23051870 100644 --- a/src/components/remotecontrol/remotecontrol.js +++ b/src/components/remotecontrol/remotecontrol.js @@ -178,9 +178,13 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL let contextButton = context.querySelector('.btnToggleContextMenu'); // We remove the previous event listener by replacing the item in each update event + const autoFocusContextButton = document.activeElement === contextButton; let contextButtonClone = contextButton.cloneNode(true); contextButton.parentNode.replaceChild(contextButtonClone, contextButton); contextButton = context.querySelector('.btnToggleContextMenu'); + if (autoFocusContextButton) { + contextButton.focus(); + } var options = { play: false, queue: false, From 737341934ac44dc1c3f8ce4bf73f79654994a344 Mon Sep 17 00:00:00 2001 From: ferferga Date: Wed, 1 Jul 2020 15:05:08 +0200 Subject: [PATCH 38/42] Add stop playback option in mobile's layout context menu --- src/components/itemContextMenu.js | 18 +++++++++++------- src/components/playback/playbackmanager.js | 5 +---- src/components/remotecontrol/remotecontrol.js | 2 ++ src/strings/en-us.json | 3 ++- src/strings/es.json | 3 ++- 5 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/components/itemContextMenu.js b/src/components/itemContextMenu.js index 434d29b32..835d62f9b 100644 --- a/src/components/itemContextMenu.js +++ b/src/components/itemContextMenu.js @@ -28,6 +28,14 @@ define(['apphost', 'globalize', 'connectionManager', 'itemHelper', 'appRouter', } } + if (playbackManager.getCurrentPlayer() !== null && options.stopPlayback) { + commands.push({ + name: globalize.translate('StopPlayback'), + id: 'stopPlayback', + icon: 'stop' + }); + } + if (playbackManager.canQueue(item)) { if (options.queue !== false) { commands.push({ @@ -44,13 +52,6 @@ define(['apphost', 'globalize', 'connectionManager', 'itemHelper', 'appRouter', icon: 'playlist_add' }); } - - //if (options.queueAllFromHere) { - // commands.push({ - // name: globalize.translate("QueueAllFromHere"), - // id: "queueallfromhere" - // }); - //} } if (item.IsFolder || item.Type === 'MusicArtist' || item.Type === 'MusicGenre') { @@ -431,6 +432,9 @@ define(['apphost', 'globalize', 'connectionManager', 'itemHelper', 'appRouter', play(item, false, true, true); getResolveFunction(resolve, id)(); break; + case 'stopPlayback': + playbackManager.stop(); + break; case 'record': require(['recordingCreator'], function (recordingCreator) { recordingCreator.show(itemId, serverId).then(getResolveFunction(resolve, id, true), getResolveFunction(resolve, id)); diff --git a/src/components/playback/playbackmanager.js b/src/components/playback/playbackmanager.js index b3193f750..33d781687 100644 --- a/src/components/playback/playbackmanager.js +++ b/src/components/playback/playbackmanager.js @@ -3709,10 +3709,7 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla return textStreamUrl; }; - PlaybackManager.prototype.stop = function (player) { - - player = player || this._currentPlayer; - + PlaybackManager.prototype.stop = function (player = this._currentPlayer) { if (player) { if (enableLocalPlaylistManagement(player)) { diff --git a/src/components/remotecontrol/remotecontrol.js b/src/components/remotecontrol/remotecontrol.js index e23051870..fcc7a7fe4 100644 --- a/src/components/remotecontrol/remotecontrol.js +++ b/src/components/remotecontrol/remotecontrol.js @@ -185,9 +185,11 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL if (autoFocusContextButton) { contextButton.focus(); } + let stopPlayback = false || layoutManager.mobile; var options = { play: false, queue: false, + stopPlayback: stopPlayback, openAlbum: false, positionTo: contextButton }; diff --git a/src/strings/en-us.json b/src/strings/en-us.json index 7df61c4c5..02fb652bf 100644 --- a/src/strings/en-us.json +++ b/src/strings/en-us.json @@ -1563,5 +1563,6 @@ "EnableBlurhashHelp": "Images that are still being loaded will be displayed with a blurred placeholder", "ButtonSyncPlay": "SyncPlay", "ButtonCast": "Cast", - "ButtonPlayer": "Player" + "ButtonPlayer": "Player", + "StopPlayback": "Stop playback" } diff --git a/src/strings/es.json b/src/strings/es.json index 84f01c94e..b86c958b5 100644 --- a/src/strings/es.json +++ b/src/strings/es.json @@ -1573,5 +1573,6 @@ "LabelRepositoryUrl": "URL del repositorio", "HeaderNewRepository": "Nuevo repositorio", "MessageNoRepositories": "Sin repositorios.", - "Writers": "Escritores" + "Writers": "Escritores", + "StopPlayback": "Detener la reproducción" } From 08a995fe2cdae4bd511428ab19f425508ac7b53a Mon Sep 17 00:00:00 2001 From: ferferga Date: Wed, 1 Jul 2020 15:54:51 +0200 Subject: [PATCH 39/42] Add 'Clear Queue' option --- src/components/itemContextMenu.js | 24 ++++++++++++++----- src/components/nowPlayingBar/nowPlayingBar.js | 1 + src/components/playback/playbackmanager.js | 9 +++++++ src/components/playback/playqueuemanager.js | 8 +++++++ src/components/remotecontrol/remotecontrol.js | 15 ++++++++---- src/strings/en-us.json | 3 ++- src/strings/es.json | 3 ++- 7 files changed, 50 insertions(+), 13 deletions(-) diff --git a/src/components/itemContextMenu.js b/src/components/itemContextMenu.js index 835d62f9b..96f8f2d35 100644 --- a/src/components/itemContextMenu.js +++ b/src/components/itemContextMenu.js @@ -28,12 +28,21 @@ define(['apphost', 'globalize', 'connectionManager', 'itemHelper', 'appRouter', } } - if (playbackManager.getCurrentPlayer() !== null && options.stopPlayback) { - commands.push({ - name: globalize.translate('StopPlayback'), - id: 'stopPlayback', - icon: 'stop' - }); + if (playbackManager.getCurrentPlayer() !== null) { + if (options.stopPlayback) { + commands.push({ + name: globalize.translate('StopPlayback'), + id: 'stopPlayback', + icon: 'stop' + }); + } + if (options.clearQueue) { + commands.push({ + name: globalize.translate('ClearQueue'), + id: 'clearQueue', + icon: 'clear_all' + }); + } } if (playbackManager.canQueue(item)) { @@ -435,6 +444,9 @@ define(['apphost', 'globalize', 'connectionManager', 'itemHelper', 'appRouter', case 'stopPlayback': playbackManager.stop(); break; + case 'clearQueue': + playbackManager.clearQueue(); + break; case 'record': require(['recordingCreator'], function (recordingCreator) { recordingCreator.show(itemId, serverId).then(getResolveFunction(resolve, id, true), getResolveFunction(resolve, id)); diff --git a/src/components/nowPlayingBar/nowPlayingBar.js b/src/components/nowPlayingBar/nowPlayingBar.js index ebae04b08..a229fab4b 100644 --- a/src/components/nowPlayingBar/nowPlayingBar.js +++ b/src/components/nowPlayingBar/nowPlayingBar.js @@ -584,6 +584,7 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', let options = { play: false, queue: false, + clearQueue: true, positionTo: contextButton }; apiClient.getCurrentUser().then(function (user) { diff --git a/src/components/playback/playbackmanager.js b/src/components/playback/playbackmanager.js index 33d781687..ae94ba8d4 100644 --- a/src/components/playback/playbackmanager.js +++ b/src/components/playback/playbackmanager.js @@ -3971,6 +3971,15 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla events.trigger(player, 'shufflequeuemodechange'); }; + PlaybackManager.prototype.clearQueue = function (clearCurrentItem = false, player = this._currentPlayer) { + if (player && !enableLocalPlaylistManagement(player)) { + return player.clearQueue(clearCurrentItem); + } + + this._playQueueManager.clearPlaylist(clearCurrentItem); + events.trigger(player, 'playlistitemremove'); + }; + PlaybackManager.prototype.trySetActiveDeviceName = function (name) { name = normalizeName(name); diff --git a/src/components/playback/playqueuemanager.js b/src/components/playback/playqueuemanager.js index 7d2f941f0..2f411091c 100644 --- a/src/components/playback/playqueuemanager.js +++ b/src/components/playback/playqueuemanager.js @@ -84,6 +84,14 @@ define([], function () { this._shuffleMode = 'Sorted'; }; + PlayQueueManager.prototype.clearPlaylist = function (clearCurrentItem = false) { + const currentPlaylistItem = this._playlist.splice(this.getCurrentPlaylistIndex(), 1)[0]; + this._playlist = []; + if (!clearCurrentItem) { + this._playlist.push(currentPlaylistItem); + } + }; + function arrayInsertAt(destArray, pos, arrayToInsert) { var args = []; args.push(pos); // where to insert diff --git a/src/components/remotecontrol/remotecontrol.js b/src/components/remotecontrol/remotecontrol.js index fcc7a7fe4..6f02bed15 100644 --- a/src/components/remotecontrol/remotecontrol.js +++ b/src/components/remotecontrol/remotecontrol.js @@ -190,6 +190,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL play: false, queue: false, stopPlayback: stopPlayback, + clearQueue: true, openAlbum: false, positionTo: contextButton }; @@ -551,14 +552,18 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL function onPlaylistItemRemoved(e, info) { var context = dlg; - var playlistItemIds = info.playlistItemIds; + if (info !== undefined) { + var playlistItemIds = info.playlistItemIds; - for (var i = 0, length = playlistItemIds.length; i < length; i++) { - var listItem = context.querySelector('.listItem[data-playlistItemId="' + playlistItemIds[i] + '"]'); + for (var i = 0, length = playlistItemIds.length; i < length; i++) { + var listItem = context.querySelector('.listItem[data-playlistItemId="' + playlistItemIds[i] + '"]'); - if (listItem) { - listItem.parentNode.removeChild(listItem); + if (listItem) { + listItem.parentNode.removeChild(listItem); + } } + } else { + onPlaylistUpdate(); } } diff --git a/src/strings/en-us.json b/src/strings/en-us.json index 02fb652bf..41dd4cd50 100644 --- a/src/strings/en-us.json +++ b/src/strings/en-us.json @@ -1564,5 +1564,6 @@ "ButtonSyncPlay": "SyncPlay", "ButtonCast": "Cast", "ButtonPlayer": "Player", - "StopPlayback": "Stop playback" + "StopPlayback": "Stop playback", + "ClearQueue": "Clear queue" } diff --git a/src/strings/es.json b/src/strings/es.json index b86c958b5..960e5a897 100644 --- a/src/strings/es.json +++ b/src/strings/es.json @@ -1574,5 +1574,6 @@ "HeaderNewRepository": "Nuevo repositorio", "MessageNoRepositories": "Sin repositorios.", "Writers": "Escritores", - "StopPlayback": "Detener la reproducción" + "StopPlayback": "Detener la reproducción", + "ClearQueue": "Borrar la cola" } From b4d4b4fab9c5735cbc0b01f80cd1b4a83c076776 Mon Sep 17 00:00:00 2001 From: ferferga Date: Wed, 1 Jul 2020 15:56:47 +0200 Subject: [PATCH 40/42] Context menu option parity between miniplayer and fullscreen player --- src/components/remotecontrol/remotecontrol.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/components/remotecontrol/remotecontrol.js b/src/components/remotecontrol/remotecontrol.js index 6f02bed15..e77d29ae3 100644 --- a/src/components/remotecontrol/remotecontrol.js +++ b/src/components/remotecontrol/remotecontrol.js @@ -195,12 +195,14 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL positionTo: contextButton }; var apiClient = connectionManager.getApiClient(item.ServerId); - apiClient.getCurrentUser().then(function (user) { - contextButton.addEventListener('click', function () { - itemContextMenu.show(Object.assign({ - item: item, - user: user - }, options)); + apiClient.getItem(apiClient.getCurrentUserId(), item.Id).then(function (fullItem) { + apiClient.getCurrentUser().then(function (user) { + contextButton.addEventListener('click', function () { + itemContextMenu.show(Object.assign({ + item: fullItem, + user: user + }, options)); + }); }); }); setImageUrl(context, state, url); From a715bd8e6a5c2a89c7092c8f839d6835148e7619 Mon Sep 17 00:00:00 2001 From: Fernando Date: Wed, 1 Jul 2020 16:37:31 +0200 Subject: [PATCH 41/42] Improve syntax Co-authored-by: Dmitry Lyzo <56478732+dmitrylyzo@users.noreply.github.com> --- src/components/remotecontrol/remotecontrol.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/remotecontrol/remotecontrol.js b/src/components/remotecontrol/remotecontrol.js index e77d29ae3..398f7bc26 100644 --- a/src/components/remotecontrol/remotecontrol.js +++ b/src/components/remotecontrol/remotecontrol.js @@ -185,7 +185,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL if (autoFocusContextButton) { contextButton.focus(); } - let stopPlayback = false || layoutManager.mobile; + const stopPlayback = !!layoutManager.mobile; var options = { play: false, queue: false, From 96acbc0307f04333bfb3e8a0f5447666ff2a7236 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Fern=C3=A1ndez?= Date: Thu, 2 Jul 2020 00:08:11 +0200 Subject: [PATCH 42/42] Update playlist visually when queuing items --- src/components/playback/playbackmanager.js | 5 +++-- src/components/remotecontrol/remotecontrol.js | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/playback/playbackmanager.js b/src/components/playback/playbackmanager.js index ae94ba8d4..c79294442 100644 --- a/src/components/playback/playbackmanager.js +++ b/src/components/playback/playbackmanager.js @@ -2878,11 +2878,11 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla } }; - self.queue = function (options, player) { + self.queue = function (options, player = this._currentPlayer) { queue(options, '', player); }; - self.queueNext = function (options, player) { + self.queueNext = function (options, player = this._currentPlayer) { queue(options, 'next', player); }; @@ -2970,6 +2970,7 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla } else { self._playQueueManager.queue(items); } + events.trigger(player, 'playlistitemadd'); } function onPlayerProgressInterval() { diff --git a/src/components/remotecontrol/remotecontrol.js b/src/components/remotecontrol/remotecontrol.js index 398f7bc26..33c44ab40 100644 --- a/src/components/remotecontrol/remotecontrol.js +++ b/src/components/remotecontrol/remotecontrol.js @@ -615,6 +615,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL events.off(player, 'shufflequeuemodechange', onShuffleQueueModeChange); events.off(player, 'playlistitemremove', onPlaylistItemRemoved); events.off(player, 'playlistitemmove', onPlaylistUpdate); + events.off(player, 'playlistitemadd', onPlaylistUpdate); events.off(player, 'playbackstop', onPlaybackStopped); events.off(player, 'volumechange', onVolumeChanged); events.off(player, 'pause', onPlayPauseStateChanged); @@ -636,6 +637,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL events.on(player, 'shufflequeuemodechange', onShuffleQueueModeChange); events.on(player, 'playlistitemremove', onPlaylistItemRemoved); events.on(player, 'playlistitemmove', onPlaylistUpdate); + events.on(player, 'playlistitemadd', onPlaylistUpdate); events.on(player, 'playbackstop', onPlaybackStopped); events.on(player, 'volumechange', onVolumeChanged); events.on(player, 'pause', onPlayPauseStateChanged);