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

Convert mediainfo PrimaryMediaInfo to react

This commit is contained in:
grafixeyehero 2024-01-31 03:01:58 +03:00
parent 2e90f669e5
commit c3b5d50313
8 changed files with 766 additions and 0 deletions

View file

@ -0,0 +1,25 @@
import React, { FC } from 'react';
import classNames from 'classnames';
import ClosedCaptionIcon from '@mui/icons-material/ClosedCaption';
import Box from '@mui/material/Box';
interface CaptionMediaInfoProps {
className?: string;
}
const CaptionMediaInfo: FC<CaptionMediaInfoProps> = ({ className }) => {
const cssClass = classNames(
'mediaInfoItem',
'mediaInfoText',
'closedCaptionMediaInfoText',
className
);
return (
<Box className={cssClass}>
<ClosedCaptionIcon fontSize={'small'} />
</Box>
);
};
export default CaptionMediaInfo;

View file

@ -0,0 +1,25 @@
import React, { FC } from 'react';
import classNames from 'classnames';
import Box from '@mui/material/Box';
interface CriticRatingMediaInfoProps {
className?: string;
criticRating: number;
}
const CriticRatingMediaInfo: FC<CriticRatingMediaInfoProps> = ({
className,
criticRating
}) => {
const cssClass = classNames(
'mediaInfoCriticRating',
'mediaInfoItem',
criticRating >= 60 ?
'mediaInfoCriticRatingFresh' :
'mediaInfoCriticRatingRotten',
className
);
return <Box className={cssClass}>{criticRating}</Box>;
};
export default CriticRatingMediaInfo;

View file

@ -0,0 +1,31 @@
import React, { FC } from 'react';
import classNames from 'classnames';
import Box from '@mui/material/Box';
import datetime from 'scripts/datetime';
import globalize from 'scripts/globalize';
interface EndsAtProps {
className?: string;
runTimeTicks: number
}
const EndsAt: FC<EndsAtProps> = ({ runTimeTicks, className }) => {
const cssClass = classNames(
'mediaInfoItem',
'mediaInfoText',
'endsAt',
className
);
const endTime = new Date().getTime() + (runTimeTicks / 10000);
const endDate = new Date(endTime);
const displayTime = datetime.getDisplayTime(endDate);
return (
<Box className={cssClass}>
{globalize.translate('EndsAtValue', displayTime)}
</Box>
);
};
export default EndsAt;

View file

@ -0,0 +1,27 @@
import React, { FC } from 'react';
import Box from '@mui/material/Box';
import classNames from 'classnames';
import type { MiscInfo } from 'types/mediaInfoItem';
interface MediaInfoItemProps {
className?: string;
miscInfo?: MiscInfo ;
}
const MediaInfoItem: FC<MediaInfoItemProps> = ({ className, miscInfo }) => {
const cssClass = classNames(
'mediaInfoItem',
'mediaInfoText',
className,
miscInfo?.cssClass
);
return (
<Box className={cssClass}>
{miscInfo?.text}
</Box>
);
};
export default MediaInfoItem;

View file

