';
var role = cast.Role ? Globalize.translate('ValueAsRole', cast.Role) : cast.Type;
if (role == "GuestStar") {
role = Globalize.translate('ValueGuestStar');
}
role = role || "";
var maxlength = 40;
if (role.length > maxlength) {
role = role.substring(0, maxlength - 3) + '...';
}
personHtml += '
' + role + '
';
personHtml += '
';
personHtml += '
';
return personHtml;
}).join('');
html += '
';
}
return html;
}
function onPositionSliderChange() {
var newPercent = parseFloat(this.value);
var newPositionTicks = (newPercent / 100) * self.getSeekableDurationTicks();
self.changeStream(Math.floor(newPositionTicks));
}
self.onAudioOptionSelected = function (index) {
self.setAudioStreamIndex(index);
};
self.onSubtitleOptionSelected = function (index) {
self.setSubtitleStreamIndex(index);
};
self.onQualityOptionSelected = function (bitrate) {
appSettings.maxStreamingBitrate(bitrate);
appSettings.enableAutomaticBitrateDetection(false);
self.changeStream(self.getCurrentTicks(), {
Bitrate: bitrate
});
};
self.toggleInfo = function () {
var button = document.querySelector('.mediaButton.infoButton');
var nowPlayingInfo = document.querySelector('.videoControls .nowPlayingInfo');
if (button.classList.contains('active')) {
button.classList.remove('active');
document.querySelector('.videoControls').classList.add('hiddenOnIdle');
fadeOutDown(nowPlayingInfo);
} else {
button.classList.add('active');
document.querySelector('.videoControls').classList.remove('hiddenOnIdle');
nowPlayingInfo.classList.remove('hide');
fadeInUp(nowPlayingInfo);
}
};
self.toggleGuide = function () {
var button = document.querySelector('.mediaButton.guideButton');
var nowPlayingInfo = document.querySelector('.videoControls .guide');
if (button.classList.contains('active')) {
button.classList.remove('active');
document.querySelector('.videoControls').classList.add('hiddenOnIdle');
fadeOutDown(nowPlayingInfo);
} else {
button.classList.add('active');
document.querySelector('.videoControls').classList.remove('hiddenOnIdle');
nowPlayingInfo.classList.remove('hide');
fadeInUp(nowPlayingInfo);
if (!self.guideInstance) {
require(['tvguide'], function (tvguide) {
self.guideInstance = new tvguide({
element: nowPlayingInfo,
enablePaging: false
});
});
}
}
};
function fadeInUp(elem) {
var keyframes = [
{ transform: 'translate3d(0, 100%, 0)', offset: 0 },
{ transform: 'translate3d(0, 0, 0)', offset: 1 }];
var timing = { duration: 300, iterations: 1 };
if (elem.animate) {
elem.animate(keyframes, timing);
}
}
function fadeOutDown(elem) {
var keyframes = [{ transform: 'translate3d(0, 0, 0)', offset: 0 },
{ transform: 'translate3d(0, 100%, 0)', offset: 1 }];
var timing = { duration: 300, iterations: 1 };
var onFinish = function () {
elem.classList.add('hide');
};
if (elem.animate) {
elem.animate(keyframes, timing).onfinish = onFinish;
} else {
onFinish();
}
}
function ensureVideoPlayerElements() {
var html = '';
html += '
';
html += '
';
html += '';
html += '';
html += '
';
var hiddenOnIdleClass = AppInfo.isNativeApp && browserInfo.android ? 'hiddenOnIdle hide' : 'hiddenOnIdle';
html += '
';
html += '';
html += '
';
html += '';
html += '';
// Embedding onclicks due to issues not firing in cordova safari
html += '';
html += '';
html += '';
html += '';
html += '
'; // videoAdvancedControls
html += '
'; // videoTopControls
// Create controls
html += '
';
html += '
';
html += '';
html += '';
html += '
'; // nowPlayingInfo
html += '
';
html += '
'; // guide
html += '
';
html += '';
html += '';
html += '';
html += '';
html += '
';
html += '';
html += '
'; // guide
html += '
--:--
';
html += '';
html += '';
html += '
';
html += '';
html += '
'; // guide
html += '';
html += '';
html += '';
html += '
';
html += '
'; // videoControls
html += '
'; // videoPlayer
var div = document.createElement('div');
div.innerHTML = html;
document.body.appendChild(div);
}
var initComplete;
function initVideoElements() {
if (initComplete) {
return;
}
initComplete = true;
ensureVideoPlayerElements();
var parent = document.querySelector("#videoPlayer");
muteButton = parent.querySelector('.muteButton');
unmuteButton = parent.querySelector('.unmuteButton');
currentTimeElement = parent.querySelector('.currentTime');
positionSlider = parent.querySelector(".videoPositionSlider", parent);
positionSlider.addEventListener('change', onPositionSliderChange);
positionSlider.getBubbleText = function (value) {
var seekableDuration = self.getSeekableDurationTicks();
if (!self.currentMediaSource || !seekableDuration) {
return '--:--';
}
var ticks = seekableDuration;
ticks /= 100;
ticks *= value;
return datetime.getDisplayRunningTime(ticks);
};
volumeSlider = parent.querySelector('.videoVolumeSlider');
volumeSliderContainer = parent.querySelector('.volumeSliderContainer');
volumeSlider.addEventListener('change', function () {
var vol = this.value;
updateVolumeButtons(vol);
self.setVolume(vol);
});
}
var idleHandlerTimeout;
function idleHandler() {
if (idleHandlerTimeout) {
window.clearTimeout(idleHandlerTimeout);
}
if (idleState == true) {
setClass(document.querySelectorAll('.hiddenOnIdle'), 'remove', 'inactive');
document.querySelector('#videoPlayer').classList.remove('idlePlayer');
}
idleState = false;
idleHandlerTimeout = window.setTimeout(function () {
idleState = true;
setClass(document.querySelectorAll('.hiddenOnIdle'), 'add', 'inactive');
document.querySelector('#videoPlayer').classList.add('idlePlayer');
}, 3500);
}
function updateVolumeButtons(vol) {
if (!AppInfo.hasPhysicalVolumeButtons) {
if (vol) {
muteButton.classList.remove('hide');
unmuteButton.classList.add('hide');
} else {
muteButton.classList.add('hide');
unmuteButton.classList.remove('hide');
}
}
}
function requestFullScreen(element) {
// Supports most browsers and their versions.
var requestMethod = element.requestFullscreen || element.webkitRequestFullscreen || element.mozRequestFullScreen || element.msRequestFullscreen;
if (requestMethod) { // Native full screen.
requestMethod.call(element);
} else {
enterFullScreen();
}
}
function enterFullScreen() {
document.querySelector("#videoPlayer").classList.add("fullscreenVideo");
}
function exitFullScreenToWindow() {
document.querySelector("#videoPlayer").classList.remove("fullscreenVideo");
}
function onPopState() {
// Stop playback on browser back button nav
window.removeEventListener("popstate", onPopState);
self.stop();
return;
}
function onFullScreenChange() {
if (self.isFullScreen()) {
enterFullScreen();
idleState = true;
} else {
exitFullScreenToWindow();
}
}
var lastMousePosition = {};
function onMouseMove(evt) {
if (evt.clientX == lastMousePosition.x && evt.clientY == lastMousePosition.y) {
return;
}
lastMousePosition.x = evt.clientX;
lastMousePosition.y = evt.clientY;
idleHandler();
}
function bindEventsForPlayback(mediaRenderer) {
Events.on(mediaRenderer, 'playing', onOnePlaying);
Events.on(mediaRenderer, 'playing', onPlaying);
Events.on(mediaRenderer, 'volumechange', onVolumeChange);
Events.on(mediaRenderer, 'pause', onPause);
Events.on(mediaRenderer, 'timeupdate', onTimeUpdate);
Events.on(mediaRenderer, 'error', onError);
Events.on(mediaRenderer, 'click', onClick);
Events.on(mediaRenderer, 'dblclick', onDoubleClick);
var hideElementsOnIdle = true;
if (hideElementsOnIdle) {
var itemVideo = document.querySelector('.itemVideo');
if (itemVideo) {
//Events.on(itemVideo, 'mousemove', onMouseMove);
itemVideo.addEventListener('keydown', idleHandler);
itemVideo.addEventListener('scroll', idleHandler);
itemVideo.addEventListener('mousedown', idleHandler);
idleHandler();
}
}
document.addEventListener('webkitfullscreenchange', onFullScreenChange);
document.addEventListener('mozfullscreenchange', onFullScreenChange);
document.addEventListener('msfullscreenchange', onFullScreenChange);
document.addEventListener('fullscreenchange', onFullScreenChange);
window.addEventListener("popstate", onPopState);
if (hideElementsOnIdle) {
document.body.addEventListener("mousemove", onMouseMove);
}
}
function unbindEventsForPlayback(mediaRenderer) {
Events.off(mediaRenderer, 'playing', onOnePlaying);
Events.off(mediaRenderer, 'playing', onPlaying);
Events.off(mediaRenderer, 'volumechange', onVolumeChange);
Events.off(mediaRenderer, 'pause', onPause);
Events.off(mediaRenderer, 'timeupdate', onTimeUpdate);
Events.off(mediaRenderer, 'error', onError);
Events.off(mediaRenderer, 'click', onClick);
Events.off(mediaRenderer, 'dblclick', onDoubleClick);
document.removeEventListener('webkitfullscreenchange', onFullScreenChange);
document.removeEventListener('mozfullscreenchange', onFullScreenChange);
document.removeEventListener('msfullscreenchange', onFullScreenChange);
document.removeEventListener('fullscreenchange', onFullScreenChange);
// Stop playback on browser back button nav
window.removeEventListener("popstate", onPopState);
document.body.removeEventListener("mousemove", onMouseMove);
var itemVideo = document.querySelector('.itemVideo');
if (itemVideo) {
//Events.off(itemVideo, 'mousemove', onMouseMove);
itemVideo.removeEventListener('keydown', idleHandler);
itemVideo.removeEventListener('scroll', idleHandler);
itemVideo.removeEventListener('mousedown', idleHandler);
}
}
// Replace audio version
self.cleanup = function (mediaRenderer) {
if (currentTimeElement) {
currentTimeElement.innerHTML = '--:--';
}
unbindEventsForPlayback(mediaRenderer);
};
self.playVideo = function (item, mediaSource, startPosition, callback) {
if (browserInfo.msie) {
if (!window.MediaSource || !mediaSource.RunTimeTicks) {
alert('Playback of this content is not supported in Internet Explorer. For a better experience, please try a modern browser such as Google Chrome, Firefox, Opera, or Microsoft Edge.');
}
}
// TODO: remove dependency on nowplayingbar
requirejs(['videorenderer', 'css!css/nowplayingbar.css', 'css!css/mediaplayer-video.css', 'emby-slider'], function () {
initVideoElements();
self.createStreamInfo('Video', item, mediaSource, startPosition).then(function (streamInfo) {
var isHls = streamInfo.url.toLowerCase().indexOf('.m3u8') != -1;
// Huge hack alert. Safari doesn't seem to like if the segments aren't available right away when playback starts
// This will start the transcoding process before actually feeding the video url into the player
// Edit: Also seeing stalls from hls.js
if (!mediaSource.RunTimeTicks && isHls && !browserInfo.edge) {
Dashboard.showLoadingMsg();
var hlsPlaylistUrl = streamInfo.url.replace('master.m3u8', 'live.m3u8');
ApiClient.ajax({
type: 'GET',
url: hlsPlaylistUrl
}).then(function () {
Dashboard.hideLoadingMsg();
streamInfo.url = hlsPlaylistUrl;
// add a delay to continue building up the buffer. without this we see failures in safari mobile
setTimeout(function () {
self.playVideoInternal(item, mediaSource, startPosition, streamInfo, callback);
}, 2000);
}, function () {
Dashboard.hideLoadingMsg();
});
} else {
self.playVideoInternal(item, mediaSource, startPosition, streamInfo, callback);
}
});
});
};
function fadeOut(elem) {
if (elem.classList.contains('hide')) {
return;
}
var onfinish = function () {
elem.classList.add('hide');
};
//if (!browserInfo.animate) {
onfinish();
return;
//}
requestAnimationFrame(function () {
var keyframes = [
{ opacity: '1', offset: 0 },
{ opacity: '0', offset: 1 }];
var timing = { duration: 600, iterations: 1, easing: 'ease-out' };
elem.animate(keyframes, timing).onfinish = onfinish;
});
}
function fadeIn(elem) {
if (!elem.classList.contains('hide')) {
return;
}
elem.classList.remove('hide');
if (!browserInfo.animate || browserInfo.slow) {
return;
}
requestAnimationFrame(function () {
var keyframes = [
{ transform: 'scale3d(.2, .2, .2) ', opacity: '.6', offset: 0 },
{ transform: 'none', opacity: '1', offset: 1 }
];
var timing = { duration: 200, iterations: 1, easing: 'ease-out' };
elem.animate(keyframes, timing);
});
}
self.playVideoInternal = function (item, mediaSource, startPosition, streamInfo, callback) {
self.startTimeTicksOffset = streamInfo.startTimeTicksOffset;
var mediaStreams = mediaSource.MediaStreams || [];
var subtitleStreams = mediaStreams.filter(function (s) {
return s.Type == 'Subtitle';
});
// Create video player
var mediaPlayerContainer = document.querySelector('#videoPlayer');
fadeIn(mediaPlayerContainer);
var videoControls = mediaPlayerContainer.querySelector('.videoControls');
//show stop button
document.querySelector('#video-playButton').classList.add('hide');
document.querySelector('#video-pauseButton').classList.remove('hide');
document.querySelector('.videoTrackControl').classList.add('hide');
document.querySelector('.videoQualityButton').classList.remove('hide');
if (mediaStreams.filter(function (s) {
return s.Type == "Audio";
}).length) {
document.querySelector('.videoAudioButton').classList.remove('hide');
} else {
document.querySelector('.videoAudioButton').classList.add('hide');
}
if (subtitleStreams.length) {
document.querySelector('.videoSubtitleButton').classList.remove('hide');
} else {
document.querySelector('.videoSubtitleButton').classList.add('hide');
}
var mediaRenderer = new VideoRenderer({
poster: self.getPosterUrl(item)
});
var requiresNativeControls = !mediaRenderer.enableCustomVideoControls();
if (requiresNativeControls || AppInfo.isNativeApp) {
videoControls.querySelector('#video-fullscreenButton').classList.add('hide');
} else {
videoControls.querySelector('#video-fullscreenButton').classList.remove('hide');
}
if (AppInfo.hasPhysicalVolumeButtons) {
volumeSliderContainer.classList.add('hide');
videoControls.querySelector('.muteButton').classList.add('hide');
videoControls.querySelector('.unmuteButton').classList.add('hide');
} else {
volumeSliderContainer.classList.remove('hide');
videoControls.querySelector('.muteButton').classList.remove('hide');
videoControls.querySelector('.unmuteButton').classList.remove('hide');
}
if (requiresNativeControls) {
videoControls.classList.add('hide');
} else {
videoControls.classList.remove('hide');
}
initialVolume = self.getSavedVolume();
mediaRenderer.volume(initialVolume);
volumeSlider.value = initialVolume * 100;
updateVolumeButtons(initialVolume);
bindEventsForPlayback(mediaRenderer);
self.currentSubtitleStreamIndex = mediaSource.DefaultSubtitleStreamIndex;
document.body.classList.add('bodyWithPopupOpen');
self.currentMediaRenderer = mediaRenderer;
self.updateNowPlayingInfo(item);
mediaRenderer.init().then(function () {
self.onBeforePlaybackStart(mediaRenderer, item, mediaSource);
self.setSrcIntoRenderer(mediaRenderer, streamInfo, item, self.currentMediaSource);
self.streamInfo = streamInfo;
if (callback) {
callback();
}
});
};
function onOnePlaying() {
Events.off(this, 'playing', onOnePlaying);
// For some reason this is firing at the start, so don't bind until playback has begun
Events.on(this, 'ended', self.onPlaybackStopped);
Events.on(this, 'ended', self.playNextAfterEnded);
self.onPlaybackStart(this, self.currentItem, self.currentMediaSource);
}
function onPlaying() {
var videoControls = document.querySelector('#videoPlayer .videoControls');
var videoElement = document.querySelector('#videoPlayer #videoElement');
videoControls.querySelector('#video-playButton').classList.add('hide');
videoControls.querySelector('#video-pauseButton').classList.remove('hide');
var buttonToAnimate = videoElement.querySelector('#play');
buttonToAnimate.classList.remove('hide');
buttonToAnimate.classList.add('fadeOut');
setTimeout(function () {
buttonToAnimate.classList.add('hide');
buttonToAnimate.classList.remove('fadeOut');
}, 300);
}
function onVolumeChange() {
updateVolumeButtons(this.volume());
}
function onPause() {
var videoControls = document.querySelector('#videoPlayer .videoControls');
var videoElement = document.querySelector('#videoPlayer #videoElement');
videoControls.querySelector('#video-playButton').classList.remove('hide');
videoControls.querySelector('#video-pauseButton').classList.add('hide');
var buttonToAnimate = videoElement.querySelector('#pause');
buttonToAnimate.classList.remove('hide');
buttonToAnimate.classList.add('fadeOut');
setTimeout(function () {
buttonToAnimate.classList.add('hide');
buttonToAnimate.classList.remove('fadeOut');
}, 300);
}
function onTimeUpdate() {
if (!positionSlider.dragging) {
self.setCurrentTime(self.getCurrentTicks(this), positionSlider, currentTimeElement);
}
}
function onError() {
var errorMsg = Globalize.translate('MessageErrorPlayingVideo');
var item = self.currentItem;
var mediaSource = self.currentMediaSource;
if (item && item.Type == "TvChannel") {
errorMsg += ' ';
errorMsg += ' ';
errorMsg += Globalize.translate('MessageEnsureOpenTuner');
}
else if (mediaSource && mediaSource.VideoType && mediaSource.VideoType != "VideoFile") {
errorMsg += ' ';
errorMsg += ' ';
errorMsg += Globalize.translate('MessageFolderRipPlaybackExperimental');
}
Dashboard.alert({
title: Globalize.translate('HeaderVideoError'),
message: errorMsg
});
var mediaRenderer = self.currentMediaRenderer;
if (mediaRenderer) {
self.onPlaybackStopped.call(mediaRenderer);
}
self.nextTrack();
}
function onClick() {
if (!browserInfo.mobile) {
if (this.paused()) {
self.unpause();
} else {
self.pause();
}
}
}
function onDoubleClick() {
if (!browserInfo.mobile) {
self.toggleFullscreen();
}
}
self.updatePlaylistUi = function () {
if (!initComplete) {
return;
}
var index = self.currentPlaylistIndex(null);
var length = self.playlist.length;
var requiresNativeControls = false;
if (self.currentMediaRenderer && self.currentMediaRenderer.enableCustomVideoControls) {
requiresNativeControls = self.currentMediaRenderer.enableCustomVideoControls();
}
if (length < 2) {
document.querySelector('.videoTrackControl').classList.add('hide');
return;
}
var controls = requiresNativeControls ? '.videoAdvancedControls' : '.videoControls';
controls = document.querySelector(controls);
var previousTrackButton = controls.getElementsByClassName('previousTrackButton')[0];
var nextTrackButton = controls.getElementsByClassName('nextTrackButton')[0];
if (index === 0) {
previousTrackButton.setAttribute('disabled', 'disabled');
} else {
previousTrackButton.removeAttribute('disabled');
}
if ((index + 1) >= length) {
nextTrackButton.setAttribute('disabled', 'disabled');
} else {
nextTrackButton.removeAttribute('disabled');
}
previousTrackButton.classList.remove('hide');
nextTrackButton.classList.remove('hide');
};
}
createVideoPlayer(MediaPlayer);
});