mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
fixes #712 - multi-version grouping
This commit is contained in:
parent
902d3a3649
commit
02a810cf1e
12 changed files with 163 additions and 304 deletions
|
@ -859,7 +859,7 @@ a.itemTag:hover {
|
|||
}
|
||||
|
||||
.mediaInfoStream {
|
||||
margin: 1em 2em 1em 0;
|
||||
margin: 1em 3em 1em 0;
|
||||
display: inline-block;
|
||||
color: #bbb;
|
||||
vertical-align: top;
|
||||
|
@ -867,7 +867,6 @@ a.itemTag:hover {
|
|||
|
||||
.mediaInfoStreamType {
|
||||
display: block;
|
||||
font-size: 16px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
|
@ -925,7 +924,7 @@ a.itemTag:hover {
|
|||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.alternateVersionIndicator {
|
||||
.mediaVersionIndicator {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
|
|
|
@ -321,60 +321,3 @@
|
|||
height: 167.625px;
|
||||
}
|
||||
}
|
||||
/********************/
|
||||
.ribbon-wrapper {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.ribbon {
|
||||
font: bold 11px Sans-Serif;
|
||||
color: #333;
|
||||
text-align: center;
|
||||
color: #eee;
|
||||
text-shadow: rgba(51, 51, 51, 0.5) 0px 1px 0px;
|
||||
-webkit-transform: rotate(-45deg);
|
||||
-moz-transform: rotate(-45deg);
|
||||
-ms-transform: rotate(-45deg);
|
||||
-o-transform: rotate(-45deg);
|
||||
position: relative;
|
||||
padding: 2px 0;
|
||||
right: 15px;
|
||||
top: 5px;
|
||||
width: 60px;
|
||||
-webkit-box-shadow: 0px 0px 3px rgba(0, 0, 0, 0.3);
|
||||
-moz-box-shadow: 0px 0px 3px rgba(0, 0, 0, 0.3);
|
||||
box-shadow: 0px 0px 3px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.ribbon-3d {
|
||||
background-color: #3344ff;
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#BFDC7A), to(#ff0033));
|
||||
background-image: -webkit-linear-gradient(top, #3344ff, #ff0033);
|
||||
background-image: -moz-linear-gradient(top, #3344ff, #ff0033);
|
||||
background-image: -ms-linear-gradient(top, #3344ff, #ff0033);
|
||||
background-image: -o-linear-gradient(top, #3344ff, #ff0033);
|
||||
}
|
||||
|
||||
.ribbon-blue {
|
||||
background-color: #1199ff;
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#1199ff), to(#3333ff));
|
||||
background-image: -webkit-linear-gradient(top, #1199ff, #3333ff);
|
||||
background-image: -moz-linear-gradient(top, #1199ff, #3333ff);
|
||||
background-image: -ms-linear-gradient(top, #1199ff, #3333ff);
|
||||
background-image: -o-linear-gradient(top, #1199ff, #3333ff);
|
||||
}
|
||||
|
||||
.ribbon-red {
|
||||
background-color: #ff3333;
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#ff3333), to(#992233));
|
||||
background-image: -webkit-linear-gradient(top, #ff3333, #992233);
|
||||
background-image: -moz-linear-gradient(top, #ff3333, #992233);
|
||||
background-image: -ms-linear-gradient(top, #ff3333, #992233);
|
||||
background-image: -o-linear-gradient(top, #ff3333, #992233);
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
<div class="viewControls" data-role="controlgroup" data-type="horizontal">
|
||||
<button data-mini="true" data-icon="sort" data-inline="true" data-iconpos="notext" title="Sort" onclick="$('#sortPanel', $(this).parents('.page')).panel( 'toggle' );">Sort</button>
|
||||
<button data-mini="true" data-icon="filter" data-inline="true" data-iconpos="notext" title="Filter" onclick="$('#filterPanel', $(this).parents('.page')).panel( 'toggle' );">Filter</button>
|
||||
<!-- <button data-mini="true" data-icon="check" data-inline="true" data-iconpos="notext" title="Select" class="btnToggleSelections">Select</button>-->
|
||||
<button data-mini="true" data-icon="check" data-inline="true" data-iconpos="notext" title="Select" class="btnToggleSelections">Select</button>
|
||||
</div>
|
||||
<div class="listTopPaging">
|
||||
</div>
|
||||
|
|
|
@ -152,7 +152,7 @@
|
|||
<br />
|
||||
<div class="tabButtons"></div>
|
||||
|
||||
<div class="detailSectionContent" style="padding: 0 .7em;">
|
||||
<div class="detailSectionContent" style="padding: .25em .7em 0;">
|
||||
|
||||
<div class="detailTab tabDetails">
|
||||
<p id="players"></p>
|
||||
|
@ -163,15 +163,12 @@
|
|||
<p class="itemExternalLinks"></p>
|
||||
</div>
|
||||
<div class="detailTab tabMediaInfo">
|
||||
|
||||
<div class="splitVersionContainer" style="border-bottom: 1px solid #444;">
|
||||
<button type="button" class="btnSplitVersions" data-mini="true" data-inline="true" data-icon="delete" title="Split Versions Apart">Split Versions Apart</button>
|
||||
</div>
|
||||
<div id="mediaInfoContent"></div>
|
||||
|
||||
<div id="alternateVersionsCollapsible" class="detailSection hide">
|
||||
<div class="detailSectionHeader" style="position: relative;">
|
||||
Media Versions
|
||||
<button type="button" class="btnSplitVersions" style="position: absolute; right: 0; top: 6px; margin-top: 0; margin-bottom: 0; display: none;" data-mini="true" data-inline="true" data-icon="delete" data-iconpos="notext" title="Split Versions Apart">Split Versions Apart</button>
|
||||
</div>
|
||||
<div id="alternateVersionsContent" class="detailSectionContent"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="detailTab tabTags">
|
||||
<p class="itemStudios"></p>
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
<div class="viewControls" data-role="controlgroup" data-type="horizontal">
|
||||
<button data-mini="true" data-icon="sort" data-inline="true" data-iconpos="notext" title="Sort" onclick="$('#sortPanel', $(this).parents('.page')).panel( 'toggle' );">Sort</button>
|
||||
<button data-mini="true" data-icon="filter" data-inline="true" data-iconpos="notext" title="Filter" onclick="$('#filterPanel', $(this).parents('.page')).panel( 'toggle' );">Filter</button>
|
||||
<!-- <button data-mini="true" data-icon="check" data-inline="true" data-iconpos="notext" title="Select" class="btnToggleSelections">Select</button>-->
|
||||
<button data-mini="true" data-icon="check" data-inline="true" data-iconpos="notext" title="Select" class="btnToggleSelections">Select</button>
|
||||
</div>
|
||||
<div class="listTopPaging">
|
||||
</div>
|
||||
|
|
|
@ -73,10 +73,10 @@
|
|||
$('#btnPlayExternalTrailer', page).attr('href', '#');
|
||||
}
|
||||
|
||||
if (user.Configuration.IsAdministrator) {
|
||||
$('.btnSplitVersions', page).show();
|
||||
if (user.Configuration.IsAdministrator && item.MediaVersions && item.MediaVersions.length > 1) {
|
||||
$('.splitVersionContainer', page).show();
|
||||
} else {
|
||||
$('.btnSplitVersions', page).hide();
|
||||
$('.splitVersionContainer', page).hide();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -210,10 +210,17 @@
|
|||
$('#childrenCollapsible', page).addClass('hide');
|
||||
}
|
||||
|
||||
if (item.MediaStreams && item.MediaStreams.length) {
|
||||
renderMediaInfo(page, item);
|
||||
if (item.MediaVersions && item.MediaVersions.length) {
|
||||
renderMediaVersions(page, item);
|
||||
}
|
||||
if (!item.Chapters || !item.Chapters.length) {
|
||||
|
||||
var primaryVersion = (item.MediaVersions || []).filter(function (v) {
|
||||
return v.IsPrimaryVersion;
|
||||
|
||||
})[0];
|
||||
var chapters = primaryVersion ? (primaryVersion.Chapters || []) : [];
|
||||
|
||||
if (!chapters.length) {
|
||||
$('#scenesCollapsible', page).hide();
|
||||
} else {
|
||||
$('#scenesCollapsible', page).show();
|
||||
|
@ -1001,7 +1008,6 @@
|
|||
useAverageAspectRatio: true,
|
||||
showTitle: true,
|
||||
centerText: true,
|
||||
formatIndicators: true,
|
||||
linkItem: false,
|
||||
showProgress: false,
|
||||
showUnplayedIndicator: false
|
||||
|
@ -1017,7 +1023,11 @@
|
|||
function renderScenes(page, item, user, limit) {
|
||||
var html = '';
|
||||
|
||||
var chapters = item.Chapters || [];
|
||||
var primaryVersion = (item.MediaVersions || []).filter(function (v) {
|
||||
return v.IsPrimaryVersion;
|
||||
|
||||
})[0];
|
||||
var chapters = primaryVersion ? (primaryVersion.Chapters || []) : [];
|
||||
|
||||
for (var i = 0, length = chapters.length; i < length; i++) {
|
||||
|
||||
|
@ -1067,13 +1077,32 @@
|
|||
$('#scenesContent', page).html(html).trigger('create');
|
||||
}
|
||||
|
||||
function renderMediaInfo(page, item) {
|
||||
function renderMediaVersions(page, item) {
|
||||
|
||||
var html = item.MediaVersions.map(function (v) {
|
||||
|
||||
return getMediaVersionHtml(item, v);
|
||||
|
||||
}).join('<div style="border-top:1px solid #444;margin: 1em 0;"></div>');
|
||||
|
||||
if (item.MediaVersions.length > 1) {
|
||||
html = '<br/>' + html;
|
||||
}
|
||||
|
||||
$('#mediaInfoContent', page).html(html).trigger('create');
|
||||
}
|
||||
|
||||
function getMediaVersionHtml(item, version) {
|
||||
|
||||
var html = '';
|
||||
|
||||
for (var i = 0, length = item.MediaStreams.length; i < length; i++) {
|
||||
if (version.Name && item.MediaVersions.length > 1) {
|
||||
html += '<span class="mediaInfoAttribute">' + version.Name + '</span><br/>';
|
||||
}
|
||||
|
||||
var stream = item.MediaStreams[i];
|
||||
for (var i = 0, length = version.MediaStreams.length; i < length; i++) {
|
||||
|
||||
var stream = version.MediaStreams[i];
|
||||
|
||||
if (stream.Type == "Data") {
|
||||
continue;
|
||||
|
@ -1155,7 +1184,11 @@
|
|||
html += '</div>';
|
||||
}
|
||||
|
||||
$('#mediaInfoContent', page).html(html).trigger('create');
|
||||
if (version.Path) {
|
||||
html += '<br/><span class="mediaInfoLabel">Path</span><span class="mediaInfoAttribute">' + version.Path + '</span>';
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
function getVideosHtml(items, user, limit, moreButtonClass) {
|
||||
|
|
|
@ -665,33 +665,11 @@
|
|||
|
||||
cssClass += ' ' + options.shape + 'PosterItem';
|
||||
|
||||
var mediaVersionCount = item.MediaVersionCount || 1;
|
||||
|
||||
var href = options.linkItem === false ? '#' : LibraryBrowser.getHref(item, options.context);
|
||||
|
||||
html += '<a data-itemid="' + item.Id + '" class="' + cssClass + '" data-alternateversioncount="' + (item.AlternateVersionCount || '0') + '" href="' + href + '">';
|
||||
|
||||
// Ribbon
|
||||
if (item.MediaType == "Video" && options.formatIndicators) {
|
||||
// This would be much better if specified in the json payload
|
||||
// Another nice thing to have in the payload would be 720 vs 1080
|
||||
// Then, rather than "HD" it could display the specific HD format
|
||||
// "HD" doesn't do much good if you have the 720p and 1080p version
|
||||
var format = "SD";
|
||||
var ribbonColor = "ribbon-red";
|
||||
if (item.IsHD) {
|
||||
format = "HD";
|
||||
ribbonColor = "ribbon-blue";
|
||||
}
|
||||
if (item.Video3DFormat) {
|
||||
format = "3D";
|
||||
ribbonColor = "ribbon-3d";
|
||||
}
|
||||
|
||||
html += '<div class="ribbon-wrapper">';
|
||||
html += '<div class="ribbon ' + ribbonColor + '">';
|
||||
html += format;
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
}
|
||||
html += '<a data-itemid="' + item.Id + '" class="' + cssClass + '" data-mediaversioncount="' + mediaVersionCount + '" href="' + href + '">';
|
||||
|
||||
var style = "";
|
||||
|
||||
|
@ -722,8 +700,8 @@
|
|||
html += LibraryBrowser.getPlayedIndicatorHtml(item);
|
||||
}
|
||||
|
||||
if (item.AlternateVersionCount) {
|
||||
html += '<div class="alternateVersionIndicator">' + (item.AlternateVersionCount + 1) + '</div>';
|
||||
if (mediaVersionCount > 1) {
|
||||
html += '<div class="mediaVersionIndicator">' + mediaVersionCount + '</div>';
|
||||
}
|
||||
if (item.IsUnidentified) {
|
||||
html += '<div class="unidentifiedIndicator"><div class="ui-icon-alert ui-btn-icon-notext"></div></div>';
|
||||
|
|
|
@ -208,7 +208,9 @@
|
|||
|
||||
items.push({ type: 'link', text: 'Images', url: 'edititemimages.html?id=' + id });
|
||||
|
||||
if (elem.getAttribute('data-alternateversioncount') != '0') {
|
||||
var versionCount = parseInt(elem.getAttribute('data-mediaversioncount') || '0');
|
||||
|
||||
if (versionCount > 1) {
|
||||
|
||||
items.push({ type: 'divider' });
|
||||
items.push({ type: 'header', text: 'Manage' });
|
||||
|
|
|
@ -8,12 +8,12 @@
|
|||
SortBy: defaultSortBy,
|
||||
SortOrder: "Ascending",
|
||||
Recursive: true,
|
||||
Fields: "MediaStreams,DateCreated,Settings,Studios",
|
||||
Fields: "MediaVersions,DateCreated,Settings,Studios",
|
||||
StartIndex: 0,
|
||||
IncludeItemTypes: "Movie",
|
||||
IsMissing: false,
|
||||
IsVirtualUnaired: false,
|
||||
Limit: 300,
|
||||
Limit: 200,
|
||||
CollapseBoxSetItems: false
|
||||
};
|
||||
|
||||
|
@ -214,6 +214,12 @@
|
|||
|
||||
var stream;
|
||||
|
||||
var primaryVersion = (item.MediaVersions || []).filter(function(v) {
|
||||
return v.IsPrimaryVersion;
|
||||
})[0] || {};
|
||||
|
||||
var mediaStreams = primaryVersion.MediaStreams || [];
|
||||
|
||||
switch (cell.type || cell.name) {
|
||||
|
||||
case 'Album Artist':
|
||||
|
@ -258,7 +264,7 @@
|
|||
}
|
||||
case 'Audio':
|
||||
{
|
||||
stream = (item.MediaStreams || []).filter(function (s) {
|
||||
stream = mediaStreams.filter(function (s) {
|
||||
|
||||
return s.Type == 'Audio';
|
||||
|
||||
|
@ -273,7 +279,7 @@
|
|||
}
|
||||
case 'Video':
|
||||
{
|
||||
stream = (item.MediaStreams || []).filter(function (s) {
|
||||
stream = mediaStreams.filter(function (s) {
|
||||
|
||||
return s.Type == 'Video';
|
||||
|
||||
|
@ -286,7 +292,7 @@
|
|||
}
|
||||
case 'Resolution':
|
||||
{
|
||||
stream = (item.MediaStreams || []).filter(function (s) {
|
||||
stream = mediaStreams.filter(function (s) {
|
||||
|
||||
return s.Type == 'Video';
|
||||
|
||||
|
@ -299,7 +305,7 @@
|
|||
}
|
||||
case 'Embedded Image':
|
||||
{
|
||||
if ((item.MediaStreams || []).filter(function (s) {
|
||||
if (mediaStreams.filter(function (s) {
|
||||
|
||||
return s.Type == 'Video';
|
||||
|
||||
|
@ -310,7 +316,7 @@
|
|||
}
|
||||
case 'Subtitles':
|
||||
{
|
||||
var hasSubtitles = (item.MediaStreams || []).filter(function (s) {
|
||||
var hasSubtitles = mediaStreams.filter(function (s) {
|
||||
|
||||
return s.Type == 'Subtitle';
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
(function () {
|
||||
videoPlayer = function(mediaPlayer, item, startPosition, user) {
|
||||
videoPlayer = function (mediaPlayer, item, mediaVersion, startPosition, user) {
|
||||
if (mediaPlayer == null) {
|
||||
throw new Error("mediaPlayer cannot be null");
|
||||
}
|
||||
|
@ -15,15 +15,15 @@
|
|||
var self = mediaPlayer;
|
||||
|
||||
var currentItem;
|
||||
var currentMediaVersion;
|
||||
var timeout;
|
||||
var video;
|
||||
var culturesPromise;
|
||||
var initialVolume;
|
||||
var fullscreenExited = false;
|
||||
var idleState = true;
|
||||
|
||||
self.initVideoPlayer = function () {
|
||||
video = playVideo(item, startPosition, user);
|
||||
video = playVideo(item, mediaVersion, startPosition, user);
|
||||
return video;
|
||||
};
|
||||
|
||||
|
@ -47,7 +47,7 @@
|
|||
$(".ui-loader").hide();
|
||||
};
|
||||
|
||||
self.exitFullScreen = function() {
|
||||
self.exitFullScreen = function () {
|
||||
if (document.exitFullscreen) {
|
||||
document.exitFullscreen();
|
||||
} else if (document.mozExitFullScreen) {
|
||||
|
@ -61,7 +61,7 @@
|
|||
fullscreenExited = true;
|
||||
};
|
||||
|
||||
self.isFullScreen = function() {
|
||||
self.isFullScreen = function () {
|
||||
return document.fullscreen || document.mozFullScreen || document.webkitIsFullScreen || document.msFullscreenElement ? true : false;
|
||||
};
|
||||
|
||||
|
@ -71,15 +71,8 @@
|
|||
|
||||
if (!flyout.is(':visible')) {
|
||||
|
||||
culturesPromise = culturesPromise || ApiClient.getCultures();
|
||||
|
||||
$("html").css("cursor", "progress");
|
||||
|
||||
culturesPromise.done(function (cultures) {
|
||||
$("html").css("cursor", "default");
|
||||
flyout.html(getSubtitleTracksHtml(currentItem, cultures)).trigger('create').scrollTop(0);
|
||||
flyout.html(getSubtitleTracksHtml()).trigger('create').scrollTop(0);
|
||||
toggleFlyout(flyout, '#video-subtitleButton');
|
||||
});
|
||||
|
||||
} else {
|
||||
toggleFlyout(flyout, '#video-subtitleButton');
|
||||
|
@ -91,7 +84,7 @@
|
|||
var flyout = $('#video-qualityFlyout');
|
||||
|
||||
if (!flyout.is(':visible')) {
|
||||
flyout.html(getQualityFlyoutHtml(currentItem)).scrollTop(0);
|
||||
flyout.html(getQualityFlyoutHtml()).scrollTop(0);
|
||||
}
|
||||
|
||||
toggleFlyout(flyout, '#video-qualityButton');
|
||||
|
@ -102,7 +95,7 @@
|
|||
var flyout = $('#video-chaptersFlyout');
|
||||
|
||||
if (!flyout.is(':visible')) {
|
||||
flyout.html(getChaptersFlyoutHtml(currentItem)).scrollTop(0);
|
||||
flyout.html(getChaptersFlyoutHtml()).scrollTop(0);
|
||||
}
|
||||
|
||||
toggleFlyout(flyout, '#video-chaptersButton');
|
||||
|
@ -114,15 +107,8 @@
|
|||
|
||||
if (!flyout.is(':visible')) {
|
||||
|
||||
culturesPromise = culturesPromise || ApiClient.getCultures();
|
||||
|
||||
$("html").css("cursor", "progress");
|
||||
|
||||
culturesPromise.done(function (cultures) {
|
||||
$("html").css("cursor", "default");
|
||||
flyout.html(getAudioTracksHtml(currentItem, cultures)).trigger('create').scrollTop(0);
|
||||
flyout.html(getAudioTracksHtml()).trigger('create').scrollTop(0);
|
||||
toggleFlyout(flyout, '#video-audioTracksButton');
|
||||
});
|
||||
} else {
|
||||
toggleFlyout(flyout, '#video-audioTracksButton');
|
||||
}
|
||||
|
@ -295,13 +281,13 @@
|
|||
$(document.body).off("mousedown.hidesearchhints");
|
||||
};
|
||||
|
||||
function getChaptersFlyoutHtml(item) {
|
||||
function getChaptersFlyoutHtml() {
|
||||
|
||||
var html = '';
|
||||
|
||||
var currentTicks = self.getCurrentTicks();
|
||||
|
||||
var chapters = item.Chapters || [];
|
||||
var chapters = currentMediaVersion.Chapters || [];
|
||||
|
||||
for (var i = 0, length = chapters.length; i < length; i++) {
|
||||
|
||||
|
@ -326,7 +312,7 @@
|
|||
|
||||
if (chapter.ImageTag) {
|
||||
|
||||
imgUrl = ApiClient.getImageUrl(item.Id, {
|
||||
imgUrl = ApiClient.getImageUrl(currentMediaVersion.ItemId, {
|
||||
maxwidth: 200,
|
||||
tag: chapter.ImageTag,
|
||||
type: "Chapter",
|
||||
|
@ -354,10 +340,10 @@
|
|||
return html;
|
||||
};
|
||||
|
||||
function getAudioTracksHtml(item, cultures) {
|
||||
function getAudioTracksHtml() {
|
||||
|
||||
var streams = item.MediaStreams.filter(function (i) {
|
||||
return i.Type == "Audio";
|
||||
var streams = currentMediaVersion.MediaStreams.filter(function (currentStream) {
|
||||
return currentStream.Type == "Audio";
|
||||
});
|
||||
|
||||
var currentIndex = getParameterByName('AudioStreamIndex', video.currentSrc);
|
||||
|
@ -378,20 +364,7 @@
|
|||
|
||||
html += '<div class="mediaFlyoutOptionContent">';
|
||||
|
||||
var language = null;
|
||||
|
||||
if (stream.Language && stream.Language != "und") {
|
||||
|
||||
var culture = cultures.filter(function (current) {
|
||||
return current.ThreeLetterISOLanguageName.toLowerCase() == stream.Language.toLowerCase();
|
||||
});
|
||||
|
||||
if (culture.length) {
|
||||
language = culture[0].DisplayName;
|
||||
}
|
||||
}
|
||||
|
||||
html += '<div class="mediaFlyoutOptionName">' + (language || stream.Language || 'Unknown language') + '</div>';
|
||||
html += '<div class="mediaFlyoutOptionName">' + (stream.Language || 'Unknown language') + '</div>';
|
||||
|
||||
var options = [];
|
||||
|
||||
|
@ -437,10 +410,10 @@
|
|||
return html;
|
||||
};
|
||||
|
||||
function getSubtitleTracksHtml(item, cultures) {
|
||||
function getSubtitleTracksHtml() {
|
||||
|
||||
var streams = item.MediaStreams.filter(function (i) {
|
||||
return i.Type == "Subtitle";
|
||||
var streams = currentMediaVersion.MediaStreams.filter(function (currentStream) {
|
||||
return currentStream.Type == "Subtitle";
|
||||
});
|
||||
|
||||
var currentIndex = getParameterByName('SubtitleStreamIndex', video.currentSrc) || -1;
|
||||
|
@ -470,25 +443,13 @@
|
|||
|
||||
html += '<div class="mediaFlyoutOptionContent">';
|
||||
|
||||
var language = null;
|
||||
var options = [];
|
||||
|
||||
if (stream.Language == "Off") {
|
||||
language = "Off";
|
||||
options.push(' ');
|
||||
}
|
||||
else if (stream.Language && stream.Language != "und") {
|
||||
|
||||
var culture = cultures.filter(function (current) {
|
||||
return current.ThreeLetterISOLanguageName.toLowerCase() == stream.Language.toLowerCase();
|
||||
});
|
||||
|
||||
if (culture.length) {
|
||||
language = culture[0].DisplayName;
|
||||
}
|
||||
}
|
||||
|
||||
html += '<div class="mediaFlyoutOptionName">' + (language || 'Unknown language') + '</div>';
|
||||
html += '<div class="mediaFlyoutOptionName">' + (stream.Language || 'Unknown language') + '</div>';
|
||||
|
||||
if (stream.Codec) {
|
||||
options.push(stream.Codec);
|
||||
|
@ -524,7 +485,7 @@
|
|||
return html;
|
||||
};
|
||||
|
||||
function getQualityFlyoutHtml(item) {
|
||||
function getQualityFlyoutHtml() {
|
||||
|
||||
var html = '';
|
||||
|
||||
|
@ -535,7 +496,7 @@
|
|||
|
||||
var currentAudioStreamIndex = getParameterByName('AudioStreamIndex', video.currentSrc);
|
||||
|
||||
var options = getVideoQualityOptions(item, currentAudioStreamIndex, transcodingExtension);
|
||||
var options = getVideoQualityOptions(currentMediaVersion.MediaStreams, currentAudioStreamIndex, transcodingExtension);
|
||||
|
||||
if (isStatic) {
|
||||
options[0].name = "Direct";
|
||||
|
@ -627,9 +588,9 @@
|
|||
return audioStreams.length ? audioStreams[0].Index : null;
|
||||
};
|
||||
|
||||
function getVideoQualityOptions(item) {
|
||||
function getVideoQualityOptions(mediaStreams) {
|
||||
|
||||
var videoStream = (item.MediaStreams || []).filter(function (stream) {
|
||||
var videoStream = mediaStreams.filter(function (stream) {
|
||||
return stream.Type == "Video";
|
||||
})[0];
|
||||
|
||||
|
@ -703,9 +664,9 @@
|
|||
return options;
|
||||
};
|
||||
|
||||
function playVideo(item, startPosition, user) {
|
||||
function playVideo(item, mediaVersion, startPosition, user) {
|
||||
|
||||
var mediaStreams = item.MediaStreams || [];
|
||||
var mediaStreams = mediaVersion.MediaStreams || [];
|
||||
|
||||
var baseParams = {
|
||||
audioChannels: 2,
|
||||
|
@ -716,20 +677,20 @@
|
|||
Static: false
|
||||
};
|
||||
|
||||
var mp4Quality = getVideoQualityOptions(item).filter(function (opt) {
|
||||
var mp4Quality = getVideoQualityOptions(mediaStreams).filter(function (opt) {
|
||||
return opt.selected;
|
||||
})[0];
|
||||
mp4Quality = $.extend(mp4Quality, self.getFinalVideoParams(item, mp4Quality.maxWidth, mp4Quality.bitrate, baseParams.AudioStreamIndex, baseParams.SubtitleStreamIndex, '.mp4'));
|
||||
mp4Quality = $.extend(mp4Quality, self.getFinalVideoParams(mediaVersion, mp4Quality.maxWidth, mp4Quality.bitrate, baseParams.AudioStreamIndex, baseParams.SubtitleStreamIndex, '.mp4'));
|
||||
|
||||
var webmQuality = getVideoQualityOptions(item).filter(function (opt) {
|
||||
var webmQuality = getVideoQualityOptions(mediaStreams).filter(function (opt) {
|
||||
return opt.selected;
|
||||
})[0];
|
||||
webmQuality = $.extend(webmQuality, self.getFinalVideoParams(item, webmQuality.maxWidth, webmQuality.bitrate, baseParams.AudioStreamIndex, baseParams.SubtitleStreamIndex, '.webm'));
|
||||
webmQuality = $.extend(webmQuality, self.getFinalVideoParams(mediaVersion, webmQuality.maxWidth, webmQuality.bitrate, baseParams.AudioStreamIndex, baseParams.SubtitleStreamIndex, '.webm'));
|
||||
|
||||
var m3U8Quality = getVideoQualityOptions(item).filter(function (opt) {
|
||||
var m3U8Quality = getVideoQualityOptions(mediaStreams).filter(function (opt) {
|
||||
return opt.selected;
|
||||
})[0];
|
||||
m3U8Quality = $.extend(m3U8Quality, self.getFinalVideoParams(item, mp4Quality.maxWidth, mp4Quality.bitrate, baseParams.AudioStreamIndex, baseParams.SubtitleStreamIndex, '.mp4'));
|
||||
m3U8Quality = $.extend(m3U8Quality, self.getFinalVideoParams(mediaVersion, mp4Quality.maxWidth, mp4Quality.bitrate, baseParams.AudioStreamIndex, baseParams.SubtitleStreamIndex, '.mp4'));
|
||||
|
||||
// Webm must be ahead of mp4 due to the issue of mp4 playing too fast in chrome
|
||||
var prioritizeWebmOverH264 = $.browser.chrome || $.browser.msie;
|
||||
|
@ -740,7 +701,7 @@
|
|||
|
||||
var seekParam = isStatic && startPosition ? '#t=' + (startPosition / 10000000) : '';
|
||||
|
||||
var mp4VideoUrl = ApiClient.getUrl('Videos/' + item.Id + '/stream.mp4', $.extend({}, baseParams, {
|
||||
var mp4VideoUrl = ApiClient.getUrl('Videos/' + mediaVersion.ItemId + '/stream.mp4', $.extend({}, baseParams, {
|
||||
profile: 'baseline',
|
||||
level: 3,
|
||||
Static: isStatic,
|
||||
|
@ -752,7 +713,7 @@
|
|||
|
||||
})) + seekParam;
|
||||
|
||||
var webmVideoUrl = ApiClient.getUrl('Videos/' + item.Id + '/stream.webm', $.extend({}, baseParams, {
|
||||
var webmVideoUrl = ApiClient.getUrl('Videos/' + mediaVersion.ItemId + '/stream.webm', $.extend({}, baseParams, {
|
||||
|
||||
VideoCodec: 'vpx',
|
||||
AudioCodec: 'Vorbis',
|
||||
|
@ -762,7 +723,7 @@
|
|||
|
||||
})) + seekParam;
|
||||
|
||||
var hlsVideoUrl = ApiClient.getUrl('Videos/' + item.Id + '/stream.m3u8', $.extend({}, baseParams, {
|
||||
var hlsVideoUrl = ApiClient.getUrl('Videos/' + mediaVersion.ItemId + '/stream.m3u8', $.extend({}, baseParams, {
|
||||
profile: 'baseline',
|
||||
level: 3,
|
||||
timeStampOffsetMs: 0,
|
||||
|
@ -842,7 +803,7 @@
|
|||
$('#video-subtitleButton', videoControls).hide();
|
||||
}
|
||||
|
||||
if (item.Chapters && item.Chapters.length) {
|
||||
if (mediaVersion.Chapters && mediaVersion.Chapters.length) {
|
||||
$('#video-chaptersButton', videoControls).show();
|
||||
} else {
|
||||
$('#video-chaptersButton', videoControls).hide();
|
||||
|
@ -889,9 +850,9 @@
|
|||
|
||||
videoElement.off("playing.once");
|
||||
|
||||
ApiClient.reportPlaybackStart(Dashboard.getCurrentUserId(), item.Id, true, item.MediaType);
|
||||
ApiClient.reportPlaybackStart(Dashboard.getCurrentUserId(), mediaVersion.ItemId, true, item.MediaType);
|
||||
|
||||
self.startProgressInterval(item.Id);
|
||||
self.startProgressInterval(mediaVersion.ItemId);
|
||||
|
||||
}).on("pause", function (e) {
|
||||
|
||||
|
@ -1007,6 +968,7 @@
|
|||
fullscreenExited = false;
|
||||
|
||||
currentItem = item;
|
||||
currentMediaVersion = mediaVersion;
|
||||
|
||||
return videoElement[0];
|
||||
};
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
var currentMediaElement;
|
||||
var currentProgressInterval;
|
||||
var currentItem;
|
||||
var currentMediaVersion;
|
||||
var curentDurationTicks;
|
||||
var canClientSeek;
|
||||
var currentPlaylistIndex = 0;
|
||||
|
@ -53,7 +54,7 @@
|
|||
|
||||
var position = Math.floor(10000000 * endTime) + self.startTimeTicksOffset;
|
||||
|
||||
ApiClient.reportPlaybackStopped(Dashboard.getCurrentUserId(), currentItem.Id, position);
|
||||
ApiClient.reportPlaybackStopped(Dashboard.getCurrentUserId(), currentMediaVersion.ItemId, position);
|
||||
|
||||
if (currentItem.MediaType == "Video") {
|
||||
ApiClient.stopActiveEncodings();
|
||||
|
@ -135,7 +136,7 @@
|
|||
|
||||
var transcodingExtension = self.getTranscodingExtension();
|
||||
|
||||
var finalParams = self.getFinalVideoParams(currentItem, maxWidth, bitrate, audioStreamIndex, subtitleStreamIndex, transcodingExtension);
|
||||
var finalParams = self.getFinalVideoParams(currentMediaVersion, maxWidth, bitrate, audioStreamIndex, subtitleStreamIndex, transcodingExtension);
|
||||
currentSrc = replaceQueryString(currentSrc, 'MaxWidth', finalParams.maxWidth);
|
||||
currentSrc = replaceQueryString(currentSrc, 'VideoBitrate', finalParams.videoBitrate);
|
||||
currentSrc = replaceQueryString(currentSrc, 'AudioBitrate', finalParams.audioBitrate);
|
||||
|
@ -155,8 +156,8 @@
|
|||
|
||||
$(this).off('play.onceafterseek').on('ended.playbackstopped', self.onPlaybackStopped).on('ended.playnext', self.playNextAfterEnded);
|
||||
|
||||
self.startProgressInterval(currentItem.Id);
|
||||
sendProgressUpdate(currentItem.Id);
|
||||
self.startProgressInterval(currentMediaVersion.ItemId);
|
||||
sendProgressUpdate(currentMediaVersion.ItemId);
|
||||
|
||||
});
|
||||
|
||||
|
@ -193,9 +194,9 @@
|
|||
self.currentTimeElement.html(timeText);
|
||||
};
|
||||
|
||||
self.canPlayVideoDirect = function (item, videoStream, audioStream, subtitleStream, maxWidth, bitrate) {
|
||||
self.canPlayVideoDirect = function (mediaVersion, videoStream, audioStream, subtitleStream, maxWidth, bitrate) {
|
||||
|
||||
if (item.VideoType != "VideoFile" || item.LocationType != "FileSystem") {
|
||||
if (mediaVersion.VideoType != "VideoFile" || mediaVersion.LocationType != "FileSystem") {
|
||||
console.log('Transcoding because the content is not a video file');
|
||||
return false;
|
||||
}
|
||||
|
@ -228,7 +229,7 @@
|
|||
return false;
|
||||
}
|
||||
|
||||
var extension = item.Path.substring(item.Path.lastIndexOf('.') + 1).toLowerCase();
|
||||
var extension = mediaVersion.Path.substring(mediaVersion.Path.lastIndexOf('.') + 1).toLowerCase();
|
||||
|
||||
if (extension == 'm4v') {
|
||||
return $.browser.chrome;
|
||||
|
@ -237,28 +238,29 @@
|
|||
return extension.toLowerCase() == 'mp4';
|
||||
};
|
||||
|
||||
self.getFinalVideoParams = function (item, maxWidth, bitrate, audioStreamIndex, subtitleStreamIndex, transcodingExtension) {
|
||||
self.getFinalVideoParams = function (mediaVersion, maxWidth, bitrate, audioStreamIndex, subtitleStreamIndex, transcodingExtension) {
|
||||
|
||||
var videoStream = (item.MediaStreams || []).filter(function (stream) {
|
||||
var mediaStreams = mediaVersion.MediaStreams;
|
||||
|
||||
var videoStream = mediaStreams.filter(function (stream) {
|
||||
return stream.Type === "Video";
|
||||
})[0];
|
||||
|
||||
var audioStream = (item.MediaStreams || []).filter(function (stream) {
|
||||
var audioStream = mediaStreams.filter(function (stream) {
|
||||
return stream.Index === audioStreamIndex;
|
||||
})[0];
|
||||
|
||||
var subtitleStream = (item.MediaStreams || []).filter(function (stream) {
|
||||
var subtitleStream = mediaStreams.filter(function (stream) {
|
||||
return stream.Index === subtitleStreamIndex;
|
||||
})[0];
|
||||
|
||||
var canPlayDirect = self.canPlayVideoDirect(item, videoStream, audioStream, subtitleStream, maxWidth, bitrate);
|
||||
var canPlayDirect = self.canPlayVideoDirect(mediaVersion, videoStream, audioStream, subtitleStream, maxWidth, bitrate);
|
||||
|
||||
var audioBitrate = bitrate >= 700000 ? 128000 : 64000;
|
||||
|
||||
var videoBitrate = bitrate - audioBitrate;
|
||||
|
||||
return {
|
||||
|
||||
isStatic: canPlayDirect,
|
||||
maxWidth: maxWidth,
|
||||
audioCodec: transcodingExtension == '.webm' ? 'vorbis' : 'aac',
|
||||
|
@ -266,7 +268,7 @@
|
|||
audioBitrate: audioBitrate,
|
||||
videoBitrate: videoBitrate
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
self.canPlay = function (item, user) {
|
||||
|
||||
|
@ -398,6 +400,13 @@
|
|||
currentPlaylistIndex = 0;
|
||||
};
|
||||
|
||||
function getOptimalMediaVersion(versions) {
|
||||
|
||||
// TODO: Implement
|
||||
|
||||
return versions[0];
|
||||
}
|
||||
|
||||
self.playInternal = function (item, startPosition, user) {
|
||||
|
||||
if (item == null) {
|
||||
|
@ -414,17 +423,25 @@
|
|||
|
||||
if (item.MediaType === "Video") {
|
||||
|
||||
videoPlayer(self, item, startPosition, user);
|
||||
mediaElement = self.initVideoPlayer();
|
||||
currentItem = item;
|
||||
curentDurationTicks = item.RunTimeTicks;
|
||||
currentMediaVersion = getOptimalMediaVersion(item.MediaVersions);
|
||||
|
||||
videoPlayer(self, item, currentMediaVersion, startPosition, user);
|
||||
mediaElement = self.initVideoPlayer();
|
||||
curentDurationTicks = currentMediaVersion.RunTimeTicks;
|
||||
|
||||
mediaControls = $("#videoControls");
|
||||
|
||||
} else if (item.MediaType === "Audio") {
|
||||
|
||||
mediaElement = playAudio(item, startPosition);
|
||||
currentItem = item;
|
||||
currentMediaVersion = getOptimalMediaVersion(item.MediaVersions);
|
||||
|
||||
mediaElement = playAudio(item, currentMediaVersion, startPosition);
|
||||
mediaControls.show();
|
||||
|
||||
curentDurationTicks = currentMediaVersion.RunTimeTicks;
|
||||
|
||||
} else {
|
||||
throw new Error("Unrecognized media type");
|
||||
}
|
||||
|
@ -917,7 +934,7 @@
|
|||
|
||||
var position = Math.floor(10000000 * endTime) + self.startTimeTicksOffset;
|
||||
|
||||
ApiClient.reportPlaybackStopped(Dashboard.getCurrentUserId(), currentItem.Id, position);
|
||||
ApiClient.reportPlaybackStopped(Dashboard.getCurrentUserId(), currentMediaVersion.ItemId, position);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -968,7 +985,7 @@
|
|||
|
||||
var newPercent = parseInt(this.value);
|
||||
|
||||
var newPositionTicks = (newPercent / 100) * currentItem.RunTimeTicks;
|
||||
var newPositionTicks = (newPercent / 100) * currentMediaVersion.RunTimeTicks;
|
||||
|
||||
self.changeStream(Math.floor(newPositionTicks));
|
||||
};
|
||||
|
@ -982,7 +999,7 @@
|
|||
return d >= 0 && text.lastIndexOf(pattern) === d;
|
||||
};
|
||||
|
||||
function playAudio(item, startPositionTicks) {
|
||||
function playAudio(item, mediaVersion, startPositionTicks) {
|
||||
|
||||
startPositionTicks = startPositionTicks || 0;
|
||||
|
||||
|
@ -992,19 +1009,19 @@
|
|||
StartTimeTicks: startPositionTicks
|
||||
};
|
||||
|
||||
var mp3Url = ApiClient.getUrl('Audio/' + item.Id + '/stream.mp3', $.extend({}, baseParams, {
|
||||
var mp3Url = ApiClient.getUrl('Audio/' + mediaVersion.ItemId + '/stream.mp3', $.extend({}, baseParams, {
|
||||
audioCodec: 'mp3'
|
||||
}));
|
||||
|
||||
var aacUrl = ApiClient.getUrl('Audio/' + item.Id + '/stream.aac', $.extend({}, baseParams, {
|
||||
var aacUrl = ApiClient.getUrl('Audio/' + mediaVersion.ItemId + '/stream.aac', $.extend({}, baseParams, {
|
||||
audioCodec: 'aac'
|
||||
}));
|
||||
|
||||
var webmUrl = ApiClient.getUrl('Audio/' + item.Id + '/stream.webm', $.extend({}, baseParams, {
|
||||
var webmUrl = ApiClient.getUrl('Audio/' + mediaVersion.ItemId + '/stream.webm', $.extend({}, baseParams, {
|
||||
audioCodec: 'Vorbis'
|
||||
}));
|
||||
|
||||
var mediaStreams = item.MediaStreams || [];
|
||||
var mediaStreams = mediaVersion.MediaStreams;
|
||||
|
||||
var isStatic = false;
|
||||
var seekParam = isStatic && startPositionTicks ? '#t=' + (startPositionTicks / 10000000) : '';
|
||||
|
@ -1091,9 +1108,9 @@
|
|||
|
||||
audioElement.off("play.once");
|
||||
|
||||
ApiClient.reportPlaybackStart(Dashboard.getCurrentUserId(), item.Id, true, item.MediaType);
|
||||
ApiClient.reportPlaybackStart(Dashboard.getCurrentUserId(), mediaVersion.ItemId, true, item.MediaType);
|
||||
|
||||
self.startProgressInterval(item.Id);
|
||||
self.startProgressInterval(mediaVersion.ItemId);
|
||||
|
||||
}).on("pause", function () {
|
||||
|
||||
|
@ -1114,9 +1131,6 @@
|
|||
|
||||
}).on("ended.playbackstopped", self.onPlaybackStopped).on('ended.playnext', self.playNextAfterEnded);
|
||||
|
||||
currentItem = item;
|
||||
curentDurationTicks = item.RunTimeTicks;
|
||||
|
||||
return audioElement[0];
|
||||
};
|
||||
|
||||
|
|
|
@ -299,18 +299,6 @@
|
|||
resumeButtonContainer.show();
|
||||
}
|
||||
}
|
||||
else if (value == "PlayFromChapter" && item.Chapters && item.Chapters.length) {
|
||||
|
||||
playFromMenu.show();
|
||||
playButtonContainer.show();
|
||||
|
||||
if (!playFromRendered) {
|
||||
playFromRendered = true;
|
||||
renderPlayFromOptions(playFromMenu, item);
|
||||
}
|
||||
|
||||
popup.popup("reposition", { tolerance: 0 });
|
||||
}
|
||||
else if (value == "Trailer") {
|
||||
|
||||
trailersElem.show();
|
||||
|
@ -384,65 +372,6 @@
|
|||
});
|
||||
}
|
||||
|
||||
function renderPlayFromOptions(elem, item) {
|
||||
|
||||
var html = '';
|
||||
|
||||
html += '<h4 style="margin: 1em 0 .5em;">Play from scene</h4>';
|
||||
|
||||
html += '<div class="playMenuOptions">';
|
||||
html += '<table class="tblRemoteControl tblRemoteControlNoHeader">';
|
||||
|
||||
html += '<tbody>';
|
||||
|
||||
for (var i = 0, length = item.Chapters.length; i < length; i++) {
|
||||
|
||||
var chapter = item.Chapters[i];
|
||||
|
||||
html += '<tr class="trSelectPlayTime" data-ticks="' + chapter.StartPositionTicks + '">';
|
||||
|
||||
var name = chapter.Name || ("Chapter " + (i + 1));
|
||||
|
||||
html += '<td class="tdSelectPlayTime"></td>';
|
||||
|
||||
html += '<td class="tdRemoteControlImage">';
|
||||
|
||||
var imgUrl;
|
||||
|
||||
if (chapter.ImageTag) {
|
||||
|
||||
imgUrl = ApiClient.getImageUrl(item.Id, {
|
||||
maxheight: 80,
|
||||
tag: chapter.ImageTag,
|
||||
type: "Chapter",
|
||||
index: i
|
||||
});
|
||||
|
||||
} else {
|
||||
imgUrl = "css/images/media/chapterflyout.png";
|
||||
}
|
||||
|
||||
html += '<img src="' + imgUrl + '" />';
|
||||
|
||||
html += '</td>';
|
||||
|
||||
html += '<td>' + name + '<br/>' + Dashboard.getDisplayTime(chapter.StartPositionTicks) + '</td>';
|
||||
|
||||
html += '</tr>';
|
||||
}
|
||||
|
||||
html += '</tbody>';
|
||||
|
||||
html += '</table>';
|
||||
html += '</div>';
|
||||
|
||||
elem.html(html);
|
||||
|
||||
$('.tdSelectPlayTime', elem).html('<input type="radio" class="chkSelectPlayTime" name="chkSelectPlayTime" />');
|
||||
|
||||
$('.chkSelectPlayTime:first', elem).checked(true);
|
||||
}
|
||||
|
||||
function renderSessionsInPlayMenu(sessions, options, elem, popup) {
|
||||
|
||||
if (!sessions.length) {
|
||||
|
@ -476,10 +405,6 @@
|
|||
|
||||
html += '<option value="Play" selected>' + playLabel + '</label>';
|
||||
|
||||
if (item.Chapters && item.Chapters.length) {
|
||||
html += '<option value="PlayFromChapter">Play from scene</label>';
|
||||
}
|
||||
|
||||
if (item.LocalTrailerCount) {
|
||||
html += '<option value="Trailer">Play trailer</label>';
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue