(function (document, setTimeout, clearTimeout, screen, localStorage, $, setInterval, window) { function mediaPlayer() { var self = this; var testableVideoElement = document.createElement('video'); var currentMediaElement; var currentProgressInterval; var positionSlider; var isPositionSliderActive; var currentTimeElement; var currentItem; var volumeSlider; var muteButton; var unmuteButton; var startTimeTicksOffset; var curentDurationTicks; var canClientSeek; var culturesPromise; var timeout; var idleState = true; self.playlist = []; var currentPlaylistIndex = 0; var channelsListPromise; var channelsListPromiseTime; function updateCanClientSeek(elem) { var duration = elem.duration; canClientSeek = duration && !isNaN(duration) && duration != Number.POSITIVE_INFINITY && duration != Number.NEGATIVE_INFINITY; } function getChannelsListPromise() { var lastUpdateTime = channelsListPromiseTime || 0; // Update every three minutes if (!channelsListPromise || !lastUpdateTime || (new Date().getTime() - lastUpdateTime) > 10800000) { channelsListPromise = ApiClient.getLiveTvChannels({ userId: Dashboard.getCurrentUserId() }); } return channelsListPromise; } 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 { $('.itemVideo').addClass('fullscreenVideo'); } } function exitFullScreen() { if (document.exitFullscreen) { document.exitFullscreen(); } else if (document.mozExitFullScreen) { document.mozExitFullScreen(); } else if (document.webkitExitFullscreen) { document.webkitExitFullscreen(); } } function isFullScreen() { return document.fullscreenEnabled || document.mozFullscreenEnabled || document.webkitIsFullScreen || document.mozFullScreen ? true : false; } $(document).on('webkitfullscreenchange mozfullscreenchange fullscreenchange', function () { var nowPlayingBar = ('#nowPlayingBar'); $('.itemVideo').off('mousemove keydown scroll', idleHandler); if (isFullScreen()) { $('.itemVideo').addClass('fullscreenVideo'); idleState = true; $('.itemVideo').on('mousemove keydown scroll', idleHandler).trigger('mousemove'); } else { $(".mediaButton,.currentTime,.nowPlayingMediaInfo,.sliderContainer,.barBackground", nowPlayingBar).removeClass("highPosition"); $('.itemVideo').removeClass('fullscreenVideo'); } }); $(window).on("beforeunload", function () { var item = currentItem; var media = currentMediaElement; // Try to report playback stopped before the browser closes if (item && media && currentProgressInterval) { var endTime = currentMediaElement.currentTime; var position = Math.floor(10000000 * endTime) + startTimeTicksOffset; ApiClient.reportPlaybackStopped(Dashboard.getCurrentUserId(), currentItem.Id, position); } }); function replaceQueryString(url, param, value) { var re = new RegExp("([?|&])" + param + "=.*?(&|$)", "i"); if (url.match(re)) return url.replace(re, '$1' + param + "=" + value + '$2'); else return url + '&' + param + "=" + value; } function updateVolumeButtons(vol) { if (vol) { muteButton.show(); unmuteButton.hide(); } else { muteButton.hide(); unmuteButton.show(); } } function getCurrentTicks(mediaElement) { return Math.floor(10000000 * (mediaElement || currentMediaElement).currentTime) + startTimeTicksOffset; } function onPlaybackStopped() { $(this).off('ended.playbackstopped'); currentTimeElement.empty(); var endTime = this.currentTime; clearProgressInterval(); var position = Math.floor(10000000 * endTime) + startTimeTicksOffset; ApiClient.reportPlaybackStopped(Dashboard.getCurrentUserId(), currentItem.Id, position); if (currentItem.MediaType == "Video") { ApiClient.stopActiveEncodings(); } } function playNextAfterEnded() { $(this).off('ended.playnext'); self.nextTrack(); } function startProgressInterval(itemId) { clearProgressInterval(); var intervalTime = ApiClient.isWebSocketOpen() ? 2000 : 20000; currentProgressInterval = setInterval(function () { if (currentMediaElement) { sendProgressUpdate(itemId); } }, intervalTime); } function sendProgressUpdate(itemId) { ApiClient.reportPlaybackProgress(Dashboard.getCurrentUserId(), itemId, getCurrentTicks(), currentMediaElement.paused, currentMediaElement.volume == 0); } function clearProgressInterval() { if (currentProgressInterval) { clearTimeout(currentProgressInterval); currentProgressInterval = null; } } function canPlayWebm() { return testableVideoElement.canPlayType('video/webm').replace(/no/, ''); } function getTranscodingExtension() { var media = testableVideoElement; // safari if (media.canPlayType('application/x-mpegURL').replace(/no/, '') || media.canPlayType('application/vnd.apple.mpegURL').replace(/no/, '')) { return '.m3u8'; } // Chrome or IE with plugin installed if (canPlayWebm() && !$.browser.mozilla) { return '.webm'; } return '.mp4'; } function changeStream(ticks, params) { var element = currentMediaElement; if (canClientSeek && params == null) { element.currentTime = ticks / (1000 * 10000); } else { params = params || {}; var currentSrc = element.currentSrc; currentSrc = replaceQueryString(currentSrc, 'starttimeticks', ticks); if (params.AudioStreamIndex != null) { currentSrc = replaceQueryString(currentSrc, 'AudioStreamIndex', params.AudioStreamIndex); } if (params.SubtitleStreamIndex != null) { currentSrc = replaceQueryString(currentSrc, 'SubtitleStreamIndex', (params.SubtitleStreamIndex == -1 ? '' : params.SubtitleStreamIndex)); } var maxWidth = params.MaxWidth || getParameterByName('MaxWidth', currentSrc); var audioStreamIndex = params.AudioStreamIndex == null ? getParameterByName('AudioStreamIndex', currentSrc) : params.AudioStreamIndex; var subtitleStreamIndex = params.SubtitleStreamIndex == null ? getParameterByName('SubtitleStreamIndex', currentSrc) : params.SubtitleStreamIndex; var videoBitrate = parseInt(getParameterByName('VideoBitrate', currentSrc) || '0'); var audioBitrate = parseInt(getParameterByName('AudioBitrate', currentSrc) || '0'); var bitrate = params.Bitrate || (videoBitrate + audioBitrate); var transcodingExtension = getTranscodingExtension(); var finalParams = getFinalVideoParams(currentItem, maxWidth, bitrate, audioStreamIndex, subtitleStreamIndex, transcodingExtension); currentSrc = replaceQueryString(currentSrc, 'MaxWidth', finalParams.maxWidth); currentSrc = replaceQueryString(currentSrc, 'VideoBitrate', finalParams.videoBitrate); currentSrc = replaceQueryString(currentSrc, 'AudioBitrate', finalParams.audioBitrate); currentSrc = replaceQueryString(currentSrc, 'Static', finalParams.isStatic); if (finalParams.isStatic) { currentSrc = currentSrc.replace('.webm', '.mp4').replace('.m3u8', '.mp4'); } else { currentSrc = currentSrc.replace('.mp4', transcodingExtension); } clearProgressInterval(); $(element).off('ended.playbackstopped').off('ended.playnext').on("play.onceafterseek", function () { updateCanClientSeek(this); $(this).off('play.onceafterseek').on('ended.playbackstopped', onPlaybackStopped).on('ended.playnext', playNextAfterEnded); startProgressInterval(currentItem.Id); sendProgressUpdate(currentItem.Id); }); ApiClient.stopActiveEncodings().done(function () { startTimeTicksOffset = ticks; element.src = currentSrc; }); } } function onPositionSliderChange() { isPositionSliderActive = false; var newPercent = parseInt(this.value); var newPositionTicks = (newPercent / 100) * currentItem.RunTimeTicks; changeStream(Math.floor(newPositionTicks)); } $(function () { muteButton = $('#muteButton'); unmuteButton = $('#unmuteButton'); currentTimeElement = $('.currentTime'); volumeSlider = $('.volumeSlider').on('slidestop', function () { var vol = this.value; updateVolumeButtons(vol); currentMediaElement.volume = vol; }); positionSlider = $(".positionSlider").on('slidestart', function () { isPositionSliderActive = true; }).on('slidestop', onPositionSliderChange); $('#chaptersFlyout').on('click', '.mediaFlyoutOption', function () { var ticks = parseInt(this.getAttribute('data-positionticks')); changeStream(ticks); hideFlyout($('#chaptersFlyout')); }); $('#audioTracksFlyout').on('click', '.mediaFlyoutOption', function () { if (!$(this).hasClass('selectedMediaFlyoutOption')) { var index = parseInt(this.getAttribute('data-index')); changeStream(getCurrentTicks(), { AudioStreamIndex: index }); } hideFlyout($('#audioTracksFlyout')); }); $('#subtitleFlyout').on('click', '.mediaFlyoutOption', function () { if (!$(this).hasClass('selectedMediaFlyoutOption')) { var index = parseInt(this.getAttribute('data-index')); changeStream(getCurrentTicks(), { SubtitleStreamIndex: index }); } hideFlyout($('#subtitleFlyout')); }); $('#qualityFlyout').on('click', '.mediaFlyoutOption', function () { if (!$(this).hasClass('selectedMediaFlyoutOption')) { var maxWidth = parseInt(this.getAttribute('data-maxwidth')); var bitrate = parseInt(this.getAttribute('data-bitrate')); localStorage.setItem('preferredVideoBitrate', bitrate); changeStream(getCurrentTicks(), { MaxWidth: maxWidth, Bitrate: bitrate }); } hideFlyout($('#qualityFlyout')); }); $('#channelsFlyout').on('click', '.mediaFlyoutOption', function () { if (!$(this).hasClass('selectedMediaFlyoutOption')) { var channelId = this.getAttribute('data-channelid'); self.playById(channelId); } hideFlyout($('#channelsFlyout')); }); }); function endsWith(text, pattern) { text = text.toLowerCase(); pattern = pattern.toLowerCase(); var d = text.length - pattern.length; return d >= 0 && text.lastIndexOf(pattern) === d; } function setCurrentTime(ticks, item, updateSlider) { // Convert to ticks ticks = Math.floor(ticks); var timeText = Dashboard.getDisplayTime(ticks); if (curentDurationTicks) { timeText += " / " + Dashboard.getDisplayTime(curentDurationTicks); if (updateSlider) { var percent = ticks / curentDurationTicks; percent *= 100; positionSlider.val(percent).slider('enable').slider('refresh'); } } else { positionSlider.slider('disable').slider('refresh'); } currentTimeElement.html(timeText); } function playAudio(item, startPositionTicks) { startPositionTicks = startPositionTicks || 0; var baseParams = { audioChannels: 2, audioBitrate: 128000, StartTimeTicks: startPositionTicks }; var mp3Url = ApiClient.getUrl('Audio/' + item.Id + '/stream.mp3', $.extend({}, baseParams, { audioCodec: 'mp3' })); var aacUrl = ApiClient.getUrl('Audio/' + item.Id + '/stream.aac', $.extend({}, baseParams, { audioCodec: 'aac' })); var webmUrl = ApiClient.getUrl('Audio/' + item.Id + '/stream.webm', $.extend({}, baseParams, { audioCodec: 'Vorbis' })); var mediaStreams = item.MediaStreams || []; var isStatic = false; var seekParam = isStatic && startPositionTicks ? '#t=' + (startPositionTicks / 10000000) : ''; for (var i = 0, length = mediaStreams.length; i < length; i++) { var stream = mediaStreams[i]; if (stream.Type == "Audio") { // Stream statically when possible if (endsWith(item.Path, ".aac") && stream.BitRate <= 256000) { aacUrl += "&static=true" + seekParam; isStatic = true; } else if (endsWith(item.Path, ".mp3") && stream.BitRate <= 256000) { mp3Url += "&static=true" + seekParam; isStatic = true; } break; } } startTimeTicksOffset = isStatic ? 0 : startPositionTicks; var html = ''; var requiresControls = $.browser.android || ($.browser.webkit && !$.browser.chrome); // Can't autoplay in these browsers so we need to use the full controls if (requiresControls) { html += '