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

590 lines
18 KiB
JavaScript
Raw Normal View History

2022-01-30 00:27:26 +03:00
import escapeHtml from 'escape-html';
2020-08-14 08:46:34 +02:00
import datetime from '../../scripts/datetime';
2024-08-14 13:31:34 -04:00
import globalize from '../../lib/globalize';
2023-05-01 10:04:13 -04:00
import { appRouter } from '../router/appRouter';
2020-08-14 08:46:34 +02:00
import itemHelper from '../itemHelper';
import indicators from '../indicators/indicators';
import 'material-design-icons-iconfont';
2021-01-26 16:25:38 -05:00
import './mediainfo.scss';
import '../guide/programs.scss';
2020-08-14 08:46:34 +02:00
import '../../elements/emby-button/emby-button';
import * as userSettings from '../../scripts/settings/userSettings';
2020-06-13 19:32:38 +03:00
2023-04-19 01:56:05 -04:00
function getTimerIndicator(item) {
let status;
if (item.Type === 'SeriesTimer') {
return '<span class="material-icons mediaInfoItem mediaInfoIconItem mediaInfoTimerIcon fiber_smart_record" aria-hidden="true"></span>';
} else if (item.TimerId || item.SeriesTimerId) {
status = item.Status || 'Cancelled';
} else if (item.Type === 'Timer') {
status = item.Status;
} else {
return '';
}
2023-04-19 01:56:05 -04:00
if (item.SeriesTimerId) {
if (status !== 'Cancelled') {
2022-02-24 20:15:24 +03:00
return '<span class="material-icons mediaInfoItem mediaInfoIconItem mediaInfoTimerIcon fiber_smart_record" aria-hidden="true"></span>';
}
2023-04-19 01:56:05 -04:00
return '<span class="material-icons mediaInfoItem mediaInfoIconItem fiber_smart_record" aria-hidden="true"></span>';
2018-10-23 01:05:09 +03:00
}
2023-04-19 01:56:05 -04:00
return '<span class="material-icons mediaInfoItem mediaInfoIconItem mediaInfoTimerIcon fiber_manual_record" aria-hidden="true"></span>';
}
2023-04-19 01:56:05 -04:00
function getProgramInfoHtml(item, options) {
let html = '';
2023-04-19 01:56:05 -04:00
const miscInfo = [];
let text;
let date;
2023-04-19 01:56:05 -04:00
if (item.StartDate && options.programTime !== false) {
try {
text = '';
2023-04-19 01:56:05 -04:00
date = datetime.parseISO8601Date(item.StartDate);
2023-04-19 01:56:05 -04:00
if (options.startDate !== false) {
text += datetime.toLocaleDateString(date, { weekday: 'short', month: 'short', day: 'numeric' });
}
2023-04-19 01:56:05 -04:00
text += ` ${datetime.getDisplayTime(date)}`;
2023-04-19 01:56:05 -04:00
if (item.EndDate) {
date = datetime.parseISO8601Date(item.EndDate);
text += ` - ${datetime.getDisplayTime(date)}`;
}
2023-04-19 01:56:05 -04:00
miscInfo.push(text);
} catch (e) {
console.error('error parsing date:', item.StartDate);
}
2023-04-19 01:56:05 -04:00
}
2023-04-19 01:56:05 -04:00
if (item.ChannelNumber) {
miscInfo.push(`CH ${item.ChannelNumber}`);
}
2023-04-19 01:56:05 -04:00
if (item.ChannelName) {
if (options.interactive && item.ChannelId) {
miscInfo.push({
html: `<a is="emby-linkbutton" class="button-flat mediaInfoItem" href="${appRouter.getRouteUrl({
2023-04-19 01:56:05 -04:00
ServerId: item.ServerId,
Type: 'TvChannel',
Name: item.ChannelName,
Id: item.ChannelId
})}">${escapeHtml(item.ChannelName)}</a>`
});
} else {
miscInfo.push(escapeHtml(item.ChannelName));
}
2023-04-19 01:56:05 -04:00
}
2023-04-19 01:56:05 -04:00
if (options.timerIndicator !== false) {
const timerHtml = getTimerIndicator(item);
if (timerHtml) {
miscInfo.push({
html: timerHtml
});
2018-10-23 01:05:09 +03:00
}
2023-04-19 01:56:05 -04:00
}
2023-04-19 01:56:05 -04:00
html += miscInfo.map(m => {
return getMediaInfoItem(m);
}).join('');
2023-04-19 01:56:05 -04:00
return html;
}
2018-10-23 01:05:09 +03:00
2023-04-19 01:56:05 -04:00
export function getMediaInfoHtml(item, options = {}) {
let html = '';
2023-04-19 01:56:05 -04:00
const miscInfo = [];
let text;
let date;
let count;
2023-04-19 01:56:05 -04:00
const showFolderRuntime = item.Type === 'MusicAlbum' || item.MediaType === 'MusicArtist' || item.Type === 'Playlist' || item.MediaType === 'Playlist' || item.MediaType === 'MusicGenre';
2023-04-19 01:56:05 -04:00
if (showFolderRuntime) {
count = item.SongCount || item.ChildCount;
2023-04-19 01:56:05 -04:00
if (count) {
miscInfo.push(globalize.translate('TrackCount', count));
}
2023-04-19 01:56:05 -04:00
if (item.RunTimeTicks) {
miscInfo.push(datetime.getDisplayDuration(item.RunTimeTicks));
}
} else if (item.Type === 'PhotoAlbum' || item.Type === 'BoxSet') {
count = item.ChildCount;
2023-04-19 01:56:05 -04:00
if (count) {
miscInfo.push(globalize.translate('ItemCount', count));
}
2023-04-19 01:56:05 -04:00
}
2023-04-19 01:56:05 -04:00
if ((item.Type === 'Episode' || item.MediaType === 'Photo')
2022-10-03 14:22:02 -04:00
&& options.originalAirDate !== false
&& item.PremiereDate
2023-04-19 01:56:05 -04:00
) {
try {
//don't modify date to locale if episode. Only Dates (not times) are stored, or editable in the edit metadata dialog
date = datetime.parseISO8601Date(item.PremiereDate, item.Type !== 'Episode');
2023-04-19 01:56:05 -04:00
text = datetime.toLocaleDateString(date);
miscInfo.push(text);
} catch (e) {
console.error('error parsing date:', item.PremiereDate);
}
2023-04-19 01:56:05 -04:00
}
2023-04-19 01:56:05 -04:00
if (item.Type === 'SeriesTimer') {
if (item.RecordAnyTime) {
miscInfo.push(globalize.translate('Anytime'));
} else {
miscInfo.push(datetime.getDisplayTime(item.StartDate));
}
2023-04-19 01:56:05 -04:00
if (item.RecordAnyChannel) {
miscInfo.push(globalize.translate('AllChannels'));
} else {
miscInfo.push(item.ChannelName || globalize.translate('OneChannel'));
}
2023-04-19 01:56:05 -04:00
}
2023-04-19 01:56:05 -04:00
if (item.StartDate && item.Type !== 'Program' && item.Type !== 'SeriesTimer' && item.Type !== 'Timer') {
try {
date = datetime.parseISO8601Date(item.StartDate);
2023-04-19 01:56:05 -04:00
text = datetime.toLocaleDateString(date);
miscInfo.push(text);
if (item.Type !== 'Recording') {
text = datetime.getDisplayTime(date);
miscInfo.push(text);
}
2023-04-19 01:56:05 -04:00
} catch (e) {
console.error('error parsing date:', item.StartDate);
}
2023-04-19 01:56:05 -04:00
}
2023-04-19 01:56:05 -04:00
if (options.year !== false && item.ProductionYear && item.Type === 'Series') {
if (item.Status === 'Continuing') {
miscInfo.push(globalize.translate('SeriesYearToPresent', datetime.toLocaleString(item.ProductionYear, { useGrouping: false })));
} else if (item.ProductionYear) {
text = datetime.toLocaleString(item.ProductionYear, { useGrouping: false });
2023-04-19 01:56:05 -04:00
if (item.EndDate) {
try {
2023-04-19 01:56:05 -04:00
const endYear = datetime.toLocaleString(datetime.parseISO8601Date(item.EndDate).getFullYear(), { useGrouping: false });
2023-10-13 12:21:32 +08:00
/* At this point, text will contain only the start year */
if (endYear !== text) {
text += ` - ${endYear}`;
2023-04-19 01:56:05 -04:00
}
} catch (e) {
2023-04-19 01:56:05 -04:00
console.error('error parsing date:', item.EndDate);
}
}
2023-04-19 01:56:05 -04:00
miscInfo.push(text);
}
2023-04-19 01:56:05 -04:00
}
2023-04-19 01:56:05 -04:00
if (item.Type === 'Program' || item.Type === 'Timer') {
let program = item;
if (item.Type === 'Timer') {
program = item.ProgramInfo;
}
2023-04-19 01:56:05 -04:00
if (options.programIndicator !== false) {
if (program.IsLive && userSettings.get('guide-indicator-live') === 'true') {
miscInfo.push({
html: `<div class="mediaInfoProgramAttribute mediaInfoItem liveTvProgram">${globalize.translate('Live')}</div>`
});
} else if (program.IsPremiere && userSettings.get('guide-indicator-premiere') === 'true') {
miscInfo.push({
html: `<div class="mediaInfoProgramAttribute mediaInfoItem premiereTvProgram">${globalize.translate('Premiere')}</div>`
});
} else if (program.IsSeries && !program.IsRepeat && userSettings.get('guide-indicator-new') === 'true') {
miscInfo.push({
html: `<div class="mediaInfoProgramAttribute mediaInfoItem newTvProgram">${globalize.translate('New')}</div>`
});
} else if (program.IsSeries && program.IsRepeat && userSettings.get('guide-indicator-repeat') === 'true') {
miscInfo.push({
html: `<div class="mediaInfoProgramAttribute mediaInfoItem repeatTvProgram">${globalize.translate('Repeat')}</div>`
});
}
}
2023-04-19 01:56:05 -04:00
if ((program.IsSeries || program.EpisodeTitle) && options.episodeTitle !== false) {
text = itemHelper.getDisplayName(program, {
includeIndexNumber: options.episodeTitleIndexNumber
});
2023-04-19 01:56:05 -04:00
if (text) {
miscInfo.push(escapeHtml(text));
}
} else if (program.IsMovie && program.ProductionYear && options.originalAirDate !== false) {
miscInfo.push(program.ProductionYear);
} else if (program.PremiereDate && options.originalAirDate !== false) {
try {
date = datetime.parseISO8601Date(program.PremiereDate);
text = globalize.translate('OriginalAirDateValue', datetime.toLocaleDateString(date));
miscInfo.push(text);
} catch (e) {
console.error('error parsing date:', program.PremiereDate);
}
} else if (program.ProductionYear && options.year !== false ) {
miscInfo.push(program.ProductionYear);
}
2023-04-19 01:56:05 -04:00
}
2023-04-19 01:56:05 -04:00
if (options.year !== false && item.Type !== 'Series' && item.Type !== 'Episode' && item.Type !== 'Person'
&& item.MediaType !== 'Photo' && item.Type !== 'Program' && item.Type !== 'Season'
) {
if (item.ProductionYear) {
miscInfo.push(item.ProductionYear);
} else if (item.PremiereDate) {
try {
text = datetime.toLocaleString(datetime.parseISO8601Date(item.PremiereDate).getFullYear(), { useGrouping: false });
miscInfo.push(text);
} catch (e) {
console.error('error parsing date:', item.PremiereDate);
}
}
2023-04-19 01:56:05 -04:00
}
2023-04-19 01:56:05 -04:00
if (item.RunTimeTicks && item.Type !== 'Series' && item.Type !== 'Program' && item.Type !== 'Timer' && item.Type !== 'Book' && !showFolderRuntime && options.runtime !== false) {
if (item.Type === 'Audio') {
miscInfo.push(datetime.getDisplayRunningTime(item.RunTimeTicks));
} else {
miscInfo.push(datetime.getDisplayDuration(item.RunTimeTicks));
}
2023-04-19 01:56:05 -04:00
}
2023-04-19 01:56:05 -04:00
if (options.officialRating !== false && item.OfficialRating && item.Type !== 'Season' && item.Type !== 'Episode') {
miscInfo.push({
text: item.OfficialRating,
2024-08-22 00:50:43 +03:00
cssClass: 'mediaInfoText mediaInfoOfficialRating'
2023-04-19 01:56:05 -04:00
});
}
2023-04-19 01:56:05 -04:00
if (item.Video3DFormat) {
miscInfo.push('3D');
}
2023-04-19 01:56:05 -04:00
if (item.MediaType === 'Photo' && item.Width && item.Height) {
miscInfo.push(`${item.Width}x${item.Height}`);
}
2023-04-19 01:56:05 -04:00
if (options.container !== false && item.Type === 'Audio' && item.Container) {
miscInfo.push(item.Container);
}
2023-04-19 01:56:05 -04:00
html += miscInfo.map(m => {
return getMediaInfoItem(m);
}).join('');
2023-04-19 01:56:05 -04:00
if (options.starRating !== false) {
html += getStarIconsHtml(item);
2018-10-23 01:05:09 +03:00
}
2023-04-19 01:56:05 -04:00
if (item.HasSubtitles && options.subtitles !== false) {
html += '<div class="mediaInfoItem mediaInfoText closedCaptionMediaInfoText">CC</div>';
}
2023-04-19 01:56:05 -04:00
if (item.CriticRating && options.criticRating !== false) {
if (item.CriticRating >= 60) {
html += `<div class="mediaInfoItem mediaInfoCriticRating mediaInfoCriticRatingFresh">${item.CriticRating}</div>`;
} else {
html += `<div class="mediaInfoItem mediaInfoCriticRating mediaInfoCriticRatingRotten">${item.CriticRating}</div>`;
2018-10-23 01:05:09 +03:00
}
2023-04-19 01:56:05 -04:00
}
2023-04-19 01:56:05 -04:00
if (options.endsAt !== false) {
const endsAt = getEndsAt(item);
if (endsAt) {
html += getMediaInfoItem(endsAt, 'endsAt');
}
2018-10-23 01:05:09 +03:00
}
2023-04-19 01:56:05 -04:00
html += indicators.getMissingIndicator(item);
return html;
}
export function getEndsAt(item) {
if (item.MediaType === 'Video' && item.RunTimeTicks && !item.StartDate) {
let endDate = new Date().getTime() + (item.RunTimeTicks / 10000);
2018-10-23 01:05:09 +03:00
endDate = new Date(endDate);
2020-06-13 19:32:38 +03:00
const displayTime = datetime.getDisplayTime(endDate);
return globalize.translate('EndsAtValue', displayTime);
2018-10-23 01:05:09 +03:00
}
2023-04-19 01:56:05 -04:00
return null;
}
2023-04-19 01:56:05 -04:00
export function getEndsAtFromPosition(runtimeTicks, positionTicks, playbackRate, includeText) {
let endDate = new Date().getTime() + (1 / playbackRate) * ((runtimeTicks - (positionTicks || 0)) / 10000);
endDate = new Date(endDate);
2018-10-23 01:05:09 +03:00
2023-04-19 01:56:05 -04:00
const displayTime = datetime.getDisplayTime(endDate);
2023-04-19 01:56:05 -04:00
if (includeText === false) {
return displayTime;
}
return globalize.translate('EndsAtValue', displayTime);
}
2023-04-19 01:56:05 -04:00
function getMediaInfoItem(m, cssClass) {
cssClass = cssClass ? (`${cssClass} mediaInfoItem`) : 'mediaInfoItem';
let mediaInfoText = m;
2023-04-19 01:56:05 -04:00
if (typeof (m) !== 'string' && typeof (m) !== 'number') {
if (m.html) {
return m.html;
}
mediaInfoText = m.text;
cssClass += ` ${m.cssClass}`;
2018-10-23 01:05:09 +03:00
}
2023-04-19 01:56:05 -04:00
return `<div class="${cssClass}">${mediaInfoText}</div>`;
}
2018-10-23 01:05:09 +03:00
2023-04-19 01:56:05 -04:00
function getStarIconsHtml(item) {
let html = '';
if (item.CommunityRating) {
html += '<div class="starRatingContainer mediaInfoItem">';
2023-04-19 01:56:05 -04:00
html += '<span class="material-icons starIcon star" aria-hidden="true"></span>';
html += item.CommunityRating.toFixed(1);
html += '</div>';
2018-10-23 01:05:09 +03:00
}
2023-04-19 01:56:05 -04:00
return html;
}
2023-04-19 01:56:05 -04:00
function dynamicEndTime(elem, item) {
const interval = setInterval(() => {
if (!document.body.contains(elem)) {
clearInterval(interval);
return;
}
2018-10-23 01:05:09 +03:00
2023-04-19 01:56:05 -04:00
elem.innerHTML = getEndsAt(item);
}, 60000);
}
2023-04-19 01:56:05 -04:00
export function fillPrimaryMediaInfo(elem, item, options) {
const html = getPrimaryMediaInfoHtml(item, options);
2018-10-23 01:05:09 +03:00
2023-04-19 01:56:05 -04:00
elem.innerHTML = html;
afterFill(elem, item, options);
}
export function fillSecondaryMediaInfo(elem, item, options) {
const html = getSecondaryMediaInfoHtml(item, options);
elem.innerHTML = html;
afterFill(elem, item, options);
}
2023-04-19 01:56:05 -04:00
function afterFill(elem, item, options) {
if (options.endsAt !== false) {
const endsAtElem = elem.querySelector('.endsAt');
if (endsAtElem) {
dynamicEndTime(endsAtElem, item);
2018-10-23 01:05:09 +03:00
}
}
2023-04-19 01:56:05 -04:00
const lnkChannel = elem.querySelector('.lnkChannel');
if (lnkChannel) {
lnkChannel.addEventListener('click', onChannelLinkClick);
}
}
2023-04-19 01:56:05 -04:00
function onChannelLinkClick(e) {
const channelId = this.getAttribute('data-id');
const serverId = this.getAttribute('data-serverid');
2023-04-19 01:56:05 -04:00
appRouter.showItem(channelId, serverId);
2018-10-23 01:05:09 +03:00
2023-04-19 01:56:05 -04:00
e.preventDefault();
return false;
}
2023-04-19 01:56:05 -04:00
export function getPrimaryMediaInfoHtml(item, options = {}) {
if (options.interactive === undefined) {
options.interactive = false;
2018-10-23 01:05:09 +03:00
}
2023-04-19 01:56:05 -04:00
return getMediaInfoHtml(item, options);
}
2023-04-19 01:56:05 -04:00
export function getSecondaryMediaInfoHtml(item, options) {
options = options || {};
if (options.interactive == null) {
options.interactive = false;
}
if (item.Type === 'Program') {
return getProgramInfoHtml(item, options);
2018-10-23 01:05:09 +03:00
}
2023-04-19 01:56:05 -04:00
return '';
}
2023-04-19 01:56:05 -04:00
export function getResolutionText(i) {
const width = i.Width;
const height = i.Height;
if (width && height) {
if (width >= 3800 || height >= 2000) {
return '4K';
}
if (width >= 2500 || height >= 1400) {
if (i.IsInterlaced) {
return '1440i';
}
2023-04-19 01:56:05 -04:00
return '1440p';
}
if (width >= 1800 || height >= 1000) {
if (i.IsInterlaced) {
return '1080i';
}
2023-04-19 01:56:05 -04:00
return '1080p';
}
if (width >= 1200 || height >= 700) {
if (i.IsInterlaced) {
return '720i';
}
2023-04-19 01:56:05 -04:00
return '720p';
}
if (width >= 700 || height >= 400) {
if (i.IsInterlaced) {
return '480i';
}
2023-04-19 01:56:05 -04:00
return '480p';
2018-10-23 01:05:09 +03:00
}
}
2023-04-19 01:56:05 -04:00
return null;
}
2018-10-23 01:05:09 +03:00
2023-04-19 01:56:05 -04:00
function getAudioStreamForDisplay(item) {
if (!item.MediaSources) {
return null;
2018-10-23 01:05:09 +03:00
}
2023-04-19 01:56:05 -04:00
const mediaSource = item.MediaSources[0];
if (!mediaSource) {
return null;
}
2023-04-19 01:56:05 -04:00
return (mediaSource.MediaStreams || []).filter(i => {
return i.Type === 'Audio' && (i.Index === mediaSource.DefaultAudioStreamIndex || mediaSource.DefaultAudioStreamIndex == null);
})[0];
}
2023-04-19 01:56:05 -04:00
export function getMediaInfoStats(item) {
const list = [];
2023-04-19 01:56:05 -04:00
const mediaSource = (item.MediaSources || [])[0] || {};
2023-04-19 01:56:05 -04:00
const videoStream = (mediaSource.MediaStreams || []).filter(i => {
return i.Type === 'Video';
})[0] || {};
const audioStream = getAudioStreamForDisplay(item) || {};
2023-04-19 01:56:05 -04:00
if (item.VideoType === 'Dvd') {
list.push({
type: 'mediainfo',
text: 'Dvd'
});
}
2023-04-19 01:56:05 -04:00
if (item.VideoType === 'BluRay') {
list.push({
type: 'mediainfo',
text: 'BluRay'
});
}
2023-04-19 01:56:05 -04:00
const resolutionText = getResolutionText(videoStream);
if (resolutionText) {
list.push({
type: 'mediainfo',
text: resolutionText
});
}
2023-04-19 01:56:05 -04:00
if (videoStream.Codec) {
list.push({
type: 'mediainfo',
text: videoStream.Codec
});
}
2023-04-19 01:56:05 -04:00
const channels = audioStream.Channels;
let channelText;
if (channels === 8) {
channelText = '7.1';
} else if (channels === 7) {
channelText = '6.1';
} else if (channels === 6) {
channelText = '5.1';
} else if (channels === 2) {
channelText = '2.0';
}
2023-04-19 01:56:05 -04:00
if (channelText) {
list.push({
type: 'mediainfo',
text: channelText
});
}
2023-04-19 01:56:05 -04:00
const audioCodec = (audioStream.Codec || '').toLowerCase();
if ((audioCodec === 'dca' || audioCodec === 'dts') && audioStream.Profile) {
list.push({
type: 'mediainfo',
text: audioStream.Profile
});
} else if (audioStream.Codec) {
list.push({
type: 'mediainfo',
text: audioStream.Codec
});
}
2023-04-19 01:56:05 -04:00
if (item.DateCreated && itemHelper.enableDateAddedDisplay(item)) {
const dateCreated = datetime.parseISO8601Date(item.DateCreated);
2023-04-19 01:56:05 -04:00
list.push({
type: 'added',
text: globalize.translate('AddedOnValue', `${datetime.toLocaleDateString(dateCreated)} ${datetime.getDisplayTime(dateCreated)}`)
});
2018-10-23 01:05:09 +03:00
}
2023-04-19 01:56:05 -04:00
return list;
}
2020-06-13 19:32:38 +03:00
export default {
getMediaInfoHtml: getPrimaryMediaInfoHtml,
getEndsAt: getEndsAt,
getEndsAtFromPosition: getEndsAtFromPosition,
getPrimaryMediaInfoHtml: getPrimaryMediaInfoHtml,
getSecondaryMediaInfoHtml: getSecondaryMediaInfoHtml,
fillPrimaryMediaInfo: fillPrimaryMediaInfo,
fillSecondaryMediaInfo: fillSecondaryMediaInfo,
getMediaInfoStats: getMediaInfoStats,
getResolutionText: getResolutionText
};