From 4d896fe8f27e12349660a6bd4113a31d933244cd Mon Sep 17 00:00:00 2001 From: Peaches_MLG Date: Sun, 11 Jan 2026 11:15:20 +0000 Subject: [PATCH 1/4] Fixed Jellyfin SDK type errors --- package-lock.json | 16 +++++++------- package.json | 4 ++-- .../features/storage/utils/space.test.ts | 21 ++++++++++--------- .../components/buttons/MoreCommandsButton.tsx | 6 +++--- .../stable/features/playback/utils/image.ts | 2 +- .../features/playback/utils/itemText.test.ts | 20 +++++++++--------- src/components/cardbuilder/Card/Card.tsx | 4 ++-- src/components/cardbuilder/Card/cardHelper.ts | 4 ++++ src/components/cardbuilder/Card/useCard.ts | 2 +- .../cardbuilder/Card/useCardImageUrl.ts | 2 +- src/components/indicators/useIndicator.tsx | 10 ++++----- .../listview/List/ListImageContainer.tsx | 4 ++-- src/components/listview/List/ListItemBody.tsx | 2 +- .../playlisteditor/playlisteditor.ts | 2 +- 14 files changed, 52 insertions(+), 47 deletions(-) diff --git a/package-lock.json b/package-lock.json index 10969cff6..aaf403a2f 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", @@ -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": { diff --git a/package.json b/package.json index 25c3e9b39..edce46452 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", @@ -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." } } 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..e7bcba30a 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..6d77de988 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() From b76582907cfe7db15765564f05a4e5b1c0e79cd0 Mon Sep 17 00:00:00 2001 From: Peaches_MLG Date: Sun, 11 Jan 2026 11:44:20 +0000 Subject: [PATCH 2/4] Revert npm restrictions --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index edce46452..55ae1d58f 100644 --- a/package.json +++ b/package.json @@ -169,7 +169,7 @@ }, "engines": { "node": ">=20.0.0", - "npm": ">=9.6.4", + "npm": ">=9.6.4 <11.0.0", "yarn": "YARN NO LONGER USED - use npm instead." } } From 6c4dee0ac0b6ff1e26377894815cf5946591ba14 Mon Sep 17 00:00:00 2001 From: Peaches_MLG Date: Sun, 11 Jan 2026 11:49:43 +0000 Subject: [PATCH 3/4] Linting Issues --- src/components/cardbuilder/Card/cardHelper.ts | 4 ++-- src/components/playlisteditor/playlisteditor.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/cardbuilder/Card/cardHelper.ts b/src/components/cardbuilder/Card/cardHelper.ts index e7bcba30a..9fb02751d 100644 --- a/src/components/cardbuilder/Card/cardHelper.ts +++ b/src/components/cardbuilder/Card/cardHelper.ts @@ -217,7 +217,7 @@ function getParentTitle( .map(artist => { const artistItem: ItemDto = { ...artist, - Key: artist.Id ?? "", + Key: artist.Id ?? '', Type: BaseItemKind.MusicArtist, IsFolder: true }; @@ -600,7 +600,7 @@ function getMediaTitle(cardOptions: CardOptions, item: ItemDto): TextLine { }); return getTextActionButton({ - Key: item.Id ?? "", + Key: item.Id ?? '', Id: item.Id, ServerId: item.ServerId, Name: name, diff --git a/src/components/playlisteditor/playlisteditor.ts b/src/components/playlisteditor/playlisteditor.ts index 6d77de988..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() From 25c65cb80b2dc0df441cd3b73659ba123c40a0eb Mon Sep 17 00:00:00 2001 From: Peaches_MLG Date: Fri, 23 Jan 2026 18:49:55 +0000 Subject: [PATCH 4/4] Fix Package lock --- package-lock.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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"