1
0
Fork 0
mirror of https://github.com/jellyfin/jellyfin-web synced 2025-03-30 19:56:21 +00:00

rework text subtitles

This commit is contained in:
Luke Pulverenti 2014-01-10 08:52:01 -05:00
parent 1447aafd24
commit 8ef09d054d
11 changed files with 300 additions and 146 deletions

View file

@ -436,13 +436,26 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
self.getLiveTvPrograms = function (options) { self.getLiveTvPrograms = function (options) {
var url = self.getUrl("LiveTv/Programs", options || {}); options = options || {};
return self.ajax({ if (options.channelIds) {
type: "GET",
url: url, return self.ajax({
dataType: "json" type: "POST",
}); url: self.getUrl("LiveTv/Programs"),
data: JSON.stringify(options),
contentType: "application/json",
dataType: "json"
});
} else {
return self.ajax({
type: "GET",
url: self.getUrl("LiveTv/Programs", options),
dataType: "json"
});
}
}; };
self.getLiveTvRecordings = function (options) { self.getLiveTvRecordings = function (options) {

View file

@ -133,12 +133,12 @@
.channelTimeslotHeader { .channelTimeslotHeader {
position: absolute; position: absolute;
left: 0; left: 15px;
} }
.timeslotHeaders { .timeslotHeaders {
position: absolute; position: absolute;
right: 13px; right: 28px;
overflow-y: hidden; overflow-y: hidden;
overflow-x: hidden; overflow-x: hidden;
white-space: nowrap; white-space: nowrap;
@ -205,14 +205,14 @@
overflow-y: hidden; overflow-y: hidden;
overflow-x: hidden; overflow-x: hidden;
position: absolute; position: absolute;
left: 0; left: 15px;
bottom: 16px; bottom: 46px;
} }
.programGrid { .programGrid {
position: absolute; position: absolute;
bottom: 0; bottom: 30px;
right: 0; right: 15px;
overflow-y: scroll; overflow-y: scroll;
overflow-x: scroll; overflow-x: scroll;
} }
@ -242,15 +242,15 @@
} }
.timeslotHeaders, .programGrid { .timeslotHeaders, .programGrid {
left: 191px; left: 206px;
} }
.channelTimeslotHeader, .timeslotHeaders { .channelTimeslotHeader, .timeslotHeaders {
top: 135px; top: 150px;
} }
.channelList, .programGrid { .channelList, .programGrid {
top: 165px; top: 180px;
} }
.channelHeaderCell, .timeslotCell { .channelHeaderCell, .timeslotCell {
@ -305,10 +305,10 @@
@media (max-width: 750px) { @media (max-width: 750px) {
.channelTimeslotHeader, .timeslotHeaders { .channelTimeslotHeader, .timeslotHeaders {
top: 100px; top: 115px;
} }
.channelList, .programGrid { .channelList, .programGrid {
top: 130px; top: 145px;
} }
} }

View file

@ -17,15 +17,15 @@
<form class="encodingSettingsForm"> <form class="encodingSettingsForm">
<fieldset data-role="controlgroup"> <fieldset data-role="controlgroup">
<legend>Transcoding Quality:</legend> <legend>Transcoding Quality Preference:</legend>
<input type="radio" name="radioEncodingQuality" class="radioEncodingQuality" id="radioAuto" value="Auto"> <input type="radio" name="radioEncodingQuality" class="radioEncodingQuality" id="radioAuto" value="Auto">
<label for="radioAuto">Auto<br /><span style="font-weight:normal;">The server will decide quality and speed</span></label> <label for="radioAuto">Auto<br /><span style="font-weight:normal;">The server will decide quality and speed</span></label>
<input type="radio" name="radioEncodingQuality" class="radioEncodingQuality" id="radioHighSpeed" value="HighSpeed"> <input type="radio" name="radioEncodingQuality" class="radioEncodingQuality" id="radioHighSpeed" value="HighSpeed">
<label for="radioHighSpeed">Prefer higher speed<br /><span style="font-weight:normal;">Lower quality, but faster encoding</span></label> <label for="radioHighSpeed">Higher speed<br /><span style="font-weight:normal;">Lower quality, but faster encoding</span></label>
<input type="radio" name="radioEncodingQuality" class="radioEncodingQuality" id="radioHighQuality" value="HighQuality"> <input type="radio" name="radioEncodingQuality" class="radioEncodingQuality" id="radioHighQuality" value="HighQuality">
<label for="radioHighQuality">Prefer higher quality<br /><span style="font-weight:normal;">Higher quality, but slower encoding</span></label> <label for="radioHighQuality">Higher quality<br /><span style="font-weight:normal;">Higher quality, but slower encoding</span></label>
<input type="radio" name="radioEncodingQuality" class="radioEncodingQuality" id="radioMaxQuality" value="MaxQuality"> <input type="radio" name="radioEncodingQuality" class="radioEncodingQuality" id="radioMaxQuality" value="MaxQuality">
<label for="radioMaxQuality">Perfer max quality<br /><span style="font-weight:normal;">Best quality with slower encoding and high CPU usage</span></label> <label for="radioMaxQuality">Max quality<br /><span style="font-weight:normal;">Best quality with slower encoding and high CPU usage</span></label>
</fieldset> </fieldset>
<br /> <br />

View file

@ -141,6 +141,9 @@
<span id="playButtonContainer" style="display: none;"> <span id="playButtonContainer" style="display: none;">
<button id="btnPlay" type="button" data-icon="play" data-inline="true" data-mini="true">Play</button> <button id="btnPlay" type="button" data-icon="play" data-inline="true" data-mini="true">Play</button>
</span> </span>
<span id="trailerButtonContainer">
<button id="btnPlayTrailer" type="button" data-icon="play" data-inline="true" data-mini="true">Trailer</button>
</span>
<span id="playExternalButtonContainer" style="display: none;"> <span id="playExternalButtonContainer" style="display: none;">
<a id="btnPlayExternal" data-role="button" data-icon="play" data-inline="true" data-mini="true" href="#" target="_blank">Play</a> <a id="btnPlayExternal" data-role="button" data-icon="play" data-inline="true" data-mini="true" href="#" target="_blank">Play</a>
</span> </span>

View file

@ -16,10 +16,15 @@
<div data-role="content" style="padding-top: 5px;"> <div data-role="content" style="padding-top: 5px;">
<div> <div>
<div> <div style="text-align: center;">
<select id="selectDate" data-mini="true" data-icon="calendar" data-inline="true"> <select id="selectDate" data-mini="true" data-icon="calendar" data-inline="true">
<option>Today</option> <option>Today</option>
</select> </select>
<div style="display: inline-block; vertical-align: middle;">
Channels
</div>
<div style="display: inline-block; vertical-align: middle;" class="channelPaging">
</div>
</div> </div>
<div> <div>

View file

@ -84,6 +84,12 @@
$('#playExternalButtonContainer', page).hide(); $('#playExternalButtonContainer', page).hide();
} }
if (item.LocalTrailerCount && item.LocationType !== "Offline") {
$('#trailerButtonContainer', page).show();
} else {
$('#trailerButtonContainer', page).hide();
}
$(".autoNumeric").autoNumeric('init'); $(".autoNumeric").autoNumeric('init');
setPeopleHeader(page, item); setPeopleHeader(page, item);
@ -189,7 +195,7 @@
$('#scenesCollapsible', page).show(); $('#scenesCollapsible', page).show();
renderScenes(page, item, 4); renderScenes(page, item, 4);
} }
if (!item.LocalTrailerCount && !item.RemoteTrailers.length) { if (item.LocalTrailerCount || !item.RemoteTrailers.length) {
$('#trailersCollapsible', page).addClass('hide'); $('#trailersCollapsible', page).addClass('hide');
} else { } else {
$('#trailersCollapsible', page).removeClass('hide'); $('#trailersCollapsible', page).removeClass('hide');
@ -1120,6 +1126,24 @@
LibraryBrowser.showPlayMenu(this, currentItem.Id, currentItem.Type, mediaType, userdata.PlaybackPositionTicks); 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 () { $('#btnPlayExternal', page).on('click', function () {
ApiClient.markPlayed(Dashboard.getCurrentUserId(), currentItem.Id, new Date()); ApiClient.markPlayed(Dashboard.getCurrentUserId(), currentItem.Id, new Date());

View file

@ -1417,7 +1417,7 @@
return html; return html;
}, },
getPagingHtml: function (query, totalRecordCount, updatePageSizeSetting) { getPagingHtml: function (query, totalRecordCount, updatePageSizeSetting, pageSizes) {
if (query.Limit && updatePageSizeSetting !== false) { if (query.Limit && updatePageSizeSetting !== false) {
localStorage.setItem('pagesize', query.Limit); localStorage.setItem('pagesize', query.Limit);
@ -1477,13 +1477,11 @@
} }
} }
options += getOption(20); pageSizes = pageSizes || [20, 50, 100, 200, 300, 400, 500];
options += getOption(50);
options += getOption(100); for (var j = 0, length = pageSizes.length; j < length; j++) {
options += getOption(200); options += getOption(pageSizes[j]);
options += getOption(300); }
options += getOption(400);
options += getOption(500);
// Add styles to defeat jquery mobile // Add styles to defeat jquery mobile
html += '<label style="display:inline;font-size:inherit;" class="labelPageSize" for="' + id + '">Limit: </label><select class="selectPageSize" id="' + id + '" data-enhance="false" data-role="none">' + options + '</select>'; html += '<label style="display:inline;font-size:inherit;" class="labelPageSize" for="' + id + '">Limit: </label><select class="selectPageSize" id="' + id + '" data-enhance="false" data-role="none">' + options + '</select>';

View file

@ -1,5 +1,10 @@
(function ($, document, apiClient) { (function ($, document, apiClient) {
var query = {
StartIndex: 0
};
function getChannelsHtml(channels) { function getChannelsHtml(channels) {
return LibraryBrowser.getPosterViewHtml({ 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 () { $(document).on('pagebeforeshow', "#liveTvChannelsPage", function () {
var page = this; var page = this;
apiClient.getLiveTvChannels({ var limit = LibraryBrowser.getDefaultPageSize();
userId: Dashboard.getCurrentUserId() // 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;
}
}).done(function (result) { query.UserId = Dashboard.getCurrentUserId();
renderChannels(page, result.Items); LibraryBrowser.loadSavedQueryValues('movies', query);
});
reloadItems(page);
}); });
})(jQuery, document, ApiClient); })(jQuery, document, ApiClient);

View file

@ -8,6 +8,12 @@
var gridLocalEndDateMs; var gridLocalEndDateMs;
var currentDate; var currentDate;
var channelQuery = {
StartIndex: 0,
Limit: 20
};
var channelsPromise; var channelsPromise;
function normalizeDateToTimeslot(date) { function normalizeDateToTimeslot(date) {
@ -26,15 +32,18 @@
return date; return date;
} }
function reloadChannels(page) {
channelsPromise = null;
reloadGuide(page);
}
function reloadGuide(page) { function reloadGuide(page) {
Dashboard.showLoadingMsg(); Dashboard.showLoadingMsg();
channelsPromise = channelsPromise || apiClient.getLiveTvChannels({ channelQuery.userId = Dashboard.getCurrentUserId();
userId: Dashboard.getCurrentUserId() channelsPromise = channelsPromise || apiClient.getLiveTvChannels(channelQuery);
});;
var date = currentDate; var date = currentDate;
@ -42,23 +51,45 @@
nextDay.setHours(0, 0, 0, 0); nextDay.setHours(0, 0, 0, 0);
nextDay.setDate(nextDay.getDate() + 1); nextDay.setDate(nextDay.getDate() + 1);
var promise1 = channelsPromise; channelsPromise.done(function(channelsResult) {
var promise2 = apiClient.getLiveTvPrograms({
UserId: Dashboard.getCurrentUserId(), apiClient.getLiveTvPrograms({
MaxStartDate: nextDay.toISOString(), UserId: Dashboard.getCurrentUserId(),
MinEndDate: date.toISOString() MaxStartDate: nextDay.toISOString(),
MinEndDate: date.toISOString(),
channelIds: channelsResult.Items.map(function(c) {
return c.Id;
}).join(',')
}); }).done(function(programsResult) {
$.when(promise1, promise2).done(function (response1, response2) { renderGuide(page, date, channelsResult.Items, programsResult.Items);
Dashboard.hideLoadingMsg();
});
var channels = response1[0].Items; var channelPagingHtml = LibraryBrowser.getPagingHtml(channelQuery, channelsResult.TotalRecordCount, false, [10, 20, 30, 50, 100]);
var programs = response2[0].Items; $('.channelPaging', page).html(channelPagingHtml).trigger('create');
renderGuide(page, date, channels, programs); $('.selectPage', page).on('change', function () {
channelQuery.StartIndex = (parseInt(this.value) - 1) * channelQuery.Limit;
reloadChannels(page);
});
Dashboard.hideLoadingMsg(); $('.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);
});
}); });
} }
@ -394,7 +425,7 @@
onProgramGridScroll(page, this); onProgramGridScroll(page, this);
}); });
$('#selectDate', page).on('change', function() { $('#selectDate', page).on('change', function () {
var date = new Date(); var date = new Date();
date.setTime(parseInt(this.value)); date.setTime(parseInt(this.value));

View file

@ -17,7 +17,7 @@
var unmuteButton; var unmuteButton;
var startTimeTicksOffset; var startTimeTicksOffset;
var curentDurationTicks; var curentDurationTicks;
var isStaticStream; var canClientSeek;
var culturesPromise; var culturesPromise;
var timeout; var timeout;
var idleState = true; var idleState = true;
@ -28,6 +28,11 @@
var channelsListPromise; var channelsListPromise;
var channelsListPromiseTime; var channelsListPromiseTime;
function updateCanClientSeek(elem) {
var duration = elem.duration;
canClientSeek = duration && !isNaN(duration) && duration != Number.POSITIVE_INFINITY && duration != Number.NEGATIVE_INFINITY;
}
function getChannelsListPromise() { function getChannelsListPromise() {
var lastUpdateTime = channelsListPromiseTime || 0; var lastUpdateTime = channelsListPromiseTime || 0;
@ -182,7 +187,8 @@
var media = testableVideoElement; var media = testableVideoElement;
// safari // 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'; return '.m3u8';
} }
@ -198,7 +204,7 @@
var element = currentMediaElement; var element = currentMediaElement;
if (isStaticStream && params == null) { if (canClientSeek && params == null) {
element.currentTime = ticks / (1000 * 10000); element.currentTime = ticks / (1000 * 10000);
@ -207,7 +213,6 @@
params = params || {}; params = params || {};
var currentSrc = element.currentSrc; var currentSrc = element.currentSrc;
console.log(currentSrc);
currentSrc = replaceQueryString(currentSrc, 'starttimeticks', ticks); currentSrc = replaceQueryString(currentSrc, 'starttimeticks', ticks);
if (params.AudioStreamIndex != null) { if (params.AudioStreamIndex != null) {
@ -246,6 +251,8 @@
$(element).off('ended.playbackstopped').off('ended.playnext').on("play.onceafterseek", function () { $(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); $(this).off('play.onceafterseek').on('ended.playbackstopped', onPlaybackStopped).on('ended.playnext', playNextAfterEnded);
startProgressInterval(currentItem.Id); startProgressInterval(currentItem.Id);
@ -256,7 +263,6 @@
ApiClient.stopActiveEncodings().done(function () { ApiClient.stopActiveEncodings().done(function () {
startTimeTicksOffset = ticks; startTimeTicksOffset = ticks;
element.src = currentSrc; element.src = currentSrc;
}); });
} }
@ -333,22 +339,20 @@
var videoBitrate = parseInt(this.getAttribute('data-videobitrate')); var videoBitrate = parseInt(this.getAttribute('data-videobitrate'));
var audioBitrate = parseInt(this.getAttribute('data-audiobitrate')); var audioBitrate = parseInt(this.getAttribute('data-audiobitrate'));
var mp4AudioCodec = this.getAttribute('data-mp4audio'); var audioCodec = this.getAttribute('data-audiocodec');
var mp4VideoCodec = this.getAttribute('data-mp4video'); var videoCodec = this.getAttribute('data-videocodec');
var isStatic = this.getAttribute('data-static'); var isStatic = this.getAttribute('data-static');
localStorage.setItem('preferredVideoBitrate', videoBitrate + audioBitrate); localStorage.setItem('preferredVideoBitrate', videoBitrate + audioBitrate);
var isMp4 = currentMediaElement.currentSrc.toLowerCase().indexOf('.mp4') != -1;
changeStream(getCurrentTicks(), { changeStream(getCurrentTicks(), {
MaxWidth: maxWidth, MaxWidth: maxWidth,
VideoBitrate: videoBitrate, VideoBitrate: videoBitrate,
AudioBitrate: audioBitrate, AudioBitrate: audioBitrate,
Static: isStatic, Static: isStatic,
AudioCodec: isMp4 ? mp4AudioCodec : null, AudioCodec: audioCodec,
VideoCodec: isMp4 ? mp4VideoCodec : null VideoCodec: videoCodec
}); });
} }
@ -399,15 +403,16 @@
currentTimeElement.html(timeText); currentTimeElement.html(timeText);
} }
function playAudio(item, params) { function playAudio(item, startPositionTicks) {
startPositionTicks = startPositionTicks || 0;
var baseParams = { var baseParams = {
audioChannels: 2, audioChannels: 2,
audioBitrate: 128000 audioBitrate: 128000,
StartTimeTicks: startPositionTicks
}; };
$.extend(baseParams, params);
var mp3Url = ApiClient.getUrl('Audio/' + item.Id + '/stream.mp3', $.extend({}, baseParams, { var mp3Url = ApiClient.getUrl('Audio/' + item.Id + '/stream.mp3', $.extend({}, baseParams, {
audioCodec: 'mp3' audioCodec: 'mp3'
})); }));
@ -422,6 +427,9 @@
var mediaStreams = item.MediaStreams || []; var mediaStreams = item.MediaStreams || [];
var isStatic = false;
var seekParam = isStatic && startPositionTicks ? '#t=' + (startPositionTicks / 10000000) : '';
for (var i = 0, length = mediaStreams.length; i < length; i++) { for (var i = 0, length = mediaStreams.length; i < length; i++) {
var stream = mediaStreams[i]; var stream = mediaStreams[i];
@ -430,16 +438,19 @@
// Stream statically when possible // Stream statically when possible
if (endsWith(item.Path, ".aac") && stream.BitRate <= 256000) { 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) { else if (endsWith(item.Path, ".mp3") && stream.BitRate <= 256000) {
mp3Url += "&static=true"; mp3Url += "&static=true" + seekParam;
isStatic = true;
} }
break; break;
} }
} }
startTimeTicksOffset = isStatic ? 0 : startPositionTicks;
var html = ''; var html = '';
var requiresControls = $.browser.android || ($.browser.webkit && !$.browser.chrome); var requiresControls = $.browser.android || ($.browser.webkit && !$.browser.chrome);
@ -498,8 +509,7 @@
audioElement.hide(); audioElement.hide();
} }
var duration = this.duration; updateCanClientSeek(this);
isStaticStream = duration && !isNaN(duration) && duration != Number.POSITIVE_INFINITY && duration != Number.NEGATIVE_INFINITY;
audioElement.off("play.once"); audioElement.off("play.once");
@ -549,7 +559,11 @@
return false; 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'; return extension.toLowerCase() == 'mp4';
} }
@ -575,7 +589,7 @@
return true; return true;
} }
function getVideoQualityOptions(item, audioStreamIndex) { function getVideoQualityOptions(item, audioStreamIndex, transcodeExtension) {
var videoStream = (item.MediaStreams || []).filter(function (stream) { var videoStream = (item.MediaStreams || []).filter(function (stream) {
return stream.Type == "Video"; return stream.Type == "Video";
@ -600,6 +614,13 @@
maxAllowedWidth = videoStream.Width; 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? // Some 1080- videos are reported as 1912?
if (maxAllowedWidth >= 1910) { if (maxAllowedWidth >= 1910) {
options.push({ name: '1080p - 10Mbps', maxWidth: 1920, bitrate: 10000000 }); options.push({ name: '1080p - 10Mbps', maxWidth: 1920, bitrate: 10000000 });
@ -630,13 +651,6 @@
options.push({ name: '360p', maxWidth: 640, bitrate: 400000 }); options.push({ name: '360p', maxWidth: 640, bitrate: 400000 });
options.push({ name: '240p', maxWidth: 426, bitrate: 320000 }); 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 videoWidth = videoStream ? videoStream.Width : null;
var i, length, option; var i, length, option;
@ -660,15 +674,18 @@
if (canPlayDirect) { if (canPlayDirect) {
option.isStatic = true; option.isStatic = true;
option.mp4VideoCodec = 'copy'; option.videoCodec = 'copy';
option.mp4AudioCodec = 'copy'; option.audioCodec = 'copy';
} }
//else if (canPlayVideoCodecDirect && transcodeExtension == '.m3u8') {
// option.videoCodec = 'copy';
//}
} }
option.isStatic = option.isStatic || false; 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; option.videoBitrate = option.bitrate - option.audioBitrate;
} }
@ -703,39 +720,60 @@
Static: false Static: false
}; };
var qualityOption = getVideoQualityOptions(item, baseParams.AudioStreamIndex).filter(function (opt) { var mp4Quality = getVideoQualityOptions(item, baseParams.AudioStreamIndex, '.mp4').filter(function (opt) {
return opt.selected; return opt.selected;
})[0]; })[0];
baseParams.maxWidth = qualityOption.maxWidth; var webmQuality = getVideoQualityOptions(item, baseParams.AudioStreamIndex, '.webm').filter(function (opt) {
baseParams.videoBitrate = qualityOption.videoBitrate; return opt.selected;
baseParams.audioBitrate = qualityOption.audioBitrate; })[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 // Webm must be ahead of mp4 due to the issue of mp4 playing too fast in chrome
var prioritizeWebmOverH264 = $.browser.chrome || $.browser.msie; 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, { var mp4VideoUrl = ApiClient.getUrl('Videos/' + item.Id + '/stream.mp4', $.extend({}, baseParams, {
VideoCodec: qualityOption.mp4VideoCodec,
AudioCodec: qualityOption.mp4AudioCodec,
profile: 'baseline', profile: 'baseline',
level: 3, 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, { var webmVideoUrl = ApiClient.getUrl('Videos/' + item.Id + '/stream.webm', $.extend({}, baseParams, {
VideoCodec: 'vpx', 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, { var hlsVideoUrl = ApiClient.getUrl('Videos/' + item.Id + '/stream.m3u8', $.extend({}, baseParams, {
VideoCodec: qualityOption.mp4VideoCodec,
AudioCodec: qualityOption.mp4AudioCodec,
profile: 'baseline', profile: 'baseline',
level: 3, level: 3,
timeStampOffsetMs: 0 timeStampOffsetMs: 0,
})); maxWidth: m3U8Quality.maxWidth,
videoBitrate: m3U8Quality.videoBitrate,
audioBitrate: m3U8Quality.audioBitrate,
VideoCodec: m3U8Quality.videoCodec,
AudioCodec: m3U8Quality.audioCodec
})) + seekParam;
var html = ''; var html = '';
@ -748,19 +786,19 @@
html += '<video class="itemVideo" autoplay preload="none">'; html += '<video class="itemVideo" autoplay preload="none">';
} }
if (!staticMp4) { if (!isStatic) {
// HLS must be at the top for safari // HLS must be at the top for safari
html += '<source type="application/x-mpegURL" src="' + hlsVideoUrl + '" />'; html += '<source type="application/x-mpegURL" src="' + hlsVideoUrl + '" />';
} }
if (prioritizeWebmOverH264 && !staticMp4) { if (prioritizeWebmOverH264 && !isStatic) {
html += '<source type="video/webm" src="' + webmVideoUrl + '" />'; html += '<source type="video/webm" src="' + webmVideoUrl + '" />';
} }
html += '<source type="video/mp4" src="' + mp4VideoUrl + '" />'; html += '<source type="video/mp4" src="' + mp4VideoUrl + '" />';
if (!prioritizeWebmOverH264 && !staticMp4) { if (!prioritizeWebmOverH264 && !isStatic) {
html += '<source type="video/webm" src="' + webmVideoUrl + '" />'; html += '<source type="video/webm" src="' + webmVideoUrl + '" />';
} }
@ -830,15 +868,10 @@
}).on("play.once", function () { }).on("play.once", function () {
var duration = this.duration; updateCanClientSeek(this);
isStaticStream = duration && !isNaN(duration) && duration != Number.POSITIVE_INFINITY && duration != Number.NEGATIVE_INFINITY;
videoElement.off("play.once"); videoElement.off("play.once");
if (startPosition && staticMp4) {
self.seek(startPosition);
}
ApiClient.reportPlaybackStart(Dashboard.getCurrentUserId(), item.Id, true, item.MediaType); ApiClient.reportPlaybackStart(Dashboard.getCurrentUserId(), item.Id, true, item.MediaType);
startProgressInterval(item.Id); startProgressInterval(item.Id);
@ -993,26 +1026,12 @@
self.canPlayMediaType = function (mediaType) { self.canPlayMediaType = function (mediaType) {
var media;
if (mediaType === "Video") { if (mediaType === "Video") {
media = testableVideoElement; return true;
if (media.canPlayType) {
return media.canPlayType('video/mp4').replace(/no/, '') || media.canPlayType('video/mp2t').replace(/no/, '') || media.canPlayType('video/webm').replace(/no/, '') || media.canPlayType('application/x-mpegURL').replace(/no/, '') || media.canPlayType('video/ogv').replace(/no/, '');
}
return false;
} }
if (mediaType === "Audio") { if (mediaType === "Audio") {
media = testableAudioElement; return true;
if (media.canPlayType) {
return media.canPlayType('audio/mpeg').replace(/no/, '') || media.canPlayType('audio/webm').replace(/no/, '') || media.canPlayType('audio/aac').replace(/no/, '');
}
return false;
} }
return false; return false;
@ -1116,13 +1135,11 @@
mediaElement = playVideo(item, startPosition, user); mediaElement = playVideo(item, startPosition, user);
} else if (item.MediaType === "Audio") { } else if (item.MediaType === "Audio") {
mediaElement = playAudio(item); mediaElement = playAudio(item, startPosition);
} else { } else {
throw new Error("Unrecognized media type"); throw new Error("Unrecognized media type");
} }
startTimeTicksOffset = startPosition || 0;
currentMediaElement = mediaElement; currentMediaElement = mediaElement;
var nowPlayingBar = $('#nowPlayingBar').show(); var nowPlayingBar = $('#nowPlayingBar').show();
@ -1834,9 +1851,26 @@
var html = ''; var html = '';
var currentSrc = currentMediaElement.currentSrc.toLowerCase();
var isStatic = currentSrc.indexOf('static=true') != -1;
var transcodingExtension = isStatic ? getTranscodingExtension() : null;
if (!transcodingExtension) {
if (currentSrc.indexOf('.m3u8') != -1) {
transcodingExtension = '.m3u8';
}
else if (currentSrc.indexOf('.webm') != -1) {
transcodingExtension = '.webm';
}
else {
transcodingExtension = '.mp4';
}
}
var currentAudioStreamIndex = getParameterByName('AudioStreamIndex', currentMediaElement.currentSrc); var currentAudioStreamIndex = getParameterByName('AudioStreamIndex', currentMediaElement.currentSrc);
var options = getVideoQualityOptions(item, currentAudioStreamIndex); var options = getVideoQualityOptions(item, currentAudioStreamIndex, transcodingExtension);
for (var i = 0, length = options.length; i < length; i++) { for (var i = 0, length = options.length; i < length; i++) {
@ -1848,7 +1882,7 @@
cssClass += " selectedMediaFlyoutOption"; cssClass += " selectedMediaFlyoutOption";
} }
html += '<div data-static="' + option.isStatic + '" data-mp4video="' + option.mp4VideoCodec + '" data-mp4audio="' + option.mp4AudioCodec + '" data-maxwidth="' + option.maxWidth + '" data-videobitrate="' + option.videoBitrate + '" data-audiobitrate="' + option.audioBitrate + '" class="' + cssClass + '">'; html += '<div data-static="' + option.isStatic + '" data-videocodec="' + option.videoCodec + '" data-audiocodec="' + option.audioCodec + '" data-maxwidth="' + option.maxWidth + '" data-videobitrate="' + option.videoBitrate + '" data-audiobitrate="' + option.audioBitrate + '" class="' + cssClass + '">';
html += '<div class="mediaFlyoutOptionContent">'; html += '<div class="mediaFlyoutOptionContent">';

View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="MediaBrowser.ApiClient.Javascript" version="3.0.219" targetFramework="net45" /> <package id="MediaBrowser.ApiClient.Javascript" version="3.0.223" targetFramework="net45" />
</packages> </packages>