jellyfin-web/dashboard-ui/scripts/nowplayingbar.js

541 lines
15 KiB
JavaScript
Raw Normal View History

(function (window, document, $, setTimeout, clearTimeout) {
var currentPlayer;
var currentTimeElement;
var nowPlayingImageElement;
var nowPlayingTextElement;
var nowPlayingUserData;
var unmuteButton;
var muteButton;
var volumeSlider;
var unpauseButton;
var pauseButton;
var positionSlider;
var lastPlayerState;
function getNowPlayingBarHtml() {
var html = '';
2015-06-17 18:41:22 -07:00
// add return false because on iOS clicking the bar often ends up clicking the content underneath.
2015-06-26 20:27:38 -07:00
html += '<div class="nowPlayingBar" style="display:none;">';
2014-06-21 22:52:31 -07:00
2015-06-27 12:53:36 -07:00
html += '<div class="nowPlayingBarPositionContainer">';
html += '<paper-slider pin step=".1" min="0" max="100" value="0" class="nowPlayingBarPositionSlider"></paper-slider>';
html += '</div>';
html += '<div class="nowPlayingBarInfoContainer">';
2015-05-01 11:37:01 -07:00
html += '<div class="nowPlayingImage"></div>';
2015-06-27 12:53:36 -07:00
html += '<div class="nowPlayingBarText"></div>';
html += '</div>';
2015-05-01 11:37:01 -07:00
2015-06-17 18:41:22 -07:00
// The onclicks are needed due to the return false above
2015-06-27 12:53:36 -07:00
html += '<div class="nowPlayingBarCenter">';
2015-05-01 11:37:01 -07:00
2015-06-23 21:38:46 -07:00
html += '<paper-icon-button icon="skip-previous" class="previousTrackButton mediaButton"></paper-icon-button>';
2014-06-21 22:52:31 -07:00
2015-06-23 21:38:46 -07:00
html += '<paper-icon-button icon="play-arrow" class="mediaButton unpauseButton"></paper-icon-button>';
html += '<paper-icon-button icon="pause" class="mediaButton pauseButton"></paper-icon-button>';
2014-06-24 14:45:21 -07:00
2015-06-23 21:38:46 -07:00
html += '<paper-icon-button icon="stop" class="stopButton mediaButton"></paper-icon-button>';
2015-06-23 21:38:46 -07:00
html += '<paper-icon-button icon="skip-next" class="nextTrackButton mediaButton"></paper-icon-button>';
2015-06-27 12:53:36 -07:00
html += '<div class="nowPlayingBarCurrentTime"></div>';
html += '</div>';
2015-06-27 12:53:36 -07:00
html += '<div class="nowPlayingBarRight">';
2015-06-23 21:38:46 -07:00
html += '<paper-icon-button icon="volume-up" class="muteButton mediaButton"></paper-icon-button>';
html += '<paper-icon-button icon="volume-off" class="unmuteButton mediaButton"></paper-icon-button>';
2015-06-27 12:53:36 -07:00
html += '<paper-slider pin step="1" min="0" max="100" value="0" class="nowPlayingBarVolumeSlider" style="width:100px;vertical-align:middle;"></paper-slider>';
html += '<div class="nowPlayingBarUserDataButtons">';
html += '</div>';
2015-06-27 12:53:36 -07:00
html += '<paper-icon-button icon="play-arrow" class="mediaButton unpauseButton"></paper-icon-button>';
html += '<paper-icon-button icon="pause" class="mediaButton pauseButton"></paper-icon-button>';
html += '<paper-icon-button icon="tablet-android" onclick="Dashboard.navigate(\'nowplaying.html\', false);" class="mediaButton remoteControlButton"></paper-icon-button>';
2015-07-03 04:51:45 -07:00
html += '<paper-icon-button icon="queue-music" class="mediaButton playlistButton"></paper-icon-button>';
2015-06-27 12:53:36 -07:00
html += '</div>';
html += '</div>';
return html;
}
function bindEvents(elem) {
2015-06-27 12:53:36 -07:00
currentTimeElement = $('.nowPlayingBarCurrentTime', elem);
nowPlayingImageElement = $('.nowPlayingImage', elem);
2015-06-27 12:53:36 -07:00
nowPlayingTextElement = $('.nowPlayingBarText', elem);
nowPlayingUserData = $('.nowPlayingBarUserDataButtons', elem);
2015-03-17 21:09:31 -07:00
$(elem).on('swipeup', function () {
Dashboard.navigate('nowplaying.html');
});
unmuteButton = $('.unmuteButton', elem).on('click', function () {
if (currentPlayer) {
currentPlayer.unMute();
}
});
muteButton = $('.muteButton', elem).on('click', function () {
if (currentPlayer) {
currentPlayer.mute();
}
});
$('.stopButton', elem).on('click', function () {
if (currentPlayer) {
currentPlayer.stop();
}
});
pauseButton = $('.pauseButton', elem).on('click', function () {
if (currentPlayer) {
currentPlayer.pause();
}
});
unpauseButton = $('.unpauseButton', elem).on('click', function () {
if (currentPlayer) {
currentPlayer.unpause();
}
});
$('.nextTrackButton', elem).on('click', function () {
if (currentPlayer) {
currentPlayer.nextTrack();
}
});
$('.previousTrackButton', elem).on('click', function () {
if (currentPlayer) {
currentPlayer.previousTrack();
}
});
2015-07-03 04:51:45 -07:00
$('.playlistButton', elem).on('click', function () {
$.mobile.changePage('nowplaying.html', {
dataUrl: 'nowplaying.html#playlist'
});
});
2015-07-11 17:33:50 -07:00
// Unfortunately this is necessary because the polymer elements might not be ready immediately and there doesn't seem to be an event-driven way to find out when
setTimeout(function() {
volumeSlider = $('.nowPlayingBarVolumeSlider', elem).on('change', function () {
2015-07-11 17:33:50 -07:00
if (currentPlayer) {
currentPlayer.setVolume(this.value);
}
2015-07-11 17:33:50 -07:00
})[0];
2015-07-11 17:33:50 -07:00
positionSlider = $('.nowPlayingBarPositionSlider', elem).on('change', function () {
2015-07-11 17:33:50 -07:00
if (currentPlayer && lastPlayerState) {
2015-07-11 17:33:50 -07:00
var newPercent = parseFloat(this.value);
var newPositionTicks = (newPercent / 100) * lastPlayerState.NowPlayingItem.RunTimeTicks;
2014-04-22 10:25:54 -07:00
2015-07-11 17:33:50 -07:00
currentPlayer.seek(Math.floor(newPositionTicks));
}
2015-06-27 12:53:36 -07:00
2015-07-11 17:33:50 -07:00
})[0];
2015-06-27 12:53:36 -07:00
2015-07-11 17:33:50 -07:00
positionSlider._setPinValue = function (value) {
2015-06-27 12:53:36 -07:00
2015-07-11 17:33:50 -07:00
var state = lastPlayerState;
2015-06-27 12:53:36 -07:00
2015-07-11 17:33:50 -07:00
if (!state || !state.NowPlayingItem || !state.NowPlayingItem.RunTimeTicks) {
this.pinValue = '--:--';
return;
}
2015-06-27 12:53:36 -07:00
2015-07-11 17:33:50 -07:00
var ticks = state.NowPlayingItem.RunTimeTicks;
ticks /= 100;
ticks *= value;
2015-06-27 12:53:36 -07:00
2015-07-11 17:33:50 -07:00
this.pinValue = Dashboard.getDisplayTime(ticks);
};
}, 300);
}
function getNowPlayingBar() {
2015-06-28 07:45:21 -07:00
var elem = document.querySelector('.nowPlayingBar');
2015-06-28 07:45:21 -07:00
if (elem) {
return elem;
}
2015-06-28 07:45:21 -07:00
elem = $(getNowPlayingBarHtml()).insertBefore('#footerNotifications')[0];
2015-06-29 18:56:25 -07:00
if (($.browser.safari || !AppInfo.isNativeApp) && $.browser.mobile) {
2015-06-27 16:18:09 -07:00
// Not handled well here. The wrong elements receive events, bar doesn't update quickly enough, etc.
2015-06-28 07:45:21 -07:00
elem.classList.add('noMediaProgress');
2015-06-27 16:18:09 -07:00
}
bindEvents(elem);
2015-07-11 17:33:50 -07:00
$.mobile.loadPage('nowplaying.html');
return elem;
}
2015-03-17 21:09:31 -07:00
2014-04-19 10:43:12 -07:00
function showButton(button) {
button.removeClass('hide');
}
2015-03-17 21:09:31 -07:00
2014-04-19 10:43:12 -07:00
function hideButton(button) {
button.addClass('hide');
}
2015-07-08 17:20:01 -07:00
var lastUpdateTime = 0;
function updatePlayerState(event, state) {
2015-06-27 16:18:09 -07:00
if (state.NowPlayingItem) {
showNowPlayingBar();
} else {
hideNowPlayingBar();
return;
}
2015-07-08 17:20:01 -07:00
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;
if (!muteButton) {
getNowPlayingBar();
}
2014-04-14 20:54:52 -07:00
var playerInfo = MediaController.getPlayerInfo();
2014-04-22 10:25:54 -07:00
var playState = state.PlayState || {};
2014-04-22 10:25:54 -07:00
if (playState.IsPaused) {
2014-04-19 10:43:12 -07:00
hideButton(pauseButton);
showButton(unpauseButton);
} else {
2014-04-19 10:43:12 -07:00
showButton(pauseButton);
hideButton(unpauseButton);
}
2015-05-21 13:53:14 -07:00
updatePlayerVolumeState(state, playerInfo);
2014-04-14 20:54:52 -07:00
2014-04-22 10:25:54 -07:00
var nowPlayingItem = state.NowPlayingItem || {};
2015-07-11 17:33:50 -07:00
// See bindEvents for why this is necessary
if (positionSlider) {
if (!positionSlider.dragging) {
2015-07-11 17:33:50 -07:00
if (nowPlayingItem.RunTimeTicks) {
2015-07-11 17:33:50 -07:00
var pct = playState.PositionTicks / nowPlayingItem.RunTimeTicks;
pct *= 100;
2015-07-11 17:33:50 -07:00
positionSlider.value = pct;
2015-07-11 17:33:50 -07:00
} else {
2014-04-30 08:07:02 -07:00
2015-07-11 17:33:50 -07:00
positionSlider.value = 0;
}
positionSlider.disabled = !playState.CanSeek;
}
}
2014-04-22 10:25:54 -07:00
var timeText = Dashboard.getDisplayTime(playState.PositionTicks);
2014-04-22 10:25:54 -07:00
if (nowPlayingItem.RunTimeTicks) {
2014-04-22 10:25:54 -07:00
timeText += " / " + Dashboard.getDisplayTime(nowPlayingItem.RunTimeTicks);
}
currentTimeElement.html(timeText);
updateNowPlayingInfo(state);
}
2015-05-21 13:53:14 -07:00
function updatePlayerVolumeState(state, playerInfo) {
playerInfo = playerInfo || MediaController.getPlayerInfo();
2015-01-03 12:38:22 -07:00
if (!muteButton) {
getNowPlayingBar();
}
var playState = state.PlayState || {};
2015-05-21 13:53:14 -07:00
var supportedCommands = playerInfo.supportedCommands;
2015-01-03 12:38:22 -07:00
2015-05-21 13:53:14 -07:00
var showMuteButton = true;
var showUnmuteButton = true;
var showVolumeSlider = true;
2015-01-03 12:38:22 -07:00
2015-05-21 13:53:14 -07:00
if (supportedCommands.indexOf('Mute') == -1) {
showMuteButton = false;
}
if (supportedCommands.indexOf('Unmute') == -1) {
showUnmuteButton = false;
}
if (playState.IsMuted) {
2015-01-03 12:38:22 -07:00
2015-05-21 13:53:14 -07:00
showMuteButton = false;
2015-01-03 12:38:22 -07:00
} else {
2015-05-21 13:53:14 -07:00
showUnmuteButton = false;
}
if (supportedCommands.indexOf('SetVolume') == -1) {
showVolumeSlider = false;
}
if (playerInfo.isLocalPlayer && AppInfo.hasPhysicalVolumeButtons) {
showMuteButton = false;
showUnmuteButton = false;
showVolumeSlider = false;
}
if (showMuteButton) {
2015-01-03 12:38:22 -07:00
showButton(muteButton);
2015-05-21 13:53:14 -07:00
} else {
hideButton(muteButton);
}
if (showUnmuteButton) {
showButton(unmuteButton);
} else {
2015-01-03 12:38:22 -07:00
hideButton(unmuteButton);
}
2015-07-11 17:33:50 -07:00
// See bindEvents for why this is necessary
if (volumeSlider) {
$(volumeSlider).visible(showVolumeSlider);
2015-05-21 13:53:14 -07:00
2015-07-11 17:33:50 -07:00
if (!volumeSlider.dragging) {
volumeSlider.value = playState.VolumeLevel || 0;
}
2015-01-03 12:38:22 -07:00
}
}
2014-04-13 10:27:13 -07:00
var currentImgUrl;
function updateNowPlayingInfo(state) {
2015-05-08 20:48:43 -07:00
var nameHtml = MediaController.getNowPlayingNameHtml(state.NowPlayingItem) || '';
2014-04-13 10:27:13 -07:00
if (nameHtml.indexOf('<br/>') != -1) {
nowPlayingTextElement.addClass('nowPlayingDoubleText');
} else {
nowPlayingTextElement.removeClass('nowPlayingDoubleText');
}
2015-06-26 20:27:38 -07:00
if (state.NowPlayingItem.Id) {
nameHtml = '<a style="color:inherit;text-decoration:none;" href="' + LibraryBrowser.getHref(state.NowPlayingItem) + '">' + nameHtml + '</a>';
}
2014-04-13 10:27:13 -07:00
nowPlayingTextElement.html(nameHtml);
var url;
2015-07-08 17:20:01 -07:00
var imgHeight = 80;
2015-03-17 21:09:31 -07:00
2014-04-22 10:25:54 -07:00
var nowPlayingItem = state.NowPlayingItem;
2014-04-22 10:25:54 -07:00
if (nowPlayingItem.PrimaryImageTag) {
url = ApiClient.getScaledImageUrl(nowPlayingItem.PrimaryImageItemId, {
type: "Primary",
2015-05-01 11:37:01 -07:00
height: imgHeight,
2014-04-22 10:25:54 -07:00
tag: nowPlayingItem.PrimaryImageTag
});
}
2014-04-22 10:25:54 -07:00
else if (nowPlayingItem.BackdropImageTag) {
url = ApiClient.getScaledImageUrl(nowPlayingItem.BackdropItemId, {
type: "Backdrop",
2015-05-01 11:37:01 -07:00
height: imgHeight,
2014-04-22 10:25:54 -07:00
tag: nowPlayingItem.BackdropImageTag,
index: 0
});
2014-04-22 10:25:54 -07:00
} else if (nowPlayingItem.ThumbImageTag) {
url = ApiClient.getScaledImageUrl(nowPlayingItem.ThumbImageItemId, {
type: "Thumb",
2015-05-01 11:37:01 -07:00
height: imgHeight,
2014-04-22 10:25:54 -07:00
tag: nowPlayingItem.ThumbImageTag
});
}
2014-04-22 10:25:54 -07:00
else if (nowPlayingItem.Type == "TvChannel" || nowPlayingItem.Type == "Recording") {
url = "css/images/items/detail/tv.png";
}
2014-04-22 10:25:54 -07:00
else if (nowPlayingItem.MediaType == "Audio") {
url = "css/images/items/detail/audio.png";
}
else {
url = "css/images/items/detail/video.png";
}
2014-04-13 10:27:13 -07:00
if (url == currentImgUrl) {
return;
}
currentImgUrl = url;
2015-03-17 21:09:31 -07:00
2015-06-26 20:27:38 -07:00
var imgHtml = '<img src="' + url + '" />';
nowPlayingImageElement.html(imgHtml);
if (nowPlayingItem.Id) {
ApiClient.getItem(Dashboard.getCurrentUserId(), nowPlayingItem.Id).done(function (item) {
nowPlayingUserData.html(LibraryBrowser.getUserDataIconsHtml(item, false));
});
2015-06-27 16:18:09 -07:00
} else {
nowPlayingUserData.html('');
}
}
function onPlaybackStart(e, state) {
2015-06-26 20:27:38 -07:00
Logger.log('nowplaying event: ' + e.type);
2014-04-19 10:43:12 -07:00
var player = this;
player.beginPlayerUpdates();
onStateChanged.call(player, e, state);
}
function showNowPlayingBar() {
var nowPlayingBar = getNowPlayingBar();
2015-06-28 07:45:21 -07:00
$(nowPlayingBar).show();
}
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
2015-06-28 07:45:21 -07:00
var elem = document.getElementsByClassName('nowPlayingBar')[0];
if (elem) {
elem.style.display = 'none';
}
}
function onPlaybackStopped(e, state) {
2015-06-26 20:27:38 -07:00
Logger.log('nowplaying event: ' + e.type);
var player = this;
player.endPlayerUpdates();
hideNowPlayingBar();
}
function onStateChanged(e, state) {
2015-06-26 20:27:38 -07:00
//Logger.log('nowplaying event: ' + e.type);
var player = this;
2014-04-22 10:25:54 -07:00
if (player.isDefaultPlayer && state.NowPlayingItem && state.NowPlayingItem.MediaType == 'Video') {
return;
}
2015-07-08 17:20:01 -07:00
updatePlayerState(e, state);
}
function releaseCurrentPlayer() {
if (currentPlayer) {
2015-06-29 11:45:42 -07:00
$(currentPlayer).off('playbackstart', onPlaybackStart)
.off('playbackstop', onPlaybackStopped)
.off('volumechange', onVolumeChanged)
.off('playstatechange', onStateChanged)
.off('positionchange', onStateChanged);
currentPlayer.endPlayerUpdates();
currentPlayer = null;
hideNowPlayingBar();
}
}
2015-01-03 12:38:22 -07:00
function onVolumeChanged(e) {
var player = this;
2015-03-17 21:09:31 -07:00
player.getPlayerState().done(function (state) {
2015-01-03 12:38:22 -07:00
if (player.isDefaultPlayer && state.NowPlayingItem && state.NowPlayingItem.MediaType == 'Video') {
return;
}
updatePlayerVolumeState(state);
});
}
function bindToPlayer(player) {
releaseCurrentPlayer();
currentPlayer = player;
player.getPlayerState().done(function (state) {
2014-04-28 20:56:20 -07:00
if (state.NowPlayingItem) {
player.beginPlayerUpdates();
}
2015-03-17 21:09:31 -07:00
onStateChanged.call(player, { type: 'init' }, state);
});
2015-06-29 11:45:42 -07:00
$(player).on('playbackstart', onPlaybackStart)
.on('playbackstop', onPlaybackStopped)
.on('volumechange', onVolumeChanged)
.on('playstatechange', onStateChanged)
.on('positionchange', onStateChanged);
}
2015-05-19 12:15:40 -07:00
Dashboard.ready(function () {
2015-06-28 07:45:21 -07:00
Events.on(MediaController, 'playerchange', function () {
bindToPlayer(MediaController.getCurrentPlayer());
});
bindToPlayer(MediaController.getCurrentPlayer());
});
})(window, document, jQuery, setTimeout, clearTimeout);