(function () { function createVideoPlayer(self) { var timeout; var initialVolume; var fullscreenExited = false; var idleState = true; var remoteFullscreen = false; var muteButton = null; var unmuteButton = null; var volumeSlider = null; var positionSlider; var isPositionSliderActive; var currentTimeElement; self.currentSubtitleStreamIndex = null; self.getCurrentSubtitleStream = function () { return self.getSubtitleStream(self.currentSubtitleStreamIndex); }; self.getSubtitleStream = function (index) { return self.currentMediaSource.MediaStreams.filter(function (s) { return s.Type == 'Subtitle' && s.Index == index; })[0]; }; self.remoteFullscreen = function () { if (remoteFullscreen) { exitFullScreenToWindow(); } else { enterFullScreen(); } remoteFullscreen = !remoteFullscreen; }; self.toggleFullscreen = function () { if (self.isFullScreen()) { if (document.cancelFullScreen) { document.cancelFullScreen(); } else if (document.mozCancelFullScreen) { document.mozCancelFullScreen(); } else if (document.webkitExitFullscreen) { document.webkitExitFullscreen(); } else if (document.webkitCancelFullScreen) { document.webkitCancelFullScreen(); } $('#videoPlayer').removeClass('fullscreenVideo'); } else { requestFullScreen(document.body); } }; self.resetEnhancements = function () { $("#mediaPlayer").hide(); $('#videoPlayer').removeClass('fullscreenVideo').removeClass('idlePlayer'); $('.hiddenOnIdle').removeClass("inactive"); $("video").remove(); }; self.exitFullScreen = function () { if (document.exitFullscreen) { document.exitFullscreen(); } else if (document.mozExitFullScreen) { document.mozExitFullScreen(); } else if (document.webkitExitFullscreen) { document.webkitExitFullscreen(); } $('#videoPlayer').removeClass('fullscreenVideo'); fullscreenExited = true; }; self.isFullScreen = function () { return document.fullscreen || document.mozFullScreen || document.webkitIsFullScreen || document.msFullscreenElement ? true : false; }; function onFlyoutClose() { $('.itemVideo').css('visibility', 'visible'); } function onPopupOpen(elem) { elem.popup("open").parents(".ui-popup-container").css("margin-top", 30); if ($.browser.safari) { $('.itemVideo').css('visibility', 'hidden'); } } self.showSubtitleMenu = function () { var elem = $('.videoSubtitlePopup').html(getSubtitleTracksHtml()) .trigger('create') .popup("option", "positionTo", $('.videoSubtitleButton')) .off('popupafterclose', onFlyoutClose) .on('popupafterclose', onFlyoutClose); onPopupOpen(elem); }; self.showQualityFlyout = function () { var elem = $('.videoQualityPopup').html(getQualityFlyoutHtml()) .trigger('create') .popup("option", "positionTo", $('.videoQualityButton')) .off('popupafterclose', onFlyoutClose) .on('popupafterclose', onFlyoutClose); onPopupOpen(elem); }; self.showChaptersFlyout = function () { var elem = $('.videoChaptersPopup').html(getChaptersFlyoutHtml()) .trigger('create') .popup("option", "positionTo", $('.videoChaptersButton')) .off('popupafterclose', onFlyoutClose) .on('popupafterclose', onFlyoutClose); onPopupOpen(elem); }; self.showAudioTracksFlyout = function () { var elem = $('.videoAudioPopup').html(getAudioTracksHtml()) .trigger('create') .popup("option", "positionTo", $('.videoAudioButton')) .off('popupafterclose', onFlyoutClose) .on('popupafterclose', onFlyoutClose); onPopupOpen(elem); }; self.setAudioStreamIndex = function (index) { self.changeStream(self.getCurrentTicks(), { AudioStreamIndex: index }); }; self.supportsSubtitleStreamExternally = function (stream) { return stream.Type == 'Subtitle' && stream.IsTextSubtitleStream && stream.SupportsExternalStream; } self.setSubtitleStreamIndex = function (index) { if (!self.supportsTextTracks()) { self.changeStream(self.getCurrentTicks(), { SubtitleStreamIndex: index }); self.currentSubtitleStreamIndex = index; return; } var currentStream = self.getCurrentSubtitleStream(); var newStream = self.getSubtitleStream(index); if (!currentStream && !newStream) return; var selectedTrackElementIndex = -1; if (currentStream && !newStream) { if (!self.supportsSubtitleStreamExternally(currentStream)) { // Need to change the transcoded stream to remove subs self.changeStream(self.getCurrentTicks(), { SubtitleStreamIndex: -1 }); } } else if (!currentStream && newStream) { if (self.supportsSubtitleStreamExternally(newStream)) { selectedTrackElementIndex = index; } else { // Need to change the transcoded stream to add subs self.changeStream(self.getCurrentTicks(), { SubtitleStreamIndex: index }); } } else if (currentStream && newStream) { if (self.supportsSubtitleStreamExternally(newStream)) { selectedTrackElementIndex = index; if (!self.supportsSubtitleStreamExternally(currentStream)) { self.changeStream(self.getCurrentTicks(), { SubtitleStreamIndex: -1 }); } } else { // Need to change the transcoded stream to add subs self.changeStream(self.getCurrentTicks(), { SubtitleStreamIndex: index }); } } self.setCurrentTrackElement(selectedTrackElementIndex); self.currentSubtitleStreamIndex = index; }; self.setCurrentTrackElement = function (index) { var modes = ['disabled', 'showing', 'hidden']; var textStreams = self.currentMediaSource.MediaStreams.filter(function (s) { return self.supportsSubtitleStreamExternally(s); }); var newStream = textStreams.filter(function (s) { return s.Index == index; })[0]; var trackIndex = newStream ? textStreams.indexOf(newStream) : -1; console.log('Setting new text track index to: ' + trackIndex); var allTracks = self.currentMediaElement.textTracks; // get list of tracks for (var i = 0; i < allTracks.length; i++) { var mode; if (trackIndex == i) { mode = 1; // show this track } else { mode = 0; // hide all other tracks } console.log('Setting track ' + i + ' mode to: ' + mode); // Safari uses integers for the mode property // http://www.jwplayer.com/html5/scripting/ var useNumericMode = false; if (!isNaN(allTracks[i].mode)) { useNumericMode = true; } if (useNumericMode) { allTracks[i].mode = mode; } else { allTracks[i].mode = modes[mode]; } } }; self.updateTextStreamUrls = function (startPositionTicks) { if (!self.supportsTextTracks()) { return; } var allTracks = self.currentMediaElement.textTracks; // get list of tracks for (var i = 0; i < allTracks.length; i++) { var track = allTracks[i]; // This throws an error in IE, but is fine in chrome // In IE it's not necessary anyway because changing the src seems to be enough try { while (track.cues.length) { track.removeCue(track.cues[0]); } } catch (e) { console.log('Error removing cue from textTrack'); } } $('track', self.currentMediaElement).each(function () { var currentSrc = this.src; currentSrc = replaceQueryString(currentSrc, 'startPositionTicks', startPositionTicks); this.src = currentSrc; }); }; self.updateNowPlayingInfo = function (item) { if (!item) { throw new Error('item cannot be null'); } var mediaControls = $("#videoPlayer"); var state = self.getPlayerStateInternal(self.currentMediaElement, item, self.currentMediaSource); var url = ""; if (state.NowPlayingItem.PrimaryImageTag) { url = ApiClient.getScaledImageUrl(state.NowPlayingItem.PrimaryImageItemId, { type: "Primary", width: 150, tag: state.NowPlayingItem.PrimaryImageTag }); } else if (state.NowPlayingItem.PrimaryImageTag) { url = ApiClient.getScaledImageUrl(state.NowPlayingItem.PrimaryImageItemId, { type: "Primary", width: 150, tag: state.NowPlayingItem.PrimaryImageTag }); } else if (state.NowPlayingItem.BackdropImageTag) { url = ApiClient.getScaledImageUrl(state.NowPlayingItem.BackdropItemId, { type: "Backdrop", height: 300, tag: state.NowPlayingItem.BackdropImageTag, index: 0 }); } else if (state.NowPlayingItem.ThumbImageTag) { url = ApiClient.getScaledImageUrl(state.NowPlayingItem.ThumbImageItemId, { type: "Thumb", height: 300, tag: state.NowPlayingItem.ThumbImageTag }); } var nowPlayingTextElement = $('.nowPlayingText', mediaControls); var nameHtml = self.getNowPlayingNameHtml(state); if (nameHtml.indexOf('
') != -1) { nowPlayingTextElement.addClass('nowPlayingDoubleText'); } else { nowPlayingTextElement.removeClass('nowPlayingDoubleText'); } if (url) { $('.nowPlayingImage', mediaControls).html(''); } else { $('.nowPlayingImage', mediaControls).html(''); } if (state.NowPlayingItem.LogoItemId) { url = ApiClient.getScaledImageUrl(state.NowPlayingItem.LogoItemId, { type: "Logo", height: 42, tag: state.NowPlayingItem.LogoImageTag }); $('.videoTopControlsLogo', mediaControls).html(''); } else { $('.videoTopControlsLogo', mediaControls).html(''); } nowPlayingTextElement.html(nameHtml); }; function onPositionSliderChange() { isPositionSliderActive = false; var newPercent = parseInt(this.value); var newPositionTicks = (newPercent / 100) * self.currentMediaSource.RunTimeTicks; self.changeStream(Math.floor(newPositionTicks)); } $(function () { var parent = $("#mediaPlayer"); muteButton = $('.muteButton', parent); unmuteButton = $('.unmuteButton', parent); currentTimeElement = $('.currentTime', parent); positionSlider = $(".positionSlider", parent).on('slidestart', function (e) { isPositionSliderActive = true; }).on('slidestop', onPositionSliderChange); volumeSlider = $('.volumeSlider', parent).on('slidestop', function () { var vol = this.value; updateVolumeButtons(vol); self.setVolume(vol * 100); }); $('.videoChaptersPopup').on('click', '.mediaPopupOption', function () { var ticks = parseInt(this.getAttribute('data-positionticks') || '0'); self.changeStream(ticks); $('.videoChaptersPopup').popup('close'); }); $('.videoAudioPopup').on('click', '.mediaPopupOption', function () { if (!$(this).hasClass('selectedMediaPopupOption')) { var index = parseInt(this.getAttribute('data-index')); self.setAudioStreamIndex(index); } $('.videoAudioPopup').popup('close'); }); $('.videoSubtitlePopup').on('click', '.mediaPopupOption', function () { $('.videoSubtitlePopup').popup('close'); if (!$(this).hasClass('selectedMediaPopupOption')) { var index = parseInt(this.getAttribute('data-index')); self.setSubtitleStreamIndex(index); } }); $('.videoQualityPopup').on('click', '.mediaPopupOption', function () { if (!$(this).hasClass('selectedMediaPopupOption')) { var bitrate = parseInt(this.getAttribute('data-bitrate')); AppSettings.maxStreamingBitrate(bitrate); self.changeStream(self.getCurrentTicks(), { Bitrate: bitrate }); } $('.videoQualityPopup').popup('close'); }); var trackChange = false; var tooltip = $('
'); $(".videoControls .positionSliderContainer .slider").on("change", function (e) { if (!trackChange) return; var pct = $(this).val(); var time = self.currentDurationTicks * (Number(pct) * .01); var tooltext = Dashboard.getDisplayTime(time); tooltip.text(tooltext); console.log("slidin", pct, self.currentDurationTicks, time); }).on("slidestart", function (e) { trackChange = true; var handle = $(".videoControls .positionSliderContainer .ui-slider-handle"); handle.after(tooltip); }).on("slidestop", function (e) { trackChange = false; tooltip.remove(); }); $('.videoSubtitleButton').on('click', function () { self.showSubtitleMenu(); }); $('.videoQualityButton').on('click', function () { self.showQualityFlyout(); }); $('.videoAudioButton').on('click', function () { self.showAudioTracksFlyout(); }); $('.videoChaptersButton').on('click', function () { self.showChaptersFlyout(); }); }); function idleHandler() { if (timeout) { window.clearTimeout(timeout); } if (idleState == true) { $('.hiddenOnIdle').removeClass("inactive"); $('#videoPlayer').removeClass('idlePlayer'); } idleState = false; timeout = window.setTimeout(function () { idleState = true; $('.hiddenOnIdle').addClass("inactive"); $('#videoPlayer').addClass('idlePlayer'); }, 4000); } function updateVolumeButtons(vol) { if (vol) { muteButton.show(); unmuteButton.hide(); } else { muteButton.hide(); unmuteButton.show(); } } function requestFullScreen(element) { // Supports most browsers and their versions. var requestMethod = element.requestFullscreen || element.webkitRequestFullscreen || element.webkitRequestFullScreen || element.mozRequestFullScreen; if (requestMethod) { // Native full screen. requestMethod.call(element); } else { enterFullScreen(); } } function enterFullScreen() { var player = $("#videoPlayer"); player.addClass("fullscreenVideo"); remoteFullscreen = true; } function exitFullScreenToWindow() { var player = $("#videoPlayer"); player.removeClass("fullscreenVideo"); remoteFullscreen = false; } function getChaptersFlyoutHtml() { var item = self.currentItem; var currentTicks = self.getCurrentTicks(); var chapters = item.Chapters || []; var html = ''; html += '
'; html += ''; html += '
'; html += ''; html += '
'; html += '
'; return html; } function getAudioTracksHtml() { var streams = self.currentMediaSource.MediaStreams.filter(function (currentStream) { return currentStream.Type == "Audio"; }); var currentIndex = getParameterByName('AudioStreamIndex', self.getCurrentSrc(self.currentMediaElement)); var html = ''; html += '
'; html += ''; html += '
'; html += ''; html += '
'; html += '
'; return html; } function getSubtitleTracksHtml() { var streams = self.currentMediaSource.MediaStreams.filter(function (currentStream) { return currentStream.Type == "Subtitle"; }); var currentIndex = self.currentSubtitleStreamIndex || -1; streams.unshift({ Index: -1, Language: "Off" }); var html = ''; html += '
'; html += ''; html += '
'; html += ''; html += '
'; html += '
'; return html; } function getQualityFlyoutHtml() { var currentSrc = self.getCurrentSrc(self.currentMediaElement).toLowerCase(); var isStatic = currentSrc.indexOf('static=true') != -1; var videoStream = self.currentMediaSource.MediaStreams.filter(function (stream) { return stream.Type == "Video"; })[0]; var videoWidth = videoStream ? videoStream.Width : null; var options = self.getVideoQualityOptions(videoWidth); if (isStatic) { options[0].name = "Direct"; } var html = ''; html += '
'; html += ''; html += '
'; html += ''; html += '
'; html += '
'; return html; } function bindEventsForPlayback() { var hideElementsOnIdle = !$.browser.mobile; if (hideElementsOnIdle) { $('.itemVideo').off('mousemove.videoplayer keydown.videoplayer scroll.videoplayer', idleHandler); $('.itemVideo').on('mousemove.videoplayer keydown.videoplayer scroll.videoplayer', idleHandler).trigger('mousemove'); } $(document).on('webkitfullscreenchange.videoplayer mozfullscreenchange.videoplayer fullscreenchange.videoplayer', function (e) { if (self.isFullScreen()) { enterFullScreen(); idleState = true; } else { exitFullScreenToWindow(); } fullscreenExited = self.isFullScreen() == false; }).on("msfullscreenchange.videoplayer", function (e) { fullscreenExited = self.isFullScreen() == false; }).on("keyup.videoplayer", function (e) { if (fullscreenExited) { $("#videoPlayer", $("#mediaPlayer")).removeClass("fullscreenVideo"); fullscreenExited = false; return; } if (e.keyCode == 27) { self.stop(); } }); // Stop playback on browser back button nav $(window).one("popstate.videoplayer", function () { self.stop(); return; }); if (hideElementsOnIdle) { $(document.body).on("mousemove.videplayer", "#itemVideo", function () { idleHandler(this); }); } } function unbindEventsForPlayback() { $(document).off('webkitfullscreenchange.videoplayer mozfullscreenchange.videoplayer fullscreenchange.videoplayer').off("msfullscreenchange.videoplayer").off("keyup.videoplayer"); // Stop playback on browser back button nav $(window).off("popstate.videoplayer"); $(document.body).off("mousemove.videplayer"); $('.itemVideo').off('mousemove.videoplayer keydown.videoplayer scroll.videoplayer'); } self.canAutoPlayVideo = function () { if ($.browser.msie || $.browser.mobile) { return false; } return true; }; // Replace audio version self.cleanup = function (playerElement) { if (playerElement.tagName.toLowerCase() == 'video') { currentTimeElement.html('--:--'); unbindEventsForPlayback(); } }; self.playVideo = function (deviceProfile, playbackInfo, item, mediaSource, startPosition) { var streamInfo = self.createStreamInfo('video', item, mediaSource, startPosition); var videoUrl = streamInfo.url; var contentType = streamInfo.contentType; var startPositionInSeekParam = streamInfo.startPositionInSeekParam; self.startTimeTicksOffset = streamInfo.startTimeTicksOffset; var mediaStreams = mediaSource.MediaStreams || []; var subtitleStreams = mediaStreams.filter(function (s) { return s.Type == 'Subtitle'; }); //======================================================================================> // Create video player var html = ''; var requiresNativeControls = !self.canAutoPlayVideo(); // Can't autoplay in these browsers so we need to use the full controls if (requiresNativeControls) { html += '