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 && ( {listOptions.showMediaInfo !== false && enableSideMediaInfo && (
<PrimaryMediaInfo <PrimaryMediaInfo
className='secondary listItemMediaInfo' className='secondary listItemMediaInfo'
infoclass='mediaInfoText'
item={item} item={item}
isRuntimeEnabled={true} showRuntimeInfo
isStarRatingEnabled={true} showOfficialRatingInfo
isCaptionIndicatorEnabled={true} showOriginalAirDateInfo
isEpisodeTitleEnabled={true} showStarRatingInfo
isOfficialRatingEnabled={true} showCaptionIndicatorInfo
getMissingIndicator={indicator.getMissingIndicator} getMissingIndicator={indicator.getMissingIndicator}
/> />
)} )}

View file

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

View file

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

View file

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

View file

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

View file

@ -1,25 +1,38 @@
import React, { type FC } from 'react'; import React, { type FC } from 'react';
import Box from '@mui/material/Box'; import Box from '@mui/material/Box';
import Link from '@mui/material/Link';
import classNames from 'classnames'; import classNames from 'classnames';
import type { MiscInfo } from 'types/mediaInfoItem'; import type { MiscInfo } from 'types/mediaInfoItem';
interface MediaInfoItemProps { interface MediaInfoItemProps {
className?: string; className?: string;
miscInfo?: MiscInfo ; miscInfo: MiscInfo ;
} }
const MediaInfoItem: FC<MediaInfoItemProps> = ({ className, miscInfo }) => { const MediaInfoItem: FC<MediaInfoItemProps> = ({ className, miscInfo }) => {
const cssClass = classNames( const { text, textAction, cssClass, type } = miscInfo;
'mediaInfoItem',
'mediaInfoText', const renderText = () => {
className, if (textAction) {
miscInfo?.cssClass return (
<Link
className={classNames(textAction.cssClass, className)}
href={textAction.url}
title={textAction.title}
color='inherit'
>
{textAction.title}
</Link>
); );
} else {
return text;
}
};
return ( return (
<Box className={cssClass}> <Box className={classNames('mediaInfoItem', cssClass, type, className)}>
{miscInfo?.text} {renderText()}
</Box> </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 { ItemMediaKind } from 'types/base/models/item-media-kind';
import type { ItemDto } from 'types/base/models/item-dto'; import type { ItemDto } from 'types/base/models/item-dto';
import type { MiscInfo } from 'types/mediaInfoItem'; import type { MiscInfo } from 'types/mediaInfoItem';
import type { PrimaryInfoOpts } from './type';
interface PrimaryMediaInfoProps { interface PrimaryMediaInfoProps extends PrimaryInfoOpts {
className?: string; className?: string;
infoclass?: string;
item: ItemDto; item: ItemDto;
isYearEnabled?: boolean; showStarRatingInfo?: boolean;
isContainerEnabled?: boolean; showCaptionIndicatorInfo?: boolean;
isEpisodeTitleEnabled?: boolean; showCriticRatingInfo?: boolean;
isCriticRatingEnabled?: boolean; showEndsAtInfo?: boolean;
isEndsAtEnabled?: boolean; getMissingIndicator?: () => React.JSX.Element | null;
isOriginalAirDateEnabled?: boolean;
isRuntimeEnabled?: boolean;
isProgramIndicatorEnabled?: boolean;
isEpisodeTitleIndexNumberEnabled?: boolean;
isOfficialRatingEnabled?: boolean;
isStarRatingEnabled?: boolean;
isCaptionIndicatorEnabled?: boolean;
isMissingIndicatorEnabled?: boolean;
getMissingIndicator: () => React.JSX.Element | null
} }
const PrimaryMediaInfo: FC<PrimaryMediaInfoProps> = ({ const PrimaryMediaInfo: FC<PrimaryMediaInfoProps> = ({
className, className,
infoclass,
item, item,
isYearEnabled = false, showYearInfo,
isContainerEnabled = false, showAudioContainerInfo,
isEpisodeTitleEnabled = false, showEpisodeTitleInfo,
isCriticRatingEnabled = false, showOriginalAirDateInfo,
isEndsAtEnabled = false, showFolderRuntimeInfo,
isOriginalAirDateEnabled = false, showRuntimeInfo,
isRuntimeEnabled = false, showItemCountInfo,
isProgramIndicatorEnabled = false, showSeriesTimerInfo,
isEpisodeTitleIndexNumberEnabled = false, showStartDateInfo,
isOfficialRatingEnabled = false, showProgramIndicatorInfo,
isStarRatingEnabled = false, includeEpisodeTitleIndexNumber,
isCaptionIndicatorEnabled = false, showOfficialRatingInfo,
isMissingIndicatorEnabled = false, showVideo3DFormatInfo,
showPhotoSizeInfo,
showStarRatingInfo = false,
showCaptionIndicatorInfo = false,
showCriticRatingInfo = false,
showEndsAtInfo = false,
getMissingIndicator getMissingIndicator
}) => { }) => {
const miscInfo = usePrimaryMediaInfo({ const miscInfo = usePrimaryMediaInfo({
item, item,
isYearEnabled, showYearInfo,
isContainerEnabled, showAudioContainerInfo,
isEpisodeTitleEnabled, showEpisodeTitleInfo,
isOriginalAirDateEnabled, showOriginalAirDateInfo,
isRuntimeEnabled, showFolderRuntimeInfo,
isProgramIndicatorEnabled, showRuntimeInfo,
isEpisodeTitleIndexNumberEnabled, showItemCountInfo,
isOfficialRatingEnabled showSeriesTimerInfo,
showStartDateInfo,
showProgramIndicatorInfo,
includeEpisodeTitleIndexNumber,
showOfficialRatingInfo,
showVideo3DFormatInfo,
showPhotoSizeInfo
}); });
const { const {
StartDate, StartDate,
@ -72,32 +77,40 @@ const PrimaryMediaInfo: FC<PrimaryMediaInfoProps> = ({
const cssClass = classNames(className); const cssClass = classNames(className);
const renderMediaInfo = (info: MiscInfo | undefined, index: number) => ( const renderMediaInfo = (info: MiscInfo, index: number) => (
<MediaInfoItem key={index} miscInfo={info} /> <MediaInfoItem key={index} className={infoclass} miscInfo={info} />
); );
return ( return (
<Box className={cssClass}> <Box className={cssClass}>
{miscInfo.map((info, index) => renderMediaInfo(info, index))} {miscInfo.map((info, index) => renderMediaInfo(info, index))}
{isStarRatingEnabled && CommunityRating && ( {showStarRatingInfo && CommunityRating && (
<StarIcons communityRating={CommunityRating} /> <StarIcons
className={infoclass}
communityRating={CommunityRating}
/>
)} )}
{HasSubtitles && isCaptionIndicatorEnabled && <CaptionMediaInfo />} {showCaptionIndicatorInfo && HasSubtitles && (
<CaptionMediaInfo className={infoclass} />
{CriticRating && isCriticRatingEnabled && (
<CriticRatingMediaInfo criticRating={CriticRating} />
)} )}
{isEndsAtEnabled {showCriticRatingInfo && CriticRating && (
<CriticRatingMediaInfo
className={infoclass}
criticRating={CriticRating}
/>
)}
{showEndsAtInfo
&& MediaType === ItemMediaKind.Video && MediaType === ItemMediaKind.Video
&& RunTimeTicks && RunTimeTicks
&& !StartDate && <EndsAt runTimeTicks={RunTimeTicks} />} && !StartDate && (
<EndsAt className={infoclass} runTimeTicks={RunTimeTicks} />
{isMissingIndicatorEnabled && (
getMissingIndicator()
)} )}
{getMissingIndicator?.()}
</Box> </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 theme = useTheme();
const cssClass = classNames( const cssClass = classNames(
'mediaInfoItem', 'mediaInfoItem',
'mediaInfoText',
'starRatingContainer', 'starRatingContainer',
className className
); );
return ( return (
<Box className={cssClass}> <Box className={cssClass}>
<StarIcon fontSize={'small'} sx={{ <StarIcon
fontSize={'small'}
sx={{
color: theme.palette.starIcon.main color: theme.palette.starIcon.main
}} /> }}
/>
{communityRating.toFixed(1)} {communityRating.toFixed(1)}
</Box> </Box>
); );

View file

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

View file

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

View file

@ -1,4 +1,11 @@
export interface MiscInfo { interface TextAction {
text?: string | number; url: string;
title: string;
cssClass?: string;
}
export interface MiscInfo {
text?: string | number;
type?: string;
textAction?: TextAction;
cssClass?: string; cssClass?: string;
} }