@ -0,0 +1,103 @@
import React, { FC } from 'react';
import classNames from 'classnames';
import Box from '@mui/material/Box';
import usePrimaryMediaInfo from './usePrimaryMediaInfo';
import MediaInfoItem from './MediaInfoItem';
import StarIcons from './StarIcons';
import CaptionMediaInfo from './CaptionMediaInfo';
import CriticRatingMediaInfo from './CriticRatingMediaInfo';
import EndsAt from './EndsAt';
import type { ItemDto } from 'types/itemDto';
import type { MiscInfo } from 'types/mediaInfoItem';
interface PrimaryMediaInfoProps {
className?: 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
}
const PrimaryMediaInfo: FC<PrimaryMediaInfoProps> = ({
className,
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,
getMissingIndicator
}) => {
const miscInfo = usePrimaryMediaInfo({
item,
isYearEnabled,
isContainerEnabled,
isEpisodeTitleEnabled,
isOriginalAirDateEnabled,
isRuntimeEnabled,
isProgramIndicatorEnabled,
isEpisodeTitleIndexNumberEnabled,
isOfficialRatingEnabled
});
const {
StartDate,
HasSubtitles,
MediaType,
RunTimeTicks,
CommunityRating,
CriticRating
} = item;
const cssClass = classNames(className);
const renderMediaInfo = (info: MiscInfo | undefined, index: number) => (
<MediaInfoItem key={index} miscInfo={info} />
);
return (
<Box className={cssClass}>
{miscInfo.map((info, index) => renderMediaInfo(info, index))}
{isStarRatingEnabled && CommunityRating && (
<StarIcons communityRating={CommunityRating} />
)}
{HasSubtitles && isCaptionIndicatorEnabled && <CaptionMediaInfo />}
{CriticRating && isCriticRatingEnabled && (
<CriticRatingMediaInfo criticRating={CriticRating} />
)}
{isEndsAtEnabled
&& MediaType === 'Video'
&& RunTimeTicks
&& !StartDate && <EndsAt runTimeTicks={RunTimeTicks} />}
{isMissingIndicatorEnabled && (
getMissingIndicator()
)}
</Box>
);
};
export default PrimaryMediaInfo;

View file

@ -0,0 +1,29 @@
import React, { FC } from 'react';
import classNames from 'classnames';
import StarIcon from '@mui/icons-material/Star';
import Box from '@mui/material/Box';
interface StarIconsProps {
className?: string;
communityRating: number;
}
const StarIcons: FC<StarIconsProps> = ({ className, communityRating }) => {
const cssClass = classNames(
'mediaInfoItem',
'mediaInfoText',
'starRatingContainer',
className
);
return (
<Box className={cssClass}>
<StarIcon fontSize={'small'} sx={{
color: '#f2b01e'
}} />
{communityRating.toFixed(1)}
</Box>
);
};
export default StarIcons;

View file

