diff --git a/dashboard-ui/scripts/itemdetailpage.js b/dashboard-ui/scripts/itemdetailpage.js
index 8ca1387f14..e5b4ce852f 100644
--- a/dashboard-ui/scripts/itemdetailpage.js
+++ b/dashboard-ui/scripts/itemdetailpage.js
@@ -83,6 +83,12 @@
$('#playButtonContainer', page).hide();
$('#playExternalButtonContainer', page).hide();
}
+
+ if (item.LocalTrailerCount && item.LocationType !== "Offline") {
+ $('#trailerButtonContainer', page).show();
+ } else {
+ $('#trailerButtonContainer', page).hide();
+ }
$(".autoNumeric").autoNumeric('init');
@@ -189,7 +195,7 @@
$('#scenesCollapsible', page).show();
renderScenes(page, item, 4);
}
- if (!item.LocalTrailerCount && !item.RemoteTrailers.length) {
+ if (item.LocalTrailerCount || !item.RemoteTrailers.length) {
$('#trailersCollapsible', page).addClass('hide');
} else {
$('#trailersCollapsible', page).removeClass('hide');
@@ -1120,6 +1126,24 @@
LibraryBrowser.showPlayMenu(this, currentItem.Id, currentItem.Type, mediaType, userdata.PlaybackPositionTicks);
});
+ $('#btnPlayTrailer', page).on('click', function () {
+
+ ApiClient.getLocalTrailers(Dashboard.getCurrentUserId(), currentItem.Id).done(function (trailers) {
+
+ var trailer = trailers[0];
+ var userdata = trailer.UserData || {};
+
+ var mediaType = trailer.MediaType;
+
+ if (trailer.Type == "MusicArtist" || trailer.Type == "MusicAlbum") {
+ mediaType = "Audio";
+ }
+
+ LibraryBrowser.showPlayMenu(this, trailer.Id, trailer.Type, mediaType, userdata.PlaybackPositionTicks);
+
+ });
+ });
+
$('#btnPlayExternal', page).on('click', function () {
ApiClient.markPlayed(Dashboard.getCurrentUserId(), currentItem.Id, new Date());
diff --git a/dashboard-ui/scripts/librarybrowser.js b/dashboard-ui/scripts/librarybrowser.js
index f1edb05ed2..8c0473b59d 100644
--- a/dashboard-ui/scripts/librarybrowser.js
+++ b/dashboard-ui/scripts/librarybrowser.js
@@ -1417,7 +1417,7 @@
return html;
},
- getPagingHtml: function (query, totalRecordCount, updatePageSizeSetting) {
+ getPagingHtml: function (query, totalRecordCount, updatePageSizeSetting, pageSizes) {
if (query.Limit && updatePageSizeSetting !== false) {
localStorage.setItem('pagesize', query.Limit);
@@ -1477,13 +1477,11 @@
}
}
- options += getOption(20);
- options += getOption(50);
- options += getOption(100);
- options += getOption(200);
- options += getOption(300);
- options += getOption(400);
- options += getOption(500);
+ pageSizes = pageSizes || [20, 50, 100, 200, 300, 400, 500];
+
+ for (var j = 0, length = pageSizes.length; j < length; j++) {
+ options += getOption(pageSizes[j]);
+ }
// Add styles to defeat jquery mobile
html += '
';
diff --git a/dashboard-ui/scripts/livetvchannels.js b/dashboard-ui/scripts/livetvchannels.js
index 1fc89c971b..a77cd7871d 100644
--- a/dashboard-ui/scripts/livetvchannels.js
+++ b/dashboard-ui/scripts/livetvchannels.js
@@ -1,5 +1,10 @@
(function ($, document, apiClient) {
+ var query = {
+
+ StartIndex: 0
+ };
+
function getChannelsHtml(channels) {
return LibraryBrowser.getPosterViewHtml({
@@ -10,23 +15,64 @@
});
}
- function renderChannels(page, channels) {
+ function renderChannels(page, result) {
- $('#items', page).html(getChannelsHtml(channels)).trigger('create');
+ $('.listTopPaging', page).html(LibraryBrowser.getPagingHtml(query, result.TotalRecordCount, true)).trigger('create');
+
+ var html = getChannelsHtml(result.Items);
+
+ html += LibraryBrowser.getPagingHtml(query, result.TotalRecordCount);
+
+ $('#items', page).html(html).trigger('create');
+
+ $('.selectPage', page).on('change', function () {
+ query.StartIndex = (parseInt(this.value) - 1) * query.Limit;
+ reloadItems(page);
+ });
+
+ $('.btnNextPage', page).on('click', function () {
+ query.StartIndex += query.Limit;
+ reloadItems(page);
+ });
+
+ $('.btnPreviousPage', page).on('click', function () {
+ query.StartIndex -= query.Limit;
+ reloadItems(page);
+ });
+
+ $('.selectPageSize', page).on('change', function () {
+ query.Limit = parseInt(this.value);
+ query.StartIndex = 0;
+ reloadItems(page);
+ });
+
+ LibraryBrowser.saveQueryValues('movies', query);
+ }
+
+ function reloadItems(page) {
+ apiClient.getLiveTvChannels(query).done(function (result) {
+
+ renderChannels(page, result);
+ });
}
$(document).on('pagebeforeshow', "#liveTvChannelsPage", function () {
var page = this;
- apiClient.getLiveTvChannels({
-
- userId: Dashboard.getCurrentUserId()
+ var limit = LibraryBrowser.getDefaultPageSize();
- }).done(function (result) {
+ // If the default page size has changed, the start index will have to be reset
+ if (limit != query.Limit) {
+ query.Limit = limit;
+ query.StartIndex = 0;
+ }
- renderChannels(page, result.Items);
- });
+ query.UserId = Dashboard.getCurrentUserId();
+
+ LibraryBrowser.loadSavedQueryValues('movies', query);
+
+ reloadItems(page);
});
})(jQuery, document, ApiClient);
\ No newline at end of file
diff --git a/dashboard-ui/scripts/livetvguide.js b/dashboard-ui/scripts/livetvguide.js
index 9ba886d032..d0af393ef0 100644
--- a/dashboard-ui/scripts/livetvguide.js
+++ b/dashboard-ui/scripts/livetvguide.js
@@ -8,6 +8,12 @@
var gridLocalEndDateMs;
var currentDate;
+
+ var channelQuery = {
+
+ StartIndex: 0,
+ Limit: 20
+ };
var channelsPromise;
function normalizeDateToTimeslot(date) {
@@ -25,16 +31,19 @@
return date;
}
+
+ function reloadChannels(page) {
+ channelsPromise = null;
+ reloadGuide(page);
+ }
function reloadGuide(page) {
Dashboard.showLoadingMsg();
-
- channelsPromise = channelsPromise || apiClient.getLiveTvChannels({
- userId: Dashboard.getCurrentUserId()
+ channelQuery.userId = Dashboard.getCurrentUserId();
- });;
+ channelsPromise = channelsPromise || apiClient.getLiveTvChannels(channelQuery);
var date = currentDate;
@@ -42,23 +51,45 @@
nextDay.setHours(0, 0, 0, 0);
nextDay.setDate(nextDay.getDate() + 1);
- var promise1 = channelsPromise;
- var promise2 = apiClient.getLiveTvPrograms({
+ channelsPromise.done(function(channelsResult) {
- UserId: Dashboard.getCurrentUserId(),
- MaxStartDate: nextDay.toISOString(),
- MinEndDate: date.toISOString()
-
- });
-
- $.when(promise1, promise2).done(function (response1, response2) {
-
- var channels = response1[0].Items;
- var programs = response2[0].Items;
-
- renderGuide(page, date, channels, programs);
+ apiClient.getLiveTvPrograms({
+ UserId: Dashboard.getCurrentUserId(),
+ MaxStartDate: nextDay.toISOString(),
+ MinEndDate: date.toISOString(),
+ channelIds: channelsResult.Items.map(function(c) {
+ return c.Id;
+ }).join(',')
+
+ }).done(function(programsResult) {
+
+ renderGuide(page, date, channelsResult.Items, programsResult.Items);
+ Dashboard.hideLoadingMsg();
+ });
- Dashboard.hideLoadingMsg();
+ var channelPagingHtml = LibraryBrowser.getPagingHtml(channelQuery, channelsResult.TotalRecordCount, false, [10, 20, 30, 50, 100]);
+ $('.channelPaging', page).html(channelPagingHtml).trigger('create');
+
+ $('.selectPage', page).on('change', function () {
+ channelQuery.StartIndex = (parseInt(this.value) - 1) * channelQuery.Limit;
+ reloadChannels(page);
+ });
+
+ $('.btnNextPage', page).on('click', function () {
+ channelQuery.StartIndex += channelQuery.Limit;
+ reloadChannels(page);
+ });
+
+ $('.btnPreviousPage', page).on('click', function () {
+ channelQuery.StartIndex -= channelQuery.Limit;
+ reloadChannels(page);
+ });
+
+ $('.selectPageSize', page).on('change', function () {
+ channelQuery.Limit = parseInt(this.value);
+ channelQuery.StartIndex = 0;
+ reloadChannels(page);
+ });
});
}
@@ -222,7 +253,7 @@
html += '
';
html += program.Name;
-
+
if (program.IsRepeat) {
html += ' (R)';
}
@@ -373,7 +404,7 @@
}
var elem = $('#selectDate', page).html(html).selectmenu('refresh');
-
+
if (currentDate) {
elem.val(currentDate.getTime()).selectmenu('refresh');
}
@@ -394,8 +425,8 @@
onProgramGridScroll(page, this);
});
- $('#selectDate', page).on('change', function() {
-
+ $('#selectDate', page).on('change', function () {
+
var date = new Date();
date.setTime(parseInt(this.value));
diff --git a/dashboard-ui/scripts/mediaplayer.js b/dashboard-ui/scripts/mediaplayer.js
index 528dd4f5ec..d14179a993 100644
--- a/dashboard-ui/scripts/mediaplayer.js
+++ b/dashboard-ui/scripts/mediaplayer.js
@@ -17,7 +17,7 @@
var unmuteButton;
var startTimeTicksOffset;
var curentDurationTicks;
- var isStaticStream;
+ var canClientSeek;
var culturesPromise;
var timeout;
var idleState = true;
@@ -28,6 +28,11 @@
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;
@@ -176,13 +181,14 @@
currentProgressInterval = null;
}
}
-
+
function getTranscodingExtension() {
var media = testableVideoElement;
-
+
// safari
- if (media.canPlayType('application/x-mpegURL').replace(/no/, '')) {
+ if (media.canPlayType('application/x-mpegURL').replace(/no/, '') ||
+ media.canPlayType('application/vnd.apple.mpegURL').replace(/no/, '')) {
return '.m3u8';
}
@@ -190,7 +196,7 @@
if (media.canPlayType('video/webm').replace(/no/, '')) {
return '.webm';
}
-
+
return '.mp4';
}
@@ -198,7 +204,7 @@
var element = currentMediaElement;
- if (isStaticStream && params == null) {
+ if (canClientSeek && params == null) {
element.currentTime = ticks / (1000 * 10000);
@@ -207,7 +213,6 @@
params = params || {};
var currentSrc = element.currentSrc;
- console.log(currentSrc);
currentSrc = replaceQueryString(currentSrc, 'starttimeticks', ticks);
if (params.AudioStreamIndex != null) {
@@ -232,7 +237,7 @@
currentSrc = replaceQueryString(currentSrc, 'VideoCodec', params.VideoCodec);
}
if (params.Static != null) {
-
+
currentSrc = replaceQueryString(currentSrc, 'Static', params.Static);
if (params.Static == 'true') {
@@ -246,6 +251,8 @@
$(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);
@@ -256,7 +263,6 @@
ApiClient.stopActiveEncodings().done(function () {
startTimeTicksOffset = ticks;
-
element.src = currentSrc;
});
}
@@ -333,22 +339,20 @@
var videoBitrate = parseInt(this.getAttribute('data-videobitrate'));
var audioBitrate = parseInt(this.getAttribute('data-audiobitrate'));
- var mp4AudioCodec = this.getAttribute('data-mp4audio');
- var mp4VideoCodec = this.getAttribute('data-mp4video');
+ var audioCodec = this.getAttribute('data-audiocodec');
+ var videoCodec = this.getAttribute('data-videocodec');
var isStatic = this.getAttribute('data-static');
localStorage.setItem('preferredVideoBitrate', videoBitrate + audioBitrate);
- var isMp4 = currentMediaElement.currentSrc.toLowerCase().indexOf('.mp4') != -1;
-
changeStream(getCurrentTicks(), {
MaxWidth: maxWidth,
VideoBitrate: videoBitrate,
AudioBitrate: audioBitrate,
Static: isStatic,
- AudioCodec: isMp4 ? mp4AudioCodec : null,
- VideoCodec: isMp4 ? mp4VideoCodec : null
+ AudioCodec: audioCodec,
+ VideoCodec: videoCodec
});
}
@@ -399,15 +403,16 @@
currentTimeElement.html(timeText);
}
- function playAudio(item, params) {
+ function playAudio(item, startPositionTicks) {
+
+ startPositionTicks = startPositionTicks || 0;
var baseParams = {
audioChannels: 2,
- audioBitrate: 128000
+ audioBitrate: 128000,
+ StartTimeTicks: startPositionTicks
};
- $.extend(baseParams, params);
-
var mp3Url = ApiClient.getUrl('Audio/' + item.Id + '/stream.mp3', $.extend({}, baseParams, {
audioCodec: 'mp3'
}));
@@ -422,6 +427,9 @@
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];
@@ -430,16 +438,19 @@
// Stream statically when possible
if (endsWith(item.Path, ".aac") && stream.BitRate <= 256000) {
- aacUrl += "&static=true";
+ aacUrl += "&static=true" + seekParam;
+ isStatic = true;
}
else if (endsWith(item.Path, ".mp3") && stream.BitRate <= 256000) {
- mp3Url += "&static=true";
+ mp3Url += "&static=true" + seekParam;
+ isStatic = true;
}
break;
}
-
}
+ startTimeTicksOffset = isStatic ? 0 : startPositionTicks;
+
var html = '';
var requiresControls = $.browser.android || ($.browser.webkit && !$.browser.chrome);
@@ -498,8 +509,7 @@
audioElement.hide();
}
- var duration = this.duration;
- isStaticStream = duration && !isNaN(duration) && duration != Number.POSITIVE_INFINITY && duration != Number.NEGATIVE_INFINITY;
+ updateCanClientSeek(this);
audioElement.off("play.once");
@@ -549,7 +559,11 @@
return false;
}
- var extension = item.Path.substring(item.Path.lastIndexOf('.') + 1);
+ var extension = item.Path.substring(item.Path.lastIndexOf('.') + 1).toLowerCase();
+
+ if (extension == 'm4v') {
+ return $.browser.chrome;
+ }
return extension.toLowerCase() == 'mp4';
}
@@ -575,7 +589,7 @@
return true;
}
- function getVideoQualityOptions(item, audioStreamIndex) {
+ function getVideoQualityOptions(item, audioStreamIndex, transcodeExtension) {
var videoStream = (item.MediaStreams || []).filter(function (stream) {
return stream.Type == "Video";
@@ -600,6 +614,13 @@
maxAllowedWidth = videoStream.Width;
}
+ var canPlayVideoCodecDirect = (videoStream ? videoStream.Codec : '').toLowerCase().indexOf('h264') != -1;
+ var canPlayAudioDirect = audioStream ? canPlayAudioStreamDirect(audioStream) : false;
+
+ var videoBitrate = videoStream ? videoStream.BitRate : null;
+ var audioBitrate = audioStream ? audioStream.BitRate : null;
+ var totalBitrate = (videoBitrate || 0) + (audioBitrate || 0);
+
// Some 1080- videos are reported as 1912?
if (maxAllowedWidth >= 1910) {
options.push({ name: '1080p - 10Mbps', maxWidth: 1920, bitrate: 10000000 });
@@ -630,13 +651,6 @@
options.push({ name: '360p', maxWidth: 640, bitrate: 400000 });
options.push({ name: '240p', maxWidth: 426, bitrate: 320000 });
- var canPlayVideoCodecDirect = (videoStream ? videoStream.Codec : '').toLowerCase().indexOf('h264') != -1;
- var canPlayAudioDirect = audioStream ? canPlayAudioStreamDirect(audioStream) : false;
-
- var videoBitrate = videoStream ? videoStream.BitRate : null;
- var audioBitrate = audioStream ? audioStream.BitRate : null;
- var totalBitrate = (videoBitrate || 0) + (audioBitrate || 0);
-
var videoWidth = videoStream ? videoStream.Width : null;
var i, length, option;
@@ -660,15 +674,18 @@
if (canPlayDirect) {
option.isStatic = true;
- option.mp4VideoCodec = 'copy';
- option.mp4AudioCodec = 'copy';
+ option.videoCodec = 'copy';
+ option.audioCodec = 'copy';
}
+ //else if (canPlayVideoCodecDirect && transcodeExtension == '.m3u8') {
+ // option.videoCodec = 'copy';
+ //}
}
option.isStatic = option.isStatic || false;
- option.mp4VideoCodec = option.mp4VideoCodec || 'h264';
- option.mp4AudioCodec = option.mp4AudioCodec || 'aac';
+ option.videoCodec = option.videoCodec || (transcodeExtension == '.webm' ? 'vpx' : 'h264');
+ option.audioCodec = option.audioCodec || (transcodeExtension == '.webm' ? 'Vorbis' : 'aac');
option.videoBitrate = option.bitrate - option.audioBitrate;
}
@@ -681,7 +698,7 @@
options[selectedIndex].selected = true;
var firstOption = options[0];
-
+
if (firstOption.isStatic) {
firstOption.name = 'Direct';
}
@@ -703,39 +720,60 @@
Static: false
};
- var qualityOption = getVideoQualityOptions(item, baseParams.AudioStreamIndex).filter(function (opt) {
+ var mp4Quality = getVideoQualityOptions(item, baseParams.AudioStreamIndex, '.mp4').filter(function (opt) {
return opt.selected;
})[0];
- baseParams.maxWidth = qualityOption.maxWidth;
- baseParams.videoBitrate = qualityOption.videoBitrate;
- baseParams.audioBitrate = qualityOption.audioBitrate;
+ var webmQuality = getVideoQualityOptions(item, baseParams.AudioStreamIndex, '.webm').filter(function (opt) {
+ return opt.selected;
+ })[0];
+
+ var m3U8Quality = getVideoQualityOptions(item, baseParams.AudioStreamIndex, '.m3u8').filter(function (opt) {
+ return opt.selected;
+ })[0];
// Webm must be ahead of mp4 due to the issue of mp4 playing too fast in chrome
var prioritizeWebmOverH264 = $.browser.chrome || $.browser.msie;
- var staticMp4 = qualityOption.isStatic;
+ var isStatic = mp4Quality.isStatic;
+
+ startTimeTicksOffset = isStatic ? 0 : startPosition || 0;
+
+ var seekParam = isStatic && startPosition ? '#t=' + (startPosition / 10000000) : '';
var mp4VideoUrl = ApiClient.getUrl('Videos/' + item.Id + '/stream.mp4', $.extend({}, baseParams, {
- VideoCodec: qualityOption.mp4VideoCodec,
- AudioCodec: qualityOption.mp4AudioCodec,
profile: 'baseline',
level: 3,
- Static: staticMp4
- }));
+ Static: isStatic,
+ maxWidth: mp4Quality.maxWidth,
+ videoBitrate: mp4Quality.videoBitrate,
+ audioBitrate: mp4Quality.audioBitrate,
+ VideoCodec: mp4Quality.videoCodec,
+ AudioCodec: mp4Quality.audioCodec
+
+ })) + seekParam;
var webmVideoUrl = ApiClient.getUrl('Videos/' + item.Id + '/stream.webm', $.extend({}, baseParams, {
+
VideoCodec: 'vpx',
- AudioCodec: 'Vorbis'
- }));
+ AudioCodec: 'Vorbis',
+ maxWidth: webmQuality.maxWidth,
+ videoBitrate: webmQuality.videoBitrate,
+ audioBitrate: webmQuality.audioBitrate
+
+ })) + seekParam;
var hlsVideoUrl = ApiClient.getUrl('Videos/' + item.Id + '/stream.m3u8', $.extend({}, baseParams, {
- VideoCodec: qualityOption.mp4VideoCodec,
- AudioCodec: qualityOption.mp4AudioCodec,
profile: 'baseline',
level: 3,
- timeStampOffsetMs: 0
- }));
+ timeStampOffsetMs: 0,
+ maxWidth: m3U8Quality.maxWidth,
+ videoBitrate: m3U8Quality.videoBitrate,
+ audioBitrate: m3U8Quality.audioBitrate,
+ VideoCodec: m3U8Quality.videoCodec,
+ AudioCodec: m3U8Quality.audioCodec
+
+ })) + seekParam;
var html = '';
@@ -748,19 +786,19 @@
html += '