diff --git a/package-lock.json b/package-lock.json index 5cd9aa0cf..74b6e332a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", @@ -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" diff --git a/package.json b/package.json index 1faddbadd..7d1e279e3 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/apps/dashboard/features/storage/utils/space.test.ts b/src/apps/dashboard/features/storage/utils/space.test.ts index f234a9fd0..6296d2941 100644 --- a/src/apps/dashboard/features/storage/utils/space.test.ts +++ b/src/apps/dashboard/features/storage/utils/space.test.ts @@ -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); }); }); diff --git a/src/apps/experimental/features/details/components/buttons/MoreCommandsButton.tsx b/src/apps/experimental/features/details/components/buttons/MoreCommandsButton.tsx index 6d623f0d9..1ced8015b 100644 --- a/src/apps/experimental/features/details/components/buttons/MoreCommandsButton.tsx +++ b/src/apps/experimental/features/details/components/buttons/MoreCommandsButton.tsx @@ -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 = ({ serverId: item?.ServerId }); playAllFromHere({ - item: item || {}, + item: item, items: items || [], serverId: item?.ServerId }).catch(err => { @@ -174,7 +174,7 @@ const MoreCommandsButton: FC = ({ }); } else if (result.command === 'queueallfromhere') { playAllFromHere({ - item: item || {}, + item: item, items: items || [], serverId: item?.ServerId, queue: true diff --git a/src/apps/stable/features/playback/utils/image.ts b/src/apps/stable/features/playback/utils/image.ts index 4497846d9..6eb5831c3 100644 --- a/src/apps/stable/features/playback/utils/image.ts +++ b/src/apps/stable/features/playback/utils/image.ts @@ -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); } diff --git a/src/apps/stable/features/playback/utils/itemText.test.ts b/src/apps/stable/features/playback/utils/itemText.test.ts index 53c8d707e..ee170c1fe 100644 --- a/src/apps/stable/features/playback/utils/itemText.test.ts +++ b/src/apps/stable/features/playback/utils/itemText.test.ts @@ -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); diff --git a/src/components/cardbuilder/Card/Card.tsx b/src/components/cardbuilder/Card/Card.tsx index 2b5314d4c..805aa1ff0 100644 --- a/src/components/cardbuilder/Card/Card.tsx +++ b/src/components/cardbuilder/Card/Card.tsx @@ -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 = ({ item = {}, cardOptions }) => { +const Card: FC = ({ item, cardOptions }) => { const { getCardWrapperProps, getCardBoxProps } = useCard({ item, cardOptions } ); const cardWrapperProps = getCardWrapperProps(); const cardBoxProps = getCardBoxProps(); diff --git a/src/components/cardbuilder/Card/cardHelper.ts b/src/components/cardbuilder/Card/cardHelper.ts index 08adcf7ad..9fb02751d 100644 --- a/src/components/cardbuilder/Card/cardHelper.ts +++ b/src/components/cardbuilder/Card/cardHelper.ts @@ -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, diff --git a/src/components/cardbuilder/Card/useCard.ts b/src/components/cardbuilder/Card/useCard.ts index afa3081ee..868800fd0 100644 --- a/src/components/cardbuilder/Card/useCard.ts +++ b/src/components/cardbuilder/Card/useCard.ts @@ -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; diff --git a/src/components/cardbuilder/Card/useCardImageUrl.ts b/src/components/cardbuilder/Card/useCardImageUrl.ts index 4fe68fac5..49137ea8f 100644 --- a/src/components/cardbuilder/Card/useCardImageUrl.ts +++ b/src/components/cardbuilder/Card/useCardImageUrl.ts @@ -111,7 +111,7 @@ function shouldShowPreferBanner( } function shouldShowPreferDisc( - imageTagsDisc: string | undefined, + imageTagsDisc: string | null | undefined, cardOptions: CardOptions ): boolean { return cardOptions.preferDisc === true && Boolean(imageTagsDisc); diff --git a/src/components/indicators/useIndicator.tsx b/src/components/indicators/useIndicator.tsx index 993da54b5..374e3785c 100644 --- a/src/components/indicators/useIndicator.tsx +++ b/src/components/indicators/useIndicator.tsx @@ -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 ( {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 ( diff --git a/src/components/listview/List/ListImageContainer.tsx b/src/components/listview/List/ListImageContainer.tsx index f5839c127..55c633e5b 100644 --- a/src/components/listview/List/ListImageContainer.tsx +++ b/src/components/listview/List/ListImageContainer.tsx @@ -28,7 +28,7 @@ interface ListImageContainerProps { } const ListImageContainer: FC = ({ - item = {}, + item, listOptions, action, isLargeStyle, @@ -73,7 +73,7 @@ const ListImageContainer: FC = ({ className={imageClass} > - + {disableIndicators !== true && mediaSourceIndicator} diff --git a/src/components/listview/List/ListItemBody.tsx b/src/components/listview/List/ListItemBody.tsx index 19ee5ca99..5cac78bc0 100644 --- a/src/components/listview/List/ListItemBody.tsx +++ b/src/components/listview/List/ListItemBody.tsx @@ -22,7 +22,7 @@ interface ListItemBodyProps { } const ListItemBody: FC = ({ - item = {}, + item, listOptions = {}, action, isLargeStyle, diff --git a/src/components/playlisteditor/playlisteditor.ts b/src/components/playlisteditor/playlisteditor.ts index ff5c83a4b..3b0ed49ac 100644 --- a/src/components/playlisteditor/playlisteditor.ts +++ b/src/components/playlisteditor/playlisteditor.ts @@ -95,7 +95,7 @@ function createPlaylist(dlg: DialogElement) { return getPlaylistsApi(api) .createPlaylist({ createPlaylistDto: { - Name: name, + Name: name ?? '', IsPublic: dlg.querySelector('#chkPlaylistPublic')?.checked, Ids: itemIds?.split(','), UserId: apiClient.getCurrentUserId()