diff --git a/dashboard-ui/scripts/itemdetailpage.js b/dashboard-ui/scripts/itemdetailpage.js index 60c840574d..6b681edb42 100644 --- a/dashboard-ui/scripts/itemdetailpage.js +++ b/dashboard-ui/scripts/itemdetailpage.js @@ -310,7 +310,7 @@ function renderDetails(page, item, context) { renderSimilarItems(page, item, context); - renderSiblingLinks(page, item); + renderSiblingLinks(page, item, context); if (item.Taglines && item.Taglines.length) { $('#itemTagline', page).html(item.Taglines[0]).show(); @@ -479,10 +479,9 @@ }); } - function renderSiblingLinks(page, item) { + function renderSiblingLinks(page, item, context) { $('.lnkSibling', page).addClass('hide'); - return; if ((item.Type != "Episode" && item.Type != "Season" && item.Type != "Audio") || item.IndexNumber == null) { return; @@ -515,6 +514,8 @@ }); } + context = context || ''; + promise.done(function (result) { for (var i = 0, length = result.Items.length; i < length; i++) { @@ -527,11 +528,11 @@ if (curr.IndexNumber < item.IndexNumber) { - $('.lnkPreviousItem', page).removeClass('hide').attr('href', 'itemdetails.html?id=' + curr.Id); + $('.lnkPreviousItem', page).removeClass('hide').attr('href', 'itemdetails.html?id=' + curr.Id + '&context=' + context); } else if (curr.IndexNumber > item.IndexNumber) { - $('.lnkNextItem', page).removeClass('hide').attr('href', 'itemdetails.html?id=' + curr.Id); + $('.lnkNextItem', page).removeClass('hide').attr('href', 'itemdetails.html?id=' + curr.Id + '&context=' + context); } } }); diff --git a/dashboard-ui/scripts/mediaplayer-video.js b/dashboard-ui/scripts/mediaplayer-video.js index 7d9b57bc74..835a7c18f4 100644 --- a/dashboard-ui/scripts/mediaplayer-video.js +++ b/dashboard-ui/scripts/mediaplayer-video.js @@ -14,8 +14,6 @@ var self = mediaPlayer; - var currentItem; - var currentMediaSource; var timeout; var video; var initialVolume; @@ -30,6 +28,8 @@ var isPositionSliderActive; var currentTimeElement; + self.currentSubtitleStreamIndex = null; + self.initVideoPlayer = function () { video = playVideo(item, mediaSource, startPosition); @@ -176,7 +176,7 @@ var newPercent = parseInt(this.value); - var newPositionTicks = (newPercent / 100) * currentMediaSource.RunTimeTicks; + var newPositionTicks = (newPercent / 100) * self.currentMediaSource.RunTimeTicks; self.changeStream(Math.floor(newPositionTicks)); } @@ -405,7 +405,7 @@ var currentTicks = self.getCurrentTicks(); - var chapters = currentItem.Chapters || []; + var chapters = self.currentItem.Chapters || []; for (var i = 0, length = chapters.length; i < length; i++) { @@ -430,7 +430,7 @@ if (chapter.ImageTag) { - imgUrl = ApiClient.getScaledImageUrl(currentItem.Id, { + imgUrl = ApiClient.getScaledImageUrl(self.currentItem.Id, { maxWidth: 100, tag: chapter.ImageTag, type: "Chapter", @@ -460,7 +460,7 @@ function getAudioTracksHtml() { - var streams = currentMediaSource.MediaStreams.filter(function (currentStream) { + var streams = self.currentMediaSource.MediaStreams.filter(function (currentStream) { return currentStream.Type == "Audio"; }); @@ -528,11 +528,11 @@ function getSubtitleTracksHtml() { - var streams = currentMediaSource.MediaStreams.filter(function (currentStream) { + var streams = self.currentMediaSource.MediaStreams.filter(function (currentStream) { return currentStream.Type == "Subtitle"; }); - var currentIndex = getParameterByName('SubtitleStreamIndex', video.currentSrc) || -1; + var currentIndex = self.currentSubtitleStreamIndex; var html = ''; @@ -610,7 +610,7 @@ var currentAudioStreamIndex = getParameterByName('AudioStreamIndex', video.currentSrc); - var options = getVideoQualityOptions(currentMediaSource.MediaStreams, currentAudioStreamIndex, transcodingExtension); + var options = getVideoQualityOptions(self.currentMediaSource.MediaStreams, currentAudioStreamIndex, transcodingExtension); if (isStatic) { options[0].name = "Direct"; @@ -688,7 +688,7 @@ options.push({ name: '720p - 4Mbps', maxWidth: 1280, bitrate: 4000000 }); options.push({ name: '720p - 3Mbps', maxWidth: 1280, bitrate: 3000000 }); options.push({ name: '720p - 2Mbps', maxWidth: 1280, bitrate: 2000000 }); - + // The extra 1 is because they're keyed off the bitrate value options.push({ name: '720p - 1Mbps', maxWidth: 1280, bitrate: 1000001 }); } @@ -724,16 +724,28 @@ var mediaStreams = mediaSource.MediaStreams || []; + var subtitleStreams = mediaStreams.filter(function (s) { + return s.Type == 'Subtitle'; + }); + + var selectedSubtitleStream = subtitleStreams.filter(function (s) { + return s.Index == mediaSource.DefaultSubtitleStreamIndex; + + })[0]; + var baseParams = { audioChannels: 2, StartTimeTicks: startPosition, - SubtitleStreamIndex: mediaSource.DefaultSubtitleStreamIndex, AudioStreamIndex: mediaSource.DefaultAudioStreamIndex, deviceId: ApiClient.deviceId(), Static: false, mediaSourceId: mediaSource.Id }; + if (selectedSubtitleStream && selectedSubtitleStream.IsGraphicalSubtitleStream) { + baseParams.SubtitleStreamIndex = mediaSource.DefaultSubtitleStreamIndex; + } + var mp4Quality = getVideoQualityOptions(mediaStreams).filter(function (opt) { return opt.selected; })[0]; @@ -830,6 +842,22 @@ html += ''; + var textStreams = subtitleStreams.filter(function (s) { + return !s.IsGraphicalSubtitleStream; + }); + + for (var i = 0, length = textStreams.length; i < length; i++) { + + var textStream = textStreams[i]; + var textStreamUrl = ApiClient.getUrl('Videos/' + item.Id + '/Subtitles/' + textStream.Index + '/Stream.vtt', { + mediaSourceId: mediaSource.Id + }); + + var defaultAttribute = i.Index == mediaSource.DefaultSubtitleStreamIndex ? ' default' : ''; + + html += ''; + } + html += ''; var mediaPlayer = $("#mediaPlayer").show(); @@ -846,17 +874,15 @@ $('#video-qualityButton', videoControls).show(); - if (mediaStreams.filter(function (i) { - return i.Type == "Audio"; + if (mediaStreams.filter(function (s) { + return s.Type == "Audio"; }).length) { $('#video-audioTracksButton', videoControls).show(); } else { $('#video-audioTracksButton', videoControls).hide(); } - if (mediaStreams.filter(function (i) { - return i.Type == "Subtitle"; - }).length) { + if (subtitleStreams.length) { $('#video-subtitleButton', videoControls).show().prop("disabled", false); } else { $('#video-subtitleButton', videoControls).hide().prop("disabled", true);; @@ -1021,8 +1047,7 @@ fullscreenExited = false; - currentItem = item; - currentMediaSource = mediaSource; + self.currentSubtitleStreamIndex = mediaSource.DefaultSubtitleStreamIndex; return video[0]; } diff --git a/dashboard-ui/scripts/mediaplayer.js b/dashboard-ui/scripts/mediaplayer.js index fa988b2f37..3d6d8a6769 100644 --- a/dashboard-ui/scripts/mediaplayer.js +++ b/dashboard-ui/scripts/mediaplayer.js @@ -7,11 +7,12 @@ var testableVideoElement = document.createElement('video'); var currentMediaElement; var currentProgressInterval; - var currentItem; - var currentMediaSource; var canClientSeek; var currentPlaylistIndex = 0; + self.currentItem = null; + self.currentMediaSource = null; + self.currentDurationTicks = null; self.startTimeTicksOffset = null; @@ -110,7 +111,7 @@ var transcodingExtension = self.getTranscodingExtension(); var isStatic; - if (currentItem.MediaType == "Video") { + if (self.currentItem.MediaType == "Video") { if (params.AudioStreamIndex != null) { currentSrc = replaceQueryString(currentSrc, 'AudioStreamIndex', params.AudioStreamIndex); @@ -121,12 +122,12 @@ 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 subtitleStreamIndex = self.currentSubtitleStreamIndex; var videoBitrate = parseInt(getParameterByName('VideoBitrate', currentSrc) || '0'); var audioBitrate = parseInt(getParameterByName('AudioBitrate', currentSrc) || '0'); var bitrate = params.Bitrate || (videoBitrate + audioBitrate); - var finalParams = self.getFinalVideoParams(currentMediaSource, maxWidth, bitrate, audioStreamIndex, subtitleStreamIndex, transcodingExtension); + var finalParams = self.getFinalVideoParams(self.currentMediaSource, maxWidth, bitrate, audioStreamIndex, subtitleStreamIndex, transcodingExtension); currentSrc = replaceQueryString(currentSrc, 'MaxWidth', finalParams.maxWidth); currentSrc = replaceQueryString(currentSrc, 'VideoBitrate', finalParams.videoBitrate); @@ -167,7 +168,7 @@ }); - if (currentItem.MediaType == "Video") { + if (self.currentItem.MediaType == "Video") { ApiClient.stopActiveEncodings().done(function () { self.startTimeTicksOffset = ticks; @@ -212,7 +213,7 @@ currentTimeElement.html(timeText); } - var state = self.getPlayerStateInternal(currentMediaElement, currentItem, currentMediaSource); + var state = self.getPlayerStateInternal(currentMediaElement, self.currentItem, self.currentMediaSource); $(self).trigger('positionchange', [state]); }; @@ -242,7 +243,7 @@ return false; } - if (subtitleStream) { + if (subtitleStream && subtitleStream.IsGraphicalSubtitleStream) { console.log('Transcoding because subtitles are required'); return false; } @@ -312,7 +313,7 @@ self.canQueueMediaType = function (mediaType) { - return currentItem && currentItem.MediaType == mediaType; + return self.currentItem && self.currentItem.MediaType == mediaType; }; function translateItemsForPlayback(items) { @@ -449,21 +450,21 @@ if (item.MediaType === "Video") { - currentItem = item; - currentMediaSource = getOptimalMediaSource(item.MediaType, item.MediaSources); + self.currentItem = item; + self.currentMediaSource = getOptimalMediaSource(item.MediaType, item.MediaSources); - videoPlayer(self, item, currentMediaSource, startPosition, user); + videoPlayer(self, item, self.currentMediaSource, startPosition, user); mediaElement = self.initVideoPlayer(); - self.currentDurationTicks = currentMediaSource.RunTimeTicks; + self.currentDurationTicks = self.currentMediaSource.RunTimeTicks; } else if (item.MediaType === "Audio") { - currentItem = item; - currentMediaSource = getOptimalMediaSource(item.MediaType, item.MediaSources); + self.currentItem = item; + self.currentMediaSource = getOptimalMediaSource(item.MediaType, item.MediaSources); - mediaElement = playAudio(item, currentMediaSource, startPosition); + mediaElement = playAudio(item, self.currentMediaSource, startPosition); - self.currentDurationTicks = currentMediaSource.RunTimeTicks; + self.currentDurationTicks = self.currentMediaSource.RunTimeTicks; } else { throw new Error("Unrecognized media type"); @@ -485,7 +486,7 @@ var mediaControls = $("#videoControls"); - var state = self.getPlayerStateInternal(currentMediaElement, item, currentMediaSource); + var state = self.getPlayerStateInternal(currentMediaElement, item, self.currentMediaSource); var url = ""; @@ -915,7 +916,7 @@ elem.pause(); - var isVideo = currentItem.MediaType == "Video"; + var isVideo = self.currentItem.MediaType == "Video"; $(elem).off("ended.playnext").on("ended", function () { @@ -927,8 +928,8 @@ elem.src = ""; currentMediaElement = null; - currentItem = null; - currentMediaSource = null; + self.currentItem = null; + self.currentMediaSource = null; }).trigger("ended"); @@ -948,7 +949,7 @@ var deferred = $.Deferred(); - var result = self.getPlayerStateInternal(currentMediaElement, currentItem, currentMediaSource); + var result = self.getPlayerStateInternal(currentMediaElement, self.currentItem, self.currentMediaSource); deferred.resolveWith(null, [result]); @@ -977,11 +978,7 @@ if (audioStreamIndex) { state.PlayState.AudioStreamIndex = parseInt(audioStreamIndex); } - var subtitleStreamIndex = getParameterByName('SubtitleStreamIndex', currentSrc); - - if (subtitleStreamIndex) { - state.PlayState.SubtitleStreamIndex = parseInt(subtitleStreamIndex); - } + state.PlayState.SubtitleStreamIndex = self.currentSubtitleStreamIndex; state.PlayState.PlayMethod = getParameterByName('static', currentSrc) == 'true' ? 'DirectStream' : @@ -1076,7 +1073,7 @@ self.saveVolume(playerElement.volume); - var state = self.getPlayerStateInternal(playerElement, currentItem, currentMediaSource); + var state = self.getPlayerStateInternal(playerElement, self.currentItem, self.currentMediaSource); $(self).trigger('volumechange', [state]); }; @@ -1091,8 +1088,8 @@ clearProgressInterval(); - var item = currentItem; - var mediaSource = currentMediaSource; + var item = self.currentItem; + var mediaSource = self.currentMediaSource; if (item.MediaType == "Video") { ApiClient.stopActiveEncodings(); @@ -1109,7 +1106,7 @@ self.onPlaystateChange = function (playerElement) { - var state = self.getPlayerStateInternal(playerElement, currentItem, currentMediaSource); + var state = self.getPlayerStateInternal(playerElement, self.currentItem, self.currentMediaSource); $(self).trigger('playstatechange', [state]); }; @@ -1117,7 +1114,7 @@ $(window).on("beforeunload popstate", function () { // Try to report playback stopped before the browser closes - if (currentItem && currentMediaElement && currentProgressInterval) { + if (self.currentItem && currentMediaElement && currentProgressInterval) { self.onPlaybackStopped.call(currentMediaElement); } @@ -1125,7 +1122,7 @@ function sendProgressUpdate() { - var state = self.getPlayerStateInternal(currentMediaElement, currentItem, currentMediaSource); + var state = self.getPlayerStateInternal(currentMediaElement, self.currentItem, self.currentMediaSource); var info = { QueueableMediaTypes: state.NowPlayingItem.MediaType,