define(['datetime', 'userdataButtons', 'itemHelper', 'events', 'browser', 'paper-icon-button-light'], function (datetime, userdataButtons, itemHelper, events, browser) { var currentPlayer; var currentTimeElement; var nowPlayingImageElement; var nowPlayingTextElement; var nowPlayingUserData; var unmuteButton; var muteButton; var volumeSlider; var volumeSliderContainer; var unpauseButtons; var pauseButtons; var positionSlider; var toggleRepeatButton; var toggleRepeatButtonIcon; var lastPlayerState; function getNowPlayingBarHtml() { var html = ''; html += '
'; html += '
'; html += '
'; html += ''; html += '
'; html += '
'; html += '
'; html += '
'; html += '
'; // The onclicks are needed due to the return false above html += '
'; html += ''; html += ''; html += ''; html += ''; html += ''; html += '
'; html += '
'; html += '
'; html += ''; html += ''; html += '
'; html += ''; html += '
'; html += ''; html += '
'; html += '
'; html += ''; html += ''; html += ''; html += ''; html += '
'; html += '
'; html += '
'; return html; } function slideDown(elem) { if (elem.classList.contains('hide')) { return; } var onfinish = function () { elem.classList.add('hide'); }; if (!browser.animate || browser.slow) { onfinish(); return; } requestAnimationFrame(function () { var keyframes = [ { transform: 'none', offset: 0 }, { transform: 'translateY(100%)', offset: 1 }]; var timing = { duration: 200, iterations: 1, fill: 'both', easing: 'ease-out' }; elem.animate(keyframes, timing).onfinish = onfinish; }); } function slideUp(elem) { if (!elem.classList.contains('hide')) { return; } elem.classList.remove('hide'); if (!browser.animate || browser.slow) { return; } requestAnimationFrame(function () { var keyframes = [ { transform: 'translateY(100%)', offset: 0 }, { transform: 'none', offset: 1 }]; var timing = { duration: 200, iterations: 1, fill: 'both', easing: 'ease-out' }; elem.animate(keyframes, timing); }); } function slideUpToFullScreen(elem) { if (!elem.classList.contains('hide')) { return; } elem.classList.remove('hide'); if (!browser.animate || browser.slow) { return; } requestAnimationFrame(function () { var keyframes = [ { transform: 'none', offset: 0 }, { transform: 'translateY(-100%)', offset: 1 }]; var timing = { duration: 200, iterations: 1, fill: 'both', easing: 'ease-out' }; elem.animate(keyframes, timing); }); } function onPauseClick() { if (currentPlayer) { currentPlayer.pause(); } } function onUnpauseClick() { if (currentPlayer) { currentPlayer.unpause(); } } function bindEvents(elem) { currentTimeElement = elem.querySelector('.nowPlayingBarCurrentTime'); nowPlayingImageElement = elem.querySelector('.nowPlayingImage'); nowPlayingTextElement = elem.querySelector('.nowPlayingBarText'); nowPlayingUserData = elem.querySelector('.nowPlayingBarUserDataButtons'); unmuteButton = elem.querySelector('.unmuteButton'); unmuteButton.addEventListener('click', function () { if (currentPlayer) { currentPlayer.unMute(); } }); muteButton = elem.querySelector('.muteButton'); muteButton.addEventListener('click', function () { if (currentPlayer) { currentPlayer.mute(); } }); elem.querySelector('.stopButton').addEventListener('click', function () { if (currentPlayer) { currentPlayer.stop(); } }); var i, length; pauseButtons = elem.querySelectorAll('.pauseButton'); for (i = 0, length = pauseButtons.length; i < length; i++) { pauseButtons[i].addEventListener('click', onPauseClick); } unpauseButtons = elem.querySelectorAll('.unpauseButton'); for (i = 0, length = unpauseButtons.length; i < length; i++) { unpauseButtons[i].addEventListener('click', onUnpauseClick); } elem.querySelector('.nextTrackButton').addEventListener('click', function () { if (currentPlayer) { currentPlayer.nextTrack(); } }); elem.querySelector('.previousTrackButton').addEventListener('click', function () { if (currentPlayer) { currentPlayer.previousTrack(); } }); elem.querySelector('.remoteControlButton').addEventListener('click', function () { showRemoteControl(); }); elem.querySelector('.playlistButton').addEventListener('click', function () { showRemoteControl(2); }); toggleRepeatButton = elem.querySelector('.toggleRepeatButton'); toggleRepeatButton.addEventListener('click', function () { if (currentPlayer) { var state = lastPlayerState || {}; switch ((state.PlayState || {}).RepeatMode) { case 'RepeatAll': currentPlayer.setRepeatMode('RepeatOne'); break; case 'RepeatOne': currentPlayer.setRepeatMode('RepeatNone'); break; default: currentPlayer.setRepeatMode('RepeatAll'); break; } } }); toggleRepeatButtonIcon = toggleRepeatButton.querySelector('i'); volumeSlider = elem.querySelector('.nowPlayingBarVolumeSlider'); volumeSliderContainer = elem.querySelector('.nowPlayingBarVolumeSliderContainer'); if (AppInfo.hasPhysicalVolumeButtons) { volumeSliderContainer.classList.add('hide'); } else { volumeSliderContainer.classList.remove('hide'); } volumeSlider.addEventListener('change', function () { if (currentPlayer) { currentPlayer.setVolume(this.value); } }); positionSlider = elem.querySelector('.nowPlayingBarPositionSlider'); positionSlider.addEventListener('change', function () { if (currentPlayer && lastPlayerState) { var newPercent = parseFloat(this.value); var newPositionTicks = (newPercent / 100) * lastPlayerState.NowPlayingItem.RunTimeTicks; currentPlayer.seek(Math.floor(newPositionTicks)); } }); positionSlider.getBubbleText = function (value) { var state = lastPlayerState; if (!state || !state.NowPlayingItem || !state.NowPlayingItem.RunTimeTicks) { return '--:--'; } var ticks = state.NowPlayingItem.RunTimeTicks; ticks /= 100; ticks *= value; return datetime.getDisplayRunningTime(ticks); }; } function showRemoteControl(tabIndex) { if (tabIndex) { Dashboard.navigate('nowplaying.html?tab=' + tabIndex); } else { Dashboard.navigate('nowplaying.html'); } } var nowPlayingBarElement; function getNowPlayingBar() { if (nowPlayingBarElement) { return Promise.resolve(nowPlayingBarElement); } return new Promise(function (resolve, reject) { require(['appfooter-shared', 'itemShortcuts', 'css!css/nowplayingbar.css', 'emby-slider'], function (appfooter, itemShortcuts) { var 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'); } var lastUpdateTime = 0; function updatePlayerState(event, state) { if (!state.NowPlayingItem) { hideNowPlayingBar(); return; } if (nowPlayingBarElement) { updatePlayerStateInternal(event, state); return; } getNowPlayingBar().then(function () { updatePlayerStateInternal(event, state); }); } function updatePlayerStateInternal(event, state) { showNowPlayingBar(); if (event.type == 'positionchange') { // Try to avoid hammering the document with changes var now = new Date().getTime(); if ((now - lastUpdateTime) < 700) { return; } lastUpdateTime = now; } lastPlayerState = state; var playerInfo = MediaController.getPlayerInfo(); var playState = state.PlayState || {}; var i, length; if (playState.IsPaused) { for (i = 0, length = pauseButtons.length; i < length; i++) { hideButton(pauseButtons[i]); } for (i = 0, length = unpauseButtons.length; i < length; i++) { showButton(unpauseButtons[i]); } } else { for (i = 0, length = pauseButtons.length; i < length; i++) { showButton(pauseButtons[i]); } for (i = 0, length = unpauseButtons.length; i < length; i++) { hideButton(unpauseButtons[i]); } } updatePlayerVolumeState(state, playerInfo); var nowPlayingItem = state.NowPlayingItem || {}; // See bindEvents for why this is necessary if (positionSlider) { if (!positionSlider.dragging) { if (nowPlayingItem.RunTimeTicks) { var pct = playState.PositionTicks / nowPlayingItem.RunTimeTicks; pct *= 100; positionSlider.value = pct; } else { positionSlider.value = 0; } positionSlider.disabled = !playState.CanSeek; } } var timeText = playState.PositionTicks == null ? '--:--' : datetime.getDisplayRunningTime(playState.PositionTicks); if (nowPlayingItem.RunTimeTicks) { timeText += " / " + datetime.getDisplayRunningTime(nowPlayingItem.RunTimeTicks); } currentTimeElement.innerHTML = timeText; updateNowPlayingInfo(state); } function updatePlayerVolumeState(state, playerInfo) { playerInfo = playerInfo || MediaController.getPlayerInfo(); var playState = state.PlayState || {}; var supportedCommands = playerInfo.supportedCommands; var showMuteButton = true; var showUnmuteButton = true; var showVolumeSlider = true; if (supportedCommands.indexOf('Mute') == -1) { showMuteButton = false; } if (supportedCommands.indexOf('Unmute') == -1) { showUnmuteButton = false; } if (playState.IsMuted) { showMuteButton = false; } else { showUnmuteButton = false; } if (supportedCommands.indexOf('SetRepeatMode') == -1) { toggleRepeatButton.classList.add('hide'); } else { toggleRepeatButton.classList.remove('hide'); } if (playState.RepeatMode == 'RepeatAll') { toggleRepeatButtonIcon.innerHTML = "repeat"; toggleRepeatButton.classList.add('repeatActive'); } else if (playState.RepeatMode == 'RepeatOne') { toggleRepeatButtonIcon.innerHTML = "repeat_one"; toggleRepeatButton.classList.add('repeatActive'); } else { toggleRepeatButtonIcon.innerHTML = "repeat"; toggleRepeatButton.classList.remove('repeatActive'); } if (supportedCommands.indexOf('SetVolume') == -1) { showVolumeSlider = false; } if (playerInfo.isLocalPlayer && AppInfo.hasPhysicalVolumeButtons) { showMuteButton = false; showUnmuteButton = false; showVolumeSlider = false; } if (showMuteButton) { showButton(muteButton); } else { hideButton(muteButton); } if (showUnmuteButton) { showButton(unmuteButton); } else { hideButton(unmuteButton); } // 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 = playState.VolumeLevel || 0; } } } function getTextActionButton(item, text) { if (!text) { text = itemHelper.getDisplayName(item); } var html = ''; return html; } var currentImgUrl; function updateNowPlayingInfo(state) { nowPlayingTextElement.innerHTML = MediaController.getNowPlayingNames(state.NowPlayingItem).map(function (nowPlayingName) { if (nowPlayingName.item) { return '
' + getTextActionButton(nowPlayingName.item, nowPlayingName.text) + '
'; } return '
' + nowPlayingName.text + '
'; }).join(''); var url; var imgHeight = 70; var nowPlayingItem = state.NowPlayingItem; if (nowPlayingItem.PrimaryImageTag) { url = ApiClient.getScaledImageUrl(nowPlayingItem.PrimaryImageItemId, { type: "Primary", height: imgHeight, tag: nowPlayingItem.PrimaryImageTag }); } else if (nowPlayingItem.BackdropImageTag) { url = ApiClient.getScaledImageUrl(nowPlayingItem.BackdropItemId, { type: "Backdrop", height: imgHeight, tag: nowPlayingItem.BackdropImageTag, index: 0 }); } else if (nowPlayingItem.ThumbImageTag) { url = ApiClient.getScaledImageUrl(nowPlayingItem.ThumbImageItemId, { type: "Thumb", height: imgHeight, tag: nowPlayingItem.ThumbImageTag }); } else if (nowPlayingItem.Type == "TvChannel" || nowPlayingItem.Type == "Recording") { url = "css/images/items/detail/tv.png"; } else if (nowPlayingItem.MediaType == "Audio") { url = "css/images/items/detail/audio.png"; } else { url = "css/images/items/detail/video.png"; } if (url == currentImgUrl) { return; } currentImgUrl = url; ImageLoader.lazyImage(nowPlayingImageElement, url); if (nowPlayingItem.Id) { ApiClient.getItem(Dashboard.getCurrentUserId(), nowPlayingItem.Id).then(function (item) { nowPlayingUserData.innerHTML = userdataButtons.getIconsHtml({ item: item, includePlayed: false }); }); } else { nowPlayingUserData.innerHTML = ''; } } function onPlaybackStart(e, state) { console.log('nowplaying event: ' + e.type); var player = this; player.beginPlayerUpdates(); onStateChanged.call(player, e, state); } function showNowPlayingBar() { getNowPlayingBar().then(slideUp); } function hideNowPlayingBar() { // Use a timeout to prevent the bar from hiding and showing quickly // in the event of a stop->play command // Don't call getNowPlayingBar here because we don't want to end up creating it just to hide it var elem = document.getElementsByClassName('nowPlayingBar')[0]; if (elem) { slideDown(elem); } } function onPlaybackStopped(e, state) { console.log('nowplaying event: ' + e.type); var player = this; player.endPlayerUpdates(); hideNowPlayingBar(); } function onStateChanged(e, state) { //console.log('nowplaying event: ' + e.type); var player = this; if (player.isDefaultPlayer && state.NowPlayingItem && state.NowPlayingItem.MediaType == 'Video') { return; } updatePlayerState(e, state); } function releaseCurrentPlayer() { if (currentPlayer) { events.off(currentPlayer, 'playbackstart', onPlaybackStart); events.off(currentPlayer, 'playbackstop', onPlaybackStopped); events.off(currentPlayer, 'volumechange', onVolumeChanged); events.off(currentPlayer, 'playstatechange', onStateChanged); events.off(currentPlayer, 'positionchange', onStateChanged); currentPlayer.endPlayerUpdates(); currentPlayer = null; hideNowPlayingBar(); } } function onVolumeChanged(e) { var player = this; Promise.all([player.getPlayerState(), getNowPlayingBar()]).then(function (responses) { var state = responses[0]; if (player.isDefaultPlayer && state.NowPlayingItem && state.NowPlayingItem.MediaType == 'Video') { return; } updatePlayerVolumeState(state); }); } function bindToPlayer(player) { releaseCurrentPlayer(); currentPlayer = player; player.getPlayerState().then(function (state) { if (state.NowPlayingItem) { player.beginPlayerUpdates(); } onStateChanged.call(player, { type: 'init' }, state); }); events.on(player, 'playbackstart', onPlaybackStart); events.on(player, 'playbackstop', onPlaybackStopped); events.on(player, 'volumechange', onVolumeChanged); events.on(player, 'playstatechange', onStateChanged); events.on(player, 'positionchange', onStateChanged); } events.on(MediaController, 'playerchange', function () { bindToPlayer(MediaController.getCurrentPlayer()); }); bindToPlayer(MediaController.getCurrentPlayer()); });