(function () { function createVideoPlayer(self) { var initialVolume; var idleState = true; 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.toggleFullscreen = function () { if (self.isFullScreen()) { self.exitFullScreen(); } 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.mozCancelFullScreen) { document.mozCancelFullScreen(); } else if (document.webkitExitFullscreen) { document.webkitExitFullscreen(); } else if (document.msExitFullscreen) { document.msExitFullscreen(); } $('#videoPlayer').removeClass('fullscreenVideo'); }; 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.showChaptersFlyout = function () { var html = getChaptersFlyoutHtml(); var elem = $('.videoChaptersPopup').html(html) .trigger('create') .popup("option", "positionTo", $('.videoChaptersButton')) .off('popupafterclose', onFlyoutClose) .on('popupafterclose', onFlyoutClose); onPopupOpen(elem); }; self.showSubtitleMenu = function () { var html = getSubtitleTracksHtml(); var elem = $('.videoSubtitlePopup').html(html) .trigger('create') .popup("option", "positionTo", $('.videoSubtitleButton')) .off('popupafterclose', onFlyoutClose) .on('popupafterclose', onFlyoutClose); onPopupOpen(elem); }; self.showQualityFlyout = function () { var html = getQualityFlyoutHtml(); var elem = $('.videoQualityPopup').html(html) .trigger('create') .popup("option", "positionTo", $('.videoQualityButton')) .off('popupafterclose', onFlyoutClose) .on('popupafterclose', onFlyoutClose); onPopupOpen(elem); }; self.showAudioTracksFlyout = function () { var html = getAudioTracksHtml(); var elem = $('.videoAudioPopup').html(html) .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.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 (currentStream.DeliveryMethod != 'External') { // Need to change the transcoded stream to remove subs self.changeStream(self.getCurrentTicks(), { SubtitleStreamIndex: -1 }); } } else if (!currentStream && newStream) { if (newStream.DeliveryMethod == 'External') { selectedTrackElementIndex = index; } else { // Need to change the transcoded stream to add subs self.changeStream(self.getCurrentTicks(), { SubtitleStreamIndex: index }); } } else if (currentStream && newStream) { if (newStream.DeliveryMethod == 'External') { selectedTrackElementIndex = index; if (currentStream.DeliveryMethod != 'External') { 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 s.DeliveryMethod == 'External'; }); 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.currentMediaRenderer, item, self.currentMediaSource); var url = ""; var imageWidth = 400; var imageHeight = 300; if (state.NowPlayingItem.PrimaryImageTag) { url = ApiClient.getScaledImageUrl(state.NowPlayingItem.PrimaryImageItemId, { type: "Primary", width: imageWidth, tag: state.NowPlayingItem.PrimaryImageTag }); } else if (state.NowPlayingItem.PrimaryImageTag) { url = ApiClient.getScaledImageUrl(state.NowPlayingItem.PrimaryImageItemId, { type: "Primary", width: imageWidth, tag: state.NowPlayingItem.PrimaryImageTag }); } else if (state.NowPlayingItem.BackdropImageTag) { url = ApiClient.getScaledImageUrl(state.NowPlayingItem.BackdropItemId, { type: "Backdrop", height: imageHeight, tag: state.NowPlayingItem.BackdropImageTag, index: 0 }); } else if (state.NowPlayingItem.ThumbImageTag) { url = ApiClient.getScaledImageUrl(state.NowPlayingItem.ThumbImageItemId, { type: "Thumb", height: imageHeight, tag: state.NowPlayingItem.ThumbImageTag }); } 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(''); } var elem = $('.nowPlayingTabs', mediaControls).html(getNowPlayingTabsHtml(item)).lazyChildren(); $('.nowPlayingTabButton', elem).on('click', function () { if (!$(this).hasClass('selectedNowPlayingTabButton')) { $('.selectedNowPlayingTabButton').removeClass('selectedNowPlayingTabButton'); $(this).addClass('selectedNowPlayingTabButton'); $('.nowPlayingTab').hide(); $('.' + this.getAttribute('data-tab')).show().trigger('scroll'); } }); $('.chapterCard', elem).on('click', function () { self.seek(parseInt(this.getAttribute('data-position'))); }); }; function getNowPlayingTabsHtml(item) { var html = ''; html += '
'; html += '' + Globalize.translate('TabInfo') + ''; if (item.Chapters && item.Chapters.length) { html += '' + Globalize.translate('TabScenes') + ''; } if (item.People && item.People.length) { html += '' + Globalize.translate('TabCast') + ''; } html += '
'; html += '
'; var nameHtml = MediaController.getNowPlayingNameHtml(item, false); nameHtml = '
' + nameHtml + '
'; var miscInfo = LibraryBrowser.getMiscInfoHtml(item); if (miscInfo) { nameHtml += '
' + miscInfo + '
'; } var ratingHtml = LibraryBrowser.getRatingHtml(item); if (ratingHtml) { nameHtml += '
' + ratingHtml + '
'; } if (item.Overview) { nameHtml += '
' + item.Overview + '
'; } html += nameHtml; html += '
'; if (item.Chapters && item.Chapters.length) { html += ''; } if (item.People && item.People.length) { html += ''; } return html; } function onPositionSliderChange() { isPositionSliderActive = false; var newPercent = parseInt(this.value); var newPositionTicks = (newPercent / 100) * self.currentMediaSource.RunTimeTicks; self.changeStream(Math.floor(newPositionTicks)); } self.onChapterOptionSelected = function (elem) { if (!$(elem).hasClass('selectedMediaPopupOption')) { var ticks = parseInt(elem.getAttribute('data-value') || '0'); self.changeStream(ticks); } $('.videoChaptersPopup').popup('close'); }; self.onAudioOptionSelected = function (elem) { if (!$(elem).hasClass('selectedMediaPopupOption')) { var index = parseInt(elem.getAttribute('data-value')); self.setAudioStreamIndex(index); } $('.videoAudioPopup').popup('close'); }; self.onSubtitleOptionSelected = function (elem) { if (!$(elem).hasClass('selectedMediaPopupOption')) { var index = parseInt(elem.getAttribute('data-value')); self.setSubtitleStreamIndex(index); } $('.videoSubtitlePopup').popup('close'); }; self.onQualityOptionSelected = function (elem) { if (!$(elem).hasClass('selectedMediaPopupOption')) { var bitrate = parseInt(elem.getAttribute('data-value')); AppSettings.maxStreamingBitrate(bitrate); $('.videoQualityPopup').popup('close'); self.changeStream(self.getCurrentTicks(), { Bitrate: bitrate }); } $('.videoSubtitlePopup').popup('close'); }; $(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); }); 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(); }); }); var idleHandlerTimeout; function idleHandler() { if (idleHandlerTimeout) { window.clearTimeout(idleHandlerTimeout); } if (idleState == true) { $('.hiddenOnIdle').removeClass("inactive"); $('#videoPlayer').removeClass('idlePlayer'); } idleState = false; idleHandlerTimeout = 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.mozRequestFullScreen || element.msRequestFullscreen; if (requestMethod) { // Native full screen. requestMethod.call(element); } else { enterFullScreen(); } } function enterFullScreen() { var player = $("#videoPlayer"); player.addClass("fullscreenVideo"); } function exitFullScreenToWindow() { var player = $("#videoPlayer"); player.removeClass("fullscreenVideo"); } 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.currentMediaRenderer)); 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.currentMediaRenderer).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 = true; if (hideElementsOnIdle) { $('.itemVideo').off('mousemove.videoplayer keydown.videoplayer scroll.videoplayer mousedown.videoplayer', idleHandler).on('mousemove.videoplayer keydown.videoplayer scroll.videoplayer mousedown.videoplayer', idleHandler).trigger('mousemove'); } $(document).on('webkitfullscreenchange.videoplayer mozfullscreenchange.videoplayer msfullscreenchange.videoplayer fullscreenchange.videoplayer', function (e) { if (self.isFullScreen()) { enterFullScreen(); idleState = true; } else { exitFullScreenToWindow(); } }); // Stop playback on browser back button nav $(window).one("popstate.videoplayer", function () { self.stop(); return; }); if (hideElementsOnIdle) { $(document.body).on("mousemove.videoplayer", function () { idleHandler(this); }); } } function unbindEventsForPlayback() { $(document).off('.videoplayer'); // Stop playback on browser back button nav $(window).off("popstate.videoplayer"); $(document.body).off("mousemove.videoplayer"); $('.itemVideo').off('mousemove.videoplayer keydown.videoplayer scroll.videoplayer mousedown.videoplayer'); } self.canAutoPlayVideo = function () { if (AppInfo.isNativeApp) { return true; } if ($.browser.mobile) { return false; } return true; }; self.enableCustomVideoControls = function () { return self.canAutoPlayVideo() && !$.browser.mobile; }; // Replace audio version self.cleanup = function (mediaRenderer) { currentTimeElement.html('--:--'); unbindEventsForPlayback(); }; self.playVideo = function (item, mediaSource, startPosition) { requirejs(['videorenderer'], function () { var streamInfo = self.createStreamInfo('Video', item, mediaSource, startPosition); // 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 if ($.browser.safari && !mediaSource.RunTimeTicks) { Dashboard.showLoadingMsg(); ApiClient.ajax({ type: 'GET', url: streamInfo.url.replace('master.m3u8', 'live.m3u8') }).always(function () { Dashboard.hideLoadingMsg(); }).done(function () { self.playVideoInternal(item, mediaSource, startPosition, streamInfo); }); } else { self.playVideoInternal(item, mediaSource, startPosition, streamInfo); } }); }; function supportsContentOverVideoPlayer() { return true; } self.playVideoInternal = function (item, mediaSource, startPosition, streamInfo) { var videoUrl = streamInfo.url; var contentType = streamInfo.mimeType; var startPositionInSeekParam = streamInfo.startPositionInSeekParam; self.startTimeTicksOffset = streamInfo.startTimeTicksOffset; var mediaStreams = mediaSource.MediaStreams || []; var subtitleStreams = mediaStreams.filter(function (s) { return s.Type == 'Subtitle'; }); // Reports of stuttering with h264 stream copy in IE if (streamInfo.playMethod == 'Transcode' && videoUrl.indexOf('.m3u8') == -1) { videoUrl += '&EnableAutoStreamCopy=false'; } var posterCode = self.getPosterUrl(item); posterCode = posterCode ? (' poster="' + posterCode + '"') : ''; //======================================================================================> // Create video player var html = ''; var requiresNativeControls = !self.enableCustomVideoControls(); // Can't autoplay in these browsers so we need to use the full controls if (requiresNativeControls && AppInfo.isNativeApp && $.browser.android) { html += '