(function () { function createVideoPlayer(self) { var timeout; 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.mozExitFullScreen) { document.mozExitFullScreen(); } 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(); if (!supportsContentOverVideoPlayer()) { showPopupUsingSelect(html, 'subtitles'); return; } 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(); if (!supportsContentOverVideoPlayer()) { showPopupUsingSelect(html, 'quality'); return; } 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(); if (!supportsContentOverVideoPlayer()) { showPopupUsingSelect(html, 'audio'); return; } var elem = $('.videoAudioPopup').html(html) .trigger('create') .popup("option", "positionTo", $('.videoAudioButton')) .off('popupafterclose', onFlyoutClose) .on('popupafterclose', onFlyoutClose); onPopupOpen(elem); }; function openSelect(selector) { var element = $(selector)[0], worked = false; if (document.createEvent) { // all browsers var e = document.createEvent("MouseEvents"); e.initMouseEvent("mousedown", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); worked = element.dispatchEvent(e); } else if (element.fireEvent) { // ie worked = element.fireEvent("onmousedown"); } if (!worked) { // unknown browser / error } } function showPopupUsingSelect(html, type) { var re = new RegExp('
', 'g'); html = html.replace(re, ' • '); var options = $('.mediaPopupOption', $(html)).get().map(function (e) { var selected = $(e).hasClass('selectedMediaPopupOption') ? ' selected="selected"' : ''; return '' + $(e).text() + ''; }); html = ''; var select = $(html).appendTo(document.body).on('change', function () { var value = this.value; $(this).remove(); if (type == 'audio') { onAudioOptionSelected(value); } else if (type == 'subtitles') { onSubtitleOptionSelected(value); } else if (type == 'quality') { onQualityOptionSelected(value); } }).on('blur', function () { $(this).remove(); }); openSelect(select); } 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.currentMediaElement, 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)); } function onAudioOptionSelected(value) { var index = parseInt(value); self.setAudioStreamIndex(index); } function onSubtitleOptionSelected(value) { var index = parseInt(value); self.setSubtitleStreamIndex(index); } function onQualityOptionSelected(value) { var bitrate = parseInt(value); AppSettings.maxStreamingBitrate(bitrate); self.changeStream(self.getCurrentTicks(), { Bitrate: bitrate }); } $(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')) { onAudioOptionSelected(this.getAttribute('data-value')); } $('.videoAudioPopup').popup('close'); }); $('.videoSubtitlePopup').on('click', '.mediaPopupOption', function () { $('.videoSubtitlePopup').popup('close'); if (!$(this).hasClass('selectedMediaPopupOption')) { onSubtitleOptionSelected(this.getAttribute('data-value')); } }); $('.videoQualityPopup').on('click', '.mediaPopupOption', function () { if (!$(this).hasClass('selectedMediaPopupOption')) { onQualityOptionSelected(this.getAttribute('data-value')); } $('.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(); }); }); 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'); }, 5000); } 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.currentMediaElement)); var 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 += '
'; 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 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('webkitfullscreenchange.videoplayer mozfullscreenchange.videoplayer msfullscreenchange.videoplayer fullscreenchange.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'); } self.canAutoPlayVideo = function () { if (Dashboard.isRunningInCordova()) { return true; } if ($.browser.mobile) { return false; } return true; }; self.enableCustomVideoControls = function () { return self.canAutoPlayVideo() && !$.browser.mobile; }; // Replace audio version self.cleanup = function (playerElement) { if (playerElement.tagName.toLowerCase() == 'video') { currentTimeElement.html('--:--'); unbindEventsForPlayback(); } }; self.playVideo = function (item, mediaSource, startPosition) { 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.contentType; 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) { html += '