diff --git a/dashboard-ui/css/images/media/audioflyout.png b/dashboard-ui/css/images/media/audioflyout.png new file mode 100644 index 0000000000..cd13df0978 Binary files /dev/null and b/dashboard-ui/css/images/media/audioflyout.png differ diff --git a/dashboard-ui/css/images/media/subtitleflyout.png b/dashboard-ui/css/images/media/subtitleflyout.png new file mode 100644 index 0000000000..821005c50c Binary files /dev/null and b/dashboard-ui/css/images/media/subtitleflyout.png differ diff --git a/dashboard-ui/css/site.css b/dashboard-ui/css/site.css index 509f2aabdf..4c0b2ba12a 100644 --- a/dashboard-ui/css/site.css +++ b/dashboard-ui/css/site.css @@ -663,7 +663,6 @@ progress { progress::-moz-progress-bar { border-radius: 5px; background-image: -moz-linear-gradient( center bottom, rgb(43,194,83) 37%, rgb(84,240,84) 69% ); - s; } /* Chrome */ @@ -894,6 +893,10 @@ input[type="range"]::-ms-fill-upper { width: 250px; } +.audioTracksFlyout { + width: 250px; +} + .mediaFlyoutOption { display: block; text-decoration: none; @@ -912,13 +915,21 @@ input[type="range"]::-ms-fill-upper { .mediaFlyoutOptionImage { display: inline-block; - width: 40%; + width: 15%; vertical-align: middle; } .mediaFlyoutOptionImage + .mediaFlyoutOptionContent { vertical-align: top; display: inline-block; + width: 85%; + } + +.chaptersFlyout .mediaFlyoutOptionImage { + width: 40%; +} + + .chaptersFlyout .mediaFlyoutOptionImage + .mediaFlyoutOptionContent { width: 60%; } diff --git a/dashboard-ui/scripts/extensions.js b/dashboard-ui/scripts/extensions.js index 753c59b0a9..6e06d47d4f 100644 --- a/dashboard-ui/scripts/extensions.js +++ b/dashboard-ui/scripts/extensions.js @@ -188,11 +188,11 @@ function humane_elapsed(firstDateStr, secondDateStr) { } -function getParameterByName(name) { +function getParameterByName(name, url) { name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]"); var regexS = "[\\?&]" + name + "=([^]*)"; var regex = new RegExp(regexS); - var results = regex.exec(window.location.search); + var results = regex.exec(url || window.location.search); if (results == null) return ""; else diff --git a/dashboard-ui/scripts/mediaplayer.js b/dashboard-ui/scripts/mediaplayer.js index fcea5e514e..56e2200bda 100644 --- a/dashboard-ui/scripts/mediaplayer.js +++ b/dashboard-ui/scripts/mediaplayer.js @@ -18,6 +18,7 @@ var startTimeTicksOffset; var curentDurationTicks; var isStaticStream; + var culturesPromise; self.playing = ''; self.queue = []; @@ -69,6 +70,10 @@ } } + function getCurrentTicks(mediaElement) { + return Math.floor(10000000 * (mediaElement || currentMediaElement).currentTime) + startTimeTicksOffset; + } + function onPlaybackStopped() { currentTimeElement.hide(); @@ -102,9 +107,8 @@ } function sendProgressUpdate(itemId) { - var position = Math.floor(10000000 * currentMediaElement.currentTime) + startTimeTicksOffset; - ApiClient.reportPlaybackProgress(Dashboard.getCurrentUserId(), itemId, position); + ApiClient.reportPlaybackProgress(Dashboard.getCurrentUserId(), itemId, getCurrentTicks()); } function clearProgressInterval() { @@ -115,24 +119,27 @@ } } - function seek(ticks) { + function changeStream(ticks, params) { var element = currentMediaElement; - if (isStaticStream) { + if (isStaticStream && params == null) { element.currentTime = ticks / (1000 * 10000); } else { + params = params || {}; + var currentSrc = element.currentSrc; - if (currentSrc.toLowerCase().indexOf('starttimeticks') == -1) { + currentSrc = replaceQueryString(currentSrc, 'starttimeticks', ticks); - currentSrc += "&starttimeticks=" + ticks; - - } else { - 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); } clearProgressInterval(); @@ -158,7 +165,7 @@ var newPositionTicks = (newPercent / 100) * currentItem.RunTimeTicks; - seek(newPositionTicks); + changeStream(newPositionTicks); } $(function () { @@ -213,10 +220,32 @@ var ticks = parseInt(this.getAttribute('data-positionticks')); - seek(ticks); - + 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')); + }); }); function endsWith(text, pattern) { @@ -385,9 +414,7 @@ if (!isPositionSliderActive) { - var ticks = startTimeTicksOffset + this.currentTime * 1000 * 10000; - - setCurrentTime(ticks, item, true); + setCurrentTime(getCurrentTicks(this), item, true); } }).on("ended.playbackstopped", onPlaybackStopped); @@ -484,6 +511,7 @@ $('#mediaElement', nowPlayingBar).html(html); $('#qualityButton', nowPlayingBar).show(); + $('#audioTracksButton', nowPlayingBar).show(); if (item.MediaStreams.filter(function (i) { @@ -552,9 +580,7 @@ if (!isPositionSliderActive) { - var ticks = startTimeTicksOffset + this.currentTime * 1000 * 10000; - - setCurrentTime(ticks, item, true); + setCurrentTime(getCurrentTicks(this), item, true); } }).on("ended.playbackstopped", onPlaybackStopped); @@ -569,10 +595,14 @@ function getInitialAudioStreamIndex(mediaStreams, user) { + var audioStreams = mediaStreams.filter(function (stream) { + return stream.Type == "Audio"; + }); + if (user.Configuration.AudioLanguagePreference) { - for (var i = 0, length = mediaStreams.length; i < length; i++) { - var mediaStream = mediaStreams[i]; + for (var i = 0, length = audioStreams.length; i < length; i++) { + var mediaStream = audioStreams[i]; if (mediaStream.Type == "Audio" && mediaStream.Language == user.Configuration.AudioLanguagePreference) { return mediaStream.Index; @@ -581,7 +611,8 @@ } } - return null; + // Just use the first audio stream + return audioStreams.length ? audioStreams[0].Index : null; } function getInitialSubtitleStreamIndex(mediaStreams, user) { @@ -951,8 +982,8 @@ var html = ''; - var currentTicks = Math.floor(10000000 * currentMediaElement.currentTime) + startTimeTicksOffset; - + var currentTicks = getCurrentTicks(); + for (var i = 0, length = item.Chapters.length; i < length; i++) { var chapter = item.Chapters[i]; @@ -1004,14 +1035,174 @@ return html; } + function getAudioTracksHtml(item, cultures) { + + var streams = item.MediaStreams.filter(function (i) { + return i.Type == "Audio"; + }); + + var currentIndex = getParameterByName('AudioStreamIndex', currentMediaElement.currentSrc); + + var html = ''; + + for (var i = 0, length = streams.length; i < length; i++) { + + var stream = streams[i]; + + if (stream.Index == currentIndex) { + html += '