import require from 'require'; import datetime from 'datetime'; import itemHelper from 'itemHelper'; import events from 'events'; import browser from 'browser'; import imageLoader from 'imageLoader'; import layoutManager from 'layoutManager'; import playbackManager from 'playbackManager'; import nowPlayingHelper from 'nowPlayingHelper'; import appHost from 'apphost'; import dom from 'dom'; import connectionManager from 'connectionManager'; import itemContextMenu from 'itemContextMenu'; import 'paper-icon-button-light'; import 'emby-ratingbutton'; /* eslint-disable indent */ let currentPlayer; let currentPlayerSupportedCommands = []; let currentTimeElement; let nowPlayingImageElement; let nowPlayingTextElement; let nowPlayingUserData; let muteButton; let volumeSlider; let volumeSliderContainer; let playPauseButtons; let positionSlider; let toggleRepeatButton; let toggleRepeatButtonIcon; let lastUpdateTime = 0; let lastPlayerState = {}; let isEnabled; let currentRuntimeTicks = 0; let isVisibilityAllowed = true; function getNowPlayingBarHtml() { let html = ''; html += '
'; return html; } function onSlideDownComplete() { this.classList.add('hide'); } function slideDown(elem) { // trigger reflow void elem.offsetWidth; elem.classList.add('nowPlayingBar-hidden'); dom.addEventListener(elem, dom.whichTransitionEvent(), onSlideDownComplete, { once: true }); } function slideUp(elem) { dom.removeEventListener(elem, dom.whichTransitionEvent(), onSlideDownComplete, { once: true }); elem.classList.remove('hide'); // trigger reflow void elem.offsetWidth; elem.classList.remove('nowPlayingBar-hidden'); } function onPlayPauseClick() { playbackManager.playPause(currentPlayer); } function bindEvents(elem) { currentTimeElement = elem.querySelector('.nowPlayingBarCurrentTime'); nowPlayingImageElement = elem.querySelector('.nowPlayingImage'); nowPlayingTextElement = elem.querySelector('.nowPlayingBarText'); nowPlayingUserData = elem.querySelector('.nowPlayingBarUserDataButtons'); muteButton = elem.querySelector('.muteButton'); muteButton.addEventListener('click', function () { if (currentPlayer) { playbackManager.toggleMute(currentPlayer); } }); elem.querySelector('.stopButton').addEventListener('click', function () { if (currentPlayer) { playbackManager.stop(currentPlayer); } }); playPauseButtons = elem.querySelectorAll('.playPauseButton'); playPauseButtons.forEach((button) => { button.addEventListener('click', onPlayPauseClick); }); elem.querySelector('.nextTrackButton').addEventListener('click', function () { if (currentPlayer) { playbackManager.nextTrack(currentPlayer); } }); elem.querySelector('.previousTrackButton').addEventListener('click', function () { if (currentPlayer) { playbackManager.previousTrack(currentPlayer); } }); 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; } } }); toggleRepeatButtonIcon = toggleRepeatButton.querySelector('.material-icons'); volumeSlider = elem.querySelector('.nowPlayingBarVolumeSlider'); volumeSliderContainer = elem.querySelector('.nowPlayingBarVolumeSliderContainer'); if (appHost.supports('physicalvolumecontrol')) { volumeSliderContainer.classList.add('hide'); } else { volumeSliderContainer.classList.remove('hide'); } function setVolume() { if (currentPlayer) { currentPlayer.setVolume(this.value); } } volumeSlider.addEventListener('change', setVolume); volumeSlider.addEventListener('mousemove', setVolume); volumeSlider.addEventListener('touchmove', setVolume); positionSlider = elem.querySelector('.nowPlayingBarPositionSlider'); positionSlider.addEventListener('change', function () { if (currentPlayer) { const newPercent = parseFloat(this.value); playbackManager.seekPercent(newPercent, currentPlayer); } }); positionSlider.getBubbleText = function (value) { const state = lastPlayerState; if (!state || !state.NowPlayingItem || !currentRuntimeTicks) { return '--:--'; } let ticks = currentRuntimeTicks; ticks /= 100; ticks *= value; return datetime.getDisplayRunningTime(ticks); }; elem.addEventListener('click', function (e) { if (!dom.parentWithTag(e.target, ['BUTTON', 'INPUT'])) { showRemoteControl(); } }); } function showRemoteControl() { import('appRouter').then(({default: appRouter}) => { appRouter.showNowPlaying(); }); } let nowPlayingBarElement; function getNowPlayingBar() { if (nowPlayingBarElement) { return Promise.resolve(nowPlayingBarElement); } return new Promise(function (resolve, reject) { Promise.all([ import('appFooter-shared'), import('itemShortcuts'), import('css!./nowPlayingBar.css'), import('emby-slider') ]) .then(([appfooter, itemShortcuts]) => { console.log(appfooter); const parentContainer = appfooter.element; nowPlayingBarElement = parentContainer.querySelector('.nowPlayingBar'); if (nowPlayingBarElement) { resolve(nowPlayingBarElement); return; } parentContainer.insertAdjacentHTML('afterbegin', getNowPlayingBarHtml()); nowPlayingBarElement = parentContainer.querySelector('.nowPlayingBar'); 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'); } itemShortcuts.on(nowPlayingBarElement); bindEvents(nowPlayingBarElement); resolve(nowPlayingBarElement); }); }); } function showButton(button) { button.classList.remove('hide'); } function hideButton(button) { button.classList.add('hide'); } function updatePlayPauseState(isPaused) { if (playPauseButtons) { playPauseButtons.forEach((button) => { const icon = button.querySelector('.material-icons'); icon.classList.remove('play_arrow', 'pause'); icon.classList.add(isPaused ? 'play_arrow' : 'pause'); }); } } function updatePlayerStateInternal(event, state, player) { showNowPlayingBar(); lastPlayerState = state; const playerInfo = playbackManager.getPlayerInfo(); const playState = state.PlayState || {}; updatePlayPauseState(playState.IsPaused); const supportedCommands = playerInfo.supportedCommands; currentPlayerSupportedCommands = supportedCommands; if (supportedCommands.indexOf('SetRepeatMode') === -1) { toggleRepeatButton.classList.add('hide'); } else { toggleRepeatButton.classList.remove('hide'); } updateRepeatModeDisplay(playState.RepeatMode); updatePlayerVolumeState(playState.IsMuted, playState.VolumeLevel); if (positionSlider && !positionSlider.dragging) { positionSlider.disabled = !playState.CanSeek; // determines if both forward and backward buffer progress will be visible const isProgressClear = state.MediaSource && state.MediaSource.RunTimeTicks == null; positionSlider.setIsClear(isProgressClear); } const nowPlayingItem = state.NowPlayingItem || {}; updateTimeDisplay(playState.PositionTicks, nowPlayingItem.RunTimeTicks, playbackManager.getBufferedRanges(player)); updateNowPlayingInfo(state); } function updateRepeatModeDisplay(repeatMode) { toggleRepeatButtonIcon.classList.remove('repeat', 'repeat_one'); 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'); } } function updateTimeDisplay(positionTicks, runtimeTicks, bufferedRanges) { // See bindEvents for why this is necessary if (positionSlider && !positionSlider.dragging) { if (runtimeTicks) { let pct = positionTicks / runtimeTicks; pct *= 100; positionSlider.value = pct; } else { positionSlider.value = 0; } } if (positionSlider) { positionSlider.setBufferedRanges(bufferedRanges, runtimeTicks, positionTicks); } if (currentTimeElement) { let timeText = positionTicks == null ? '--:--' : datetime.getDisplayRunningTime(positionTicks); if (runtimeTicks) { timeText += ' / ' + datetime.getDisplayRunningTime(runtimeTicks); } currentTimeElement.innerHTML = timeText; } } function updatePlayerVolumeState(isMuted, volumeLevel) { const supportedCommands = currentPlayerSupportedCommands; let showMuteButton = true; let showVolumeSlider = true; if (supportedCommands.indexOf('ToggleMute') === -1) { showMuteButton = false; } const muteButtonIcon = muteButton.querySelector('.material-icons'); muteButtonIcon.classList.remove('volume_off', 'volume_up'); muteButtonIcon.classList.add(isMuted ? 'volume_off' : 'volume_up'); if (supportedCommands.indexOf('SetVolume') === -1) { showVolumeSlider = false; } if (currentPlayer.isLocalPlayer && appHost.supports('physicalvolumecontrol')) { showMuteButton = false; showVolumeSlider = false; } if (showMuteButton) { showButton(muteButton); } else { hideButton(muteButton); } // See bindEvents for why this is necessary if (volumeSlider) { if (showVolumeSlider) { volumeSliderContainer.classList.remove('hide'); } else { volumeSliderContainer.classList.add('hide'); } if (!volumeSlider.dragging) { volumeSlider.value = volumeLevel || 0; } } } function getTextActionButton(item, text) { if (!text) { text = itemHelper.getDisplayName(item); } return `${text}`; } function seriesImageUrl(item, options) { if (!item) { throw new Error('item cannot be null!'); } if (item.Type !== 'Episode') { return null; } options = options || {}; options.type = options.type || 'Primary'; if (options.type === 'Primary') { if (item.SeriesPrimaryImageTag) { options.tag = item.SeriesPrimaryImageTag; return connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.SeriesId, options); } } if (options.type === 'Thumb') { if (item.SeriesThumbImageTag) { options.tag = item.SeriesThumbImageTag; return connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.SeriesId, options); } if (item.ParentThumbImageTag) { options.tag = item.ParentThumbImageTag; return connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.ParentThumbItemId, options); } } return null; } function imageUrl(item, options) { if (!item) { throw new Error('item cannot be null!'); } options = options || {}; options.type = options.type || 'Primary'; if (item.ImageTags && item.ImageTags[options.type]) { options.tag = item.ImageTags[options.type]; return connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.PrimaryImageItemId || item.Id, options); } if (item.AlbumId && item.AlbumPrimaryImageTag) { options.tag = item.AlbumPrimaryImageTag; return connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.AlbumId, options); } return null; } let currentImgUrl; function updateNowPlayingInfo(state) { const nowPlayingItem = state.NowPlayingItem; const textLines = nowPlayingItem ? nowPlayingHelper.getNowPlayingNames(nowPlayingItem) : []; if (textLines.length > 1) { textLines[1].secondary = true; } nowPlayingTextElement.innerHTML = textLines.map(function (nowPlayingName) { const cssClass = nowPlayingName.secondary ? ' class="nowPlayingBarSecondaryText"' : ''; if (nowPlayingName.item) { const nowPlayingText = getTextActionButton(nowPlayingName.item, nowPlayingName.text); return `