diff --git a/src/components/itemMediaInfo/itemMediaInfo.js b/src/components/itemMediaInfo/itemMediaInfo.js new file mode 100644 index 0000000000..90a13d9cc8 --- /dev/null +++ b/src/components/itemMediaInfo/itemMediaInfo.js @@ -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('
'); + if (item.MediaSources.length > 1) { + html = "
" + html; + } + var mediaInfoContent = page.querySelector("#mediaInfoContent"); + mediaInfoContent.innerHTML = html; + } + + function getMediaSourceHtml(user, item, version) { + var html = ""; + if (version.Name) { + html += '

' + version.Name + "

"; + } + if (version.Container) { + html += createAttribute(globalize.translate("MediaInfoContainer"), version.Container) + "
"; + } + if (version.Formats && version.Formats.length) { + html += createAttribute(globalize.translate("MediaInfoFormat"), version.Formats.join(",")) + "
"; + } + if (version.Path && user && user.Policy.IsAdministrator) { + html += createAttribute(globalize.translate("MediaInfoPath"), version.Path) + "
"; + } + if (version.Size && user && user.Policy.IsAdministrator) { + var size = (version.Size / (1024 * 1024)).toFixed(0) + " MB"; + html += createAttribute(globalize.translate("MediaInfoSize"), size) + "
"; + } + for (var i = 0, length = version.MediaStreams.length; i < length; i++) { + var stream = version.MediaStreams[i]; + if (stream.Type === "Data") { + continue; + } + html += '
'; + var displayType = globalize.translate("MediaInfoStreamType" + stream.Type); + html += '

' + displayType + "

"; + 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("
"); + html += "
"; + } + return html; + } + + function createAttribute(label, value) { + return '' + label + '' + value + "" + } + + 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 + }; +}); diff --git a/src/components/itemMediaInfo/itemMediaInfo.template.html b/src/components/itemMediaInfo/itemMediaInfo.template.html new file mode 100644 index 0000000000..7e011b4cf4 --- /dev/null +++ b/src/components/itemMediaInfo/itemMediaInfo.template.html @@ -0,0 +1,12 @@ +
+ +

+ ${HeaderMediaInfo} +

+
+ +
+
+
+
+
diff --git a/src/components/itemcontextmenu.js b/src/components/itemcontextmenu.js index 2a985b026a..5f649a2b02 100644 --- a/src/components/itemcontextmenu.js +++ b/src/components/itemcontextmenu.js @@ -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 }; -}); \ No newline at end of file +}); diff --git a/src/controllers/itemdetailpage.js b/src/controllers/itemdetailpage.js index 31d5a0eb22..2afa817cfc 100644 --- a/src/controllers/itemdetailpage.js +++ b/src/controllers/itemdetailpage.js @@ -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('
'); - item.MediaSources.length > 1 && (html = "
" + 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 += '
' + version.Name + "

"); - for (var i = 0, length = version.MediaStreams.length; i < length; i++) { - var stream = version.MediaStreams[i]; - if ("Data" != stream.Type) { - html += '
'; - html += '

'; - 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 += "

"; - 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("
"), html += "
" - } - } - if (version.Container && (html += '
' + globalize.translate("MediaInfoContainer") + '' + version.Container + "
"), version.Formats && version.Formats.length, version.Path && "Http" != version.Protocol && user && user.Policy.IsAdministrator && (html += '
' + globalize.translate("MediaInfoPath") + '' + version.Path + "
"), version.Size) { - var size = (version.Size / 1048576).toFixed(0); - html += '
' + globalize.translate("MediaInfoSize") + '' + size + " MB
" - } - return html - } - - function createAttribute(label, value) { - return '' + label + '' + value + "" - } - 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) { diff --git a/src/itemdetails.html b/src/itemdetails.html index b53e4049a0..45e4a40dff 100644 --- a/src/itemdetails.html +++ b/src/itemdetails.html @@ -170,6 +170,10 @@ + + - -
- - diff --git a/src/scripts/site.js b/src/scripts/site.js index d41f14122b..0a91030cf9 100644 --- a/src/scripts/site.js +++ b/src/scripts/site.js @@ -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); diff --git a/src/strings/en-us.json b/src/strings/en-us.json index 2327bcf8f4..2c7b98167f 100644 --- a/src/strings/en-us.json +++ b/src/strings/en-us.json @@ -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}.",