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

Merge pull request #5938 from grafixeyehero/Add-SecondaryMediaInfo&Stats

Add Secondary Media Info & Media Info Stats
This commit is contained in:
Bill Thornton 2024-09-04 12:25:52 -04:00 committed by GitHub
commit e528847b7c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 738 additions and 188 deletions

View file

@ -78,12 +78,13 @@ const ListContent: FC<ListContentProps> = ({
{listOptions.showMediaInfo !== false && enableSideMediaInfo && (
<PrimaryMediaInfo
className='secondary listItemMediaInfo'
infoclass='mediaInfoText'
item={item}
isRuntimeEnabled={true}
isStarRatingEnabled={true}
isCaptionIndicatorEnabled={true}
isEpisodeTitleEnabled={true}
isOfficialRatingEnabled={true}
showRuntimeInfo
showOfficialRatingInfo
showOriginalAirDateInfo
showStarRatingInfo
showCaptionIndicatorInfo
getMissingIndicator={indicator.getMissingIndicator}
/>
)}

View file

@ -61,10 +61,11 @@ const ListItemBody: FC<ListItemBodyProps> = ({
{listOptions.showMediaInfo !== false && !enableSideMediaInfo && (
<PrimaryMediaInfo
className='secondary listItemMediaInfo listItemBodyText'
infoclass='mediaInfoText'
item={item}
isEpisodeTitleEnabled={true}
isOriginalAirDateEnabled={true}
isCaptionIndicatorEnabled={true}
showEpisodeTitleInfo
showOriginalAirDateInfo
showCaptionIndicatorInfo
getMissingIndicator={getMissingIndicator}
/>
)}

View file

@ -7,6 +7,7 @@
.listItemMediaInfo {
align-items: center;
margin-right: 1em;
}
.listItem {

View file

@ -10,14 +10,13 @@ interface CaptionMediaInfoProps {
const CaptionMediaInfo: FC<CaptionMediaInfoProps> = ({ className }) => {
const cssClass = classNames(
'mediaInfoItem',
'mediaInfoText',
'closedCaptionMediaInfoText',
className
);
return (
<Box className={cssClass}>
<ClosedCaptionIcon fontSize={'small'} />
<ClosedCaptionIcon />
</Box>
);
};

View file

@ -12,7 +12,6 @@ interface EndsAtProps {
const EndsAt: FC<EndsAtProps> = ({ runTimeTicks, className }) => {
const cssClass = classNames(
'mediaInfoItem',
'mediaInfoText',
'endsAt',
className
);

View file

@ -1,25 +1,38 @@
import React, { type FC } from 'react';
import Box from '@mui/material/Box';
import Link from '@mui/material/Link';
import classNames from 'classnames';
import type { MiscInfo } from 'types/mediaInfoItem';
interface MediaInfoItemProps {
className?: string;
miscInfo?: MiscInfo ;
miscInfo: MiscInfo ;
}
const MediaInfoItem: FC<MediaInfoItemProps> = ({ className, miscInfo }) => {
const cssClass = classNames(
'mediaInfoItem',
'mediaInfoText',
className,
miscInfo?.cssClass
);
const { text, textAction, cssClass, type } = miscInfo;
const renderText = () => {
if (textAction) {
return (
<Link
className={classNames(textAction.cssClass, className)}
href={textAction.url}
title={textAction.title}
color='inherit'
>
{textAction.title}
</Link>
);
} else {
return text;
}
};
return (
<Box className={cssClass}>
{miscInfo?.text}
<Box className={classNames('mediaInfoItem', cssClass, type, className)}>
{renderText()}
</Box>
);
};

View file

@ -0,0 +1,49 @@
import React, { type FC } from 'react';
import classNames from 'classnames';
import Box from '@mui/material/Box';
import useMediaInfoStats from './useMediaInfoStats';
import MediaInfoItem from './MediaInfoItem';
import type { ItemDto } from 'types/base/models/item-dto';
import type { MiscInfo } from 'types/mediaInfoItem';
import type { MediaInfoStatsOpts } from './type';
interface MediaInfoStatsProps extends MediaInfoStatsOpts {
className?: string;
infoclass?: string;
item: ItemDto;
}
const MediaInfoStats: FC<MediaInfoStatsProps> = ({
className,
infoclass,
item,
showResolutionInfo,
showVideoStreamCodecInfo,
showAudoChannelInfo,
showAudioStreamCodecInfo,
showDateAddedInfo
}) => {
const mediaInfoStats = useMediaInfoStats({
item,
showResolutionInfo,
showVideoStreamCodecInfo,
showAudoChannelInfo,
showAudioStreamCodecInfo,
showDateAddedInfo
});
const cssClass = classNames(className);
const renderMediaInfo = (info: MiscInfo, index: number) => (
<MediaInfoItem key={index} className={infoclass} miscInfo={info} />
);
return (
<Box className={cssClass}>
{mediaInfoStats.map((info, index) => renderMediaInfo(info, index))}
</Box>
);
};
export default MediaInfoStats;

View file

@ -12,54 +12,59 @@ import EndsAt from './EndsAt';
import { ItemMediaKind } from 'types/base/models/item-media-kind';
import type { ItemDto } from 'types/base/models/item-dto';
import type { MiscInfo } from 'types/mediaInfoItem';
import type { PrimaryInfoOpts } from './type';
interface PrimaryMediaInfoProps {
interface PrimaryMediaInfoProps extends PrimaryInfoOpts {
className?: string;
infoclass?: string;
item: ItemDto;
isYearEnabled?: boolean;
isContainerEnabled?: boolean;
isEpisodeTitleEnabled?: boolean;
isCriticRatingEnabled?: boolean;
isEndsAtEnabled?: boolean;
isOriginalAirDateEnabled?: boolean;
isRuntimeEnabled?: boolean;
isProgramIndicatorEnabled?: boolean;
isEpisodeTitleIndexNumberEnabled?: boolean;
isOfficialRatingEnabled?: boolean;
isStarRatingEnabled?: boolean;
isCaptionIndicatorEnabled?: boolean;
isMissingIndicatorEnabled?: boolean;
getMissingIndicator: () => React.JSX.Element | null
showStarRatingInfo?: boolean;
showCaptionIndicatorInfo?: boolean;
showCriticRatingInfo?: boolean;
showEndsAtInfo?: boolean;
getMissingIndicator?: () => React.JSX.Element | null;
}
const PrimaryMediaInfo: FC<PrimaryMediaInfoProps> = ({
className,
infoclass,
item,
isYearEnabled = false,
isContainerEnabled = false,
isEpisodeTitleEnabled = false,
isCriticRatingEnabled = false,
isEndsAtEnabled = false,
isOriginalAirDateEnabled = false,
isRuntimeEnabled = false,
isProgramIndicatorEnabled = false,
isEpisodeTitleIndexNumberEnabled = false,
isOfficialRatingEnabled = false,
isStarRatingEnabled = false,
isCaptionIndicatorEnabled = false,
isMissingIndicatorEnabled = false,
showYearInfo,
showAudioContainerInfo,
showEpisodeTitleInfo,
showOriginalAirDateInfo,
showFolderRuntimeInfo,
showRuntimeInfo,
showItemCountInfo,
showSeriesTimerInfo,
showStartDateInfo,
showProgramIndicatorInfo,
includeEpisodeTitleIndexNumber,
showOfficialRatingInfo,
showVideo3DFormatInfo,
showPhotoSizeInfo,
showStarRatingInfo = false,
showCaptionIndicatorInfo = false,
showCriticRatingInfo = false,
showEndsAtInfo = false,
getMissingIndicator
}) => {
const miscInfo = usePrimaryMediaInfo({
item,
isYearEnabled,
isContainerEnabled,
isEpisodeTitleEnabled,
isOriginalAirDateEnabled,
isRuntimeEnabled,
isProgramIndicatorEnabled,
isEpisodeTitleIndexNumberEnabled,
isOfficialRatingEnabled
showYearInfo,
showAudioContainerInfo,
showEpisodeTitleInfo,
showOriginalAirDateInfo,
showFolderRuntimeInfo,
showRuntimeInfo,
showItemCountInfo,
showSeriesTimerInfo,
showStartDateInfo,
showProgramIndicatorInfo,
includeEpisodeTitleIndexNumber,
showOfficialRatingInfo,
showVideo3DFormatInfo,
showPhotoSizeInfo
});
const {
StartDate,
@ -72,32 +77,40 @@ const PrimaryMediaInfo: FC<PrimaryMediaInfoProps> = ({
const cssClass = classNames(className);
const renderMediaInfo = (info: MiscInfo | undefined, index: number) => (
<MediaInfoItem key={index} miscInfo={info} />
const renderMediaInfo = (info: MiscInfo, index: number) => (
<MediaInfoItem key={index} className={infoclass} miscInfo={info} />
);
return (
<Box className={cssClass}>
{miscInfo.map((info, index) => renderMediaInfo(info, index))}
{isStarRatingEnabled && CommunityRating && (
<StarIcons communityRating={CommunityRating} />
{showStarRatingInfo && CommunityRating && (
<StarIcons
className={infoclass}
communityRating={CommunityRating}
/>
)}
{HasSubtitles && isCaptionIndicatorEnabled && <CaptionMediaInfo />}
{CriticRating && isCriticRatingEnabled && (
<CriticRatingMediaInfo criticRating={CriticRating} />
{showCaptionIndicatorInfo && HasSubtitles && (
<CaptionMediaInfo className={infoclass} />
)}
{isEndsAtEnabled
{showCriticRatingInfo && CriticRating && (
<CriticRatingMediaInfo
className={infoclass}
criticRating={CriticRating}
/>
)}
{showEndsAtInfo
&& MediaType === ItemMediaKind.Video
&& RunTimeTicks
&& !StartDate && <EndsAt runTimeTicks={RunTimeTicks} />}
{isMissingIndicatorEnabled && (
getMissingIndicator()
&& !StartDate && (
<EndsAt className={infoclass} runTimeTicks={RunTimeTicks} />
)}
{getMissingIndicator?.()}
</Box>
);
};

View file

@ -0,0 +1,55 @@
import React, { type FC } from 'react';
import classNames from 'classnames';
import Box from '@mui/material/Box';
import useSecondaryMediaInfo from './useSecondaryMediaInfo';
import useIndicator from 'components/indicators/useIndicator';
import MediaInfoItem from './MediaInfoItem';
import type { ItemDto } from 'types/base/models/item-dto';
import { MiscInfo } from 'types/mediaInfoItem';
import type { SecondaryInfoOpts } from './type';
interface SecondaryMediaInfoProps extends SecondaryInfoOpts {
className?: string;
infoclass?: string;
item: ItemDto;
showTimerIndicatorInfo?: boolean;
}
const SecondaryMediaInfo: FC<SecondaryMediaInfoProps> = ({
className,
infoclass,
item,
showProgramTimeInfo,
showStartDateInfo,
showChannelNumberInfo,
showChannelInfo,
channelInteractive,
showTimerIndicatorInfo = false
}) => {
const miscInfo = useSecondaryMediaInfo({
item,
showProgramTimeInfo,
showStartDateInfo,
showChannelNumberInfo,
showChannelInfo,
channelInteractive
});
const indicator = useIndicator(item);
const cssClass = classNames(className);
const renderMediaInfo = (info: MiscInfo, index: number) => (
<MediaInfoItem key={index} className={infoclass} miscInfo={info} />
);
return (
<Box className={cssClass}>
{miscInfo.map((info, index) => renderMediaInfo(info, index))}
{showTimerIndicatorInfo !== false && indicator.getTimerIndicator()}
</Box>
);
};
export default SecondaryMediaInfo;

View file

@ -13,16 +13,18 @@ const StarIcons: FC<StarIconsProps> = ({ className, communityRating }) => {
const theme = useTheme();
const cssClass = classNames(
'mediaInfoItem',
'mediaInfoText',
'starRatingContainer',
className
);
return (
<Box className={cssClass}>
<StarIcon fontSize={'small'} sx={{
color: theme.palette.starIcon.main
}} />
<StarIcon
fontSize={'small'}
sx={{
color: theme.palette.starIcon.main
}}
/>
{communityRating.toFixed(1)}
</Box>
);

View file

@ -271,7 +271,7 @@ export function getMediaInfoHtml(item, options = {}) {
if (options.officialRating !== false && item.OfficialRating && item.Type !== 'Season' && item.Type !== 'Episode') {
miscInfo.push({
text: item.OfficialRating,
cssClass: 'mediaInfoOfficialRating'
cssClass: 'mediaInfoText mediaInfoOfficialRating'
});
}

View file

@ -46,8 +46,6 @@
align-items: center;
justify-content: center;
vertical-align: middle;
padding-top: 0;
padding-bottom: 0;
}
.starIcon {
@ -94,15 +92,3 @@
.closedCaptionMediaInfoText {
font-weight: bold;
}
.mediaInfoOfficialRating {
border: 0.09em solid currentColor;
padding: 0 0.6em;
height: 1.3em;
line-height: 1.8em;
display: flex;
align-items: center;
justify-content: center;
border-radius: 0.1em;
font-size: 96%;
}

View file

@ -0,0 +1,34 @@
export interface PrimaryInfoOpts {
showYearInfo?: boolean;
showAudioContainerInfo?: boolean;
showEpisodeTitleInfo?: boolean;
includeEpisodeTitleIndexNumber?: boolean;
showOriginalAirDateInfo?: boolean;
showFolderRuntimeInfo?: boolean;
showRuntimeInfo?: boolean;
showItemCountInfo?: boolean;
showSeriesTimerInfo?: boolean;
showStartDateInfo?: boolean;
showProgramIndicatorInfo?: boolean;
showOfficialRatingInfo?: boolean;
showVideo3DFormatInfo?: boolean;
showPhotoSizeInfo?: boolean;
}
export interface SecondaryInfoOpts {
showProgramTimeInfo?: boolean;
showStartDateInfo?: boolean;
showEndDateInfo?: boolean;
showChannelNumberInfo?: boolean;
showChannelInfo?: boolean;
channelInteractive?: boolean;
}
export interface MediaInfoStatsOpts {
showVideoTypeInfo?: boolean;
showResolutionInfo?: boolean;
showVideoStreamCodecInfo?: boolean;
showAudoChannelInfo?: boolean;
showAudioStreamCodecInfo?: boolean;
showDateAddedInfo?: boolean;
}

View file

@ -0,0 +1,229 @@
import { MediaStreamType } from '@jellyfin/sdk/lib/generated-client/models/media-stream-type';
import { VideoType } from '@jellyfin/sdk/lib/generated-client/models/video-type';
import type { MediaStream } from '@jellyfin/sdk/lib/generated-client/models/media-stream';
import itemHelper from 'components/itemHelper';
import datetime from 'scripts/datetime';
import globalize from 'lib/globalize';
import type { ItemDto } from 'types/base/models/item-dto';
import type { MiscInfo } from 'types/mediaInfoItem';
import type { NullableString } from 'types/base/common/shared/types';
import type { MediaInfoStatsOpts } from './type';
const getResolution = (label: string, isInterlaced?: boolean) =>
isInterlaced ? `${label}i` : label;
const getResolutionText = (
showResolutionInfo: boolean,
stream: MediaStream
) => {
const { Width, Height, IsInterlaced } = stream;
if (showResolutionInfo && Width && Height) {
switch (true) {
case Width >= 3800 || Height >= 2000:
return '4K';
case Width >= 2500 || Height >= 1400:
return getResolution('1440p', IsInterlaced);
case Width >= 1800 || Height >= 1000:
return getResolution('1080p', IsInterlaced);
case Width >= 1200 || Height >= 700:
return getResolution('720p', IsInterlaced);
case Width >= 700 || Height >= 400:
return getResolution('480p', IsInterlaced);
default:
return null;
}
}
return null;
};
const getAudoChannelText = (
showAudoChannelInfo: boolean,
stream: MediaStream
) => {
const { Channels } = stream;
if (showAudoChannelInfo && Channels) {
switch (true) {
case Channels === 8:
return '7.1';
case Channels === 7:
return '6.1';
case Channels === 6:
return '5.1';
case Channels === 2:
return '2.0';
default:
return null;
}
}
return null;
};
function getAudioStreamForDisplay(item: ItemDto) {
const mediaSource = (item.MediaSources || [])[0] || {};
return (
(mediaSource.MediaStreams || []).filter((i) => {
return (
i.Type === MediaStreamType.Audio
&& (i.Index === mediaSource.DefaultAudioStreamIndex
|| mediaSource.DefaultAudioStreamIndex == null)
);
})[0] || {}
);
}
function getVideoStreamForDisplay(item: ItemDto) {
const mediaSource = (item.MediaSources || [])[0] || {};
return (
(mediaSource.MediaStreams || []).filter((i) => {
return i.Type === MediaStreamType.Video;
})[0] || {}
);
}
function addVideoType(
showVideoTypeInfo: boolean,
itemVideoType: VideoType | undefined,
addMiscInfo: (val: MiscInfo) => void
): void {
if (showVideoTypeInfo) {
if (itemVideoType === VideoType.Dvd) {
addMiscInfo({ type: 'mediainfo', text: 'Dvd' });
}
if (itemVideoType === VideoType.BluRay) {
addMiscInfo({ type: 'mediainfo', text: 'BluRay' });
}
}
}
function addResolution(
showResolutionInfo: boolean,
videoStream: MediaStream,
addMiscInfo: (val: MiscInfo) => void
): void {
const resolutionText = getResolutionText(showResolutionInfo, videoStream);
if (resolutionText) {
addMiscInfo({ type: 'mediainfo', text: resolutionText });
}
}
function addVideoStreamCodec(
showVideoCodecInfo: boolean,
videoStreamCodec: NullableString,
addMiscInfo: (val: MiscInfo) => void
): void {
if (showVideoCodecInfo && videoStreamCodec) {
addMiscInfo({ type: 'mediainfo', text: videoStreamCodec });
}
}
function addAudoChannel(
showAudoChannelInfo: boolean,
audioStream: MediaStream,
addMiscInfo: (val: MiscInfo) => void
): void {
const audioChannelText = getAudoChannelText(
showAudoChannelInfo,
audioStream
);
if (audioChannelText) {
addMiscInfo({ type: 'mediainfo', text: audioChannelText });
}
}
function addAudioStreamCodec(
showAudioStreamCodecInfo: boolean,
audioStream: MediaStream,
addMiscInfo: (val: MiscInfo) => void
): void {
const audioCodec = (audioStream.Codec || '').toLowerCase();
if (showAudioStreamCodecInfo) {
if (
(audioCodec === 'dca' || audioCodec === 'dts')
&& audioStream?.Profile
) {
addMiscInfo({ type: 'mediainfo', text: audioStream.Profile });
} else if (audioStream?.Codec) {
addMiscInfo({ type: 'mediainfo', text: audioStream.Codec });
}
}
}
function addDateAdded(
showDateAddedInfo: boolean,
item: ItemDto,
addMiscInfo: (val: MiscInfo) => void
): void {
if (
showDateAddedInfo
&& item.DateCreated
&& itemHelper.enableDateAddedDisplay(item)
) {
const dateCreated = datetime.parseISO8601Date(item.DateCreated);
addMiscInfo({
type: 'added',
text: globalize.translate(
'AddedOnValue',
`${datetime.toLocaleDateString(
dateCreated
)} ${datetime.getDisplayTime(dateCreated)}`
)
});
}
}
interface UseMediaInfoStatsProps extends MediaInfoStatsOpts {
item: ItemDto;
}
function useMediaInfoStats({
item,
showVideoTypeInfo = false,
showResolutionInfo = false,
showVideoStreamCodecInfo = false,
showAudoChannelInfo = false,
showAudioStreamCodecInfo = false,
showDateAddedInfo = false
}: UseMediaInfoStatsProps) {
const miscInfo: MiscInfo[] = [];
const addMiscInfo = (val: MiscInfo) => {
if (val) {
miscInfo.push(val);
}
};
const videoStream = getVideoStreamForDisplay(item);
const audioStream = getAudioStreamForDisplay(item);
addVideoType(showVideoTypeInfo, item.VideoType, addMiscInfo);
addResolution(showResolutionInfo, videoStream, addMiscInfo);
addVideoStreamCodec(
showVideoStreamCodecInfo,
videoStream.Codec,
addMiscInfo
);
addAudoChannel(showAudoChannelInfo, audioStream, addMiscInfo);
addAudioStreamCodec(showAudioStreamCodecInfo, audioStream, addMiscInfo);
addDateAdded(showDateAddedInfo, item, addMiscInfo);
return miscInfo;
}
export default useMediaInfoStats;

View file

@ -6,41 +6,53 @@ import itemHelper from '../itemHelper';
import { ItemKind } from 'types/base/models/item-kind';
import { ItemMediaKind } from 'types/base/models/item-media-kind';
import { ItemStatus } from 'types/base/models/item-status';
import type { NullableNumber, NullableString } from 'types/base/common/shared/types';
import type {
NullableNumber,
NullableString
} from 'types/base/common/shared/types';
import type { ItemDto } from 'types/base/models/item-dto';
import type { MiscInfo } from 'types/mediaInfoItem';
import { PrimaryInfoOpts } from './type';
function shouldShowFolderRuntime(
showFolderRuntimeInfo: boolean,
itemType: ItemKind,
itemMediaType: ItemMediaKind
): boolean {
return (
itemType === ItemKind.MusicAlbum
|| itemMediaType === ItemMediaKind.MusicArtist
|| itemType === ItemKind.Playlist
|| itemMediaType === ItemMediaKind.Playlist
|| itemMediaType === ItemMediaKind.MusicGenre
showFolderRuntimeInfo
&& (itemType === ItemKind.MusicAlbum
|| itemMediaType === ItemMediaKind.MusicArtist
|| itemType === ItemKind.Playlist
|| itemMediaType === ItemMediaKind.Playlist
|| itemMediaType === ItemMediaKind.MusicGenre)
);
}
function addTrackCountOrItemCount(
showFolderRuntime: boolean,
isFolderRuntimeEnabled: boolean,
showItemCountInfo: boolean,
itemSongCount: NullableNumber,
itemChildCount: NullableNumber,
itemRunTimeTicks: NullableNumber,
itemType: NullableString,
itemType: ItemKind,
addMiscInfo: (val: MiscInfo) => void
): void {
if (showFolderRuntime) {
const count = itemSongCount ?? itemChildCount;
if (isFolderRuntimeEnabled) {
const count = itemSongCount || itemChildCount;
if (count) {
addMiscInfo({ text: globalize.translate('TrackCount', count) });
}
if (itemRunTimeTicks) {
addMiscInfo({ text: datetime.getDisplayDuration(itemRunTimeTicks) });
addMiscInfo({
text: datetime.getDisplayDuration(itemRunTimeTicks)
});
}
} else if (itemType === ItemKind.PhotoAlbum || itemType === ItemKind.BoxSet) {
} else if (
showItemCountInfo
&& (itemType === ItemKind.PhotoAlbum || itemType === ItemKind.BoxSet)
) {
const count = itemChildCount;
if (count) {
addMiscInfo({ text: globalize.translate('ItemCount', count) });
@ -49,16 +61,17 @@ function addTrackCountOrItemCount(
}
function addOriginalAirDateInfo(
showOriginalAirDateInfo: boolean,
itemType: ItemKind,
itemMediaType: ItemMediaKind,
isOriginalAirDateEnabled: boolean,
itemPremiereDate: NullableString,
addMiscInfo: (val: MiscInfo) => void
): void {
if (
itemPremiereDate
&& (itemType === ItemKind.Episode || itemMediaType === ItemMediaKind.Photo)
&& isOriginalAirDateEnabled
showOriginalAirDateInfo
&& (itemType === ItemKind.Episode
|| itemMediaType === ItemMediaKind.Photo)
&& itemPremiereDate
) {
try {
//don't modify date to locale if episode. Only Dates (not times) are stored, or editable in the edit metadata dialog
@ -74,6 +87,7 @@ function addOriginalAirDateInfo(
}
function addSeriesTimerInfo(
showSeriesTimerInfo: boolean,
itemType: ItemKind,
itemRecordAnyTime: boolean | undefined,
itemStartDate: NullableString,
@ -81,7 +95,7 @@ function addSeriesTimerInfo(
itemChannelName: NullableString,
addMiscInfo: (val: MiscInfo) => void
): void {
if (itemType === ItemKind.SeriesTimer) {
if (showSeriesTimerInfo && itemType === ItemKind.SeriesTimer) {
if (itemRecordAnyTime) {
addMiscInfo({ text: globalize.translate('Anytime') });
} else {
@ -92,7 +106,7 @@ function addSeriesTimerInfo(
addMiscInfo({ text: globalize.translate('AllChannels') });
} else {
addMiscInfo({
text: itemChannelName ?? globalize.translate('OneChannel')
text: itemChannelName || globalize.translate('OneChannel')
});
}
}
@ -104,7 +118,7 @@ function addProgramIndicatorInfo(
): void {
if (
program?.IsLive
&& userSettings.get('guide-indicator-live', false) === 'true'
&& userSettings.get('guide-indicator-live') === 'true'
) {
addMiscInfo({
text: globalize.translate('Live'),
@ -112,7 +126,7 @@ function addProgramIndicatorInfo(
});
} else if (
program?.IsPremiere
&& userSettings.get('guide-indicator-premiere', false) === 'true'
&& userSettings.get('guide-indicator-premiere') === 'true'
) {
addMiscInfo({
text: globalize.translate('Premiere'),
@ -121,7 +135,7 @@ function addProgramIndicatorInfo(
} else if (
program?.IsSeries
&& !program?.IsRepeat
&& userSettings.get('guide-indicator-new', false) === 'true'
&& userSettings.get('guide-indicator-new') === 'true'
) {
addMiscInfo({
text: globalize.translate('New'),
@ -130,7 +144,7 @@ function addProgramIndicatorInfo(
} else if (
program?.IsSeries
&& program?.IsRepeat
&& userSettings.get('guide-indicator-repeat', false) === 'true'
&& userSettings.get('guide-indicator-repeat') === 'true'
) {
addMiscInfo({
text: globalize.translate('Repeat'),
@ -140,12 +154,12 @@ function addProgramIndicatorInfo(
}
function addProgramIndicators(
showYearInfo: boolean,
showEpisodeTitleInfo: boolean,
showOriginalAirDateInfo: boolean,
showProgramIndicatorInfo: boolean,
includeEpisodeTitleIndexNumber: boolean,
item: ItemDto,
isYearEnabled: boolean,
isEpisodeTitleEnabled: boolean,
isOriginalAirDateEnabled: boolean,
isProgramIndicatorEnabled: boolean,
isEpisodeTitleIndexNumberEnabled: boolean,
addMiscInfo: (val: MiscInfo) => void
): void {
if (item.Type === ItemKind.Program || item.Type === ItemKind.Timer) {
@ -154,45 +168,43 @@ function addProgramIndicators(
program = item.ProgramInfo;
}
if (isProgramIndicatorEnabled !== false) {
if (showProgramIndicatorInfo) {
addProgramIndicatorInfo(program, addMiscInfo);
}
addProgramTextInfo(
showEpisodeTitleInfo,
includeEpisodeTitleIndexNumber,
showOriginalAirDateInfo,
showYearInfo,
program,
isEpisodeTitleEnabled,
isEpisodeTitleIndexNumberEnabled,
isOriginalAirDateEnabled,
isYearEnabled,
addMiscInfo
);
}
}
function addProgramTextInfo(
showEpisodeTitleInfo: boolean,
includeEpisodeTitleIndexNumber: boolean,
showOriginalAirDateInfo: boolean,
showYearInfo: boolean,
program: ItemDto,
isEpisodeTitleEnabled: boolean,
isEpisodeTitleIndexNumberEnabled: boolean,
isOriginalAirDateEnabled: boolean,
isYearEnabled: boolean,
addMiscInfo: (val: MiscInfo) => void
): void {
if ((program?.IsSeries || program?.EpisodeTitle)
&& isEpisodeTitleEnabled !== false) {
if (showEpisodeTitleInfo && (program.IsSeries || program.EpisodeTitle)) {
const text = itemHelper.getDisplayName(program, {
includeIndexNumber: isEpisodeTitleIndexNumberEnabled
includeIndexNumber: includeEpisodeTitleIndexNumber
});
if (text) {
addMiscInfo({ text: text });
}
} else if (
program?.ProductionYear
&& ((program?.IsMovie && isOriginalAirDateEnabled !== false)
|| isYearEnabled !== false)
((showOriginalAirDateInfo && program.IsMovie) || showYearInfo)
&& program.ProductionYear
) {
addMiscInfo({ text: program.ProductionYear });
} else if (program?.PremiereDate && isOriginalAirDateEnabled !== false) {
} else if (showOriginalAirDateInfo && program.PremiereDate) {
try {
const date = datetime.parseISO8601Date(program.PremiereDate);
const text = globalize.translate(
@ -207,12 +219,14 @@ function addProgramTextInfo(
}
function addStartDateInfo(
showStartDateInfo: boolean,
itemStartDate: NullableString,
itemType: ItemKind,
addMiscInfo: (val: MiscInfo) => void
): void {
if (
itemStartDate
showStartDateInfo
&& itemStartDate
&& itemType !== ItemKind.Program
&& itemType !== ItemKind.SeriesTimer
&& itemType !== ItemKind.Timer
@ -231,14 +245,14 @@ function addStartDateInfo(
}
function addSeriesProductionYearInfo(
showYearInfo: boolean,
itemProductionYear: NullableNumber,
itemType: ItemKind,
isYearEnabled: boolean,
itemStatus: ItemStatus,
itemEndDate: NullableString,
addMiscInfo: (val: MiscInfo) => void
): void {
if (itemProductionYear && isYearEnabled && itemType === ItemKind.Series) {
if (showYearInfo && itemProductionYear && itemType === ItemKind.Series) {
if (itemStatus === ItemStatus.Continuing) {
addMiscInfo({
text: globalize.translate(
@ -249,7 +263,11 @@ function addSeriesProductionYearInfo(
)
});
} else {
addproductionYearWithEndDate(itemProductionYear, itemEndDate, addMiscInfo);
addproductionYearWithEndDate(
itemProductionYear,
itemEndDate,
addMiscInfo
);
}
}
}
@ -281,7 +299,7 @@ function addproductionYearWithEndDate(
}
function addYearInfo(
isYearEnabled: boolean,
showYearInfo: boolean,
itemType: ItemKind,
itemMediaType: ItemMediaKind,
itemProductionYear: NullableNumber,
@ -289,7 +307,7 @@ function addYearInfo(
addMiscInfo: (val: MiscInfo) => void
): void {
if (
isYearEnabled
showYearInfo
&& itemType !== ItemKind.Series
&& itemType !== ItemKind.Episode
&& itemType !== ItemKind.Person
@ -314,103 +332,116 @@ function addYearInfo(
}
function addVideo3DFormat(
showVideo3DFormatInfo: boolean,
itemVideo3DFormat: NullableString,
addMiscInfo: (val: MiscInfo) => void
): void {
if (itemVideo3DFormat) {
if (showVideo3DFormatInfo && itemVideo3DFormat) {
addMiscInfo({ text: '3D' });
}
}
function addRunTimeInfo(
isFolderRuntimeEnabled: boolean,
showRuntimeInfo: boolean,
itemRunTimeTicks: NullableNumber,
itemType: ItemKind,
showFolderRuntime: boolean,
isRuntimeEnabled: boolean,
addMiscInfo: (val: MiscInfo) => void
): void {
if (
itemRunTimeTicks
!isFolderRuntimeEnabled
&& showRuntimeInfo
&& itemRunTimeTicks
&& itemType !== ItemKind.Series
&& itemType !== ItemKind.Program
&& itemType !== ItemKind.Timer
&& itemType !== ItemKind.Book
&& !showFolderRuntime
&& isRuntimeEnabled
) {
if (itemType === ItemKind.Audio) {
addMiscInfo({ text: datetime.getDisplayRunningTime(itemRunTimeTicks) });
addMiscInfo({
text: datetime.getDisplayRunningTime(itemRunTimeTicks)
});
} else {
addMiscInfo({ text: datetime.getDisplayDuration(itemRunTimeTicks) });
addMiscInfo({
text: datetime.getDisplayDuration(itemRunTimeTicks)
});
}
}
}
function addOfficialRatingInfo(
showOfficialRatingInfo: boolean,
itemOfficialRating: NullableString,
itemType: ItemKind,
isOfficialRatingEnabled: boolean,
addMiscInfo: (val: MiscInfo) => void
): void {
if (
itemOfficialRating
&& isOfficialRatingEnabled
showOfficialRatingInfo
&& itemOfficialRating
&& itemType !== ItemKind.Season
&& itemType !== ItemKind.Episode
) {
addMiscInfo({
text: itemOfficialRating,
cssClass: 'mediaInfoOfficialRating'
cssClass: 'mediaInfoText mediaInfoOfficialRating'
});
}
}
function addAudioContainer(
showAudioContainerInfo: boolean,
itemContainer: NullableString,
isContainerEnabled: boolean,
itemType: ItemKind,
addMiscInfo: (val: MiscInfo) => void
): void {
if (itemContainer && isContainerEnabled && itemType === ItemKind.Audio) {
if (
showAudioContainerInfo
&& itemContainer
&& itemType === ItemKind.Audio
) {
addMiscInfo({ text: itemContainer });
}
}
function addPhotoSize(
showPhotoSizeInfo: boolean,
itemMediaType: ItemMediaKind,
itemWidth: NullableNumber,
itemHeight: NullableNumber,
addMiscInfo: (val: MiscInfo) => void
): void {
if (itemMediaType === ItemMediaKind.Photo && itemWidth && itemHeight) {
if (
showPhotoSizeInfo
&& itemMediaType === ItemMediaKind.Photo
&& itemWidth
&& itemHeight
) {
const size = `${itemWidth}x${itemHeight}`;
addMiscInfo({ text: size });
}
}
interface UsePrimaryMediaInfoProps {
interface UsePrimaryMediaInfoProps extends PrimaryInfoOpts {
item: ItemDto;
isYearEnabled: boolean;
isContainerEnabled: boolean;
isEpisodeTitleEnabled: boolean;
isOriginalAirDateEnabled: boolean;
isRuntimeEnabled: boolean;
isProgramIndicatorEnabled: boolean;
isEpisodeTitleIndexNumberEnabled: boolean;
isOfficialRatingEnabled: boolean;
}
function usePrimaryMediaInfo({
item,
isYearEnabled = false,
isContainerEnabled = false,
isEpisodeTitleEnabled = false,
isOriginalAirDateEnabled = false,
isRuntimeEnabled = false,
isProgramIndicatorEnabled = false,
isEpisodeTitleIndexNumberEnabled = false,
isOfficialRatingEnabled = false
showYearInfo = false,
showAudioContainerInfo = false,
showEpisodeTitleInfo = false,
showOriginalAirDateInfo = false,
showFolderRuntimeInfo = false,
showRuntimeInfo = false,
showItemCountInfo = false,
showSeriesTimerInfo = false,
showStartDateInfo = false,
showProgramIndicatorInfo = false,
includeEpisodeTitleIndexNumber = false,
showOfficialRatingInfo = false,
showVideo3DFormatInfo = false,
showPhotoSizeInfo = false
}: UsePrimaryMediaInfoProps) {
const {
EndDate,
@ -441,10 +472,15 @@ function usePrimaryMediaInfo({
}
};
const showFolderRuntime = shouldShowFolderRuntime(Type, MediaType);
const isFolderRuntimeEnabled = shouldShowFolderRuntime(
showFolderRuntimeInfo,
Type,
MediaType
);
addTrackCountOrItemCount(
showFolderRuntime,
isFolderRuntimeEnabled,
showItemCountInfo,
SongCount,
ChildCount,
RunTimeTicks,
@ -453,14 +489,15 @@ function usePrimaryMediaInfo({
);
addOriginalAirDateInfo(
showOriginalAirDateInfo,
Type,
MediaType,
isOriginalAirDateEnabled,
PremiereDate,
addMiscInfo
);
addSeriesTimerInfo(
showSeriesTimerInfo,
Type,
RecordAnyTime,
StartDate,
@ -469,29 +506,29 @@ function usePrimaryMediaInfo({
addMiscInfo
);
addStartDateInfo(StartDate, Type, addMiscInfo);
addStartDateInfo(showStartDateInfo, StartDate, Type, addMiscInfo);
addSeriesProductionYearInfo(
showYearInfo,
ProductionYear,
Type,
isYearEnabled,
Status,
EndDate,
addMiscInfo
);
addProgramIndicators(
showProgramIndicatorInfo,
showEpisodeTitleInfo,
includeEpisodeTitleIndexNumber,
showOriginalAirDateInfo,
showYearInfo,
item,
isProgramIndicatorEnabled,
isEpisodeTitleEnabled,
isEpisodeTitleIndexNumberEnabled,
isOriginalAirDateEnabled,
isYearEnabled,
addMiscInfo
);
addYearInfo(
isYearEnabled,
showYearInfo,
Type,
MediaType,
ProductionYear,
@ -500,25 +537,25 @@ function usePrimaryMediaInfo({
);
addRunTimeInfo(
isFolderRuntimeEnabled,
showRuntimeInfo,
RunTimeTicks,
Type,
showFolderRuntime,
isRuntimeEnabled,
addMiscInfo
);
addOfficialRatingInfo(
showOfficialRatingInfo,
OfficialRating,
Type,
isOfficialRatingEnabled,
addMiscInfo
);
addVideo3DFormat(Video3DFormat, addMiscInfo);
addVideo3DFormat(showVideo3DFormatInfo, Video3DFormat, addMiscInfo);
addPhotoSize(MediaType, Width, Height, addMiscInfo);
addPhotoSize(showPhotoSizeInfo, MediaType, Width, Height, addMiscInfo);
addAudioContainer(Container, isContainerEnabled, Type, addMiscInfo);
addAudioContainer(showAudioContainerInfo, Container, Type, addMiscInfo);
return miscInfo;
}

View file

@ -0,0 +1,124 @@
import datetime from 'scripts/datetime';
import { appRouter } from 'components/router/appRouter';
import type { NullableString } from 'types/base/common/shared/types';
import type { ItemDto } from 'types/base/models/item-dto';
import type { MiscInfo } from 'types/mediaInfoItem';
import { ItemKind } from 'types/base/models/item-kind';
import type { SecondaryInfoOpts } from './type';
function addProgramTime(
showProgramTimeInfo: boolean,
showStartDateInfo: boolean,
showEndDateInfo: boolean,
itemStartDate: NullableString,
itemEndDate: NullableString,
addMiscInfo: (val: MiscInfo) => void
): void {
let programTimeText = '';
let date;
if (showProgramTimeInfo && itemStartDate) {
try {
date = datetime.parseISO8601Date(itemStartDate);
if (showStartDateInfo) {
programTimeText += datetime.toLocaleDateString(date, {
weekday: 'short',
month: 'short',
day: 'numeric'
});
}
programTimeText += ` ${datetime.getDisplayTime(date)}`;
if (showEndDateInfo && itemEndDate) {
date = datetime.parseISO8601Date(itemEndDate);
programTimeText += ` - ${datetime.getDisplayTime(date)}`;
}
addMiscInfo({ text: programTimeText });
} catch (e) {
console.error('error parsing date:', itemStartDate);
}
}
}
function addChannelNumber(
showChannelNumberInfo: boolean,
itemChannelNumber: NullableString,
addMiscInfo: (val: MiscInfo) => void
): void {
if (showChannelNumberInfo && itemChannelNumber) {
addMiscInfo({
text: `CH ${itemChannelNumber}`
});
}
}
const addChannelName = (
showChannelInfo: boolean,
channelInteractive: boolean,
item: ItemDto,
addMiscInfo: (val: MiscInfo) => void
) => {
if (showChannelInfo && item.ChannelName) {
if (channelInteractive && item.ChannelId) {
const url = appRouter.getRouteUrl({
ServerId: item.ServerId,
Type: ItemKind.TvChannel,
Name: item.ChannelName,
Id: item.ChannelId
});
addMiscInfo({
textAction: {
url,
title: item.ChannelName
}
});
} else {
addMiscInfo({ text: item.ChannelName });
}
}
};
interface UseSecondaryMediaInfoProps extends SecondaryInfoOpts {
item: ItemDto;
}
function useSecondaryMediaInfo({
item,
showProgramTimeInfo = false,
showStartDateInfo = false,
showEndDateInfo = false,
showChannelNumberInfo = false,
showChannelInfo = false,
channelInteractive = false
}: UseSecondaryMediaInfoProps) {
const { EndDate, StartDate, ChannelNumber } = item;
const miscInfo: MiscInfo[] = [];
if (item.Type === ItemKind.Program) {
const addMiscInfo = (val: MiscInfo) => {
if (val) {
miscInfo.push(val);
}
};
addProgramTime(
showProgramTimeInfo,
showStartDateInfo,
showEndDateInfo,
StartDate,
EndDate,
addMiscInfo
);
addChannelNumber(showChannelNumberInfo, ChannelNumber, addMiscInfo);
addChannelName(showChannelInfo, channelInteractive, item, addMiscInfo);
}
return miscInfo;
}
export default useSecondaryMediaInfo;