Fixed Jellyfin SDK type errors

This commit is contained in:
Peaches_MLG
2026-01-11 11:15:20 +00:00
parent 1a792e9f82
commit 4d896fe8f2
14 changed files with 52 additions and 47 deletions
+8 -8
View File
@@ -18,7 +18,7 @@
"@fontsource/noto-sans-sc": "5.2.6",
"@fontsource/noto-sans-tc": "5.2.6",
"@jellyfin/libass-wasm": "4.2.3",
"@jellyfin/sdk": "0.0.0-unstable.202512091852",
"@jellyfin/sdk": "0.0.0-unstable.202601090504",
"@jellyfin/ux-web": "1.0.0",
"@mui/icons-material": "6.4.12",
"@mui/material": "6.4.12",
@@ -140,7 +140,7 @@
},
"engines": {
"node": ">=20.0.0",
"npm": ">=9.6.4 <11.0.0",
"npm": ">=9.6.4",
"yarn": "YARN NO LONGER USED - use npm instead."
},
"optionalDependencies": {
@@ -4130,9 +4130,9 @@
"license": "LGPL-2.1-or-later AND (FTL OR GPL-2.0-or-later) AND MIT AND MIT-Modern-Variant AND ISC AND NTP AND Zlib AND BSL-1.0"
},
"node_modules/@jellyfin/sdk": {
"version": "0.0.0-unstable.202512091852",
"resolved": "https://registry.npmjs.org/@jellyfin/sdk/-/sdk-0.0.0-unstable.202512091852.tgz",
"integrity": "sha512-N+QEsrKk4KculkV6KMBb7XpzTLWcXEzqTHbS+b4rov0VYVwR6DIsJkmUzB3hM2YZsrLIHEFKhFRy/r4itkFeHw==",
"version": "0.0.0-unstable.202601090504",
"resolved": "https://registry.npmjs.org/@jellyfin/sdk/-/sdk-0.0.0-unstable.202601090504.tgz",
"integrity": "sha512-ZTtSUMYjyfin54dXGZ5i9fZxLkKbMPxxsFxnmLoSVrjNrvL8JMV3Thb0fevCgd5gpkJpLVJXpx5YjwNbrze6DQ==",
"license": "MPL-2.0",
"peerDependencies": {
"axios": "^1.12.0"
@@ -27150,9 +27150,9 @@
"integrity": "sha512-C0OlBxIr9NdeFESMTA/OVDqNSWtog6Mi7wwzwH12xbZpxsMD0RgCupUcIP7zZgcpTNecW3fZq5d6xVo7Q8HEJw=="
},
"@jellyfin/sdk": {
"version": "0.0.0-unstable.202512091852",
"resolved": "https://registry.npmjs.org/@jellyfin/sdk/-/sdk-0.0.0-unstable.202512091852.tgz",
"integrity": "sha512-N+QEsrKk4KculkV6KMBb7XpzTLWcXEzqTHbS+b4rov0VYVwR6DIsJkmUzB3hM2YZsrLIHEFKhFRy/r4itkFeHw==",
"version": "0.0.0-unstable.202601090504",
"resolved": "https://registry.npmjs.org/@jellyfin/sdk/-/sdk-0.0.0-unstable.202601090504.tgz",
"integrity": "sha512-ZTtSUMYjyfin54dXGZ5i9fZxLkKbMPxxsFxnmLoSVrjNrvL8JMV3Thb0fevCgd5gpkJpLVJXpx5YjwNbrze6DQ==",
"requires": {}
},
"@jellyfin/ux-web": {
+2 -2
View File
@@ -85,7 +85,7 @@
"@fontsource/noto-sans-sc": "5.2.6",
"@fontsource/noto-sans-tc": "5.2.6",
"@jellyfin/libass-wasm": "4.2.3",
"@jellyfin/sdk": "0.0.0-unstable.202512091852",
"@jellyfin/sdk": "0.0.0-unstable.202601090504",
"@jellyfin/ux-web": "1.0.0",
"@mui/icons-material": "6.4.12",
"@mui/material": "6.4.12",
@@ -169,7 +169,7 @@
},
"engines": {
"node": ">=20.0.0",
"npm": ">=9.6.4 <11.0.0",
"npm": ">=9.6.4",
"yarn": "YARN NO LONGER USED - use npm instead."
}
}
@@ -1,35 +1,36 @@
import type { FolderStorageDto } from '@jellyfin/sdk/lib/generated-client/models/folder-storage-dto';
import { describe, expect, it } from 'vitest';
import { calculateUsedPercentage, calculateTotal } from './space';
describe('calculateTotal()', () => {
it('should return the total', () => {
expect(calculateTotal({ FreeSpace: 1, UsedSpace: 2 })).toBe(3);
expect(calculateTotal({ FreeSpace: 1, UsedSpace: 2 } as FolderStorageDto)).toBe(3);
});
it('should return -1 for invalid used space values', () => {
expect(calculateTotal({ FreeSpace: 1 })).toBe(-1);
expect(calculateTotal({ FreeSpace: 1, UsedSpace: -1 })).toBe(-1);
expect(calculateTotal({ FreeSpace: 1 } as FolderStorageDto)).toBe(-1);
expect(calculateTotal({ FreeSpace: 1, UsedSpace: -1 } as FolderStorageDto)).toBe(-1);
});
it('should treat invalid free space values as 0', () => {
expect(calculateTotal({ UsedSpace: 1 })).toBe(1);
expect(calculateTotal({ FreeSpace: -1, UsedSpace: 1 })).toBe(1);
expect(calculateTotal({ UsedSpace: 1 } as FolderStorageDto)).toBe(1);
expect(calculateTotal({ FreeSpace: -1, UsedSpace: 1 } as FolderStorageDto)).toBe(1);
});
});
describe('calculateUsedPercentage', () => {
it('should return the percentage used', () => {
expect(calculateUsedPercentage({ FreeSpace: 1, UsedSpace: 3 })).toBe(75);
expect(calculateUsedPercentage({ FreeSpace: 1, UsedSpace: 3 } as FolderStorageDto)).toBe(75);
});
it('should return 0 for invalid used space values', () => {
expect(calculateUsedPercentage({ FreeSpace: 1 })).toBe(0);
expect(calculateUsedPercentage({ FreeSpace: 1, UsedSpace: -1 })).toBe(0);
expect(calculateUsedPercentage({ FreeSpace: 1 } as FolderStorageDto)).toBe(0);
expect(calculateUsedPercentage({ FreeSpace: 1, UsedSpace: -1 } as FolderStorageDto)).toBe(0);
});
it('should return 100 for invalid free space values', () => {
expect(calculateUsedPercentage({ UsedSpace: 1 })).toBe(100);
expect(calculateUsedPercentage({ FreeSpace: -1, UsedSpace: 1 })).toBe(100);
expect(calculateUsedPercentage({ UsedSpace: 1 } as FolderStorageDto)).toBe(100);
expect(calculateUsedPercentage({ FreeSpace: -1, UsedSpace: 1 } as FolderStorageDto)).toBe(100);
});
});
@@ -15,7 +15,7 @@ import type { NullableString } from 'types/base/common/shared/types';
import type { ItemDto } from 'types/base/models/item-dto';
interface PlayAllFromHereOptions {
item: ItemDto;
item?: ItemDto;
items: ItemDto[];
serverId: NullableString;
queue?: boolean;
@@ -166,7 +166,7 @@ const MoreCommandsButton: FC<MoreCommandsButtonProps> = ({
serverId: item?.ServerId
});
playAllFromHere({
item: item || {},
item: item,
items: items || [],
serverId: item?.ServerId
}).catch(err => {
@@ -174,7 +174,7 @@ const MoreCommandsButton: FC<MoreCommandsButtonProps> = ({
});
} else if (result.command === 'queueallfromhere') {
playAllFromHere({
item: item || {},
item: item,
items: items || [],
serverId: item?.ServerId,
queue: true
@@ -47,7 +47,7 @@ export function getImageUrl(item: ItemDto, options: ImageOptions = {}) {
const itemId = item.PrimaryImageItemId || item.Id;
if (itemId && item.ImageTags?.[options.type]) {
options.tag = item.ImageTags[options.type];
options.tag = item.ImageTags[options.type] ?? undefined;
return ServerConnections.getApiClient(item.ServerId).getScaledImageUrl(itemId, options);
}
@@ -7,7 +7,7 @@ import { getItemTextLines } from './itemText';
describe('getItemTextLines', () => {
it('Should return undefined if item is invalid', () => {
let lines = getItemTextLines({});
let lines = getItemTextLines({} as ItemDto);
expect(lines).toBeUndefined();
lines = getItemTextLines(null);
expect(lines).toBeUndefined();
@@ -16,9 +16,9 @@ describe('getItemTextLines', () => {
});
it('Should return the name and index number', () => {
const item: ItemDto = {
const item = {
Name: 'Item Name'
};
} as ItemDto;
let lines = getItemTextLines(item);
expect(lines).toBeDefined();
expect(lines).toHaveLength(1);
@@ -39,13 +39,13 @@ describe('getItemTextLines', () => {
});
it('Should add artist names', () => {
let item: ItemDto = {
let item = {
Name: 'Item Name',
ArtistItems: [
{ Name: 'Artist 1' },
{ Name: 'Artist 2' }
]
};
} as ItemDto;
let lines = getItemTextLines(item);
expect(lines).toBeDefined();
expect(lines).toHaveLength(2);
@@ -58,7 +58,7 @@ describe('getItemTextLines', () => {
'Artist 1',
'Artist 2'
]
};
} as ItemDto;
lines = getItemTextLines(item);
expect(lines).toBeDefined();
expect(lines).toHaveLength(2);
@@ -67,10 +67,10 @@ describe('getItemTextLines', () => {
});
it('Should add album or series name', () => {
let item: ItemDto = {
let item = {
Name: 'Item Name',
SeriesName: 'Series'
};
} as ItemDto;
let lines = getItemTextLines(item);
expect(lines).toBeDefined();
expect(lines).toHaveLength(2);
@@ -80,7 +80,7 @@ describe('getItemTextLines', () => {
item = {
Name: 'Item Name',
Album: 'Album'
};
} as ItemDto;
lines = getItemTextLines(item);
expect(lines).toBeDefined();
expect(lines).toHaveLength(2);
@@ -92,7 +92,7 @@ describe('getItemTextLines', () => {
const item = {
Name: 'Item Name',
ProductionYear: 2025
};
} as ItemDto;
const lines = getItemTextLines(item);
expect(lines).toBeDefined();
expect(lines).toHaveLength(2);
+2 -2
View File
@@ -7,11 +7,11 @@ import type { CardOptions } from 'types/cardOptions';
import type { ItemDto } from 'types/base/models/item-dto';
interface CardProps {
item?: ItemDto;
item: ItemDto;
cardOptions: CardOptions;
}
const Card: FC<CardProps> = ({ item = {}, cardOptions }) => {
const Card: FC<CardProps> = ({ item, cardOptions }) => {
const { getCardWrapperProps, getCardBoxProps } = useCard({ item, cardOptions } );
const cardWrapperProps = getCardWrapperProps();
const cardBoxProps = getCardBoxProps();
@@ -217,6 +217,7 @@ function getParentTitle(
.map(artist => {
const artistItem: ItemDto = {
...artist,
Key: artist.Id ?? "",
Type: BaseItemKind.MusicArtist,
IsFolder: true
};
@@ -511,6 +512,7 @@ function getChannelName(item: ItemDto) {
if (item.ChannelId) {
return getTextActionButton(
{
Key: item.ChannelId,
Id: item.ChannelId,
ServerId: item.ServerId,
Name: item.ChannelName,
@@ -598,6 +600,7 @@ function getMediaTitle(cardOptions: CardOptions, item: ItemDto): TextLine {
});
return getTextActionButton({
Key: item.Id ?? "",
Id: item.Id,
ServerId: item.ServerId,
Name: name,
@@ -620,6 +623,7 @@ function getParentTitleOrTitle(
) {
if (item.SeriesId) {
return getTextActionButton({
Key: item.SeriesId,
Id: item.SeriesId,
ServerId: item.ServerId,
Name: item.SeriesName,
+1 -1
View File
@@ -39,7 +39,7 @@ function useCard({ item, cardOptions }: UseCardProps) {
shape
});
const imgUrl = imgInfo.imgUrl;
const blurhash = imgInfo.blurhash;
const blurhash = imgInfo.blurhash ?? undefined;
const forceName = imgInfo.forceName;
const coveredImage = cardOptions.coverImage ?? imgInfo.coverImage;
const overlayText = cardOptions.overlayText;
@@ -111,7 +111,7 @@ function shouldShowPreferBanner(
}
function shouldShowPreferDisc(
imageTagsDisc: string | undefined,
imageTagsDisc: string | null | undefined,
cardOptions: CardOptions
): boolean {
return cardOptions.preferDisc === true && Boolean(imageTagsDisc);
+5 -5
View File
@@ -154,8 +154,8 @@ const useIndicator = (item: ItemDto) => {
const getPlayedIndicator = () => {
if (enablePlayedIndicator(item)) {
const userData = item.UserData || {};
if (userData.UnplayedItemCount) {
const userData = item.UserData;
if (userData?.UnplayedItemCount) {
return (
<Box className='countIndicator indicator unplayedItemCount'>
{formatCountIndicator(userData.UnplayedItemCount)}
@@ -164,9 +164,9 @@ const useIndicator = (item: ItemDto) => {
}
if (
(userData.PlayedPercentage
&& userData.PlayedPercentage >= 100)
|| userData.Played
(userData?.PlayedPercentage
&& userData?.PlayedPercentage >= 100)
|| userData?.Played
) {
return (
<Box className='playedIndicator indicator'>
@@ -28,7 +28,7 @@ interface ListImageContainerProps {
}
const ListImageContainer: FC<ListImageContainerProps> = ({
item = {},
item,
listOptions,
action,
isLargeStyle,
@@ -73,7 +73,7 @@ const ListImageContainer: FC<ListImageContainerProps> = ({
className={imageClass}
>
<Media item={item} imgUrl={imgUrl} blurhash={blurhash} defaultCardImageIcon={defaultCardImageIcon} />
<Media item={item} imgUrl={imgUrl} blurhash={blurhash ?? undefined} defaultCardImageIcon={defaultCardImageIcon} />
{disableIndicators !== true && mediaSourceIndicator}
@@ -22,7 +22,7 @@ interface ListItemBodyProps {
}
const ListItemBody: FC<ListItemBodyProps> = ({
item = {},
item,
listOptions = {},
action,
isLargeStyle,
@@ -95,7 +95,7 @@ function createPlaylist(dlg: DialogElement) {
return getPlaylistsApi(api)
.createPlaylist({
createPlaylistDto: {
Name: name,
Name: name ?? "",
IsPublic: dlg.querySelector<HTMLInputElement>('#chkPlaylistPublic')?.checked,
Ids: itemIds?.split(','),
UserId: apiClient.getCurrentUserId()