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

587 lines
20 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';
2022-07-05 15:28:13 -04:00
import globalize from '../../scripts/globalize';
2020-08-16 20:24:45 +02:00
import { appRouter } from '../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';
2020-06-13 19:32:38 +03:00
/* eslint-disable indent */
2018-10-23 01:05:09 +03:00
function getTimerIndicator(item) {
2020-06-13 19:32:38 +03:00
let status;
if (item.Type === 'SeriesTimer') {
2022-02-24 20:15:24 +03:00
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 '';
2018-10-23 01:05:09 +03: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>';
}
2022-02-24 20:15:24 +03:00
return '<span class="material-icons mediaInfoItem mediaInfoIconItem fiber_smart_record" aria-hidden="true"></span>';
}
2022-02-24 20:15:24 +03:00
return '<span class="material-icons mediaInfoItem mediaInfoIconItem mediaInfoTimerIcon fiber_manual_record" aria-hidden="true"></span>';
2018-10-23 01:05:09 +03:00
}
function getProgramInfoHtml(item, options) {
2020-06-13 19:32:38 +03:00
let html = '';
2020-06-13 19:32:38 +03:00
const miscInfo = [];
let text;
let date;
if (item.StartDate && options.programTime !== false) {
try {
text = '';
date = datetime.parseISO8601Date(item.StartDate);
if (options.startDate !== false) {
text += datetime.toLocaleDateString(date, { weekday: 'short', month: 'short', day: 'numeric' });
}
2020-06-13 19:32:38 +03:00
text += ` ${datetime.getDisplayTime(date)}`;
if (item.EndDate) {
date = datetime.parseISO8601Date(item.EndDate);
2020-06-13 19:32:38 +03:00
text += ` - ${datetime.getDisplayTime(date)}`;
}
miscInfo.push(text);
} catch (e) {
2020-07-22 17:30:42 +03:00
console.error('error parsing date:', item.StartDate);
}
}
if (item.ChannelNumber) {
2020-06-13 19:32:38 +03:00
miscInfo.push(`CH ${item.ChannelNumber}`);
}
if (item.ChannelName) {
if (options.interactive && item.ChannelId) {
miscInfo.push({
2020-06-13 19:32:38 +03:00
html: `<a is="emby-linkbutton" class="button-flat mediaInfoItem" href="${appRouter.getRouteUrl({
ServerId: item.ServerId,
Type: 'TvChannel',
Name: item.ChannelName,
Id: item.ChannelId
2022-01-30 00:27:26 +03:00
})}">${escapeHtml(item.ChannelName)}</a>`
});
} else {
2022-01-30 00:27:26 +03:00
miscInfo.push(escapeHtml(item.ChannelName));
}
}
if (options.timerIndicator !== false) {
2020-06-13 19:32:38 +03:00
const timerHtml = getTimerIndicator(item);
if (timerHtml) {
miscInfo.push({
html: timerHtml
});
}
2018-10-23 01:05:09 +03:00
}
2020-06-13 19:32:38 +03:00
html += miscInfo.map(m => {
return getMediaInfoItem(m);
}).join('');
return html;
2018-10-23 01:05:09 +03:00
}
2020-08-20 20:39:33 +02:00
export function getMediaInfoHtml(item, options = {}) {
2020-06-13 19:32:38 +03:00
let html = '';
2020-06-13 19:32:38 +03:00
const miscInfo = [];
let text;
let date;
let count;
const showFolderRuntime = item.Type === 'MusicAlbum' || item.MediaType === 'MusicArtist' || item.Type === 'Playlist' || item.MediaType === 'Playlist' || item.MediaType === 'MusicGenre';
if (showFolderRuntime) {
count = item.SongCount || item.ChildCount;
if (count) {
miscInfo.push(globalize.translate('TrackCount', count));
}
if (item.RunTimeTicks) {
2021-09-11 12:44:49 +03:00
miscInfo.push(datetime.getDisplayDuration(item.RunTimeTicks));
}
2020-05-04 12:44:12 +02:00
} else if (item.Type === 'PhotoAlbum' || item.Type === 'BoxSet') {
count = item.ChildCount;
if (count) {
miscInfo.push(globalize.translate('ItemCount', count));
}
}
2022-10-03 14:22:02 -04:00
if ((item.Type === 'Episode' || item.MediaType === 'Photo')
&& options.originalAirDate !== false
&& item.PremiereDate
) {
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');
2022-10-03 14:22:02 -04:00
text = datetime.toLocaleDateString(date);
miscInfo.push(text);
} catch (e) {
console.error('error parsing date:', item.PremiereDate);
}
}
if (item.Type === 'SeriesTimer') {
if (item.RecordAnyTime) {
miscInfo.push(globalize.translate('Anytime'));
} else {
miscInfo.push(datetime.getDisplayTime(item.StartDate));
}
if (item.RecordAnyChannel) {
miscInfo.push(globalize.translate('AllChannels'));
} else {
miscInfo.push(item.ChannelName || globalize.translate('OneChannel'));
}
}
if (item.StartDate && item.Type !== 'Program' && item.Type !== 'SeriesTimer') {
try {
date = datetime.parseISO8601Date(item.StartDate);
text = datetime.toLocaleDateString(date);
miscInfo.push(text);
2020-05-04 12:44:12 +02:00
if (item.Type !== 'Recording') {
text = datetime.getDisplayTime(date);
miscInfo.push(text);
}
} catch (e) {
2020-07-22 17:30:42 +03:00
console.error('error parsing date:', item.StartDate);
}
}
2020-05-04 12:44:12 +02: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});
if (item.EndDate) {
try {
const endYear = datetime.toLocaleString(datetime.parseISO8601Date(item.EndDate).getFullYear(), {useGrouping: false});
if (endYear !== item.ProductionYear) {
2022-07-02 15:50:52 -04:00
text += `-${endYear}`;
}
} catch (e) {
2020-07-22 17:30:42 +03:00
console.error('error parsing date:', item.EndDate);
}
}
miscInfo.push(text);
}
}
if (item.Type === 'Program') {
if (options.programIndicator !== false) {
if (item.IsLive) {
miscInfo.push({
2020-06-13 19:32:38 +03:00
html: `<div class="mediaInfoProgramAttribute mediaInfoItem liveTvProgram">${globalize.translate('Live')}</div>`
});
} else if (item.IsPremiere) {
miscInfo.push({
2020-06-13 19:32:38 +03:00
html: `<div class="mediaInfoProgramAttribute mediaInfoItem premiereTvProgram">${globalize.translate('Premiere')}</div>`
});
} else if (item.IsSeries && !item.IsRepeat) {
miscInfo.push({
2020-08-13 21:23:51 +09:00
html: `<div class="mediaInfoProgramAttribute mediaInfoItem newTvProgram">${globalize.translate('New')}</div>`
});
} else if (item.IsSeries && item.IsRepeat) {
miscInfo.push({
2020-06-13 19:32:38 +03:00
html: `<div class="mediaInfoProgramAttribute mediaInfoItem repeatTvProgram">${globalize.translate('Repeat')}</div>`
});
}
}
if ((item.IsSeries || item.EpisodeTitle) && options.episodeTitle !== false) {
text = itemHelper.getDisplayName(item, {
includeIndexNumber: options.episodeTitleIndexNumber
});
if (text) {
2022-01-30 00:27:26 +03:00
miscInfo.push(escapeHtml(text));
}
} else if (item.IsMovie && item.ProductionYear && options.originalAirDate !== false) {
miscInfo.push(item.ProductionYear);
} else if (item.PremiereDate && options.originalAirDate !== false) {
try {
date = datetime.parseISO8601Date(item.PremiereDate);
text = globalize.translate('OriginalAirDateValue', datetime.toLocaleDateString(date));
miscInfo.push(text);
} catch (e) {
2020-07-22 17:30:42 +03:00
console.error('error parsing date:', item.PremiereDate);
}
} else if (item.ProductionYear) {
miscInfo.push(item.ProductionYear);
}
}
2022-10-03 14:22:02 -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});
2022-10-03 14:22:02 -04:00
miscInfo.push(text);
} catch (e) {
console.error('error parsing date:', item.PremiereDate);
}
}
}
if (item.RunTimeTicks && item.Type !== 'Series' && item.Type !== 'Program' && item.Type !== 'Book' && !showFolderRuntime && options.runtime !== false) {
2020-05-04 12:44:12 +02:00
if (item.Type === 'Audio') {
miscInfo.push(datetime.getDisplayRunningTime(item.RunTimeTicks));
} else {
2021-09-11 12:44:49 +03:00
miscInfo.push(datetime.getDisplayDuration(item.RunTimeTicks));
}
}
2020-05-04 12:44:12 +02:00
if (item.OfficialRating && item.Type !== 'Season' && item.Type !== 'Episode') {
miscInfo.push({
2018-10-23 01:05:09 +03:00
text: item.OfficialRating,
cssClass: 'mediaInfoOfficialRating'
});
}
if (item.Video3DFormat) {
2020-05-04 12:44:12 +02:00
miscInfo.push('3D');
}
if (item.MediaType === 'Photo' && item.Width && item.Height) {
2020-06-13 19:32:38 +03:00
miscInfo.push(`${item.Width}x${item.Height}`);
}
if (options.container !== false && item.Type === 'Audio' && item.Container) {
miscInfo.push(item.Container);
}
2020-06-13 19:32:38 +03:00
html += miscInfo.map(m => {
return getMediaInfoItem(m);
}).join('');
2020-08-20 20:39:33 +02:00
if (options.starRating !== false) {
html += getStarIconsHtml(item);
}
if (item.HasSubtitles && options.subtitles !== false) {
html += '<div class="mediaInfoItem mediaInfoText closedCaptionMediaInfoText">CC</div>';
}
if (item.CriticRating && options.criticRating !== false) {
if (item.CriticRating >= 60) {
2020-06-13 19:32:38 +03:00
html += `<div class="mediaInfoItem mediaInfoCriticRating mediaInfoCriticRatingFresh">${item.CriticRating}</div>`;
} else {
2020-06-13 19:32:38 +03:00
html += `<div class="mediaInfoItem mediaInfoCriticRating mediaInfoCriticRatingRotten">${item.CriticRating}</div>`;
}
}
if (options.endsAt !== false) {
2020-06-13 19:32:38 +03:00
const endsAt = getEndsAt(item);
if (endsAt) {
html += getMediaInfoItem(endsAt, 'endsAt');
}
2018-10-23 01:05:09 +03:00
}
html += indicators.getMissingIndicator(item);
return html;
2018-10-23 01:05:09 +03:00
}
2020-06-13 19:32:38 +03:00
export function getEndsAt(item) {
2022-10-03 14:22:02 -04:00
if (item.MediaType === 'Video' && item.RunTimeTicks && !item.StartDate) {
let endDate = new Date().getTime() + (item.RunTimeTicks / 10000);
endDate = new Date(endDate);
2022-10-03 14:22:02 -04:00
const displayTime = datetime.getDisplayTime(endDate);
return globalize.translate('EndsAtValue', displayTime);
2018-10-23 01:05:09 +03:00
}
return null;
2018-10-23 01:05:09 +03:00
}
2021-07-03 17:08:25 +05:30
export function getEndsAtFromPosition(runtimeTicks, positionTicks, playbackRate, includeText) {
let endDate = new Date().getTime() + (1 / playbackRate) * ((runtimeTicks - (positionTicks || 0)) / 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);
if (includeText === false) {
return displayTime;
}
return globalize.translate('EndsAtValue', displayTime);
2018-10-23 01:05:09 +03:00
}
function getMediaInfoItem(m, cssClass) {
2020-06-13 19:32:38 +03:00
cssClass = cssClass ? (`${cssClass} mediaInfoItem`) : 'mediaInfoItem';
let mediaInfoText = m;
if (typeof (m) !== 'string' && typeof (m) !== 'number') {
if (m.html) {
return m.html;
}
mediaInfoText = m.text;
2020-06-13 19:32:38 +03:00
cssClass += ` ${m.cssClass}`;
2018-10-23 01:05:09 +03:00
}
2020-06-13 19:32:38 +03:00
return `<div class="${cssClass}">${mediaInfoText}</div>`;
2018-10-23 01:05:09 +03:00
}
function getStarIconsHtml(item) {
2020-06-13 19:32:38 +03:00
let html = '';
2020-02-08 23:37:49 +01:00
if (item.CommunityRating) {
html += '<div class="starRatingContainer mediaInfoItem">';
2022-02-24 20:15:24 +03:00
html += '<span class="material-icons starIcon star" aria-hidden="true"></span>';
2020-02-08 23:37:49 +01:00
html += item.CommunityRating.toFixed(1);
html += '</div>';
}
return html;
2018-10-23 01:05:09 +03:00
}
function dynamicEndTime(elem, item) {
2020-06-13 19:32:38 +03:00
const interval = setInterval(() => {
if (!document.body.contains(elem)) {
clearInterval(interval);
return;
}
elem.innerHTML = getEndsAt(item);
}, 60000);
2018-10-23 01:05:09 +03:00
}
2020-06-13 19:32:38 +03:00
export function fillPrimaryMediaInfo(elem, item, options) {
const html = getPrimaryMediaInfoHtml(item, options);
elem.innerHTML = html;
afterFill(elem, item, options);
2018-10-23 01:05:09 +03:00
}
2020-06-13 19:32:38 +03:00
export function fillSecondaryMediaInfo(elem, item, options) {
const html = getSecondaryMediaInfoHtml(item, options);
elem.innerHTML = html;
afterFill(elem, item, options);
2018-10-23 01:05:09 +03:00
}
function afterFill(elem, item, options) {
if (options.endsAt !== false) {
2020-06-13 19:32:38 +03:00
const endsAtElem = elem.querySelector('.endsAt');
if (endsAtElem) {
dynamicEndTime(endsAtElem, item);
}
}
2020-06-13 19:32:38 +03:00
const lnkChannel = elem.querySelector('.lnkChannel');
if (lnkChannel) {
lnkChannel.addEventListener('click', onChannelLinkClick);
2018-10-23 01:05:09 +03:00
}
}
function onChannelLinkClick(e) {
2020-06-13 19:32:38 +03:00
const channelId = this.getAttribute('data-id');
const serverId = this.getAttribute('data-serverid');
appRouter.showItem(channelId, serverId);
e.preventDefault();
return false;
2018-10-23 01:05:09 +03:00
}
2020-08-20 20:39:33 +02:00
export function getPrimaryMediaInfoHtml(item, options = {}) {
if (options.interactive === undefined) {
options.interactive = false;
}
return getMediaInfoHtml(item, options);
2018-10-23 01:05:09 +03:00
}
2020-06-13 19:32:38 +03:00
export function getSecondaryMediaInfoHtml(item, options) {
options = options || {};
if (options.interactive == null) {
options.interactive = false;
}
if (item.Type === 'Program') {
return getProgramInfoHtml(item, options);
}
return '';
2018-10-23 01:05:09 +03:00
}
2020-06-13 19:32:38 +03:00
export function getResolutionText(i) {
const width = i.Width;
const height = i.Height;
2018-10-23 01:05:09 +03:00
if (width && height) {
if (width >= 3800 || height >= 2000) {
return '4K';
}
if (width >= 2500 || height >= 1400) {
if (i.IsInterlaced) {
return '1440i';
}
return '1440p';
}
if (width >= 1800 || height >= 1000) {
if (i.IsInterlaced) {
return '1080i';
}
return '1080p';
}
if (width >= 1200 || height >= 700) {
if (i.IsInterlaced) {
return '720i';
}
return '720p';
}
if (width >= 700 || height >= 400) {
if (i.IsInterlaced) {
return '480i';
}
return '480p';
}
2018-10-23 01:05:09 +03:00
}
return null;
2018-10-23 01:05:09 +03:00
}
function getAudioStreamForDisplay(item) {
if (!item.MediaSources) {
return null;
}
2020-06-13 19:32:38 +03:00
const mediaSource = item.MediaSources[0];
if (!mediaSource) {
return null;
}
2020-06-13 19:32:38 +03:00
return (mediaSource.MediaStreams || []).filter(i => {
return i.Type === 'Audio' && (i.Index === mediaSource.DefaultAudioStreamIndex || mediaSource.DefaultAudioStreamIndex == null);
})[0];
2018-10-23 01:05:09 +03:00
}
2021-01-26 22:20:12 -05:00
export function getMediaInfoStats(item) {
2020-06-13 19:32:38 +03:00
const list = [];
2020-06-13 19:32:38 +03:00
const mediaSource = (item.MediaSources || [])[0] || {};
2020-06-13 19:32:38 +03:00
const videoStream = (mediaSource.MediaStreams || []).filter(i => {
return i.Type === 'Video';
})[0] || {};
2020-06-13 19:32:38 +03:00
const audioStream = getAudioStreamForDisplay(item) || {};
if (item.VideoType === 'Dvd') {
list.push({
type: 'mediainfo',
text: 'Dvd'
});
}
if (item.VideoType === 'BluRay') {
list.push({
type: 'mediainfo',
text: 'BluRay'
});
}
2020-06-13 19:32:38 +03:00
const resolutionText = getResolutionText(videoStream);
if (resolutionText) {
list.push({
type: 'mediainfo',
text: resolutionText
});
}
if (videoStream.Codec) {
list.push({
type: 'mediainfo',
text: videoStream.Codec
});
}
2020-06-13 19:32:38 +03: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';
}
if (channelText) {
list.push({
type: 'mediainfo',
text: channelText
});
}
2020-06-13 19:32:38 +03:00
const audioCodec = (audioStream.Codec || '').toLowerCase();
if ((audioCodec === 'dca' || audioCodec === 'dts') && audioStream.Profile) {
list.push({
type: 'mediainfo',
2018-10-23 01:05:09 +03:00
text: audioStream.Profile
});
} else if (audioStream.Codec) {
list.push({
type: 'mediainfo',
text: audioStream.Codec
});
}
if (item.DateCreated && itemHelper.enableDateAddedDisplay(item)) {
2020-06-13 19:32:38 +03:00
const dateCreated = datetime.parseISO8601Date(item.DateCreated);
2018-10-23 01:05:09 +03:00
list.push({
type: 'added',
2020-06-13 19:32:38 +03:00
text: globalize.translate('AddedOnValue', `${datetime.toLocaleDateString(dateCreated)} ${datetime.getDisplayTime(dateCreated)}`)
});
2018-10-23 01:05:09 +03:00
}
return list;
2018-10-23 01:05:09 +03:00
}
2020-06-13 19:32:38 +03:00
/* eslint-enable indent */
export default {
getMediaInfoHtml: getPrimaryMediaInfoHtml,
getEndsAt: getEndsAt,
getEndsAtFromPosition: getEndsAtFromPosition,
getPrimaryMediaInfoHtml: getPrimaryMediaInfoHtml,
getSecondaryMediaInfoHtml: getSecondaryMediaInfoHtml,
fillPrimaryMediaInfo: fillPrimaryMediaInfo,
fillSecondaryMediaInfo: fillSecondaryMediaInfo,
getMediaInfoStats: getMediaInfoStats,
getResolutionText: getResolutionText
};