Change media info display (#386)

* Add btn more media info

* Add event listener

* change class and add close btn

* Add style

* Add event listener display

* hide btn

* remove media info form itemdetail page

* Add media templete

* cleanup unused stayle

* Add to context menu

* cleanup

* cleanup

* fix

* add todo to file path info

* allow admin and user to view the media info

* Add to strings

* Remove media info menu from book and music

* Remove splitVersions function

* Readd splitVersions function on item deatil page

* Remove unused elements

* double quotes & removing newlines

* minor style changes

* fix format and path info on itemdetails page

* update
This commit is contained in:
grafixeyehero 2019-08-19 12:35:57 +03:00 committed by dkanada
parent c46f577f08
commit 5fda5a8738
7 changed files with 251 additions and 196 deletions

View file

@ -0,0 +1,164 @@
define(["dialogHelper", "require", "layoutManager", "globalize", "userSettings", "connectionManager", "loading", "focusManager", "dom", "apphost", "emby-select", "listViewStyle", "paper-icon-button-light", "css!./../formdialog", "material-icons", "emby-button", "flexStyles"], function (dialogHelper, require, layoutManager, globalize, userSettings, connectionManager, loading, focusManager, dom, appHost) {
"use strict";
function setMediaInfo(user, page, item) {
var html = item.MediaSources.map(function (version) {
return getMediaSourceHtml(user, item, version);
}).join('<div style="border-top:1px solid #444;margin: 1em 0;"></div>');
if (item.MediaSources.length > 1) {
html = "<br/>" + html;
}
var mediaInfoContent = page.querySelector("#mediaInfoContent");
mediaInfoContent.innerHTML = html;
}
function getMediaSourceHtml(user, item, version) {
var html = "";
if (version.Name) {
html += '<div><h2 class="mediaInfoStreamType">' + version.Name + "</h2></div>";
}
if (version.Container) {
html += createAttribute(globalize.translate("MediaInfoContainer"), version.Container) + "<br/>";
}
if (version.Formats && version.Formats.length) {
html += createAttribute(globalize.translate("MediaInfoFormat"), version.Formats.join(",")) + "<br/>";
}
if (version.Path && user && user.Policy.IsAdministrator) {
html += createAttribute(globalize.translate("MediaInfoPath"), version.Path) + "<br/>";
}
if (version.Size && user && user.Policy.IsAdministrator) {
var size = (version.Size / (1024 * 1024)).toFixed(0) + " MB";
html += createAttribute(globalize.translate("MediaInfoSize"), size) + "<br/>";
}
for (var i = 0, length = version.MediaStreams.length; i < length; i++) {
var stream = version.MediaStreams[i];
if (stream.Type === "Data") {
continue;
}
html += '<div class="mediaInfoStream">';
var displayType = globalize.translate("MediaInfoStreamType" + stream.Type);
html += '<h2 class="mediaInfoStreamType">' + displayType + "</h2>";
var attributes = [];
if (stream.DisplayTitle) {
attributes.push(createAttribute("Title", stream.DisplayTitle));
}
if (stream.Language && stream.Type !== "Video") {
attributes.push(createAttribute(globalize.translate("MediaInfoLanguage"), stream.Language));
}
if (stream.Codec) {
attributes.push(createAttribute(globalize.translate("MediaInfoCodec"), stream.Codec.toUpperCase()));
}
if (stream.CodecTag) {
attributes.push(createAttribute(globalize.translate("MediaInfoCodecTag"), stream.CodecTag));
}
if (stream.IsAVC != null) {
attributes.push(createAttribute("AVC", (stream.IsAVC ? "Yes" : "No")));
}
if (stream.Profile) {
attributes.push(createAttribute(globalize.translate("MediaInfoProfile"), stream.Profile));
}
if (stream.Level) {
attributes.push(createAttribute(globalize.translate("MediaInfoLevel"), stream.Level));
}
if (stream.Width || stream.Height) {
attributes.push(createAttribute(globalize.translate("MediaInfoResolution"), stream.Width + "x" + stream.Height));
}
if (stream.AspectRatio && stream.Codec !== "mjpeg") {
attributes.push(createAttribute(globalize.translate("MediaInfoAspectRatio"), stream.AspectRatio));
}
if (stream.Type === "Video") {
if (stream.IsAnamorphic != null) {
attributes.push(createAttribute(globalize.translate("MediaInfoAnamorphic"), (stream.IsAnamorphic ? "Yes" : "No")));
}
attributes.push(createAttribute(globalize.translate("MediaInfoInterlaced"), (stream.IsInterlaced ? "Yes" : "No")));
}
if (stream.AverageFrameRate || stream.RealFrameRate) {
attributes.push(createAttribute(globalize.translate("MediaInfoFramerate"), (stream.AverageFrameRate || stream.RealFrameRate)));
}
if (stream.ChannelLayout) {
attributes.push(createAttribute(globalize.translate("MediaInfoLayout"), stream.ChannelLayout));
}
if (stream.Channels) {
attributes.push(createAttribute(globalize.translate("MediaInfoChannels"), stream.Channels + " ch"));
}
if (stream.BitRate && stream.Codec !== "mjpeg") {
attributes.push(createAttribute(globalize.translate("MediaInfoBitrate"), (parseInt(stream.BitRate / 1000)) + " kbps"));
}
if (stream.SampleRate) {
attributes.push(createAttribute(globalize.translate("MediaInfoSampleRate"), stream.SampleRate + " Hz"));
}
if (stream.BitDepth) {
attributes.push(createAttribute(globalize.translate("MediaInfoBitDepth"), stream.BitDepth + " bit"));
}
if (stream.PixelFormat) {
attributes.push(createAttribute(globalize.translate("MediaInfoPixelFormat"), stream.PixelFormat));
}
if (stream.RefFrames) {
attributes.push(createAttribute(globalize.translate("MediaInfoRefFrames"), stream.RefFrames));
}
if (stream.NalLengthSize) {
attributes.push(createAttribute("NAL", stream.NalLengthSize));
}
if (stream.Type !== "Video") {
attributes.push(createAttribute(globalize.translate("MediaInfoDefault"), (stream.IsDefault ? "Yes" : "No")));
}
if (stream.Type === "Subtitle") {
attributes.push(createAttribute(globalize.translate("MediaInfoForced"), (stream.IsForced ? "Yes" : "No")));
attributes.push(createAttribute(globalize.translate("MediaInfoExternal"), (stream.IsExternal ? "Yes" : "No")));
}
if (stream.Type === "Video" && version.Timestamp) {
attributes.push(createAttribute(globalize.translate("MediaInfoTimestamp"), version.Timestamp));
}
html += attributes.join("<br/>");
html += "</div>";
}
return html;
}
function createAttribute(label, value) {
return '<span class="mediaInfoLabel">' + label + '</span><span class="mediaInfoAttribute">' + value + "</span>"
}
function showMediaInfoMore(itemId, serverId, template) {
var apiClient = connectionManager.getApiClient(serverId);
return apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(function (item) {
var dialogOptions = {
size: "small",
removeOnClose: true,
scrollY: false
};
if (layoutManager.tv) {
dialogOptions.size = "fullscreen";
}
var dlg = dialogHelper.createDialog(dialogOptions);
dlg.classList.add("formDialog");
var html = "";
html += globalize.translateDocument(template, "core");
dlg.innerHTML = html;
if (layoutManager.tv) {
scrollHelper.centerFocus.on(dlg.querySelector(".formDialogContent"), false);
}
dialogHelper.open(dlg);
dlg.querySelector(".btnCancel").addEventListener("click", function (e) {
dialogHelper.close(dlg);
});
apiClient.getCurrentUser().then(function (user) {
setMediaInfo(user, dlg, item);
});
loading.hide();
});
}
function showMediaInfo(itemId, serverId) {
loading.show();
return new Promise(function (resolve, reject) {
require(["text!./itemMediaInfo.template.html"], function (template) {
showMediaInfoMore(itemId, serverId, template).then(resolve, reject);
});
});
}
return {
show: showMediaInfo
};
});

View file

@ -0,0 +1,12 @@
<div class="formDialogHeader">
<button is="paper-icon-button-light" class="btnCancel autoSize" tabindex="-1"><i class="md-icon">&#xE5C4;</i></button>
<h3 class="formDialogHeaderTitle">
${HeaderMediaInfo}
</h3>
</div>
<div class="formDialogContent smoothScrollY">
<div class="dialogContentInner" style="padding: 1em">
<div id="mediaInfoContent" class="mediaInfoContent"></div>
</div>
</div>

View file

@ -2,23 +2,19 @@ define(['apphost', 'globalize', 'connectionManager', 'itemHelper', 'appRouter',
'use strict';
function getCommands(options) {
var item = options.item;
var canPlay = playbackManager.canPlay(item);
var commands = [];
var restrictOptions = (browser.operaTv || browser.web0s) && !user.Policy.IsAdministrator;
var user = options.user;
var restrictOptions = (browser.operaTv || browser.web0s) && !user.Policy.IsAdministrator;
var commands = [];
if (canPlay && item.MediaType !== 'Photo') {
if (options.play !== false) {
commands.push({
name: globalize.translate('Play'),
id: 'resume'
});
});
}
if (options.playAllFromHere && item.Type !== 'Program' && item.Type !== 'TvChannel') {
@ -30,7 +26,6 @@ define(['apphost', 'globalize', 'connectionManager', 'itemHelper', 'appRouter',
}
if (playbackManager.canQueue(item)) {
if (options.queue !== false) {
commands.push({
name: globalize.translate('AddToPlayQueue'),
@ -143,20 +138,16 @@ define(['apphost', 'globalize', 'connectionManager', 'itemHelper', 'appRouter',
var canEdit = itemHelper.canEdit(user, item);
if (canEdit) {
if (options.edit !== false && item.Type !== 'SeriesTimer') {
var text = (item.Type === 'Timer' || item.Type === 'SeriesTimer') ? globalize.translate('Edit') : globalize.translate('EditMetadata');
commands.push({
name: text,
commands.push({
name: text,
id: 'edit'
});
}
}
}
if (itemHelper.canEditImages(user, item)) {
if (options.editImages !== false) {
commands.push({
name: globalize.translate('EditImages'),
@ -166,7 +157,6 @@ define(['apphost', 'globalize', 'connectionManager', 'itemHelper', 'appRouter',
}
if (canEdit) {
if (item.MediaType === 'Video' && item.Type !== 'TvChannel' && item.Type !== 'Program' && item.LocationType !== 'Virtual' && !(item.Type === 'Recording' && item.Status !== 'Completed')) {
if (options.editSubtitles !== false) {
commands.push({
@ -186,8 +176,16 @@ define(['apphost', 'globalize', 'connectionManager', 'itemHelper', 'appRouter',
}
}
if (item.Type === 'Program' && options.record !== false) {
if (item.MediaSources) {
if (options.moremediainfo !== false) {
commands.push({
name: globalize.translate('MoreMediaInfo'),
id: 'moremediainfo'
});
}
}
if (item.Type === 'Program' && options.record !== false) {
if (item.TimerId) {
commands.push({
name: Globalize.translate('ManageRecording'),
@ -197,7 +195,6 @@ define(['apphost', 'globalize', 'connectionManager', 'itemHelper', 'appRouter',
}
if (item.Type === 'Program' && options.record !== false) {
if (!item.TimerId) {
commands.push({
name: Globalize.translate('Record'),
@ -265,7 +262,6 @@ define(['apphost', 'globalize', 'connectionManager', 'itemHelper', 'appRouter',
}
function getResolveFunction(resolve, id, changed, deleted) {
return function () {
resolve({
command: id,
@ -273,146 +269,108 @@ define(['apphost', 'globalize', 'connectionManager', 'itemHelper', 'appRouter',
deleted: deleted
});
};
}
}
function executeCommand(item, id, options) {
var itemId = item.Id;
var serverId = item.ServerId;
var apiClient = connectionManager.getApiClient(serverId);
return new Promise(function (resolve, reject) {
switch (id) {
case 'addtocollection':
{
require(['collectionEditor'], function (collectionEditor) {
new collectionEditor().show({
require(['collectionEditor'], function (collectionEditor) {
new collectionEditor().show({
items: [itemId],
serverId: serverId
}).then(getResolveFunction(resolve, id, true), getResolveFunction(resolve, id));
}).then(getResolveFunction(resolve, id, true), getResolveFunction(resolve, id));
});
break;
}
case 'addtoplaylist':
{
require(['playlistEditor'], function (playlistEditor) {
new playlistEditor().show({
require(['playlistEditor'], function (playlistEditor) {
new playlistEditor().show({
items: [itemId],
serverId: serverId
}).then(getResolveFunction(resolve, id, true), getResolveFunction(resolve, id));
}).then(getResolveFunction(resolve, id, true), getResolveFunction(resolve, id));
});
break;
}
case 'download':
{
require(['fileDownloader'], function (fileDownloader) {
require(['fileDownloader'], function (fileDownloader) {
var downloadHref = apiClient.getItemDownloadUrl(itemId);
fileDownloader.download([{
url: downloadHref,
itemId: itemId,
serverId: serverId
}]);
getResolveFunction(getResolveFunction(resolve, id), id)();
fileDownloader.download([{
url: downloadHref,
itemId: itemId,
serverId: serverId
}]);
getResolveFunction(getResolveFunction(resolve, id), id)();
});
break;
}
case 'editsubtitles':
{
require(['subtitleEditor'], function (subtitleEditor) {
subtitleEditor.show(itemId, serverId).then(getResolveFunction(resolve, id, true), getResolveFunction(resolve, id));
require(['subtitleEditor'], function (subtitleEditor) {
subtitleEditor.show(itemId, serverId).then(getResolveFunction(resolve, id, true), getResolveFunction(resolve, id));
});
break;
}
case 'edit':
{
editItem(apiClient, item).then(getResolveFunction(resolve, id, true), getResolveFunction(resolve, id));
editItem(apiClient, item).then(getResolveFunction(resolve, id, true), getResolveFunction(resolve, id));
break;
}
case 'editimages':
{
require(['imageEditor'], function (imageEditor) {
require(['imageEditor'], function (imageEditor) {
imageEditor.show({
itemId: itemId,
serverId: serverId
}).then(getResolveFunction(resolve, id, true), getResolveFunction(resolve, id));
}).then(getResolveFunction(resolve, id, true), getResolveFunction(resolve, id));
});
break;
}
case 'identify':
{
require(['itemIdentifier'], function (itemIdentifier) {
itemIdentifier.show(itemId, serverId).then(getResolveFunction(resolve, id, true), getResolveFunction(resolve, id));
require(['itemIdentifier'], function (itemIdentifier) {
itemIdentifier.show(itemId, serverId).then(getResolveFunction(resolve, id, true), getResolveFunction(resolve, id));
});
break;
case 'moremediainfo':
require(['itemMediaInfo'], function (itemMediaInfo) {
itemMediaInfo.show(itemId, serverId).then(getResolveFunction(resolve, id, true), getResolveFunction(resolve, id));
});
break;
}
case 'refresh':
{
refresh(apiClient, item);
getResolveFunction(resolve, id)();
refresh(apiClient, item);
getResolveFunction(resolve, id)();
break;
}
case 'open':
{
appRouter.showItem(item);
getResolveFunction(resolve, id)();
appRouter.showItem(item);
getResolveFunction(resolve, id)();
break;
}
case 'play':
{
play(item, false);
getResolveFunction(resolve, id)();
play(item, false);
getResolveFunction(resolve, id)();
break;
}
case 'resume':
{
play(item, true);
getResolveFunction(resolve, id)();
play(item, true);
getResolveFunction(resolve, id)();
break;
}
case 'queue':
{
play(item, false, true);
getResolveFunction(resolve, id)();
play(item, false, true);
getResolveFunction(resolve, id)();
break;
}
case 'queuenext':
{
play(item, false, true, true);
getResolveFunction(resolve, id)();
play(item, false, true, true);
getResolveFunction(resolve, id)();
break;
}
case 'record':
require(['recordingCreator'], function (recordingCreator) {
recordingCreator.show(itemId, serverId).then(getResolveFunction(resolve, id, true), getResolveFunction(resolve, id));
});
break;
case 'shuffle':
{
playbackManager.shuffle(item);
getResolveFunction(resolve, id)();
playbackManager.shuffle(item);
getResolveFunction(resolve, id)();
break;
}
case 'instantmix':
{
playbackManager.instantMix(item);
getResolveFunction(resolve, id)();
playbackManager.instantMix(item);
getResolveFunction(resolve, id)();
break;
}
case 'delete':
{
deleteItem(apiClient, item).then(getResolveFunction(resolve, id, true, true), getResolveFunction(resolve, id));
deleteItem(apiClient, item).then(getResolveFunction(resolve, id, true, true), getResolveFunction(resolve, id));
break;
}
case 'share':
navigator.share({
title: item.Name,
@ -432,10 +390,8 @@ define(['apphost', 'globalize', 'connectionManager', 'itemHelper', 'appRouter',
getResolveFunction(resolve, id)();
break;
case 'queueallfromhere':
{
getResolveFunction(resolve, id)();
break;
}
case 'removefromplaylist':
apiClient.ajax({
url: apiClient.getUrl('Playlists/' + options.playlistId + '/Items', {
@ -471,11 +427,8 @@ define(['apphost', 'globalize', 'connectionManager', 'itemHelper', 'appRouter',
}
function deleteTimer(apiClient, item, resolve, command) {
require(['recordingHelper'], function (recordingHelper) {
var timerId = item.TimerId || item.Id;
recordingHelper.cancelTimerWithConfirmation(timerId, item.ServerId).then(function () {
getResolveFunction(resolve, command, true)();
});
@ -483,9 +436,7 @@ define(['apphost', 'globalize', 'connectionManager', 'itemHelper', 'appRouter',
}
function deleteSeriesTimer(apiClient, item, resolve, command) {
require(['recordingHelper'], function (recordingHelper) {
recordingHelper.cancelSeriesTimerWithConfirmation(item.Id, item.ServerId).then(function () {
getResolveFunction(resolve, command, true)();
});
@ -493,7 +444,6 @@ define(['apphost', 'globalize', 'connectionManager', 'itemHelper', 'appRouter',
}
function play(item, resume, queue, queueNext) {
var method = queue ? (queueNext ? 'queueNext' : 'queue') : 'play';
var startPosition = 0;
@ -503,37 +453,32 @@ define(['apphost', 'globalize', 'connectionManager', 'itemHelper', 'appRouter',
if (item.Type === 'Program') {
playbackManager[method]({
ids: [item.ChannelId],
startPositionTicks: startPosition,
serverId: item.ServerId
ids: [item.ChannelId],
startPositionTicks: startPosition,
serverId: item.ServerId
});
} else {
playbackManager[method]({
items: [item],
startPositionTicks: startPosition
items: [item],
startPositionTicks: startPosition
});
}
}
function editItem(apiClient, item) {
return new Promise(function (resolve, reject) {
var serverId = apiClient.serverInfo().Id;
if (item.Type === 'Timer') {
require(['recordingEditor'], function (recordingEditor) {
recordingEditor.show(item.Id, serverId).then(resolve, reject);
});
} else if (item.Type === 'SeriesTimer') {
require(['seriesRecordingEditor'], function (recordingEditor) {
recordingEditor.show(item.Id, serverId).then(resolve, reject);
});
} else {
require(['metadataEditor'], function (metadataEditor) {
metadataEditor.show(item.Id, serverId).then(resolve, reject);
});
}
@ -541,28 +486,19 @@ define(['apphost', 'globalize', 'connectionManager', 'itemHelper', 'appRouter',
}
function deleteItem(apiClient, item) {
return new Promise(function (resolve, reject) {
require(['deleteHelper'], function (deleteHelper) {
deleteHelper.deleteItem({
item: item,
navigate: false
}).then(function () {
resolve(true);
}, reject);
});
});
}
function refresh(apiClient, item) {
require(['refreshDialog'], function (refreshDialog) {
new refreshDialog({
itemIds: [item.Id],
@ -573,20 +509,15 @@ define(['apphost', 'globalize', 'connectionManager', 'itemHelper', 'appRouter',
}
function show(options) {
var commands = getCommands(options);
if (!commands.length) {
return Promise.reject();
}
return actionsheet.show({
items: commands,
positionTo: options.positionTo,
resolveOnClick: ['share']
}).then(function (id) {
return executeCommand(options.item, id, options);
});
@ -596,4 +527,4 @@ define(['apphost', 'globalize', 'connectionManager', 'itemHelper', 'appRouter',
getCommands: getCommands,
show: show
};
});
});

View file

@ -301,7 +301,7 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "cardBuild
var groupedVersions = (item.MediaSources || []).filter(function(g) {
return "Grouping" == g.Type
});
user.Policy.IsAdministrator && groupedVersions.length ? page.querySelector(".splitVersionContainer").classList.remove("hide") : page.querySelector(".splitVersionContainer").classList.add("hide"), itemContextMenu.getCommands(getContextMenuOptions(item, user)).length ? hideAll(page, "btnMoreCommands", !0) : hideAll(page, "btnMoreCommands");
user.Policy.IsAdministrator && groupedVersions.length ? page.querySelector(".btnSplitVersions").classList.remove("hide") : page.querySelector(".btnSplitVersions").classList.add("hide"), itemContextMenu.getCommands(getContextMenuOptions(item, user)).length ? hideAll(page, "btnMoreCommands", !0) : hideAll(page, "btnMoreCommands");
var itemBirthday = page.querySelector("#itemBirthday");
if ("Person" == item.Type && item.PremiereDate) try {
var birthday = datetime.parseISO8601Date(item.PremiereDate, !0).toDateString();
@ -474,7 +474,7 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "cardBuild
}
function setInitialCollapsibleState(page, item, apiClient, context, user) {
page.querySelector(".collectionItems").innerHTML = "", "Playlist" == item.Type ? (page.querySelector("#childrenCollapsible").classList.remove("hide"), renderPlaylistItems(page, item, user)) : "Studio" == item.Type || "Person" == item.Type || "Genre" == item.Type || "MusicGenre" == item.Type || "MusicArtist" == item.Type ? (page.querySelector("#childrenCollapsible").classList.remove("hide"), renderItemsByName(page, item, user)) : item.IsFolder ? ("BoxSet" == item.Type && page.querySelector("#childrenCollapsible").classList.add("hide"), renderChildren(page, item)) : page.querySelector("#childrenCollapsible").classList.add("hide"), "Series" == item.Type && renderSeriesSchedule(page, item, user), "Series" == item.Type ? renderNextUp(page, item, user) : page.querySelector(".nextUpSection").classList.add("hide"), item.MediaSources && item.MediaSources.length && (null == item.EnableMediaSourceDisplay ? "Channel" !== item.SourceType : item.EnableMediaSourceDisplay) ? renderMediaSources(page, user, item) : page.querySelector(".audioVideoMediaInfo").classList.add("hide"), renderScenes(page, item), item.SpecialFeatureCount && 0 != item.SpecialFeatureCount && "Series" != item.Type ? (page.querySelector("#specialsCollapsible").classList.remove("hide"), renderSpecials(page, item, user, 6)) : page.querySelector("#specialsCollapsible").classList.add("hide"), renderCast(page, item, context, enableScrollX() ? null : 12), item.PartCount && item.PartCount > 1 ? (page.querySelector("#additionalPartsCollapsible").classList.remove("hide"), renderAdditionalParts(page, item, user)) : page.querySelector("#additionalPartsCollapsible").classList.add("hide"), "MusicAlbum" == item.Type ? renderMusicVideos(page, item, user) : page.querySelector("#musicVideosCollapsible").classList.add("hide")
page.querySelector(".collectionItems").innerHTML = "", "Playlist" == item.Type ? (page.querySelector("#childrenCollapsible").classList.remove("hide"), renderPlaylistItems(page, item, user)) : "Studio" == item.Type || "Person" == item.Type || "Genre" == item.Type || "MusicGenre" == item.Type || "MusicArtist" == item.Type ? (page.querySelector("#childrenCollapsible").classList.remove("hide"), renderItemsByName(page, item, user)) : item.IsFolder ? ("BoxSet" == item.Type && page.querySelector("#childrenCollapsible").classList.add("hide"), renderChildren(page, item)) : page.querySelector("#childrenCollapsible").classList.add("hide"), "Series" == item.Type && renderSeriesSchedule(page, item, user), "Series" == item.Type ? renderNextUp(page, item, user) : page.querySelector(".nextUpSection").classList.add("hide"), renderScenes(page, item), item.SpecialFeatureCount && 0 != item.SpecialFeatureCount && "Series" != item.Type ? (page.querySelector("#specialsCollapsible").classList.remove("hide"), renderSpecials(page, item, user, 6)) : page.querySelector("#specialsCollapsible").classList.add("hide"), renderCast(page, item, context, enableScrollX() ? null : 12), item.PartCount && item.PartCount > 1 ? (page.querySelector("#additionalPartsCollapsible").classList.remove("hide"), renderAdditionalParts(page, item, user)) : page.querySelector("#additionalPartsCollapsible").classList.add("hide"), "MusicAlbum" == item.Type ? renderMusicVideos(page, item, user) : page.querySelector("#musicVideosCollapsible").classList.add("hide")
}
function renderOverview(elems, item) {
@ -671,8 +671,8 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "cardBuild
if ("Series" != item.Type) return void seriesAirTime.classList.add("hide");
var html = "";
if (item.AirDays && item.AirDays.length && (html += 7 == item.AirDays.length ? "daily" : item.AirDays.map(function(a) {
return a + "s"
}).join(",")), item.AirTime && (html += " at " + item.AirTime), item.Studios.length)
return a + "s"
}).join(",")), item.AirTime && (html += " at " + item.AirTime), item.Studios.length)
if (isStatic) html += " on " + item.Studios[0].Name;
else {
var context = inferContext(item),
@ -976,54 +976,6 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "cardBuild
} else page.querySelector("#scenesCollapsible").classList.add("hide")
}
function renderMediaSources(page, user, item) {
var html = item.MediaSources.map(function(v) {
return getMediaSourceHtml(user, item, v)
}).join('<div style="border-top:1px solid #444;margin: 1em 0;"></div>');
item.MediaSources.length > 1 && (html = "<br/>" + html), page.querySelector("#mediaInfoContent").innerHTML = html, html ? page.querySelector(".audioVideoMediaInfo").classList.remove("hide") : page.querySelector(".audioVideoMediaInfo").classList.add("hide")
}
function getMediaSourceHtml(user, item, version) {
var html = "";
version.Name && item.MediaSources.length > 1 && (html += '<div><span class="mediaInfoAttribute">' + version.Name + "</span></div><br/>");
for (var i = 0, length = version.MediaStreams.length; i < length; i++) {
var stream = version.MediaStreams[i];
if ("Data" != stream.Type) {
html += '<div class="mediaInfoStream">';
html += '<h3 class="mediaInfoStreamType">';
switch (stream.Type) {
case 'Audio':
html += globalize.translate("MediaInfoStreamTypeAudio");
break;
case 'Subtitle':
html += globalize.translate("MediaInfoStreamTypeSubtitle");
break;
case 'Video':
html += globalize.translate("MediaInfoStreamTypeVideo");
break;
case 'Data':
html += globalize.translate("MediaInfoStreamTypeData");
break;
case 'EmbeddedImage':
html += globalize.translate("MediaInfoStreamTypeEmbeddedImage");
break;
}
html += "</h3>";
var attributes = [];
stream.DisplayTitle && attributes.push(createAttribute("Title", stream.DisplayTitle)), stream.Language && "Video" != stream.Type && attributes.push(createAttribute(globalize.translate("MediaInfoLanguage"), stream.Language)), stream.Codec && attributes.push(createAttribute(globalize.translate("MediaInfoCodec"), stream.Codec.toUpperCase())), stream.CodecTag && attributes.push(createAttribute(globalize.translate("MediaInfoCodecTag"), stream.CodecTag)), null != stream.IsAVC && attributes.push(createAttribute("AVC", stream.IsAVC ? "Yes" : "No")), stream.Profile && attributes.push(createAttribute(globalize.translate("MediaInfoProfile"), stream.Profile)), stream.Level && attributes.push(createAttribute(globalize.translate("MediaInfoLevel"), stream.Level)), (stream.Width || stream.Height) && attributes.push(createAttribute(globalize.translate("MediaInfoResolution"), stream.Width + "x" + stream.Height)), stream.AspectRatio && "mjpeg" != stream.Codec && attributes.push(createAttribute(globalize.translate("MediaInfoAspectRatio"), stream.AspectRatio)), "Video" == stream.Type && (null != stream.IsAnamorphic && attributes.push(createAttribute(globalize.translate("MediaInfoAnamorphic"), stream.IsAnamorphic ? "Yes" : "No")), attributes.push(createAttribute(globalize.translate("MediaInfoInterlaced"), stream.IsInterlaced ? "Yes" : "No"))), (stream.AverageFrameRate || stream.RealFrameRate) && attributes.push(createAttribute(globalize.translate("MediaInfoFramerate"), stream.AverageFrameRate || stream.RealFrameRate)), stream.ChannelLayout && attributes.push(createAttribute(globalize.translate("MediaInfoLayout"), stream.ChannelLayout)), stream.Channels && attributes.push(createAttribute(globalize.translate("MediaInfoChannels"), stream.Channels + " ch")), stream.BitRate && "mjpeg" != stream.Codec && attributes.push(createAttribute(globalize.translate("MediaInfoBitrate"), parseInt(stream.BitRate / 1e3) + " kbps")), stream.SampleRate && attributes.push(createAttribute(globalize.translate("MediaInfoSampleRate"), stream.SampleRate + " Hz")), stream.VideoRange && "SDR" !== stream.VideoRange && attributes.push(createAttribute(globalize.translate("VideoRange"), stream.VideoRange)), stream.ColorPrimaries && attributes.push(createAttribute(globalize.translate("ColorPrimaries"), stream.ColorPrimaries)), stream.ColorSpace && attributes.push(createAttribute(globalize.translate("ColorSpace"), stream.ColorSpace)), stream.ColorTransfer && attributes.push(createAttribute(globalize.translate("ColorTransfer"), stream.ColorTransfer)), stream.BitDepth && attributes.push(createAttribute(globalize.translate("MediaInfoBitDepth"), stream.BitDepth + " bit")), stream.PixelFormat && attributes.push(createAttribute(globalize.translate("MediaInfoPixelFormat"), stream.PixelFormat)), stream.RefFrames && attributes.push(createAttribute(globalize.translate("MediaInfoRefFrames"), stream.RefFrames)), stream.NalLengthSize && attributes.push(createAttribute("NAL", stream.NalLengthSize)), "Video" != stream.Type && attributes.push(createAttribute(globalize.translate("MediaInfoDefault"), stream.IsDefault ? "Yes" : "No")), "Subtitle" == stream.Type && (attributes.push(createAttribute(globalize.translate("MediaInfoForced"), stream.IsForced ? "Yes" : "No")), attributes.push(createAttribute(globalize.translate("MediaInfoExternal"), stream.IsExternal ? "Yes" : "No"))), "Video" == stream.Type && version.Timestamp && attributes.push(createAttribute(globalize.translate("MediaInfoTimestamp"), version.Timestamp)), html += attributes.join("<br/>"), html += "</div>"
}
}
if (version.Container && (html += '<div><span class="mediaInfoLabel">' + globalize.translate("MediaInfoContainer") + '</span><span class="mediaInfoAttribute">' + version.Container + "</span></div>"), version.Formats && version.Formats.length, version.Path && "Http" != version.Protocol && user && user.Policy.IsAdministrator && (html += '<div><span class="mediaInfoLabel">' + globalize.translate("MediaInfoPath") + '</span><span class="mediaInfoAttribute">' + version.Path + "</span></div>"), version.Size) {
var size = (version.Size / 1048576).toFixed(0);
html += '<div><span class="mediaInfoLabel">' + globalize.translate("MediaInfoSize") + '</span><span class="mediaInfoAttribute">' + size + " MB</span></div>"
}
return html
}
function createAttribute(label, value) {
return '<span class="mediaInfoLabel">' + label + '</span><span class="mediaInfoAttribute">' + value + "</span>"
}
function getVideosHtml(items, user, limit, moreButtonClass) {
var html = cardBuilder.getCardsHtml({
items: items,
@ -1064,7 +1016,7 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "cardBuild
function itemDetailPage() {
var self = this;
self.setInitialCollapsibleState = setInitialCollapsibleState, self.renderDetails = renderDetails, self.renderCast = renderCast, self.renderMediaSources = renderMediaSources
self.setInitialCollapsibleState = setInitialCollapsibleState, self.renderDetails = renderDetails, self.renderCast = renderCast
}
function bindAll(view, selector, eventName, fn) {

View file

@ -170,6 +170,10 @@
<i class="md-icon">&#xE87D;</i>
</button>
<button is="emby-button" type="button" class="fab btnSplitVersions hide detailButton">
<i class="md-icon">&#xe0b6;</i>
</button>
<button is="emby-button" type="button" class="button-flat btnMoreCommands hide detailButton-mobile">
<div class="detailButton-mobile-content">
<i class="md-icon detailButton-mobile-icon"></i>
@ -303,15 +307,5 @@
</div>
</div>
</div>
<div class="verticalSection detailVerticalSection audioVideoMediaInfo hide">
<h2 class="sectionTitle padded-left" style="margin-bottom:0;">${HeaderMediaInfo}</h2>
<div class="padded-left padded-right">
<div class="splitVersionContainer" style="border-bottom: 1px solid #444; padding: 1em 0;">
<button is="emby-button" type="button" class="raised btnSplitVersions subdued">${ButtonSplitVersionsApart}</button>
</div>
<div id="mediaInfoContent" class="mediaInfoContent"></div>
</div>
</div>
</div>
</div>

View file

@ -765,6 +765,7 @@ var AppInfo = {};
define("subtitleEditor", [componentsPath + "/subtitleeditor/subtitleeditor"], returnFirstDependency);
define("subtitleSync", [componentsPath + "/subtitlesync/subtitlesync"], returnFirstDependency);
define("itemIdentifier", [componentsPath + "/itemidentifier/itemidentifier"], returnFirstDependency);
define("itemMediaInfo", [componentsPath + "/itemMediaInfo/itemMediaInfo"], returnFirstDependency);
define("mediaInfo", [componentsPath + "/mediainfo/mediainfo"], returnFirstDependency);
define("itemContextMenu", [componentsPath + "/itemcontextmenu"], returnFirstDependency);
define("imageEditor", [componentsPath + "/imageeditor/imageeditor"], returnFirstDependency);

View file

@ -989,6 +989,7 @@
"Monday": "Monday",
"MoreFromValue": "More from {0}",
"MoreUsersCanBeAddedLater": "More users can be added later from within the dashboard.",
"MoreMediaInfo": "Media Info",
"MoveLeft": "Move left",
"MoveRight": "Move right",
"MovieLibraryHelp": "Review the {0}Jellyfin movie naming guide{1}.",