(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 currentTimeElement; var currentItem; var muteButton; var unmuteButton; var curentDurationTicks; var canClientSeek; var currentPlaylistIndex = 0; self.isPositionSliderActive; self.volumeSlider; self.startTimeTicksOffset; self.playlist = []; self.updateCanClientSeek = function (elem) { var duration = elem.duration; canClientSeek = duration && !isNaN(duration) && duration != Number.POSITIVE_INFINITY && duration != Number.NEGATIVE_INFINITY; } $(window).on("beforeunload popstate", 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) + self.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; } self.updateVolumeButtons = function (vol) { if (vol) { muteButton.show(); unmuteButton.hide(); } else { muteButton.hide(); unmuteButton.show(); } } self.getCurrentTicks = function (mediaElement) { return Math.floor(10000000 * (mediaElement || currentMediaElement).currentTime) + self.startTimeTicksOffset; } self.onPlaybackStopped = function () { $(this).off('ended.playbackstopped'); currentTimeElement.empty(); var endTime = this.currentTime; clearProgressInterval(); var position = Math.floor(10000000 * endTime) + self.startTimeTicksOffset; ApiClient.reportPlaybackStopped(Dashboard.getCurrentUserId(), currentItem.Id, position); if (currentItem.MediaType == "Video") { ApiClient.stopActiveEncodings(); if (self.isFullScreen()) { self.exitFullScreen(); } self.resetEnhancements(); } } self.playNextAfterEnded = function () { $(this).off('ended.playnext'); self.nextTrack(); } self.startProgressInterval = function (itemId) { clearProgressInterval(); var intervalTime = ApiClient.isWebSocketOpen() ? 2000 : 20000; currentProgressInterval = setInterval(function () { if (currentMediaElement) { sendProgressUpdate(itemId); } }, intervalTime); } function sendProgressUpdate(itemId) { ApiClient.reportPlaybackProgress(Dashboard.getCurrentUserId(), itemId, self.getCurrentTicks(), currentMediaElement.paused, currentMediaElement.volume == 0); } function clearProgressInterval() { if (currentProgressInterval) { clearTimeout(currentProgressInterval); currentProgressInterval = null; } } function canPlayWebm() { return testableVideoElement.canPlayType('video/webm').replace(/no/, ''); } self.getTranscodingExtension = function () { 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'; } self.changeStream = function (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 = self.getTranscodingExtension(); var finalParams = self.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 () { self.updateCanClientSeek(this); $(this).off('play.onceafterseek').on('ended.playbackstopped', self.onPlaybackStopped).on('ended.playnext', self.playNextAfterEnded); self.startProgressInterval(currentItem.Id); sendProgressUpdate(currentItem.Id); }); ApiClient.stopActiveEncodings().done(function () { self.startTimeTicksOffset = ticks; element.src = currentSrc; }); } } function onPositionSliderChange() { self.isPositionSliderActive = false; var newPercent = parseInt(this.value); var newPositionTicks = (newPercent / 100) * currentItem.RunTimeTicks; self.changeStream(Math.floor(newPositionTicks)); } $(function () { muteButton = $('#muteButton'); unmuteButton = $('#unmuteButton'); currentTimeElement = $('.currentTime'); self.volumeSlider = $('.volumeSlider').on('slidestop', function () { console.log("slidestop"); var vol = this.value; self.updateVolumeButtons(vol); currentMediaElement.volume = vol; }); positionSlider = $(".positionSlider").on('slidestart', function () { self.isPositionSliderActive = true; }).on('slidestop', onPositionSliderChange); }); function endsWith(text, pattern) { text = text.toLowerCase(); pattern = pattern.toLowerCase(); var d = text.length - pattern.length; return d >= 0 && text.lastIndexOf(pattern) === d; } self.setCurrentTime = function (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; } } self.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 += '