import { BaseItemKind, ImageType } from '@jellyfin/sdk/lib/generated-client'; import { getImageApi } from '@jellyfin/sdk/lib/utils/api/image-api'; import { useApi } from 'hooks/useApi'; import { getDesiredAspect } from '../cardBuilderUtils'; import type { ItemDto, NullableNumber, NullableString } from 'types/itemDto'; import type { CardOptions } from 'types/cardOptions'; function getPreferThumbInfo(item: ItemDto, cardOptions: CardOptions) { let imgType; let itemId; let imgTag; let forceName = false; if (item.ImageTags?.Thumb) { imgType = ImageType.Thumb; imgTag = item.ImageTags.Thumb; itemId = item.Id; } else if (item.SeriesThumbImageTag && cardOptions.inheritThumb !== false) { imgType = ImageType.Thumb; imgTag = item.SeriesThumbImageTag; itemId = item.SeriesId; } else if ( item.ParentThumbItemId && cardOptions.inheritThumb !== false && item.MediaType !== 'Photo' ) { imgType = ImageType.Thumb; imgTag = item.ParentThumbImageTag; itemId = item.ParentThumbItemId; } else if (item.BackdropImageTags?.length) { imgType = ImageType.Backdrop; imgTag = item.BackdropImageTags[0]; itemId = item.Id; forceName = true; } else if ( item.ParentBackdropImageTags?.length && cardOptions.inheritThumb !== false && item.Type === BaseItemKind.Episode ) { imgType = ImageType.Backdrop; imgTag = item.ParentBackdropImageTags[0]; itemId = item.ParentBackdropItemId; } return { itemId: itemId, imgTag: imgTag, imgType: imgType, forceName: forceName }; } function getPreferLogoInfo(item: ItemDto) { let imgType; let itemId; let imgTag; if (item.ImageTags?.Logo) { imgType = ImageType.Logo; imgTag = item.ImageTags.Logo; itemId = item.Id; } else if (item.ParentLogoImageTag && item.ParentLogoItemId) { imgType = ImageType.Logo; imgTag = item.ParentLogoImageTag; itemId = item.ParentLogoItemId; } return { itemId: itemId, imgTag: imgTag, imgType: imgType }; } function getCalculatedHeight( itemWidth: NullableNumber, itemPrimaryImageAspectRatio: NullableNumber ) { if (itemWidth && itemPrimaryImageAspectRatio) { return Math.round(itemWidth / itemPrimaryImageAspectRatio); } } function isForceName(cardOptions: CardOptions) { return !!(cardOptions.preferThumb && cardOptions.showTitle !== false); } function isCoverImage( itemPrimaryImageAspectRatio: NullableNumber, uiAspect: NullableNumber ) { if (itemPrimaryImageAspectRatio && uiAspect) { return Math.abs(itemPrimaryImageAspectRatio - uiAspect) / uiAspect <= 0.2; } return false; } function shouldShowPreferBanner( imageTagsBanner: NullableString, cardOptions: CardOptions, shape: NullableString ): boolean { return ( (cardOptions.preferBanner || shape === 'banner') && Boolean(imageTagsBanner) ); } function shouldShowPreferDisc( imageTagsDisc: string | undefined, cardOptions: CardOptions ): boolean { return cardOptions.preferDisc === true && Boolean(imageTagsDisc); } function shouldShowImageTagsPrimary(item: ItemDto): boolean { return ( Boolean(item.ImageTags?.Primary) && (item.Type !== BaseItemKind.Episode || item.ChildCount !== 0) ); } function shouldShowImageTagsThumb(item: ItemDto): boolean { return item.Type === BaseItemKind.Season && Boolean(item.ImageTags?.Thumb); } function shouldShowSeriesThumbImageTag( itemSeriesThumbImageTag: NullableString, cardOptions: CardOptions ): boolean { return ( Boolean(itemSeriesThumbImageTag) && cardOptions.inheritThumb !== false ); } function shouldShowParentThumbImageTag( itemParentThumbItemId: NullableString, cardOptions: CardOptions ): boolean { return ( Boolean(itemParentThumbItemId) && cardOptions.inheritThumb !== false ); } function shouldShowParentBackdropImageTags(item: ItemDto): boolean { return Boolean(item.AlbumId) && Boolean(item.AlbumPrimaryImageTag); } function shouldShowPreferThumb(itemType: NullableString, cardOptions: CardOptions): boolean { return Boolean(cardOptions.preferThumb) && !(itemType === BaseItemKind.Program || itemType === BaseItemKind.Episode); } function getCardImageInfo( item: ItemDto, cardOptions: CardOptions, shape: NullableString ) { const width = cardOptions.width; let height; const primaryImageAspectRatio = item.PrimaryImageAspectRatio; let forceName = false; let imgTag; let coverImage = false; const uiAspect = getDesiredAspect(shape); let imgType; let itemId; if (shouldShowPreferThumb(item.Type, cardOptions)) { const preferThumbInfo = getPreferThumbInfo(item, cardOptions); imgType = preferThumbInfo.imgType; imgTag = preferThumbInfo.imgTag; itemId = preferThumbInfo.itemId; forceName = preferThumbInfo.forceName; } else if (shouldShowPreferBanner(item.ImageTags?.Banner, cardOptions, shape)) { imgType = ImageType.Banner; imgTag = item.ImageTags?.Banner; itemId = item.Id; } else if (shouldShowPreferDisc(item.ImageTags?.Disc, cardOptions)) { imgType = ImageType.Disc; imgTag = item.ImageTags?.Disc; itemId = item.Id; } else if (cardOptions.preferLogo) { const preferLogoInfo = getPreferLogoInfo(item); imgType = preferLogoInfo.imgType; imgTag = preferLogoInfo.imgType; itemId = preferLogoInfo.itemId; } else if (shouldShowImageTagsPrimary(item)) { imgType = ImageType.Primary; imgTag = item.ImageTags?.Primary; itemId = item.Id; height = getCalculatedHeight(width, primaryImageAspectRatio); forceName = isForceName(cardOptions); coverImage = isCoverImage(primaryImageAspectRatio, uiAspect); } else if (item.SeriesPrimaryImageTag) { imgType = ImageType.Primary; imgTag = item.SeriesPrimaryImageTag; itemId = item.SeriesId; } else if (item.PrimaryImageTag) { imgType = ImageType.Primary; imgTag = item.PrimaryImageTag; itemId = item.PrimaryImageItemId; height = getCalculatedHeight(width, primaryImageAspectRatio); forceName = isForceName(cardOptions); coverImage = isCoverImage(primaryImageAspectRatio, uiAspect); } else if (item.ParentPrimaryImageTag) { imgType = ImageType.Primary; imgTag = item.ParentPrimaryImageTag; itemId = item.ParentPrimaryImageItemId; } else if (shouldShowParentBackdropImageTags(item)) { imgType = ImageType.Primary; imgTag = item.AlbumPrimaryImageTag; itemId = item.AlbumId; height = getCalculatedHeight(width, primaryImageAspectRatio); forceName = isForceName(cardOptions); coverImage = isCoverImage(primaryImageAspectRatio, uiAspect); } else if (shouldShowImageTagsThumb(item)) { imgType = ImageType.Thumb; imgTag = item.ImageTags?.Thumb; itemId = item.Id; } else if (item.BackdropImageTags?.length) { imgType = ImageType.Backdrop; imgTag = item.BackdropImageTags[0]; itemId = item.Id; } else if (shouldShowSeriesThumbImageTag(item.SeriesThumbImageTag, cardOptions)) { imgType = ImageType.Thumb; imgTag = item.SeriesThumbImageTag; itemId = item.SeriesId; } else if (shouldShowParentThumbImageTag(item.ParentThumbItemId, cardOptions)) { imgType = ImageType.Thumb; imgTag = item.ParentThumbImageTag; itemId = item.ParentThumbItemId; } else if ( item.ParentBackdropImageTags?.length && cardOptions.inheritThumb !== false ) { imgType = ImageType.Backdrop; imgTag = item.ParentBackdropImageTags[0]; itemId = item.ParentBackdropItemId; } return { imgType, imgTag, itemId, width, height, forceName, coverImage }; } interface UseCardImageUrlProps { item: ItemDto; cardOptions: CardOptions; shape: NullableString; } function useCardImageUrl({ item, cardOptions, shape }: UseCardImageUrlProps) { const { api } = useApi(); const imgInfo = getCardImageInfo(item, cardOptions, shape); let width = imgInfo.width; let height = imgInfo.height; const imgTag = imgInfo.imgTag; const imgType = imgInfo.imgType; const itemId = imgInfo.itemId; const ratio = window.devicePixelRatio || 1; let imgUrl; let blurhash; if (api && imgTag && imgType && itemId) { if (width) { width = Math.round(width * ratio); } if (height) { height = Math.round(height * ratio); } imgUrl = getImageApi(api).getItemImageUrlById(itemId, imgType, { quality: 96, fillWidth: width, fillHeight: height, tag: imgTag }); blurhash = item?.ImageBlurHashes?.[imgType]?.[imgTag]; } return { imgUrl: imgUrl, blurhash: blurhash, forceName: imgInfo.forceName, coverImage: imgInfo.coverImage }; } export default useCardImageUrl;