mirror of
https://github.com/jellyfin/jellyfin-web.git
synced 2026-09-03 03:50:02 +03:00
Extract getCardImageUrl and remove duplicate hook
This commit is contained in:
@@ -2,6 +2,7 @@ import classNames from 'classnames';
|
||||
|
||||
import layoutManager from 'components/layoutManager';
|
||||
import { ItemAction } from 'constants/itemAction';
|
||||
import { useApi } from 'hooks/useApi';
|
||||
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';
|
||||
@@ -9,11 +10,11 @@ import type { CardOptions } from 'types/cardOptions';
|
||||
import { CardShape } from 'utils/card';
|
||||
import { getDataAttributes } from 'utils/items';
|
||||
|
||||
import useCardImageUrl from './useCardImageUrl';
|
||||
import {
|
||||
resolveAction,
|
||||
resolveMixedShapeByAspectRatio
|
||||
} from '../cardBuilderUtils';
|
||||
import { getCardImageUrl } from '../utils/url';
|
||||
|
||||
interface UseCardProps {
|
||||
item: ItemDto;
|
||||
@@ -21,6 +22,7 @@ interface UseCardProps {
|
||||
}
|
||||
|
||||
function useCard({ item, cardOptions }: UseCardProps) {
|
||||
const { api } = useApi();
|
||||
const action = resolveAction({
|
||||
defaultAction: cardOptions.action ?? ItemAction.Link,
|
||||
isFolder: item.IsFolder ?? false,
|
||||
@@ -33,15 +35,18 @@ function useCard({ item, cardOptions }: UseCardProps) {
|
||||
shape = resolveMixedShapeByAspectRatio(item.PrimaryImageAspectRatio);
|
||||
}
|
||||
|
||||
const imgInfo = useCardImageUrl({
|
||||
const {
|
||||
imgUrl,
|
||||
blurhash,
|
||||
forceName,
|
||||
coverImage
|
||||
} = getCardImageUrl({
|
||||
api,
|
||||
item: item.ProgramInfo ?? item,
|
||||
cardOptions,
|
||||
options: cardOptions,
|
||||
shape
|
||||
});
|
||||
const imgUrl = imgInfo.imgUrl;
|
||||
const blurhash = imgInfo.blurhash ?? undefined;
|
||||
const forceName = imgInfo.forceName;
|
||||
const coveredImage = cardOptions.coverImage ?? imgInfo.coverImage;
|
||||
const coveredImage = cardOptions.coverImage ?? coverImage;
|
||||
const overlayText = cardOptions.overlayText;
|
||||
|
||||
const nameWithPrefix = item.SortName ?? item.Name ?? '';
|
||||
@@ -112,7 +117,7 @@ function useCard({ item, cardOptions }: UseCardProps) {
|
||||
className: cardBoxClass,
|
||||
shape,
|
||||
imgUrl,
|
||||
blurhash,
|
||||
blurhash: blurhash ?? undefined,
|
||||
forceName,
|
||||
coveredImage,
|
||||
overlayText
|
||||
|
||||
@@ -1,300 +0,0 @@
|
||||
import { ImageType } from '@jellyfin/sdk/lib/generated-client/models/image-type';
|
||||
import { getImageApi } from '@jellyfin/sdk/lib/utils/api/image-api';
|
||||
import { useApi } from 'hooks/useApi';
|
||||
import { getDesiredAspect } from '../cardBuilderUtils';
|
||||
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 { ItemDto } from 'types/base/models/item-dto';
|
||||
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 !== ItemMediaKind.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
|
||||
) {
|
||||
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: CardShape | undefined
|
||||
): boolean {
|
||||
return (
|
||||
(cardOptions.preferBanner || shape === CardShape.Banner)
|
||||
&& Boolean(imageTagsBanner)
|
||||
);
|
||||
}
|
||||
|
||||
function shouldShowPreferDisc(
|
||||
imageTagsDisc: string | null | undefined,
|
||||
cardOptions: CardOptions
|
||||
): boolean {
|
||||
return cardOptions.preferDisc === true && Boolean(imageTagsDisc);
|
||||
}
|
||||
|
||||
function shouldShowImageTagsPrimary(item: ItemDto): boolean {
|
||||
return (
|
||||
Boolean(item.ImageTags?.Primary) && (item.Type !== ItemKind.Episode || item.ChildCount !== 0)
|
||||
);
|
||||
}
|
||||
|
||||
function shouldShowImageTagsThumb(item: ItemDto): boolean {
|
||||
return item.Type === ItemKind.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) && Boolean(cardOptions.inheritThumb)
|
||||
);
|
||||
}
|
||||
|
||||
function shouldShowAlbumPrimaryImageTag(item: ItemDto): boolean {
|
||||
return Boolean(item.AlbumId) && Boolean(item.AlbumPrimaryImageTag);
|
||||
}
|
||||
|
||||
function shouldShowPreferThumb(itemType: ItemKind, cardOptions: CardOptions): boolean {
|
||||
return Boolean(cardOptions.preferThumb) && !(itemType === ItemKind.Program || itemType === ItemKind.Episode);
|
||||
}
|
||||
|
||||
function getCardImageInfo(
|
||||
item: ItemDto,
|
||||
cardOptions: CardOptions,
|
||||
shape: CardShape | undefined
|
||||
) {
|
||||
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 (shouldShowParentThumbImageTag(item.ParentThumbItemId, cardOptions)) {
|
||||
imgType = ImageType.Thumb;
|
||||
imgTag = item.ParentThumbImageTag;
|
||||
itemId = item.ParentThumbItemId;
|
||||
} 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 (shouldShowAlbumPrimaryImageTag(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 (
|
||||
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: CardShape | undefined;
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -16,6 +16,7 @@ import globalize from 'lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import { getBackdropShape, getPortraitShape, getSquareShape } from 'utils/card';
|
||||
import { getItemTypeIcon, getLibraryIcon } from 'utils/image';
|
||||
import { toApi } from 'utils/jellyfin-apiclient/compat';
|
||||
|
||||
import focusManager from '../focusManager';
|
||||
import imageLoader from '../images/imageLoader';
|
||||
@@ -41,6 +42,7 @@ import {
|
||||
resolveCardImageContainerCssClasses,
|
||||
resolveMixedShapeByAspectRatio
|
||||
} from './cardBuilderUtils';
|
||||
import { getCardImageUrl } from './utils/url';
|
||||
|
||||
const enableFocusTransform = !browser.slow && !browser.edge;
|
||||
|
||||
@@ -251,162 +253,6 @@ function buildCardsHtmlInternal(items, options) {
|
||||
return html;
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} CardImageUrl
|
||||
* @property {string} imgUrl - Image URL.
|
||||
* @property {string} blurhash - Image blurhash.
|
||||
* @property {boolean} forceName - Force name.
|
||||
* @property {boolean} coverImage - Use cover style.
|
||||
*/
|
||||
|
||||
/** Get the URL of the card's image.
|
||||
* @param {Object} item - Item for which to generate a card.
|
||||
* @param {Object} apiClient - API client object.
|
||||
* @param {Object} options - Options of the card.
|
||||
* @param {string} shape - Shape of the desired image.
|
||||
* @returns {CardImageUrl} Object representing the URL of the card's image.
|
||||
*/
|
||||
export function getCardImageUrl(item, apiClient, options, shape) {
|
||||
item = item.ProgramInfo || item;
|
||||
|
||||
const width = options.width;
|
||||
let height = null;
|
||||
const primaryImageAspectRatio = item.PrimaryImageAspectRatio;
|
||||
let forceName = false;
|
||||
let imgUrl = null;
|
||||
let imgTag = null;
|
||||
let coverImage = false;
|
||||
const uiAspect = getDesiredAspect(shape);
|
||||
let imgType = null;
|
||||
let itemId = null;
|
||||
|
||||
/* eslint-disable sonarjs/no-duplicated-branches */
|
||||
if (options.preferThumb && item.ImageTags?.Thumb) {
|
||||
imgType = 'Thumb';
|
||||
imgTag = item.ImageTags.Thumb;
|
||||
} else if ((options.preferBanner || shape === 'banner') && item.ImageTags?.Banner) {
|
||||
imgType = 'Banner';
|
||||
imgTag = item.ImageTags.Banner;
|
||||
} else if (options.preferDisc && item.ImageTags?.Disc) {
|
||||
imgType = 'Disc';
|
||||
imgTag = item.ImageTags.Disc;
|
||||
} else if (options.preferLogo && item.ImageTags?.Logo) {
|
||||
imgType = 'Logo';
|
||||
imgTag = item.ImageTags.Logo;
|
||||
} else if (options.preferLogo && item.ParentLogoImageTag && item.ParentLogoItemId) {
|
||||
imgType = 'Logo';
|
||||
imgTag = item.ParentLogoImageTag;
|
||||
itemId = item.ParentLogoItemId;
|
||||
} else if (options.preferThumb && item.SeriesThumbImageTag && options.inheritThumb !== false) {
|
||||
imgType = 'Thumb';
|
||||
imgTag = item.SeriesThumbImageTag;
|
||||
itemId = item.SeriesId;
|
||||
} else if (options.preferThumb && item.ParentThumbItemId && options.inheritThumb !== false && item.MediaType !== 'Photo') {
|
||||
imgType = 'Thumb';
|
||||
imgTag = item.ParentThumbImageTag;
|
||||
itemId = item.ParentThumbItemId;
|
||||
} else if (options.preferThumb && item.BackdropImageTags?.length) {
|
||||
imgType = 'Backdrop';
|
||||
imgTag = item.BackdropImageTags[0];
|
||||
forceName = true;
|
||||
} else if (options.preferThumb && item.ParentBackdropImageTags?.length && options.inheritThumb !== false && item.Type === 'Episode') {
|
||||
imgType = 'Backdrop';
|
||||
imgTag = item.ParentBackdropImageTags[0];
|
||||
itemId = item.ParentBackdropItemId;
|
||||
} else if (item.ImageTags?.Primary && (item.Type !== 'Episode' || item.ChildCount !== 0)) {
|
||||
imgType = 'Primary';
|
||||
imgTag = item.ImageTags.Primary;
|
||||
height = width && primaryImageAspectRatio ? Math.round(width / primaryImageAspectRatio) : null;
|
||||
|
||||
if (options.preferThumb && options.showTitle !== false) {
|
||||
forceName = true;
|
||||
}
|
||||
|
||||
if (primaryImageAspectRatio && uiAspect) {
|
||||
coverImage = (Math.abs(primaryImageAspectRatio - uiAspect) / uiAspect) <= 0.2;
|
||||
}
|
||||
} else if (item.SeriesPrimaryImageTag) {
|
||||
imgType = 'Primary';
|
||||
imgTag = item.SeriesPrimaryImageTag;
|
||||
itemId = item.SeriesId;
|
||||
} else if (item.PrimaryImageTag) {
|
||||
imgType = 'Primary';
|
||||
imgTag = item.PrimaryImageTag;
|
||||
itemId = item.PrimaryImageItemId;
|
||||
height = width && primaryImageAspectRatio ? Math.round(width / primaryImageAspectRatio) : null;
|
||||
|
||||
if (options.preferThumb && options.showTitle !== false) {
|
||||
forceName = true;
|
||||
}
|
||||
|
||||
if (primaryImageAspectRatio && uiAspect) {
|
||||
coverImage = (Math.abs(primaryImageAspectRatio - uiAspect) / uiAspect) <= 0.2;
|
||||
}
|
||||
} else if (item.ParentPrimaryImageTag) {
|
||||
imgType = 'Primary';
|
||||
imgTag = item.ParentPrimaryImageTag;
|
||||
itemId = item.ParentPrimaryImageItemId;
|
||||
} else if (item.AlbumId && item.AlbumPrimaryImageTag) {
|
||||
imgType = 'Primary';
|
||||
imgTag = item.AlbumPrimaryImageTag;
|
||||
itemId = item.AlbumId;
|
||||
height = width && primaryImageAspectRatio ? Math.round(width / primaryImageAspectRatio) : null;
|
||||
|
||||
if (primaryImageAspectRatio && uiAspect) {
|
||||
coverImage = (Math.abs(primaryImageAspectRatio - uiAspect) / uiAspect) <= 0.2;
|
||||
}
|
||||
} else if (item.Type === 'Season' && item.ImageTags?.Thumb) {
|
||||
imgType = 'Thumb';
|
||||
imgTag = item.ImageTags.Thumb;
|
||||
} else if (item.BackdropImageTags?.length) {
|
||||
imgType = 'Backdrop';
|
||||
imgTag = item.BackdropImageTags[0];
|
||||
} else if (item.ImageTags?.Thumb) {
|
||||
imgType = 'Thumb';
|
||||
imgTag = item.ImageTags.Thumb;
|
||||
} else if (item.SeriesThumbImageTag && options.inheritThumb !== false) {
|
||||
imgType = 'Thumb';
|
||||
imgTag = item.SeriesThumbImageTag;
|
||||
itemId = item.SeriesId;
|
||||
} else if (item.ParentThumbItemId && options.inheritThumb !== false) {
|
||||
imgType = 'Thumb';
|
||||
imgTag = item.ParentThumbImageTag;
|
||||
itemId = item.ParentThumbItemId;
|
||||
} else if (item.ParentBackdropImageTags?.length && options.inheritThumb !== false) {
|
||||
imgType = 'Backdrop';
|
||||
imgTag = item.ParentBackdropImageTags[0];
|
||||
itemId = item.ParentBackdropItemId;
|
||||
}
|
||||
/* eslint-enable sonarjs/no-duplicated-branches */
|
||||
|
||||
if (!itemId) {
|
||||
itemId = item.Id;
|
||||
}
|
||||
|
||||
if (imgTag && imgType) {
|
||||
// TODO: This place is a mess. Could do with a good spring cleaning.
|
||||
if (!height && width && uiAspect) {
|
||||
height = width / uiAspect;
|
||||
}
|
||||
imgUrl = apiClient.getScaledImageUrl(itemId, {
|
||||
type: imgType,
|
||||
fillHeight: height,
|
||||
fillWidth: width,
|
||||
quality: 96,
|
||||
tag: imgTag
|
||||
});
|
||||
}
|
||||
|
||||
const blurHashes = options.imageBlurhashes || item.ImageBlurHashes || {};
|
||||
|
||||
return {
|
||||
imgUrl: imgUrl,
|
||||
blurhash: blurHashes[imgType]?.[imgTag],
|
||||
forceName: forceName,
|
||||
coverImage: coverImage
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the HTML markup for a card's text.
|
||||
* @param {Array} lines - Array containing the text lines.
|
||||
@@ -900,7 +746,7 @@ function buildCard(index, item, apiClient, options) {
|
||||
|
||||
// TODO move card creation code to Card component
|
||||
|
||||
const imgInfo = getCardImageUrl(item, apiClient, options, shape);
|
||||
const imgInfo = getCardImageUrl({ api: toApi(apiClient), item, options, shape });
|
||||
const imgUrl = imgInfo.imgUrl;
|
||||
const blurhash = imgInfo.blurhash;
|
||||
const forceName = imgInfo.forceName;
|
||||
|
||||
@@ -4,8 +4,10 @@ import type { ApiClient } from 'jellyfin-apiclient';
|
||||
|
||||
import type { CardOptions } from 'types/cardOptions';
|
||||
import { CardShape } from 'utils/card';
|
||||
import { toApi } from 'utils/jellyfin-apiclient/compat';
|
||||
|
||||
import { getCardImageUrl, getDefaultText } from './cardBuilder';
|
||||
import { getDefaultText } from './cardBuilder';
|
||||
import { getCardImageUrl } from './utils/url';
|
||||
|
||||
/**
|
||||
* Builds an html string for a basic image only card.
|
||||
@@ -28,12 +30,12 @@ export function buildCardImage(
|
||||
}
|
||||
}
|
||||
|
||||
const image = getCardImageUrl(
|
||||
const image = getCardImageUrl({
|
||||
api: toApi(apiClient),
|
||||
item,
|
||||
apiClient,
|
||||
options,
|
||||
shape
|
||||
);
|
||||
});
|
||||
|
||||
if (!image) return '';
|
||||
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
import { Api } from '@jellyfin/sdk';
|
||||
import { ImageType } from '@jellyfin/sdk/lib/generated-client/models/image-type';
|
||||
import { getImageApi } from '@jellyfin/sdk/lib/utils/api/image-api';
|
||||
|
||||
import { ItemDto } from 'types/base/models/item-dto';
|
||||
import { CardOptions } from 'types/cardOptions';
|
||||
import { CardShape } from 'utils/card';
|
||||
|
||||
import { getDesiredAspect } from '../cardBuilderUtils';
|
||||
|
||||
interface CardImageUrlParams {
|
||||
api?: Api;
|
||||
item: ItemDto;
|
||||
options: CardOptions;
|
||||
shape?: CardShape;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||
export function getCardImageUrl({
|
||||
api,
|
||||
item,
|
||||
options,
|
||||
shape
|
||||
}: CardImageUrlParams) {
|
||||
if (!api) {
|
||||
throw new Error('API instance is required to get card image URL');
|
||||
}
|
||||
|
||||
item = item.ProgramInfo || item;
|
||||
|
||||
const width = options.width;
|
||||
let height: number | undefined;
|
||||
const primaryImageAspectRatio = item.PrimaryImageAspectRatio;
|
||||
let forceName = false;
|
||||
let imgUrl: string | undefined;
|
||||
let imgTag = null;
|
||||
let coverImage = false;
|
||||
const uiAspect = getDesiredAspect(shape);
|
||||
let imgType: ImageType | undefined;
|
||||
let itemId = null;
|
||||
|
||||
/* eslint-disable sonarjs/no-duplicated-branches */
|
||||
if (options.preferThumb && item.ImageTags?.Thumb) {
|
||||
imgType = ImageType.Thumb;
|
||||
imgTag = item.ImageTags.Thumb;
|
||||
} else if ((options.preferBanner || shape === CardShape.Banner) && item.ImageTags?.Banner) {
|
||||
imgType = ImageType.Banner;
|
||||
imgTag = item.ImageTags.Banner;
|
||||
} else if (options.preferDisc && item.ImageTags?.Disc) {
|
||||
imgType = ImageType.Disc;
|
||||
imgTag = item.ImageTags.Disc;
|
||||
} else if (options.preferLogo && item.ImageTags?.Logo) {
|
||||
imgType = ImageType.Logo;
|
||||
imgTag = item.ImageTags.Logo;
|
||||
} else if (options.preferLogo && item.ParentLogoImageTag && item.ParentLogoItemId) {
|
||||
imgType = ImageType.Logo;
|
||||
imgTag = item.ParentLogoImageTag;
|
||||
itemId = item.ParentLogoItemId;
|
||||
} else if (options.preferThumb && item.SeriesThumbImageTag && options.inheritThumb !== false) {
|
||||
imgType = ImageType.Thumb;
|
||||
imgTag = item.SeriesThumbImageTag;
|
||||
itemId = item.SeriesId;
|
||||
} else if (options.preferThumb && item.ParentThumbItemId && options.inheritThumb !== false && item.MediaType !== 'Photo') {
|
||||
imgType = ImageType.Thumb;
|
||||
imgTag = item.ParentThumbImageTag;
|
||||
itemId = item.ParentThumbItemId;
|
||||
} else if (options.preferThumb && item.BackdropImageTags?.length) {
|
||||
imgType = ImageType.Backdrop;
|
||||
imgTag = item.BackdropImageTags[0];
|
||||
forceName = true;
|
||||
} else if (options.preferThumb && item.ParentBackdropImageTags?.length && options.inheritThumb !== false && item.Type === 'Episode') {
|
||||
imgType = ImageType.Backdrop;
|
||||
imgTag = item.ParentBackdropImageTags[0];
|
||||
itemId = item.ParentBackdropItemId;
|
||||
} else if (item.ImageTags?.Primary && (item.Type !== 'Episode' || item.ChildCount !== 0)) {
|
||||
imgType = ImageType.Primary;
|
||||
imgTag = item.ImageTags.Primary;
|
||||
height = width && primaryImageAspectRatio ? (width / primaryImageAspectRatio) : undefined;
|
||||
|
||||
if (options.preferThumb && options.showTitle !== false) {
|
||||
forceName = true;
|
||||
}
|
||||
|
||||
if (primaryImageAspectRatio && uiAspect) {
|
||||
coverImage = (Math.abs(primaryImageAspectRatio - uiAspect) / uiAspect) <= 0.2;
|
||||
}
|
||||
} 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 = width && primaryImageAspectRatio ? (width / primaryImageAspectRatio) : undefined;
|
||||
|
||||
if (options.preferThumb && options.showTitle !== false) {
|
||||
forceName = true;
|
||||
}
|
||||
|
||||
if (primaryImageAspectRatio && uiAspect) {
|
||||
coverImage = (Math.abs(primaryImageAspectRatio - uiAspect) / uiAspect) <= 0.2;
|
||||
}
|
||||
} else if (item.ParentPrimaryImageTag) {
|
||||
imgType = ImageType.Primary;
|
||||
imgTag = item.ParentPrimaryImageTag;
|
||||
itemId = item.ParentPrimaryImageItemId;
|
||||
} else if (item.AlbumId && item.AlbumPrimaryImageTag) {
|
||||
imgType = ImageType.Primary;
|
||||
imgTag = item.AlbumPrimaryImageTag;
|
||||
itemId = item.AlbumId;
|
||||
height = width && primaryImageAspectRatio ? (width / primaryImageAspectRatio) : undefined;
|
||||
|
||||
if (primaryImageAspectRatio && uiAspect) {
|
||||
coverImage = (Math.abs(primaryImageAspectRatio - uiAspect) / uiAspect) <= 0.2;
|
||||
}
|
||||
} else if (item.Type === 'Season' && item.ImageTags?.Thumb) {
|
||||
imgType = ImageType.Thumb;
|
||||
imgTag = item.ImageTags.Thumb;
|
||||
} else if (item.BackdropImageTags?.length) {
|
||||
imgType = ImageType.Backdrop;
|
||||
imgTag = item.BackdropImageTags[0];
|
||||
} else if (item.ImageTags?.Thumb) {
|
||||
imgType = ImageType.Thumb;
|
||||
imgTag = item.ImageTags.Thumb;
|
||||
} else if (item.SeriesThumbImageTag && options.inheritThumb !== false) {
|
||||
imgType = ImageType.Thumb;
|
||||
imgTag = item.SeriesThumbImageTag;
|
||||
itemId = item.SeriesId;
|
||||
} else if (item.ParentThumbItemId && options.inheritThumb !== false) {
|
||||
imgType = ImageType.Thumb;
|
||||
imgTag = item.ParentThumbImageTag;
|
||||
itemId = item.ParentThumbItemId;
|
||||
} else if (item.ParentBackdropImageTags?.length && options.inheritThumb !== false) {
|
||||
imgType = ImageType.Backdrop;
|
||||
imgTag = item.ParentBackdropImageTags[0];
|
||||
itemId = item.ParentBackdropItemId;
|
||||
}
|
||||
/* eslint-enable sonarjs/no-duplicated-branches */
|
||||
|
||||
if (!itemId) {
|
||||
itemId = item.Id;
|
||||
}
|
||||
|
||||
if (itemId && imgTag && imgType) {
|
||||
if (!height && width && uiAspect) {
|
||||
height = width / uiAspect;
|
||||
}
|
||||
|
||||
imgUrl = getImageApi(api).getItemImageUrlById(
|
||||
itemId,
|
||||
imgType,
|
||||
{
|
||||
// Dimensions must be rounded or the API will reject the request
|
||||
fillHeight: height ? Math.round(height) : undefined,
|
||||
fillWidth: width ? Math.round(width) : undefined,
|
||||
quality: 96,
|
||||
tag: imgTag
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const blurHashes = options.imageBlurhashes || item.ImageBlurHashes || {};
|
||||
|
||||
return {
|
||||
imgUrl,
|
||||
blurhash: (imgType && imgTag) ? blurHashes[imgType]?.[imgTag] : undefined,
|
||||
forceName,
|
||||
coverImage
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user