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

Add shared ItemStatus and ItemDtoQueryResult Type

This commit is contained in:
grafixeyehero 2024-08-21 03:27:03 +03:00
parent 656799cce7
commit 5fbc417e3b
24 changed files with 219 additions and 183 deletions

View file

@ -1,4 +1,3 @@
import type { BaseItemDto } from '@jellyfin/sdk/lib/generated-client';
import { CollectionType } from '@jellyfin/sdk/lib/generated-client/models/collection-type'; import { CollectionType } from '@jellyfin/sdk/lib/generated-client/models/collection-type';
import { ItemFields } from '@jellyfin/sdk/lib/generated-client/models/item-fields'; import { ItemFields } from '@jellyfin/sdk/lib/generated-client/models/item-fields';
import { ImageType } from '@jellyfin/sdk/lib/generated-client/models/image-type'; import { ImageType } from '@jellyfin/sdk/lib/generated-client/models/image-type';
@ -12,12 +11,13 @@ import { appRouter } from 'components/router/appRouter';
import SectionContainer from './SectionContainer'; import SectionContainer from './SectionContainer';
import { CardShape } from 'utils/card'; import { CardShape } from 'utils/card';
import type { ParentId } from 'types/library'; import type { ParentId } from 'types/library';
import type { ItemDto } from 'types/base/models/item-dto';
interface GenresSectionContainerProps { interface GenresSectionContainerProps {
parentId: ParentId; parentId: ParentId;
collectionType: CollectionType | undefined; collectionType: CollectionType | undefined;
itemType: BaseItemKind[]; itemType: BaseItemKind[];
genre: BaseItemDto; genre: ItemDto;
} }
const GenresSectionContainer: FC<GenresSectionContainerProps> = ({ const GenresSectionContainer: FC<GenresSectionContainerProps> = ({
@ -47,7 +47,7 @@ const GenresSectionContainer: FC<GenresSectionContainerProps> = ({
const { isLoading, data: itemsResult } = useGetItems(getParametersOptions()); const { isLoading, data: itemsResult } = useGetItems(getParametersOptions());
const getRouteUrl = (item: BaseItemDto) => { const getRouteUrl = (item: ItemDto) => {
return appRouter.getRouteUrl(item, { return appRouter.getRouteUrl(item, {
context: collectionType, context: collectionType,
parentId: parentId parentId: parentId

View file

@ -1,4 +1,3 @@
import type { BaseItemDto, SeriesTimerInfoDto } from '@jellyfin/sdk/lib/generated-client';
import React, { FC, useCallback } from 'react'; import React, { FC, useCallback } from 'react';
import { IconButton } from '@mui/material'; import { IconButton } from '@mui/material';
import PlayArrowIcon from '@mui/icons-material/PlayArrow'; import PlayArrowIcon from '@mui/icons-material/PlayArrow';
@ -8,10 +7,11 @@ import globalize from 'lib/globalize';
import { getFiltersQuery } from 'utils/items'; import { getFiltersQuery } from 'utils/items';
import { LibraryViewSettings } from 'types/library'; import { LibraryViewSettings } from 'types/library';
import { LibraryTab } from 'types/libraryTab'; import { LibraryTab } from 'types/libraryTab';
import type { ItemDto } from 'types/base/models/item-dto';
interface PlayAllButtonProps { interface PlayAllButtonProps {
item: BaseItemDto | null | undefined; item: ItemDto | undefined;
items: BaseItemDto[] | SeriesTimerInfoDto[]; items: ItemDto[];
viewType: LibraryTab; viewType: LibraryTab;
hasFilters: boolean; hasFilters: boolean;
libraryViewSettings: LibraryViewSettings libraryViewSettings: LibraryViewSettings

View file

@ -1,14 +1,14 @@
import type { BaseItemDto, SeriesTimerInfoDto } from '@jellyfin/sdk/lib/generated-client';
import React, { FC, useCallback } from 'react'; import React, { FC, useCallback } from 'react';
import { IconButton } from '@mui/material'; import { IconButton } from '@mui/material';
import QueueIcon from '@mui/icons-material/Queue'; import QueueIcon from '@mui/icons-material/Queue';
import { playbackManager } from 'components/playback/playbackmanager'; import { playbackManager } from 'components/playback/playbackmanager';
import globalize from 'lib/globalize'; import globalize from 'lib/globalize';
import type { ItemDto } from 'types/base/models/item-dto';
interface QueueButtonProps { interface QueueButtonProps {
item: BaseItemDto | undefined item: ItemDto | undefined
items: BaseItemDto[] | SeriesTimerInfoDto[]; items: ItemDto[];
hasFilters: boolean; hasFilters: boolean;
} }

View file

@ -1,4 +1,3 @@
import type { BaseItemDto, TimerInfoDto } from '@jellyfin/sdk/lib/generated-client';
import React, { FC } from 'react'; import React, { FC } from 'react';
import ItemsContainer from 'elements/emby-itemscontainer/ItemsContainer'; import ItemsContainer from 'elements/emby-itemscontainer/ItemsContainer';
@ -6,11 +5,12 @@ import Scroller from 'elements/emby-scroller/Scroller';
import LinkButton from 'elements/emby-button/LinkButton'; import LinkButton from 'elements/emby-button/LinkButton';
import Cards from 'components/cardbuilder/Card/Cards'; import Cards from 'components/cardbuilder/Card/Cards';
import type { CardOptions } from 'types/cardOptions'; import type { CardOptions } from 'types/cardOptions';
import type { ItemDto } from 'types/base/models/item-dto';
interface SectionContainerProps { interface SectionContainerProps {
url?: string; url?: string;
sectionTitle: string; sectionTitle: string;
items: BaseItemDto[] | TimerInfoDto[]; items: ItemDto[];
cardOptions: CardOptions; cardOptions: CardOptions;
reloadItems?: () => void; reloadItems?: () => void;
} }

View file

@ -1,4 +1,3 @@
import type { BaseItemDto, SeriesTimerInfoDto } from '@jellyfin/sdk/lib/generated-client';
import { ItemSortBy } from '@jellyfin/sdk/lib/models/api/item-sort-by'; import { ItemSortBy } from '@jellyfin/sdk/lib/models/api/item-sort-by';
import React, { FC, useCallback } from 'react'; import React, { FC, useCallback } from 'react';
import { IconButton } from '@mui/material'; import { IconButton } from '@mui/material';
@ -9,10 +8,11 @@ import globalize from 'lib/globalize';
import { getFiltersQuery } from 'utils/items'; import { getFiltersQuery } from 'utils/items';
import { LibraryViewSettings } from 'types/library'; import { LibraryViewSettings } from 'types/library';
import { LibraryTab } from 'types/libraryTab'; import { LibraryTab } from 'types/libraryTab';
import type { ItemDto } from 'types/base/models/item-dto';
interface ShuffleButtonProps { interface ShuffleButtonProps {
item: BaseItemDto | null | undefined; item: ItemDto | undefined;
items: BaseItemDto[] | SeriesTimerInfoDto[]; items: ItemDto[];
viewType: LibraryTab viewType: LibraryTab
hasFilters: boolean; hasFilters: boolean;
libraryViewSettings: LibraryViewSettings libraryViewSettings: LibraryViewSettings

View file

@ -1,7 +1,5 @@
import { import type { RecommendationDto } from '@jellyfin/sdk/lib/generated-client/models/recommendation-dto';
type RecommendationDto, import { RecommendationType } from '@jellyfin/sdk/lib/generated-client/models/recommendation-type';
RecommendationType
} from '@jellyfin/sdk/lib/generated-client';
import React, { type FC } from 'react'; import React, { type FC } from 'react';
import { import {
useGetMovieRecommendations, useGetMovieRecommendations,
@ -14,6 +12,7 @@ import SectionContainer from './SectionContainer';
import { CardShape } from 'utils/card'; import { CardShape } from 'utils/card';
import type { ParentId } from 'types/library'; import type { ParentId } from 'types/library';
import type { Section, SectionType } from 'types/sections'; import type { Section, SectionType } from 'types/sections';
import type { ItemDto } from 'types/base/models/item-dto';
interface SuggestionsSectionViewProps { interface SuggestionsSectionViewProps {
parentId: ParentId; parentId: ParentId;
@ -116,7 +115,7 @@ const SuggestionsSectionView: FC<SuggestionsSectionViewProps> = ({
// eslint-disable-next-line react/no-array-index-key // eslint-disable-next-line react/no-array-index-key
key={`${recommendation.CategoryId}-${index}`} // use a unique id return value may have duplicate id key={`${recommendation.CategoryId}-${index}`} // use a unique id return value may have duplicate id
sectionTitle={getRecommendationTittle(recommendation)} sectionTitle={getRecommendationTittle(recommendation)}
items={recommendation.Items ?? []} items={(recommendation.Items as ItemDto[]) ?? []}
cardOptions={{ cardOptions={{
queryKey: ['MovieRecommendations'], queryKey: ['MovieRecommendations'],
shape: CardShape.PortraitOverflow, shape: CardShape.PortraitOverflow,

View file

@ -6,6 +6,7 @@ import CardHoverMenu from './CardHoverMenu';
import CardOuterFooter from './CardOuterFooter'; import CardOuterFooter from './CardOuterFooter';
import CardContent from './CardContent'; import CardContent from './CardContent';
import { CardShape } from 'utils/card'; import { CardShape } from 'utils/card';
import type { ItemDto } from 'types/base/models/item-dto'; import type { ItemDto } from 'types/base/models/item-dto';
import type { CardOptions } from 'types/cardOptions'; import type { CardOptions } from 'types/cardOptions';

View file

@ -2,10 +2,11 @@ import React, { type FC } from 'react';
import Box from '@mui/material/Box'; import Box from '@mui/material/Box';
import useCardText from './useCardText'; import useCardText from './useCardText';
import layoutManager from 'components/layoutManager'; import layoutManager from 'components/layoutManager';
import MoreVertIconButton from '../../common/MoreVertIconButton'; import MoreVertIconButton from 'components/common/MoreVertIconButton';
import Image from 'components/common/Image';
import type { ItemDto } from 'types/base/models/item-dto'; import type { ItemDto } from 'types/base/models/item-dto';
import type { CardOptions } from 'types/cardOptions'; import type { CardOptions } from 'types/cardOptions';
import Image from 'components/common/Image';
const shouldShowDetailsMenu = ( const shouldShowDetailsMenu = (
cardOptions: CardOptions, cardOptions: CardOptions,

View file

@ -1,4 +1,3 @@
import { BaseItemKind } from '@jellyfin/sdk/lib/generated-client/models/base-item-kind';
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 classNames from 'classnames'; import classNames from 'classnames';
@ -7,6 +6,7 @@ import RefreshIndicator from 'elements/emby-itemrefreshindicator/RefreshIndicato
import Media from '../../common/Media'; import Media from '../../common/Media';
import CardInnerFooter from './CardInnerFooter'; import CardInnerFooter from './CardInnerFooter';
import { ItemKind } from 'types/base/models/item-kind';
import type { ItemDto } from 'types/base/models/item-dto'; import type { ItemDto } from 'types/base/models/item-dto';
import type { CardOptions } from 'types/cardOptions'; import type { CardOptions } from 'types/cardOptions';
@ -33,7 +33,7 @@ const CardImageContainer: FC<CardImageContainerProps> = ({
const cardImageClass = classNames( const cardImageClass = classNames(
'cardImageContainer', 'cardImageContainer',
{ coveredImage: coveredImage }, { coveredImage: coveredImage },
{ 'coveredImage-contain': coveredImage && item.Type === BaseItemKind.TvChannel } { 'coveredImage-contain': coveredImage && item.Type === ItemKind.TvChannel }
); );
return ( return (
@ -53,7 +53,7 @@ const CardImageContainer: FC<CardImageContainerProps> = ({
indicator.getChildCountIndicator() : indicator.getChildCountIndicator() :
indicator.getPlayedIndicator()} indicator.getPlayedIndicator()}
{(item.Type === BaseItemKind.CollectionFolder {(item.Type === ItemKind.CollectionFolder
|| item.CollectionType) && ( || item.CollectionType) && (
<RefreshIndicator item={item} /> <RefreshIndicator item={item} />
)} )}

View file

@ -1,6 +1,7 @@
import React, { type FC } from 'react'; import React, { type FC } from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import CardFooterText from './CardFooterText'; import CardFooterText from './CardFooterText';
import type { ItemDto } from 'types/base/models/item-dto'; import type { ItemDto } from 'types/base/models/item-dto';
import type { CardOptions } from 'types/cardOptions'; import type { CardOptions } from 'types/cardOptions';

View file

@ -1,4 +1,3 @@
import { BaseItemKind } from '@jellyfin/sdk/lib/generated-client/models/base-item-kind';
import { LocationType } from '@jellyfin/sdk/lib/generated-client/models/location-type'; import { LocationType } from '@jellyfin/sdk/lib/generated-client/models/location-type';
import React, { type FC } from 'react'; import React, { type FC } from 'react';
import ButtonGroup from '@mui/material/ButtonGroup'; import ButtonGroup from '@mui/material/ButtonGroup';
@ -7,6 +6,8 @@ import { appRouter } from 'components/router/appRouter';
import PlayArrowIconButton from '../../common/PlayArrowIconButton'; import PlayArrowIconButton from '../../common/PlayArrowIconButton';
import MoreVertIconButton from '../../common/MoreVertIconButton'; import MoreVertIconButton from '../../common/MoreVertIconButton';
import { ItemKind } from 'types/base/models/item-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 { CardOptions } from 'types/cardOptions'; import type { CardOptions } from 'types/cardOptions';
@ -19,8 +20,8 @@ const sholudShowOverlayPlayButton = (
&& !item.IsPlaceHolder && !item.IsPlaceHolder
&& (item.LocationType !== LocationType.Virtual && (item.LocationType !== LocationType.Virtual
|| !item.MediaType || !item.MediaType
|| item.Type === BaseItemKind.Program) || item.Type === ItemKind.Program)
&& item.Type !== BaseItemKind.Person && item.Type !== ItemKind.Person
); );
}; };
@ -41,7 +42,7 @@ const CardOverlayButtons: FC<CardOverlayButtonsProps> = ({
&& !cardOptions.overlayInfoButton && !cardOptions.overlayInfoButton
&& !cardOptions.cardLayout && !cardOptions.cardLayout
) { ) {
overlayPlayButton = item.MediaType === 'Video'; overlayPlayButton = item.MediaType === ItemMediaKind.Video;
} }
const url = appRouter.getRouteUrl(item, { const url = appRouter.getRouteUrl(item, {

View file

@ -1,10 +1,6 @@
import {
BaseItemDto,
BaseItemKind,
BaseItemPerson,
ImageType
} from '@jellyfin/sdk/lib/generated-client';
import { Api } from '@jellyfin/sdk'; import { Api } from '@jellyfin/sdk';
import type { BaseItemPerson } from '@jellyfin/sdk/lib/generated-client/models/base-item-person';
import { ImageType } from '@jellyfin/sdk/lib/generated-client/models/image-type';
import { getImageApi } from '@jellyfin/sdk/lib/utils/api/image-api'; import { getImageApi } from '@jellyfin/sdk/lib/utils/api/image-api';
import { appRouter } from 'components/router/appRouter'; import { appRouter } from 'components/router/appRouter';
@ -12,14 +8,15 @@ import layoutManager from 'components/layoutManager';
import itemHelper from 'components/itemHelper'; import itemHelper from 'components/itemHelper';
import globalize from 'lib/globalize'; import globalize from 'lib/globalize';
import datetime from 'scripts/datetime'; import datetime from 'scripts/datetime';
import { isUsingLiveTvNaming } from '../cardBuilderUtils'; import { isUsingLiveTvNaming } from '../cardBuilderUtils';
import { getDataAttributes } from 'utils/items';
import { ItemKind } from 'types/base/models/item-kind';
import { ItemMediaKind } from 'types/base/models/item-media-kind';
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 { CardOptions } from 'types/cardOptions'; import type { CardOptions } from 'types/cardOptions';
import type { DataAttributes } from 'types/dataAttributes'; import type { DataAttributes } from 'types/dataAttributes';
import { getDataAttributes } from 'utils/items';
export function getCardLogoUrl( export function getCardLogoUrl(
item: ItemDto, item: ItemDto,
@ -142,15 +139,15 @@ export function getAirTimeText(
return airTimeText; return airTimeText;
} }
function isGenreOrStudio(itemType: NullableString) { function isGenreOrStudio(itemType: ItemKind) {
return itemType === BaseItemKind.Genre || itemType === BaseItemKind.Studio; return itemType === ItemKind.Genre || itemType === ItemKind.Studio;
} }
function isMusicGenreOrMusicArtist( function isMusicGenreOrMusicArtist(
itemType: NullableString, itemType: ItemKind,
context: NullableString context: NullableString
) { ) {
return itemType === BaseItemKind.MusicGenre || context === 'MusicArtist'; return itemType === ItemKind.MusicGenre || context === 'MusicArtist';
} }
function getMovieCount(itemMovieCount: NullableNumber) { function getMovieCount(itemMovieCount: NullableNumber) {
@ -213,8 +210,8 @@ function getParentTitle(
item: ItemDto item: ItemDto
) { ) {
if (isOuterFooter && item.AlbumArtists?.length) { if (isOuterFooter && item.AlbumArtists?.length) {
(item.AlbumArtists[0] as BaseItemDto).Type = BaseItemKind.MusicArtist; (item.AlbumArtists[0] as ItemDto).Type = ItemKind.MusicArtist;
(item.AlbumArtists[0] as BaseItemDto).IsFolder = true; (item.AlbumArtists[0] as ItemDto).IsFolder = true;
return getTextActionButton(item.AlbumArtists[0], null, serverId); return getTextActionButton(item.AlbumArtists[0], null, serverId);
} else { } else {
return { return {
@ -250,7 +247,7 @@ export function getItemCounts(cardOptions: CardOptions, item: ItemDto) {
} }
}; };
if (item.Type === BaseItemKind.Playlist) { if (item.Type === ItemKind.Playlist) {
const runTimeTicksText = getRunTimeTicks(item.RunTimeTicks); const runTimeTicksText = getRunTimeTicks(item.RunTimeTicks);
addCount(runTimeTicksText); addCount(runTimeTicksText);
} else if (isGenreOrStudio(item.Type)) { } else if (isGenreOrStudio(item.Type)) {
@ -271,7 +268,7 @@ export function getItemCounts(cardOptions: CardOptions, item: ItemDto) {
const musicVideoCountText = getMusicVideoCount(item.MusicVideoCount); const musicVideoCountText = getMusicVideoCount(item.MusicVideoCount);
addCount(musicVideoCountText); addCount(musicVideoCountText);
} else if (item.Type === BaseItemKind.Series) { } else if (item.Type === ItemKind.Series) {
const recursiveItemCountText = getRecursiveItemCount( const recursiveItemCountText = getRecursiveItemCount(
item.RecursiveItemCount item.RecursiveItemCount
); );
@ -283,12 +280,12 @@ export function getItemCounts(cardOptions: CardOptions, item: ItemDto) {
export function shouldShowTitle( export function shouldShowTitle(
showTitle: boolean | string | undefined, showTitle: boolean | string | undefined,
itemType: NullableString itemType: ItemKind
) { ) {
return ( return (
Boolean(showTitle) Boolean(showTitle)
|| itemType === BaseItemKind.PhotoAlbum || itemType === ItemKind.PhotoAlbum
|| itemType === BaseItemKind.Folder || itemType === ItemKind.Folder
); );
} }
@ -300,12 +297,12 @@ export function shouldShowOtherText(
} }
export function shouldShowParentTitleUnderneath( export function shouldShowParentTitleUnderneath(
itemType: NullableString itemType: ItemKind
) { ) {
return ( return (
itemType === BaseItemKind.MusicAlbum itemType === ItemKind.MusicAlbum
|| itemType === BaseItemKind.Audio || itemType === ItemKind.Audio
|| itemType === BaseItemKind.MusicVideo || itemType === ItemKind.MusicVideo
); );
} }
@ -338,16 +335,16 @@ function shouldShowSeriesYearOrYear(
function shouldShowCurrentProgram( function shouldShowCurrentProgram(
showCurrentProgram: boolean | undefined, showCurrentProgram: boolean | undefined,
itemType: NullableString itemType: ItemKind
) { ) {
return showCurrentProgram && itemType === BaseItemKind.TvChannel; return showCurrentProgram && itemType === ItemKind.TvChannel;
} }
function shouldShowCurrentProgramTime( function shouldShowCurrentProgramTime(
showCurrentProgramTime: boolean | undefined, showCurrentProgramTime: boolean | undefined,
itemType: NullableString itemType: ItemKind
) { ) {
return showCurrentProgramTime && itemType === BaseItemKind.TvChannel; return showCurrentProgramTime && itemType === ItemKind.TvChannel;
} }
function shouldShowPersonRoleOrType( function shouldShowPersonRoleOrType(
@ -475,7 +472,7 @@ function getSeriesTimerTime(item: ItemDto) {
} }
} }
function getCurrentProgramTime(CurrentProgram: BaseItemDto | undefined) { function getCurrentProgramTime(CurrentProgram: ItemDto | undefined) {
if (CurrentProgram) { if (CurrentProgram) {
return getAirTimeText(CurrentProgram, false, true) || ''; return getAirTimeText(CurrentProgram, false, true) || '';
} else { } else {
@ -483,7 +480,7 @@ function getCurrentProgramTime(CurrentProgram: BaseItemDto | undefined) {
} }
} }
function getCurrentProgramName(CurrentProgram: BaseItemDto | undefined) { function getCurrentProgramName(CurrentProgram: ItemDto | undefined) {
if (CurrentProgram) { if (CurrentProgram) {
return CurrentProgram.Name; return CurrentProgram.Name;
} else { } else {
@ -498,7 +495,7 @@ function getChannelName(item: ItemDto) {
Id: item.ChannelId, Id: item.ChannelId,
ServerId: item.ServerId, ServerId: item.ServerId,
Name: item.ChannelName, Name: item.ChannelName,
Type: BaseItemKind.TvChannel, Type: ItemKind.TvChannel,
MediaType: item.MediaType, MediaType: item.MediaType,
IsFolder: false IsFolder: false
}, },
@ -548,7 +545,7 @@ function getProductionYear(item: ItemDto) {
&& datetime.toLocaleString(item.ProductionYear, { && datetime.toLocaleString(item.ProductionYear, {
useGrouping: false useGrouping: false
}); });
if (item.Type === BaseItemKind.Series) { if (item.Type === ItemKind.Series) {
if (item.Status === 'Continuing') { if (item.Status === 'Continuing') {
return globalize.translate( return globalize.translate(
'SeriesYearToPresent', 'SeriesYearToPresent',
@ -575,7 +572,7 @@ function getMediaTitle(cardOptions: CardOptions, item: ItemDto): TextLine {
const name = const name =
cardOptions.showTitle === 'auto' cardOptions.showTitle === 'auto'
&& !item.IsFolder && !item.IsFolder
&& item.MediaType === 'Photo' ? && item.MediaType === ItemMediaKind.Photo ?
'' : '' :
itemHelper.getDisplayName(item, { itemHelper.getDisplayName(item, {
includeParentInfo: cardOptions.includeParentInfoInTitle includeParentInfo: cardOptions.includeParentInfoInTitle
@ -599,7 +596,7 @@ function getParentTitleOrTitle(
): TextLine { ): TextLine {
if ( if (
isOuterFooter isOuterFooter
&& item.Type === BaseItemKind.Episode && item.Type === ItemKind.Episode
&& item.SeriesName && item.SeriesName
) { ) {
if (item.SeriesId) { if (item.SeriesId) {
@ -607,7 +604,7 @@ function getParentTitleOrTitle(
Id: item.SeriesId, Id: item.SeriesId,
ServerId: item.ServerId, ServerId: item.ServerId,
Name: item.SeriesName, Name: item.SeriesName,
Type: BaseItemKind.Series, Type: ItemKind.Series,
IsFolder: true IsFolder: true
}); });
} else { } else {

View file

@ -1,4 +1,3 @@
import { BaseItemKind } from '@jellyfin/sdk/lib/generated-client/models/base-item-kind';
import classNames from 'classnames'; import classNames from 'classnames';
import useCardImageUrl from './useCardImageUrl'; import useCardImageUrl from './useCardImageUrl';
import { import {
@ -9,6 +8,8 @@ import { getDataAttributes } from 'utils/items';
import { CardShape } from 'utils/card'; import { CardShape } from 'utils/card';
import layoutManager from 'components/layoutManager'; import layoutManager from 'components/layoutManager';
import { ItemKind } from 'types/base/models/item-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 { CardOptions } from 'types/cardOptions'; import type { CardOptions } from 'types/cardOptions';
@ -21,7 +22,7 @@ function useCard({ item, cardOptions }: UseCardProps) {
const action = resolveAction({ const action = resolveAction({
defaultAction: cardOptions.action ?? 'link', defaultAction: cardOptions.action ?? 'link',
isFolder: item.IsFolder ?? false, isFolder: item.IsFolder ?? false,
isPhoto: item.MediaType === 'Photo' isPhoto: item.MediaType === ItemMediaKind.Photo
}); });
let shape = cardOptions.shape; let shape = cardOptions.shape;
@ -84,9 +85,9 @@ function useCard({ item, cardOptions }: UseCardProps) {
{ groupedCard: cardOptions.showChildCountIndicator && item.ChildCount }, { groupedCard: cardOptions.showChildCountIndicator && item.ChildCount },
{ {
'card-withuserdata': 'card-withuserdata':
item.Type !== BaseItemKind.MusicAlbum item.Type !== ItemKind.MusicAlbum
&& item.Type !== BaseItemKind.MusicArtist && item.Type !== ItemKind.MusicArtist
&& item.Type !== BaseItemKind.Audio && item.Type !== ItemKind.Audio
}, },
{ itemAction: layoutManager.tv } { itemAction: layoutManager.tv }
); );

View file

@ -1,9 +1,11 @@
import { BaseItemKind } from '@jellyfin/sdk/lib/generated-client/models/base-item-kind';
import { ImageType } from '@jellyfin/sdk/lib/generated-client/models/image-type'; import { ImageType } from '@jellyfin/sdk/lib/generated-client/models/image-type';
import { getImageApi } from '@jellyfin/sdk/lib/utils/api/image-api'; import { getImageApi } from '@jellyfin/sdk/lib/utils/api/image-api';
import { useApi } from 'hooks/useApi'; import { useApi } from 'hooks/useApi';
import { getDesiredAspect } from '../cardBuilderUtils'; import { getDesiredAspect } from '../cardBuilderUtils';
import { CardShape } from 'utils/card'; import { CardShape } from 'utils/card';
import { ItemKind } from 'types/base/models/item-kind';
import { ItemMediaKind } from 'types/base/models/item-media-kind';
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 { CardOptions } from 'types/cardOptions'; import type { CardOptions } from 'types/cardOptions';
@ -25,7 +27,7 @@ function getPreferThumbInfo(item: ItemDto, cardOptions: CardOptions) {
} else if ( } else if (
item.ParentThumbItemId item.ParentThumbItemId
&& cardOptions.inheritThumb !== false && cardOptions.inheritThumb !== false
&& item.MediaType !== 'Photo' && item.MediaType !== ItemMediaKind.Photo
) { ) {
imgType = ImageType.Thumb; imgType = ImageType.Thumb;
imgTag = item.ParentThumbImageTag; imgTag = item.ParentThumbImageTag;
@ -117,12 +119,12 @@ function shouldShowPreferDisc(
function shouldShowImageTagsPrimary(item: ItemDto): boolean { function shouldShowImageTagsPrimary(item: ItemDto): boolean {
return ( return (
Boolean(item.ImageTags?.Primary) && (item.Type !== BaseItemKind.Episode || item.ChildCount !== 0) Boolean(item.ImageTags?.Primary) && (item.Type !== ItemKind.Episode || item.ChildCount !== 0)
); );
} }
function shouldShowImageTagsThumb(item: ItemDto): boolean { function shouldShowImageTagsThumb(item: ItemDto): boolean {
return item.Type === BaseItemKind.Season && Boolean(item.ImageTags?.Thumb); return item.Type === ItemKind.Season && Boolean(item.ImageTags?.Thumb);
} }
function shouldShowSeriesThumbImageTag( function shouldShowSeriesThumbImageTag(
@ -147,8 +149,8 @@ function shouldShowAlbumPrimaryImageTag(item: ItemDto): boolean {
return Boolean(item.AlbumId) && Boolean(item.AlbumPrimaryImageTag); return Boolean(item.AlbumId) && Boolean(item.AlbumPrimaryImageTag);
} }
function shouldShowPreferThumb(itemType: NullableString, cardOptions: CardOptions): boolean { function shouldShowPreferThumb(itemType: ItemKind, cardOptions: CardOptions): boolean {
return Boolean(cardOptions.preferThumb) && !(itemType === BaseItemKind.Program || itemType === BaseItemKind.Episode); return Boolean(cardOptions.preferThumb) && !(itemType === ItemKind.Program || itemType === ItemKind.Episode);
} }
function getCardImageInfo( function getCardImageInfo(

View file

@ -1,4 +1,3 @@
import { BaseItemKind } from '@jellyfin/sdk/lib/generated-client/models/base-item-kind';
import { LocationType } from '@jellyfin/sdk/lib/generated-client/models/location-type'; import { LocationType } from '@jellyfin/sdk/lib/generated-client/models/location-type';
import React from 'react'; import React from 'react';
import Box from '@mui/material/Box'; import Box from '@mui/material/Box';
@ -13,9 +12,15 @@ import FolderIcon from '@mui/icons-material/Folder';
import PhotoAlbumIcon from '@mui/icons-material/PhotoAlbum'; import PhotoAlbumIcon from '@mui/icons-material/PhotoAlbum';
import PhotoIcon from '@mui/icons-material/Photo'; import PhotoIcon from '@mui/icons-material/Photo';
import classNames from 'classnames'; import classNames from 'classnames';
import datetime from 'scripts/datetime'; import datetime from 'scripts/datetime';
import itemHelper from 'components/itemHelper'; import itemHelper from 'components/itemHelper';
import AutoTimeProgressBar from 'elements/emby-progressbar/AutoTimeProgressBar'; import AutoTimeProgressBar from 'elements/emby-progressbar/AutoTimeProgressBar';
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 { NullableString } from 'types/base/common/shared/types'; import type { 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 { ProgressOptions } from 'types/progressOptions'; import type { ProgressOptions } from 'types/progressOptions';
@ -32,25 +37,25 @@ const getTypeIcon = (itemType: NullableString) => {
}; };
const enableProgressIndicator = ( const enableProgressIndicator = (
itemType: NullableString, itemType: ItemKind,
itemMediaType: NullableString itemMediaType: ItemMediaKind
) => { ) => {
return ( return (
(itemMediaType === 'Video' && itemType !== BaseItemKind.TvChannel) (itemMediaType === ItemMediaKind.Video && itemType !== ItemKind.TvChannel)
|| itemType === BaseItemKind.AudioBook || itemType === ItemKind.AudioBook
|| itemType === 'AudioPodcast' || itemType === ItemKind.AudioPodcast
); );
}; };
const enableAutoTimeProgressIndicator = ( const enableAutoTimeProgressIndicator = (
itemType: NullableString, itemType: ItemKind,
itemStartDate: NullableString, itemStartDate: NullableString,
itemEndDate: NullableString itemEndDate: NullableString
) => { ) => {
return ( return (
(itemType === BaseItemKind.Program (itemType === ItemKind.Program
|| itemType === 'Timer' || itemType === ItemKind.Timer
|| itemType === BaseItemKind.Recording) || itemType === ItemKind.Recording)
&& Boolean(itemStartDate) && Boolean(itemStartDate)
&& Boolean(itemEndDate) && Boolean(itemEndDate)
); );
@ -76,7 +81,7 @@ const useIndicator = (item: ItemDto) => {
const getMissingIndicator = () => { const getMissingIndicator = () => {
if ( if (
item.Type === BaseItemKind.Episode item.Type === ItemKind.Episode
&& item.LocationType === LocationType.Virtual && item.LocationType === LocationType.Virtual
) { ) {
if (item.PremiereDate) { if (item.PremiereDate) {
@ -102,11 +107,11 @@ const useIndicator = (item: ItemDto) => {
let status; let status;
if (item.Type === 'SeriesTimer') { if (item.Type === ItemKind.SeriesTimer) {
return <FiberSmartRecordIcon className={indicatorIconClass} />; return <FiberSmartRecordIcon className={indicatorIconClass} />;
} else if (item.TimerId || item.SeriesTimerId) { } else if (item.TimerId || item.SeriesTimerId) {
status = item.Status || 'Cancelled'; status = item.Status || ItemStatus.Cancelled;
} else if (item.Type === 'Timer') { } else if (item.Type === ItemKind.Timer) {
status = item.Status; status = item.Status;
} else { } else {
return null; return null;
@ -116,7 +121,7 @@ const useIndicator = (item: ItemDto) => {
return ( return (
<FiberSmartRecordIcon <FiberSmartRecordIcon
className={`${indicatorIconClass} ${ className={`${indicatorIconClass} ${
status === 'Cancelled' ? 'timerIndicator-inactive' : '' status === ItemStatus.Cancelled ? 'timerIndicator-inactive' : ''
}`} }`}
/> />
); );
@ -198,7 +203,7 @@ const useIndicator = (item: ItemDto) => {
const getProgressBar = (progressOptions?: ProgressOptions) => { const getProgressBar = (progressOptions?: ProgressOptions) => {
if ( if (
enableProgressIndicator(item.Type, item.MediaType) enableProgressIndicator(item.Type, item.MediaType)
&& item.Type !== BaseItemKind.Recording && item.Type !== ItemKind.Recording
) { ) {
const playedPercentage = progressOptions?.userData?.PlayedPercentage ? const playedPercentage = progressOptions?.userData?.PlayedPercentage ?
progressOptions.userData.PlayedPercentage : progressOptions.userData.PlayedPercentage :
@ -231,8 +236,8 @@ const useIndicator = (item: ItemDto) => {
if (pct > 0 && pct < 100) { if (pct > 0 && pct < 100) {
const isRecording = const isRecording =
item.Type === 'Timer' item.Type === ItemKind.Timer
|| item.Type === BaseItemKind.Recording || item.Type === ItemKind.Recording
|| Boolean(item.TimerId); || Boolean(item.TimerId);
return ( return (
<AutoTimeProgressBar <AutoTimeProgressBar

View file

@ -8,6 +8,8 @@ import StarIcons from './StarIcons';
import CaptionMediaInfo from './CaptionMediaInfo'; import CaptionMediaInfo from './CaptionMediaInfo';
import CriticRatingMediaInfo from './CriticRatingMediaInfo'; import CriticRatingMediaInfo from './CriticRatingMediaInfo';
import EndsAt from './EndsAt'; import EndsAt from './EndsAt';
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';
@ -89,7 +91,7 @@ const PrimaryMediaInfo: FC<PrimaryMediaInfoProps> = ({
)} )}
{isEndsAtEnabled {isEndsAtEnabled
&& MediaType === 'Video' && MediaType === ItemMediaKind.Video
&& RunTimeTicks && RunTimeTicks
&& !StartDate && <EndsAt runTimeTicks={RunTimeTicks} />} && !StartDate && <EndsAt runTimeTicks={RunTimeTicks} />}

View file

@ -1,22 +1,25 @@
import { BaseItemKind } from '@jellyfin/sdk/lib/generated-client/models/base-item-kind';
import * as userSettings from 'scripts/settings/userSettings'; import * as userSettings from 'scripts/settings/userSettings';
import datetime from 'scripts/datetime'; import datetime from 'scripts/datetime';
import globalize from 'lib/globalize'; import globalize from 'lib/globalize';
import itemHelper from '../itemHelper'; 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 { ItemDto } from 'types/base/models/item-dto';
import type { MiscInfo } from 'types/mediaInfoItem'; import type { MiscInfo } from 'types/mediaInfoItem';
function shouldShowFolderRuntime( function shouldShowFolderRuntime(
itemType: NullableString, itemType: ItemKind,
itemMediaType: NullableString itemMediaType: ItemMediaKind
): boolean { ): boolean {
return ( return (
itemType === BaseItemKind.MusicAlbum itemType === ItemKind.MusicAlbum
|| itemMediaType === 'MusicArtist' || itemMediaType === ItemMediaKind.MusicArtist
|| itemType === BaseItemKind.Playlist || itemType === ItemKind.Playlist
|| itemMediaType === 'Playlist' || itemMediaType === ItemMediaKind.Playlist
|| itemMediaType === 'MusicGenre' || itemMediaType === ItemMediaKind.MusicGenre
); );
} }
@ -37,7 +40,7 @@ function addTrackCountOrItemCount(
if (itemRunTimeTicks) { if (itemRunTimeTicks) {
addMiscInfo({ text: datetime.getDisplayDuration(itemRunTimeTicks) }); addMiscInfo({ text: datetime.getDisplayDuration(itemRunTimeTicks) });
} }
} else if (itemType === BaseItemKind.PhotoAlbum || itemType === BaseItemKind.BoxSet) { } else if (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) });
@ -46,22 +49,22 @@ function addTrackCountOrItemCount(
} }
function addOriginalAirDateInfo( function addOriginalAirDateInfo(
itemType: NullableString, itemType: ItemKind,
itemMediaType: NullableString, itemMediaType: ItemMediaKind,
isOriginalAirDateEnabled: boolean, isOriginalAirDateEnabled: boolean,
itemPremiereDate: NullableString, itemPremiereDate: NullableString,
addMiscInfo: (val: MiscInfo) => void addMiscInfo: (val: MiscInfo) => void
): void { ): void {
if ( if (
itemPremiereDate itemPremiereDate
&& (itemType === BaseItemKind.Episode || itemMediaType === 'Photo') && (itemType === ItemKind.Episode || itemMediaType === ItemMediaKind.Photo)
&& isOriginalAirDateEnabled && isOriginalAirDateEnabled
) { ) {
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
const date = datetime.parseISO8601Date( const date = datetime.parseISO8601Date(
itemPremiereDate, itemPremiereDate,
itemType !== BaseItemKind.Episode itemType !== ItemKind.Episode
); );
addMiscInfo({ text: datetime.toLocaleDateString(date) }); addMiscInfo({ text: datetime.toLocaleDateString(date) });
} catch (e) { } catch (e) {
@ -71,14 +74,14 @@ function addOriginalAirDateInfo(
} }
function addSeriesTimerInfo( function addSeriesTimerInfo(
itemType: NullableString, itemType: ItemKind,
itemRecordAnyTime: boolean | undefined, itemRecordAnyTime: boolean | undefined,
itemStartDate: NullableString, itemStartDate: NullableString,
itemRecordAnyChannel: boolean | undefined, itemRecordAnyChannel: boolean | undefined,
itemChannelName: NullableString, itemChannelName: NullableString,
addMiscInfo: (val: MiscInfo) => void addMiscInfo: (val: MiscInfo) => void
): void { ): void {
if (itemType === 'SeriesTimer') { if (itemType === ItemKind.SeriesTimer) {
if (itemRecordAnyTime) { if (itemRecordAnyTime) {
addMiscInfo({ text: globalize.translate('Anytime') }); addMiscInfo({ text: globalize.translate('Anytime') });
} else { } else {
@ -145,9 +148,9 @@ function addProgramIndicators(
isEpisodeTitleIndexNumberEnabled: boolean, isEpisodeTitleIndexNumberEnabled: boolean,
addMiscInfo: (val: MiscInfo) => void addMiscInfo: (val: MiscInfo) => void
): void { ): void {
if (item.Type === BaseItemKind.Program || item.Type === 'Timer') { if (item.Type === ItemKind.Program || item.Type === ItemKind.Timer) {
let program = item; let program = item;
if (item.Type === 'Timer' && item.ProgramInfo) { if (item.Type === ItemKind.Timer && item.ProgramInfo) {
program = item.ProgramInfo; program = item.ProgramInfo;
} }
@ -205,20 +208,20 @@ function addProgramTextInfo(
function addStartDateInfo( function addStartDateInfo(
itemStartDate: NullableString, itemStartDate: NullableString,
itemType: NullableString, itemType: ItemKind,
addMiscInfo: (val: MiscInfo) => void addMiscInfo: (val: MiscInfo) => void
): void { ): void {
if ( if (
itemStartDate itemStartDate
&& itemType !== BaseItemKind.Program && itemType !== ItemKind.Program
&& itemType !== 'SeriesTimer' && itemType !== ItemKind.SeriesTimer
&& itemType !== 'Timer' && itemType !== ItemKind.Timer
) { ) {
try { try {
const date = datetime.parseISO8601Date(itemStartDate); const date = datetime.parseISO8601Date(itemStartDate);
addMiscInfo({ text: datetime.toLocaleDateString(date) }); addMiscInfo({ text: datetime.toLocaleDateString(date) });
if (itemType !== BaseItemKind.Recording) { if (itemType !== ItemKind.Recording) {
addMiscInfo({ text: datetime.getDisplayTime(date) }); addMiscInfo({ text: datetime.getDisplayTime(date) });
} }
} catch (e) { } catch (e) {
@ -229,14 +232,14 @@ function addStartDateInfo(
function addSeriesProductionYearInfo( function addSeriesProductionYearInfo(
itemProductionYear: NullableNumber, itemProductionYear: NullableNumber,
itemType: NullableString, itemType: ItemKind,
isYearEnabled: boolean, isYearEnabled: boolean,
itemStatus: NullableString, itemStatus: ItemStatus,
itemEndDate: NullableString, itemEndDate: NullableString,
addMiscInfo: (val: MiscInfo) => void addMiscInfo: (val: MiscInfo) => void
): void { ): void {
if (itemProductionYear && isYearEnabled && itemType === BaseItemKind.Series) { if (itemProductionYear && isYearEnabled && itemType === ItemKind.Series) {
if (itemStatus === 'Continuing') { if (itemStatus === ItemStatus.Continuing) {
addMiscInfo({ addMiscInfo({
text: globalize.translate( text: globalize.translate(
'SeriesYearToPresent', 'SeriesYearToPresent',
@ -279,20 +282,20 @@ function addproductionYearWithEndDate(
function addYearInfo( function addYearInfo(
isYearEnabled: boolean, isYearEnabled: boolean,
itemType: NullableString, itemType: ItemKind,
itemMediaType: NullableString, itemMediaType: ItemMediaKind,
itemProductionYear: NullableNumber, itemProductionYear: NullableNumber,
itemPremiereDate: NullableString, itemPremiereDate: NullableString,
addMiscInfo: (val: MiscInfo) => void addMiscInfo: (val: MiscInfo) => void
): void { ): void {
if ( if (
isYearEnabled isYearEnabled
&& itemType !== BaseItemKind.Series && itemType !== ItemKind.Series
&& itemType !== BaseItemKind.Episode && itemType !== ItemKind.Episode
&& itemType !== BaseItemKind.Person && itemType !== ItemKind.Person
&& itemMediaType !== 'Photo' && itemMediaType !== ItemMediaKind.Photo
&& itemType !== BaseItemKind.Program && itemType !== ItemKind.Program
&& itemType !== BaseItemKind.Season && itemType !== ItemKind.Season
) { ) {
if (itemProductionYear) { if (itemProductionYear) {
addMiscInfo({ text: itemProductionYear }); addMiscInfo({ text: itemProductionYear });
@ -321,21 +324,21 @@ function addVideo3DFormat(
function addRunTimeInfo( function addRunTimeInfo(
itemRunTimeTicks: NullableNumber, itemRunTimeTicks: NullableNumber,
itemType: NullableString, itemType: ItemKind,
showFolderRuntime: boolean, showFolderRuntime: boolean,
isRuntimeEnabled: boolean, isRuntimeEnabled: boolean,
addMiscInfo: (val: MiscInfo) => void addMiscInfo: (val: MiscInfo) => void
): void { ): void {
if ( if (
itemRunTimeTicks itemRunTimeTicks
&& itemType !== BaseItemKind.Series && itemType !== ItemKind.Series
&& itemType !== BaseItemKind.Program && itemType !== ItemKind.Program
&& itemType !== 'Timer' && itemType !== ItemKind.Timer
&& itemType !== BaseItemKind.Book && itemType !== ItemKind.Book
&& !showFolderRuntime && !showFolderRuntime
&& isRuntimeEnabled && isRuntimeEnabled
) { ) {
if (itemType === BaseItemKind.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) });
@ -345,15 +348,15 @@ function addRunTimeInfo(
function addOfficialRatingInfo( function addOfficialRatingInfo(
itemOfficialRating: NullableString, itemOfficialRating: NullableString,
itemType: NullableString, itemType: ItemKind,
isOfficialRatingEnabled: boolean, isOfficialRatingEnabled: boolean,
addMiscInfo: (val: MiscInfo) => void addMiscInfo: (val: MiscInfo) => void
): void { ): void {
if ( if (
itemOfficialRating itemOfficialRating
&& isOfficialRatingEnabled && isOfficialRatingEnabled
&& itemType !== BaseItemKind.Season && itemType !== ItemKind.Season
&& itemType !== BaseItemKind.Episode && itemType !== ItemKind.Episode
) { ) {
addMiscInfo({ addMiscInfo({
text: itemOfficialRating, text: itemOfficialRating,
@ -365,21 +368,21 @@ function addOfficialRatingInfo(
function addAudioContainer( function addAudioContainer(
itemContainer: NullableString, itemContainer: NullableString,
isContainerEnabled: boolean, isContainerEnabled: boolean,
itemType: NullableString, itemType: ItemKind,
addMiscInfo: (val: MiscInfo) => void addMiscInfo: (val: MiscInfo) => void
): void { ): void {
if (itemContainer && isContainerEnabled && itemType === BaseItemKind.Audio) { if (itemContainer && isContainerEnabled && itemType === ItemKind.Audio) {
addMiscInfo({ text: itemContainer }); addMiscInfo({ text: itemContainer });
} }
} }
function addPhotoSize( function addPhotoSize(
itemMediaType: NullableString, itemMediaType: ItemMediaKind,
itemWidth: NullableNumber, itemWidth: NullableNumber,
itemHeight: NullableNumber, itemHeight: NullableNumber,
addMiscInfo: (val: MiscInfo) => void addMiscInfo: (val: MiscInfo) => void
): void { ): void {
if (itemMediaType === 'Photo' && itemWidth && itemHeight) { if (itemMediaType === ItemMediaKind.Photo && itemWidth && itemHeight) {
const size = `${itemWidth}x${itemHeight}`; const size = `${itemWidth}x${itemHeight}`;
addMiscInfo({ text: size }); addMiscInfo({ text: size });

View file

@ -1,7 +1,5 @@
import type { AxiosRequestConfig } from 'axios'; import type { AxiosRequestConfig } from 'axios';
import type { ItemsApiGetItemsRequest, PlaylistsApiMoveItemRequest } from '@jellyfin/sdk/lib/generated-client'; import type { ItemsApiGetItemsRequest, PlaylistsApiMoveItemRequest } from '@jellyfin/sdk/lib/generated-client';
import type { BaseItemDto } from '@jellyfin/sdk/lib/generated-client/models/base-item-dto';
import type { TimerInfoDto } from '@jellyfin/sdk/lib/generated-client/models/timer-info-dto';
import type { BaseItemKind } from '@jellyfin/sdk/lib/generated-client/models/base-item-kind'; import type { BaseItemKind } from '@jellyfin/sdk/lib/generated-client/models/base-item-kind';
import { ImageType } from '@jellyfin/sdk/lib/generated-client/models/image-type'; import { ImageType } from '@jellyfin/sdk/lib/generated-client/models/image-type';
import { ItemFields } from '@jellyfin/sdk/lib/generated-client/models/item-fields'; import { ItemFields } from '@jellyfin/sdk/lib/generated-client/models/item-fields';
@ -26,9 +24,13 @@ import globalize from 'lib/globalize';
import { type JellyfinApiContext, useApi } from './useApi'; import { type JellyfinApiContext, useApi } from './useApi';
import { getAlphaPickerQuery, getFieldsQuery, getFiltersQuery, getLimitQuery } from 'utils/items'; import { getAlphaPickerQuery, getFieldsQuery, getFiltersQuery, getLimitQuery } from 'utils/items';
import { getProgramSections, getSuggestionSections } from 'utils/sections'; import { getProgramSections, getSuggestionSections } from 'utils/sections';
import type { LibraryViewSettings, ParentId } from 'types/library'; import type { LibraryViewSettings, ParentId } from 'types/library';
import { type Section, type SectionType, SectionApiMethod } from 'types/sections'; import { type Section, type SectionType, SectionApiMethod } from 'types/sections';
import { LibraryTab } from 'types/libraryTab'; import { LibraryTab } from 'types/libraryTab';
import { ItemKind } from 'types/base/models/item-kind';
import type { ItemDtoQueryResult } from 'types/base/models/item-dto-query-result';
import type { ItemDto } from 'types/base/models/item-dto';
const fetchGetItems = async ( const fetchGetItems = async (
currentApi: JellyfinApiContext, currentApi: JellyfinApiContext,
@ -46,7 +48,7 @@ const fetchGetItems = async (
signal: options?.signal signal: options?.signal
} }
); );
return response.data; return response.data as ItemDtoQueryResult;
} }
}; };
@ -61,7 +63,8 @@ export const useGetItems = (parametersOptions: ItemsApiGetItemsRequest) => {
], ],
queryFn: ({ signal }) => queryFn: ({ signal }) =>
fetchGetItems(currentApi, parametersOptions, { signal }), fetchGetItems(currentApi, parametersOptions, { signal }),
gcTime: parametersOptions.sortBy?.includes(ItemSortBy.Random) ? 0 : undefined gcTime: parametersOptions.sortBy?.includes(ItemSortBy.Random) ? 0 : undefined,
enabled: !!currentApi.api && !!currentApi.user?.Id
}); });
}; };
@ -95,8 +98,8 @@ export const useGetMovieRecommendations = (isMovieRecommendationEnabled: boolean
const currentApi = useApi(); const currentApi = useApi();
return useQuery({ return useQuery({
queryKey: ['MovieRecommendations', isMovieRecommendationEnabled, parentId], queryKey: ['MovieRecommendations', isMovieRecommendationEnabled, parentId],
queryFn: ({ signal }) => queryFn: ({ signal }) => fetchGetMovieRecommendations(currentApi, parentId, { signal }),
isMovieRecommendationEnabled ? fetchGetMovieRecommendations(currentApi, parentId, { signal }) : [] enabled: !!currentApi.api && !!currentApi.user?.Id && isMovieRecommendationEnabled
}); });
}; };
@ -121,7 +124,7 @@ const fetchGetGenres = async (
signal: options?.signal signal: options?.signal
} }
); );
return response.data; return response.data as ItemDtoQueryResult;
} }
}; };
@ -131,7 +134,7 @@ export const useGetGenres = (itemType: BaseItemKind[], parentId: ParentId) => {
queryKey: ['Genres', parentId], queryKey: ['Genres', parentId],
queryFn: ({ signal }) => queryFn: ({ signal }) =>
fetchGetGenres(currentApi, itemType, parentId, { signal }), fetchGetGenres(currentApi, itemType, parentId, { signal }),
enabled: !!parentId enabled: !!currentApi.api && !!currentApi.user?.Id && !!parentId
}); });
}; };
@ -170,7 +173,7 @@ export const useGetStudios = (parentId: ParentId, itemType: BaseItemKind[]) => {
queryKey: ['Studios', parentId, itemType], queryKey: ['Studios', parentId, itemType],
queryFn: ({ signal }) => queryFn: ({ signal }) =>
fetchGetStudios(currentApi, parentId, itemType, { signal }), fetchGetStudios(currentApi, parentId, itemType, { signal }),
enabled: !!parentId && !isLivetv enabled: !!currentApi.api && !!currentApi.user?.Id && !!parentId && !isLivetv
}); });
}; };
@ -208,7 +211,7 @@ export const useGetQueryFiltersLegacy = (
fetchGetQueryFiltersLegacy(currentApi, parentId, itemType, { fetchGetQueryFiltersLegacy(currentApi, parentId, itemType, {
signal signal
}), }),
enabled: !!parentId && !isLivetv enabled: !!currentApi.api && !!currentApi.user?.Id && !!parentId && !isLivetv
}); });
}; };
@ -334,7 +337,7 @@ const fetchGetItemsViewByType = async (
break; break;
} }
} }
return response.data; return response.data as ItemDtoQueryResult;
} }
}; };
@ -366,8 +369,8 @@ export const useGetItemsViewByType = (
), ),
refetchOnWindowFocus: false, refetchOnWindowFocus: false,
placeholderData : keepPreviousData, placeholderData : keepPreviousData,
enabled: enabled: !!currentApi.api && !!currentApi.user?.Id
[ && [
LibraryTab.Movies, LibraryTab.Movies,
LibraryTab.Favorites, LibraryTab.Favorites,
LibraryTab.Collections, LibraryTab.Collections,
@ -413,13 +416,13 @@ export const usePlaylistsMoveItemMutation = () => {
type GroupsUpcomingEpisodes = { type GroupsUpcomingEpisodes = {
name: string; name: string;
items: BaseItemDto[]; items: ItemDto[];
}; };
function groupsUpcomingEpisodes(items: BaseItemDto[]) { function groupsUpcomingEpisodes(items: ItemDto[]) {
const groups: GroupsUpcomingEpisodes[] = []; const groups: GroupsUpcomingEpisodes[] = [];
let currentGroupName = ''; let currentGroupName = '';
let currentGroup: BaseItemDto[] = []; let currentGroup: ItemDto[] = [];
for (const item of items) { for (const item of items) {
let dateText = ''; let dateText = '';
@ -483,7 +486,7 @@ const fetchGetGroupsUpcomingEpisodes = async (
signal: options?.signal signal: options?.signal
} }
); );
const items = response.data.Items ?? []; const items = (response.data.Items as ItemDto[]) || [];
return groupsUpcomingEpisodes(items); return groupsUpcomingEpisodes(items);
} }
@ -495,7 +498,7 @@ export const useGetGroupsUpcomingEpisodes = (parentId: ParentId) => {
queryKey: ['GroupsUpcomingEpisodes', parentId], queryKey: ['GroupsUpcomingEpisodes', parentId],
queryFn: ({ signal }) => queryFn: ({ signal }) =>
fetchGetGroupsUpcomingEpisodes(currentApi, parentId, { signal }), fetchGetGroupsUpcomingEpisodes(currentApi, parentId, { signal }),
enabled: !!parentId enabled: !!currentApi.api && !!currentApi.user?.Id && !!parentId
}); });
}; };
@ -573,17 +576,17 @@ export const useTogglePlayedMutation = () => {
export type GroupsTimers = { export type GroupsTimers = {
name: string; name: string;
timerInfo: TimerInfoDto[]; timerInfo: ItemDto[];
}; };
function groupsTimers(timers: TimerInfoDto[], indexByDate?: boolean) { function groupsTimers(timers: ItemDto[], indexByDate?: boolean) {
const items = timers.map(function (t) { const items = timers.map(function (t) {
t.Type = 'Timer'; t.Type = ItemKind.Timer;
return t; return t;
}); });
const groups: GroupsTimers[] = []; const groups: GroupsTimers[] = [];
let currentGroupName = ''; let currentGroupName = '';
let currentGroup: TimerInfoDto[] = []; let currentGroup: ItemDto[] = [];
for (const item of items) { for (const item of items) {
let dateText = ''; let dateText = '';
@ -642,7 +645,7 @@ const fetchGetTimers = async (
} }
); );
const timers = response.data.Items ?? []; const timers = (response.data.Items as ItemDto[]) || [];
return groupsTimers(timers, indexByDate); return groupsTimers(timers, indexByDate);
} }
@ -652,8 +655,8 @@ export const useGetTimers = (isUpcomingRecordingsEnabled: boolean, indexByDate?:
const currentApi = useApi(); const currentApi = useApi();
return useQuery({ return useQuery({
queryKey: ['Timers', { isUpcomingRecordingsEnabled, indexByDate }], queryKey: ['Timers', { isUpcomingRecordingsEnabled, indexByDate }],
queryFn: ({ signal }) => queryFn: ({ signal }) => fetchGetTimers(currentApi, indexByDate, { signal }),
isUpcomingRecordingsEnabled ? fetchGetTimers(currentApi, indexByDate, { signal }) : [] enabled: !!currentApi.api && !!currentApi.user?.Id && isUpcomingRecordingsEnabled
}); });
}; };
@ -838,13 +841,13 @@ const fetchGetSectionItems = async (
break; break;
} }
} }
return response; return response as ItemDto[] || [] ;
} }
}; };
type SectionWithItems = { type SectionWithItems = {
section: Section; section: Section;
items: BaseItemDto[]; items: ItemDto[];
}; };
const getSectionsWithItems = async ( const getSectionsWithItems = async (
@ -886,7 +889,7 @@ export const useGetSuggestionSectionsWithItems = (
queryKey: ['SuggestionSectionWithItems', { suggestionSectionType }], queryKey: ['SuggestionSectionWithItems', { suggestionSectionType }],
queryFn: ({ signal }) => queryFn: ({ signal }) =>
getSectionsWithItems(currentApi, parentId, sections, suggestionSectionType, { signal }), getSectionsWithItems(currentApi, parentId, sections, suggestionSectionType, { signal }),
enabled: !!parentId enabled: !!currentApi.api && !!currentApi.user?.Id && !!parentId
}); });
}; };
@ -898,7 +901,7 @@ export const useGetProgramsSectionsWithItems = (
const sections = getProgramSections(); const sections = getProgramSections();
return useQuery({ return useQuery({
queryKey: ['ProgramSectionWithItems', { programSectionType }], queryKey: ['ProgramSectionWithItems', { programSectionType }],
queryFn: ({ signal }) => getSectionsWithItems(currentApi, parentId, sections, programSectionType, { signal }) queryFn: ({ signal }) => getSectionsWithItems(currentApi, parentId, sections, programSectionType, { signal }),
enabled: !!currentApi.api && !!currentApi.user?.Id
}); });
}; };

View file

@ -4,6 +4,7 @@ import { queryOptions, useQuery } from '@tanstack/react-query';
import type { AxiosRequestConfig } from 'axios'; import type { AxiosRequestConfig } from 'axios';
import { useApi } from './useApi'; import { useApi } from './useApi';
import type { ItemDto } from 'types/base/models/item-dto';
const fetchItem = async ( const fetchItem = async (
api?: Api, api?: Api,
@ -16,7 +17,7 @@ const fetchItem = async (
const response = await getUserLibraryApi(api) const response = await getUserLibraryApi(api)
.getItem({ userId, itemId }, options); .getItem({ userId, itemId }, options);
return response.data; return response.data as ItemDto;
}; };
export const getItemQuery = ( export const getItemQuery = (

View file

@ -0,0 +1,7 @@
import { ItemDto } from './item-dto';
export interface ItemDtoQueryResult {
Items?: Array<ItemDto>;
TotalRecordCount?: number;
StartIndex?: number;
}

View file

@ -1,7 +1,10 @@
import type { BaseItemDto, BaseItemKind, CollectionTypeOptions, RecordingStatus, SearchHint, SeriesTimerInfoDto, TimerInfoDto, UserItemDataDto, VirtualFolderInfo } from '@jellyfin/sdk/lib/generated-client'; import type { BaseItemDto, CollectionTypeOptions, SearchHint, SeriesTimerInfoDto, TimerInfoDto, UserItemDataDto, VirtualFolderInfo } from '@jellyfin/sdk/lib/generated-client';
import type { ItemStatus } from './item-status';
import type { ItemKind } from './item-kind';
import type { ItemMediaKind } from './item-media-kind';
type BaseItem = Omit<BaseItemDto, 'ChannelId' | 'EndDate' | 'Id' | 'StartDate' | 'Status' | 'Type' | 'Artists' | 'MediaType' | 'Name' | 'CollectionType'>; type BaseItem = Omit<BaseItemDto, 'ChannelId' | 'EndDate' | 'Id' | 'StartDate' | 'Status' | 'Type' | 'Artists' | 'MediaType' | 'Name' | 'CollectionType' | 'CurrentProgram'>;
type TimerInfo = Omit<TimerInfoDto, 'ChannelId' | 'EndDate' | 'Id' | 'StartDate' | 'Status' | 'Type' | 'Name'>; type TimerInfo = Omit<TimerInfoDto, 'ChannelId' | 'EndDate' | 'Id' | 'StartDate' | 'Status' | 'Type' | 'Name' | 'ProgramInfo'>;
type SeriesTimerInfo = Omit<SeriesTimerInfoDto, 'ChannelId' | 'EndDate' | 'Id' | 'StartDate' | 'Type' | 'Name'>; type SeriesTimerInfo = Omit<SeriesTimerInfoDto, 'ChannelId' | 'EndDate' | 'Id' | 'StartDate' | 'Type' | 'Name'>;
type SearchHintItem = Omit<SearchHint, 'ItemId' |'Artists' | 'Id' | 'MediaType' | 'Name' | 'StartDate' | 'Type'>; type SearchHintItem = Omit<SearchHint, 'ItemId' |'Artists' | 'Id' | 'MediaType' | 'Name' | 'StartDate' | 'Type'>;
type UserItem = Omit<UserItemDataDto, 'ItemId'>; type UserItem = Omit<UserItemDataDto, 'ItemId'>;
@ -12,11 +15,13 @@ export interface ItemDto extends BaseItem, TimerInfo, SeriesTimerInfo, SearchHin
'EndDate'?: string | null; 'EndDate'?: string | null;
'Id'?: string | null; 'Id'?: string | null;
'StartDate'?: string | null; 'StartDate'?: string | null;
'Type'?: BaseItemKind | string | null; 'Type'?: ItemKind;
'Status'?: RecordingStatus | string | null; 'Status'?: ItemStatus;
'CollectionType'?: CollectionTypeOptions | string | null; 'CollectionType'?: CollectionTypeOptions | string | null;
'Artists'?: Array<string> | null; 'Artists'?: Array<string> | null;
'MediaType'?: string | null; 'MediaType'?: ItemMediaKind;
'Name'?: string | null; 'Name'?: string | null;
'ItemId'?: string | null; 'ItemId'?: string | null;
'ProgramInfo'?: ItemDto;
'CurrentProgram'?: ItemDto;
} }

View file

@ -3,10 +3,9 @@ import { BaseItemKind } from '@jellyfin/sdk/lib/generated-client/models/base-ite
export const ItemKind = { export const ItemKind = {
...BaseItemKind, ...BaseItemKind,
Timer: 'Timer', Timer: 'Timer',
SeriesTimer: 'SeriesTimer' SeriesTimer: 'SeriesTimer',
AudioPodcast: 'AudioPodcast'
} as const; } as const;
// eslint-disable-next-line @typescript-eslint/no-redeclare // eslint-disable-next-line @typescript-eslint/no-redeclare
export type ItemKind = keyof typeof ItemKind; export type ItemKind = typeof ItemKind[keyof typeof ItemKind] | undefined;
export type ItemType = ItemKind | null | undefined;

View file

@ -10,6 +10,4 @@ export const ItemMediaKind = {
} as const; } as const;
// eslint-disable-next-line @typescript-eslint/no-redeclare // eslint-disable-next-line @typescript-eslint/no-redeclare
export type ItemMediaKind = keyof typeof ItemMediaKind; export type ItemMediaKind = typeof ItemMediaKind[keyof typeof ItemMediaKind] | undefined;
export type ItemMediaType = ItemMediaKind | null | undefined;

View file

@ -0,0 +1,10 @@
import { RecordingStatus } from '@jellyfin/sdk/lib/generated-client/models/recording-status';
import { SeriesStatus } from '@jellyfin/sdk/lib/generated-client/models/series-status';
export const ItemStatus = {
...RecordingStatus,
...SeriesStatus
} as const;
// eslint-disable-next-line @typescript-eslint/no-redeclare
export type ItemStatus = typeof ItemStatus[keyof typeof ItemStatus] | undefined;