@ -0,0 +1,522 @@
import { BaseItemKind } from '@jellyfin/sdk/lib/generated-client';
import * as userSettings from 'scripts/settings/userSettings';
import datetime from 'scripts/datetime';
import globalize from 'scripts/globalize';
import itemHelper from '../itemHelper';
import type { ItemDto, NullableNumber, NullableString } from 'types/itemDto';
import type { MiscInfo } from 'types/mediaInfoItem';
function shouldShowFolderRuntime(
itemType: NullableString,
itemMediaType: NullableString
): boolean {
return (
itemType === BaseItemKind.MusicAlbum
|| itemMediaType === 'MusicArtist'
|| itemType === BaseItemKind.Playlist
|| itemMediaType === 'Playlist'
|| itemMediaType === 'MusicGenre'
);
}
function addTrackCountOrItemCount(
showFolderRuntime: boolean,
itemSongCount: NullableNumber,
itemChildCount: NullableNumber,
itemRunTimeTicks: NullableNumber,
itemType: NullableString,
addMiscInfo: (val: MiscInfo) => void
): void {
if (showFolderRuntime) {
const count = itemSongCount ?? itemChildCount;
if (count) {
addMiscInfo({ text: globalize.translate('TrackCount', count) });
}
if (itemRunTimeTicks) {
addMiscInfo({ text: datetime.getDisplayDuration(itemRunTimeTicks) });
}
} else if (itemType === BaseItemKind.PhotoAlbum || itemType === BaseItemKind.BoxSet) {
const count = itemChildCount;
if (count) {
addMiscInfo({ text: globalize.translate('ItemCount', count) });
}
}
}
function addOriginalAirDateInfo(
itemType: NullableString,
itemMediaType: NullableString,
isOriginalAirDateEnabled: boolean,
itemPremiereDate: NullableString,
addMiscInfo: (val: MiscInfo) => void
): void {
if (
itemPremiereDate
&& (itemType === BaseItemKind.Episode || itemMediaType === 'Photo')
&& isOriginalAirDateEnabled
) {
try {
//don't modify date to locale if episode. Only Dates (not times) are stored, or editable in the edit metadata dialog
const date = datetime.parseISO8601Date(
itemPremiereDate,
itemType !== BaseItemKind.Episode
);
addMiscInfo({ text: datetime.toLocaleDateString(date) });
} catch (e) {
console.error('error parsing date:', itemPremiereDate);
}
}
}
function addSeriesTimerInfo(
itemType: NullableString,
itemRecordAnyTime: boolean | undefined,
itemStartDate: NullableString,
itemRecordAnyChannel: boolean | undefined,
itemChannelName: NullableString,
addMiscInfo: (val: MiscInfo) => void
): void {
if (itemType === 'SeriesTimer') {
if (itemRecordAnyTime) {
addMiscInfo({ text: globalize.translate('Anytime') });
} else {
addMiscInfo({ text: datetime.getDisplayTime(itemStartDate) });
}
if (itemRecordAnyChannel) {
addMiscInfo({ text: globalize.translate('AllChannels') });
} else {
addMiscInfo({
text: itemChannelName ?? globalize.translate('OneChannel')
});
}
}
}
function addProgramIndicatorInfo(
program: ItemDto | undefined,
addMiscInfo: (val: MiscInfo) => void
): void {
if (
program?.IsLive
&& userSettings.get('guide-indicator-live', false) === 'true'
) {
addMiscInfo({
text: globalize.translate('Live'),
cssClass: 'mediaInfoProgramAttribute liveTvProgram'
});
} else if (
program?.IsPremiere
&& userSettings.get('guide-indicator-premiere', false) === 'true'
) {
addMiscInfo({
text: globalize.translate('Premiere'),
cssClass: 'mediaInfoProgramAttribute premiereTvProgram'
});
} else if (
program?.IsSeries
&& !program?.IsRepeat
&& userSettings.get('guide-indicator-new', false) === 'true'
) {
addMiscInfo({
text: globalize.translate('New'),
cssClass: 'mediaInfoProgramAttribute newTvProgram'
});
} else if (
program?.IsSeries
&& program?.IsRepeat
&& userSettings.get('guide-indicator-repeat', false) === 'true'
) {
addMiscInfo({
text: globalize.translate('Repeat'),
cssClass: 'mediaInfoProgramAttribute repeatTvProgram'
});
}
}
function addProgramIndicators(
item: ItemDto,
isYearEnabled: boolean,
isEpisodeTitleEnabled: boolean,
isOriginalAirDateEnabled: boolean,
isProgramIndicatorEnabled: boolean,
isEpisodeTitleIndexNumberEnabled: boolean,
addMiscInfo: (val: MiscInfo) => void
): void {
if (item.Type === BaseItemKind.Program || item.Type === 'Timer') {
let program = item;
if (item.Type === 'Timer' && item.ProgramInfo) {
program = item.ProgramInfo;
}
if (isProgramIndicatorEnabled !== false) {
addProgramIndicatorInfo(program, addMiscInfo);
}
addProgramTextInfo(
program,
isEpisodeTitleEnabled,
isEpisodeTitleIndexNumberEnabled,
isOriginalAirDateEnabled,
isYearEnabled,
addMiscInfo
);
}
}
function addProgramTextInfo(
program: ItemDto,
isEpisodeTitleEnabled: boolean,
isEpisodeTitleIndexNumberEnabled: boolean,
isOriginalAirDateEnabled: boolean,
isYearEnabled: boolean,
addMiscInfo: (val: MiscInfo) => void
): void {
if ((program?.IsSeries || program?.EpisodeTitle)
&& isEpisodeTitleEnabled !== false) {
const text = itemHelper.getDisplayName(program, {
includeIndexNumber: isEpisodeTitleIndexNumberEnabled
});
if (text) {
addMiscInfo({ text: text });
}
} else if (
program?.ProductionYear
&& ((program?.IsMovie && isOriginalAirDateEnabled !== false)
|| isYearEnabled !== false)
) {
addMiscInfo({ text: program.ProductionYear });
} else if (program?.PremiereDate && isOriginalAirDateEnabled !== false) {
try {
const date = datetime.parseISO8601Date(program.PremiereDate);
const text = globalize.translate(
'OriginalAirDateValue',
datetime.toLocaleDateString(date)
);
addMiscInfo({ text: text });
} catch (e) {
console.error('error parsing date:', program.PremiereDate);
}
}
}
function addStartDateInfo(
itemStartDate: NullableString,
itemType: NullableString,
addMiscInfo: (val: MiscInfo) => void
): void {
if (
itemStartDate
&& itemType !== BaseItemKind.Program
&& itemType !== 'SeriesTimer'
&& itemType !== 'Timer'
) {
try {
const date = datetime.parseISO8601Date(itemStartDate);
addMiscInfo({ text: datetime.toLocaleDateString(date) });
if (itemType !== BaseItemKind.Recording) {
addMiscInfo({ text: datetime.getDisplayTime(date) });
}
} catch (e) {
console.error('error parsing date:', itemStartDate);
}
}
}
function addSeriesProductionYearInfo(
itemProductionYear: NullableNumber,
itemType: NullableString,
isYearEnabled: boolean,
itemStatus: NullableString,
itemEndDate: NullableString,
addMiscInfo: (val: MiscInfo) => void
): void {
if (itemProductionYear && isYearEnabled && itemType === BaseItemKind.Series) {
if (itemStatus === 'Continuing') {
addMiscInfo({
text: globalize.translate(
'SeriesYearToPresent',
datetime.toLocaleString(itemProductionYear, {
useGrouping: false
})
)
});
} else {
addproductionYearWithEndDate(itemProductionYear, itemEndDate, addMiscInfo);
}
}
}
function addproductionYearWithEndDate(
itemProductionYear: number,
itemEndDate: NullableString,
addMiscInfo: (val: MiscInfo) => void
): void {
let productionYear = datetime.toLocaleString(itemProductionYear, {
useGrouping: false
});
if (itemEndDate) {
try {
const endYear = datetime.toLocaleString(
datetime.parseISO8601Date(itemEndDate).getFullYear(),
{ useGrouping: false }
);
/* At this point, text will contain only the start year */
if (endYear !== itemProductionYear) {
productionYear += `-${endYear}`;
}
} catch (e) {
console.error('error parsing date:', itemEndDate);
}
}
addMiscInfo({ text: productionYear });
}
function addYearInfo(
isYearEnabled: boolean,
itemType: NullableString,
itemMediaType: NullableString,
itemProductionYear: NullableNumber,
itemPremiereDate: NullableString,
addMiscInfo: (val: MiscInfo) => void
): void {
if (
isYearEnabled
&& itemType !== BaseItemKind.Series
&& itemType !== BaseItemKind.Episode
&& itemType !== BaseItemKind.Person
&& itemMediaType !== 'Photo'
&& itemType !== BaseItemKind.Program
&& itemType !== BaseItemKind.Season
) {
if (itemProductionYear) {
addMiscInfo({ text: itemProductionYear });
} else if (itemPremiereDate) {
try {
const text = datetime.toLocaleString(
datetime.parseISO8601Date(itemPremiereDate).getFullYear(),
{ useGrouping: false }
);
addMiscInfo({ text: text });
} catch (e) {
console.error('error parsing date:', itemPremiereDate);
}
}
}
}
function addVideo3DFormat(
itemVideo3DFormat: NullableString,
addMiscInfo: (val: MiscInfo) => void
): void {
if (itemVideo3DFormat) {
addMiscInfo({ text: '3D' });
}
}
function addRunTimeInfo(
itemRunTimeTicks: NullableNumber,
itemType: NullableString,
showFolderRuntime: boolean,
isRuntimeEnabled: boolean,
addMiscInfo: (val: MiscInfo) => void
): void {
if (
itemRunTimeTicks
&& itemType !== BaseItemKind.Series
&& itemType !== BaseItemKind.Program
&& itemType !== 'Timer'
&& itemType !== BaseItemKind.Book
&& !showFolderRuntime
&& isRuntimeEnabled
) {
if (itemType === BaseItemKind.Audio) {
addMiscInfo({ text: datetime.getDisplayRunningTime(itemRunTimeTicks) });
} else {
addMiscInfo({ text: datetime.getDisplayDuration(itemRunTimeTicks) });
}
}
}
function addOfficialRatingInfo(
itemOfficialRating: NullableString,
itemType: NullableString,
isOfficialRatingEnabled: boolean,
addMiscInfo: (val: MiscInfo) => void
): void {
if (
itemOfficialRating
&& isOfficialRatingEnabled
&& itemType !== BaseItemKind.Season
&& itemType !== BaseItemKind.Episode
) {
addMiscInfo({
text: itemOfficialRating,
cssClass: 'mediaInfoOfficialRating'
});
}
}
function addAudioContainer(
itemContainer: NullableString,
isContainerEnabled: boolean,
itemType: NullableString,
addMiscInfo: (val: MiscInfo) => void
): void {
if (itemContainer && isContainerEnabled && itemType === BaseItemKind.Audio) {
addMiscInfo({ text: itemContainer });
}
}
function addPhotoSize(
itemMediaType: NullableString,
itemWidth: NullableNumber,
itemHeight: NullableNumber,
addMiscInfo: (val: MiscInfo) => void
): void {
if (itemMediaType === 'Photo' && itemWidth && itemHeight) {
const size = `${itemWidth}x${itemHeight}`;
addMiscInfo({ text: size });
}
}
interface UsePrimaryMediaInfoProps {
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
}: UsePrimaryMediaInfoProps) {
const {
EndDate,
Status,
StartDate,
ProductionYear,
Video3DFormat,
Type,
Width,
Height,
MediaType,
SongCount,
RecordAnyTime,
RecordAnyChannel,
ChannelName,
ChildCount,
RunTimeTicks,
PremiereDate,
OfficialRating,
Container
} = item;
const miscInfo: MiscInfo[] = [];
const addMiscInfo = (val: MiscInfo) => {
if (val) {
miscInfo.push(val);
}
};
const showFolderRuntime = shouldShowFolderRuntime(Type, MediaType);
addTrackCountOrItemCount(
showFolderRuntime,
SongCount,
ChildCount,
RunTimeTicks,
Type,
addMiscInfo
);
addOriginalAirDateInfo(
Type,
MediaType,
isOriginalAirDateEnabled,
PremiereDate,
addMiscInfo
);
addSeriesTimerInfo(
Type,
RecordAnyTime,
StartDate,
RecordAnyChannel,
ChannelName,
addMiscInfo
);
addStartDateInfo(StartDate, Type, addMiscInfo);
addSeriesProductionYearInfo(
ProductionYear,
Type,
isYearEnabled,
Status,
EndDate,
addMiscInfo
);
addProgramIndicators(
item,
isProgramIndicatorEnabled,
isEpisodeTitleEnabled,
isEpisodeTitleIndexNumberEnabled,
isOriginalAirDateEnabled,
isYearEnabled,
addMiscInfo
);
addYearInfo(
isYearEnabled,
Type,
MediaType,
ProductionYear,
PremiereDate,
addMiscInfo
);
addRunTimeInfo(
RunTimeTicks,
Type,
showFolderRuntime,
isRuntimeEnabled,
addMiscInfo
);
addOfficialRatingInfo(
OfficialRating,
Type,
isOfficialRatingEnabled,
addMiscInfo
);
addVideo3DFormat(Video3DFormat, addMiscInfo);
addPhotoSize(MediaType, Width, Height, addMiscInfo);
addAudioContainer(Container, isContainerEnabled, Type, addMiscInfo);
return miscInfo;
}
export default usePrimaryMediaInfo;

View file

@ -0,0 +1,4 @@
export interface MiscInfo {
text?: string | number;
cssClass?: string;
}