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 bc9c3c1a88..9732951f59 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 073c925339..115fc2c24a 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 089915a834..f5de415a36 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 9732951f59..33f9aa405b 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 f5de415a36..c1c5bfd6da 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 a5a479f2f5..2f3a386bff 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 c1c5bfd6da..89c56090eb 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 5f235a562e..efeb2526d5 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 115fc2c24a..de7e689ac3 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 89c56090eb..d850d1be60 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 efeb2526d5..4266b01f01 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 73f07a05f2..ad1b156d67 100644 --- a/src/components/playback/playbackmanager.js +++ b/src/components/playback/playbackmanager.js @@ -2097,6 +2097,7 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla state.PlayState.IsMuted = player.isMuted(); state.PlayState.IsPaused = player.paused(); state.PlayState.RepeatMode = self.getRepeatMode(player); + state.PlayState.ShuffleMode = self.getPlaylistShuffleMode(player); state.PlayState.MaxStreamingBitrate = self.getMaxStreamingBitrate(player); state.PlayState.PositionTicks = getCurrentTicks(player); @@ -3304,6 +3305,11 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla sendProgressUpdate(player, 'repeatmodechange'); } + function onShufflePlaylistModeChange() { + var player = this; + sendProgressUpdate(player, 'shuffleplaylistmodechange'); + } + function onPlaylistItemMove(e) { var player = this; sendProgressUpdate(player, 'playlistitemmove', true); @@ -3358,6 +3364,7 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla events.on(player, 'unpause', onPlaybackUnpause); events.on(player, 'volumechange', onPlaybackVolumeChange); events.on(player, 'repeatmodechange', onRepeatModeChange); + events.on(player, 'shuffleplaylistmodechange', onShufflePlaylistModeChange); events.on(player, 'playlistitemmove', onPlaylistItemMove); events.on(player, 'playlistitemremove', onPlaylistItemRemove); events.on(player, 'playlistitemadd', onPlaylistItemAdd); @@ -3370,6 +3377,7 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla events.on(player, 'unpause', onPlaybackUnpause); events.on(player, 'volumechange', onPlaybackVolumeChange); events.on(player, 'repeatmodechange', onRepeatModeChange); + events.on(player, 'shuffleplaylistmodechange', onShufflePlaylistModeChange); events.on(player, 'playlistitemmove', onPlaylistItemMove); events.on(player, 'playlistitemremove', onPlaylistItemRemove); events.on(player, 'playlistitemadd', onPlaylistItemAdd); @@ -3811,7 +3819,7 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla }); }; - PlaybackManager.prototype.shuffle = function (shuffleItem, player, queryOptions) { + PlaybackManager.prototype.shuffle = function (shuffleItem, player) { player = player || this._currentPlayer; if (player && player.shuffle) { @@ -3878,6 +3886,7 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla 'GoToSearch', 'DisplayMessage', 'SetRepeatMode', + 'SetPlaylistShuffleMode', 'PlayMediaSource', 'PlayTrailers' ]; @@ -3932,6 +3941,27 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla return this._playQueueManager.getRepeatMode(); }; + PlaybackManager.prototype.setPlaylistShuffleMode = function (value, player) { + + player = player || this._currentPlayer; + if (player && !enableLocalPlaylistManagement(player)) { + return player.setShuffleMode(value); + } + + this._playQueueManager.setShuffleMode(value); + events.trigger(player, 'shuffleplaylistmodechange'); + }; + + PlaybackManager.prototype.getPlaylistShuffleMode = function (player) { + + player = player || this._currentPlayer; + if (player && !enableLocalPlaylistManagement(player)) { + return player.getPlaylistShuffleMode(); + } + + return this._playQueueManager.getShuffleMode(); + }; + PlaybackManager.prototype.trySetActiveDeviceName = function (name) { name = normalizeName(name); @@ -4000,6 +4030,9 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla case 'SetRepeatMode': this.setRepeatMode(cmd.Arguments.RepeatMode, player); break; + case 'SetPlaylistShuffleMode': + this.setPlaylistShuffleMode(cmd.Arguments.ShuffleMode, player); + break; case 'VolumeUp': this.volumeUp(player); break; diff --git a/src/components/playback/playqueuemanager.js b/src/components/playback/playqueuemanager.js index 565cb6993e..e26a738a02 100644 --- a/src/components/playback/playqueuemanager.js +++ b/src/components/playback/playqueuemanager.js @@ -24,8 +24,10 @@ define([], function () { function PlayQueueManager() { + this._sortedPlaylist = []; this._playlist = []; this._repeatMode = 'RepeatNone'; + this._shuffleMode = 'Sorted'; } PlayQueueManager.prototype.getPlaylist = function () { @@ -56,6 +58,30 @@ define([], function () { } }; + PlayQueueManager.prototype.shufflePlaylist = function () { + this._sortedPlaylist = []; + for (let item of this._playlist) { + this._sortedPlaylist.push(item); + } + + for (let i = this._playlist.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * i); + const temp = this._playlist[i]; + this._playlist[i] = this._playlist[j]; + this._playlist[j] = temp; + } + this._shuffleMode = 'Shuffle'; + }; + + PlayQueueManager.prototype.sortShuffledPlaylist = function () { + this._playlist = []; + for (let item of this._sortedPlaylist) { + this._playlist.push(item); + } + this._sortedPlaylist = []; + this._shuffleMode = 'Sorted'; + }; + function arrayInsertAt(destArray, pos, arrayToInsert) { var args = []; args.push(pos); // where to insert @@ -176,21 +202,39 @@ define([], function () { PlayQueueManager.prototype.reset = function () { + this._sortedPlaylist = []; this._playlist = []; this._currentPlaylistItemId = null; this._repeatMode = 'RepeatNone'; + this._shuffleMode = 'Sorted'; }; PlayQueueManager.prototype.setRepeatMode = function (value) { - - this._repeatMode = value; + if (value === 'RepeatOne' || value === 'RepeatAll' || value === 'RepeatNone') { + this._repeatMode = value; + } else { + throw new TypeError('invalid value provided for setRepeatMode'); + } }; PlayQueueManager.prototype.getRepeatMode = function () { - return this._repeatMode; }; + PlayQueueManager.prototype.setShuffleMode = function (value) { + if (value === 'Sorted') { + this.sortShuffledPlaylist(); + } else if (value === 'Shuffle') { + this.shufflePlaylist(); + } else { + throw new TypeError('invalid value provided for setShuffleMode'); + } + }; + + PlayQueueManager.prototype.getShuffleMode = function () { + return this._shuffleMode; + }; + PlayQueueManager.prototype.getNextItemInfo = function () { var newIndex; diff --git a/src/components/remotecontrol/remotecontrol.css b/src/components/remotecontrol/remotecontrol.css index de7e689ac3..34a65f61e3 100644 --- a/src/components/remotecontrol/remotecontrol.css +++ b/src/components/remotecontrol/remotecontrol.css @@ -298,7 +298,7 @@ font-size: smaller; } - .nowPlayingInfoButtons .btnFastForward { + .nowPlayingInfoButtons .btnShuffleMobile { position: absolute; right: 0; margin-right: 0; diff --git a/src/components/remotecontrol/remotecontrol.js b/src/components/remotecontrol/remotecontrol.js index d850d1be60..8755fb491c 100644 --- a/src/components/remotecontrol/remotecontrol.js +++ b/src/components/remotecontrol/remotecontrol.js @@ -297,6 +297,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL buttonVisible(context.querySelector('.btnRewind'), false); buttonVisible(context.querySelector('.btnFastForward'), false); buttonVisible(context.querySelector('.nowPlayingInfoButtons').querySelector('.repeatToggleButton'), true); + buttonVisible(context.querySelector('.nowPlayingInfoButtons').querySelector('.btnShuffleMobile'), true); } else { buttonVisible(context.querySelector('.btnRewind'), null != item); buttonVisible(context.querySelector('.btnFastForward'), null != item); @@ -495,6 +496,18 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL updateRepeatModeDisplay(playbackManager.getRepeatMode(player)); } + function onShufflePlaylistModeChange() { + let shuffleMode = playbackManager.getPlaylistShuffleMode(this); + let context = dlg; + let shuffleButton = context.querySelector('.btnShuffle'); + if ('Sorted' === shuffleMode) { + shuffleButton.classList.remove('shuffleButton-active'); + } else if ('Shuffle' === shuffleMode) { + shuffleButton.classList.add('shuffleButton-active'); + } + onPlaylistUpdate(); + } + function onPlaylistUpdate(e) { loadPlaylist(dlg, this); } @@ -556,7 +569,8 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL events.off(player, 'playbackstart', onPlaybackStart); events.off(player, 'statechange', onStateChanged); events.off(player, 'repeatmodechange', onRepeatModeChange); - events.off(player, 'playlistitemremove', onPlaylistUpdate); + events.off(player, 'shuffleplaylistmodechange', onShufflePlaylistModeChange); + events.off(player, 'playlistitemremove', onPlaylistItemRemoved); events.off(player, 'playlistitemmove', onPlaylistUpdate); events.off(player, 'playbackstop', onPlaybackStopped); events.off(player, 'volumechange', onVolumeChanged); @@ -576,6 +590,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL events.on(player, 'playbackstart', onPlaybackStart); events.on(player, 'statechange', onStateChanged); events.on(player, 'repeatmodechange', onRepeatModeChange); + events.on(player, 'shuffleplaylistmodechange', onShufflePlaylistModeChange); events.on(player, 'playlistitemremove', onPlaylistItemRemoved); events.on(player, 'playlistitemmove', onPlaylistUpdate); events.on(player, 'playbackstop', onPlaybackStopped); @@ -676,6 +691,16 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL playbackManager.fastForward(currentPlayer); } }); + context.querySelector('.btnShuffle').addEventListener('click', function () { + if (currentPlayer) { + if (playbackManager.getPlaylistShuffleMode(currentPlayer) === 'Sorted') { + playbackManager.setPlaylistShuffleMode('Shuffle', currentPlayer); + } else { + playbackManager.setPlaylistShuffleMode('Sorted', currentPlayer); + } + } + }); + context.querySelector('.btnPreviousTrack').addEventListener('click', function () { console.log(currentPlayer); if (currentPlayer) { diff --git a/src/nowplaying.html b/src/nowplaying.html index 4266b01f01..70f8e298ef 100644 --- a/src/nowplaying.html +++ b/src/nowplaying.html @@ -58,7 +58,10 @@ - + +
diff --git a/src/plugins/sessionPlayer/plugin.js b/src/plugins/sessionPlayer/plugin.js index 489b57493d..241d3fba48 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 b3ce2c7e92..9d0ccdc9d8 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 74a60c91c0..5215f4bb6c 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 a32e606386..1e8a996c98 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 114ef7c3b1..82b2f50755 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 de69a5542a..140969f657 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 e7d4c0371b..a62d38297e 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 b1e77715ff..e83ef729d0 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 42f32ba794..4ce8526c98 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 33f9aa405b..e1d8ebba6a 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 34a65f61e3..6e6dc3bf19 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 8755fb491c..0b6ba6d571 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 70f8e298ef..0c76882466 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 e1d8ebba6a..958b0ebfcb 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 0b6ba6d571..e084afc8e4 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 958b0ebfcb..e50c0cee57 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 e084afc8e4..f2a298ac27 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 e50c0cee57..9eebdfcfc1 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 f2a298ac27..3d1faadffa 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 9eebdfcfc1..1c1fbb9f0a 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 3d1faadffa..dd4e42c0ce 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 f258f5fe4b..4f0e4e5063 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 1c1fbb9f0a..50830af104 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 dd4e42c0ce..3a87fff806 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 bf77f630ae..fcfb3e7110 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 eedfa3d739..09b5f2cae1 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 50830af104..f76055226d 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 6e6dc3bf19..7df8fefc32 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 3a87fff806..c31d57a747 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 f76055226d..6fc5264897 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 09b5f2cae1..126692923d 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 e83ef729d0..a2d5b4479c 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 7df8fefc32..d80d7a3c3a 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 c31d57a747..0cf8e2f95c 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 d80d7a3c3a..6dbd3b7160 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 0cf8e2f95c..c31d57a747 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 b3f75f7a6d..22242959fe 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 241d3fba48..b607f04417 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 2553c284f0..77c51f33c1 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 6fc5264897..eb21f90441 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 ad1b156d67..4d58e85cf3 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 c31d57a747..a671bf5aff 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 22242959fe..0809fb82c3 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 b607f04417..5ed07bb4ff 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 77c51f33c1..c1d9c95e9c 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 eb21f90441..07cdaac4fe 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 4d58e85cf3..38ce9bf9f1 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 a671bf5aff..784e7ff5b1 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 2f3a386bff..429ed7a650 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 c24fcf6ba6..f02f1de6bf 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 e540a40211..37c0e26969 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 4ce8526c98..1bbde6fdbc 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 a2d5b4479c..c22429eedc 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 07cdaac4fe..f0b585e9d0 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 6dbd3b7160..3fd0ab0971 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 784e7ff5b1..18a8f2005c 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 0c76882466..e429647076 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 9d0ccdc9d8..8b6b3e1902 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 5215f4bb6c..d8d7525d3c 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 1e8a996c98..9491b2cae9 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 82b2f50755..abb5a58fb4 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 140969f657..ca8fe1fdfa 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 a62d38297e..7def8215fe 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 18a8f2005c..6626d83c49 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 e26a738a02..4eac7d7c76 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 0386981bfb..69f7025d6e 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 e429647076..56610ca6ee 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 f02f1de6bf..75943a0c45 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 f594a3dafd..b08e5024aa 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 f0b585e9d0..b8a2a5082b 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 38ce9bf9f1..3bd4be9c0c 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 4eac7d7c76..03f5246b89 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 69f7025d6e..d4511a9dd7 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 6626d83c49..89598d09a8 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 0809fb82c3..6384ce7690 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 5ed07bb4ff..6a266941d7 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 c1d9c95e9c..cddd2cf794 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 b8a2a5082b..97227ca71d 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 03f5246b89..8ffefd6b84 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 b9c2c9eb87..36d703fd87 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 97227ca71d..8573d94182 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 3bd4be9c0c..b3193f7505 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 8ffefd6b84..7d2f941f0a 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 36d703fd87..cbc7262388 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 cbc7262388..050ab3eb1f 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 35d71f0549..6c6aeb0011 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 8573d94182..4158ae6f9f 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 050ab3eb1f..81ea967ec7 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 56610ca6ee..9460cb814b 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 6a266941d7..095cbc696e 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 27818f34b2..e5c188bdcc 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 4158ae6f9f..dfb73a8605 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 81ea967ec7..3a69353b43 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 6c6aeb0011..e545d82d1e 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 3a69353b43..a83009ad90 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 dfb73a8605..ebae04b08a 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 a83009ad90..092c08680d 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 095cbc696e..084aa027cf 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 655761d932..e23051870b 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 434d29b32a..835d62f9b7 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 b3193f7505..33d781687b 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 e23051870b..fcc7a7fe42 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 7df61c4c5d..02fb652bf7 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 84f01c94e6..b86c958b55 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 835d62f9b7..96f8f2d356 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 ebae04b08a..a229fab4ba 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 33d781687b..ae94ba8d4d 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 7d2f941f0a..2f411091c6 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 fcc7a7fe42..6f02bed15d 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 02fb652bf7..41dd4cd507 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 b86c958b55..960e5a8977 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 6f02bed15d..e77d29ae3d 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 e77d29ae3d..398f7bc261 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 ae94ba8d4d..c79294442e 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 398f7bc261..33c44ab400 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);