From 56f05ddafc6201a8083a38308b247441f50331ec Mon Sep 17 00:00:00 2001 From: ChrisScott9456 Date: Sun, 28 Jun 2026 20:19:12 -0400 Subject: [PATCH] Replace startScan (full library refresh) with refreshItem (individual item refresh) for jellyfin metadata editing --- src/renderer/api/controller.ts | 6 +++--- src/renderer/api/jellyfin/jellyfin-api.ts | 8 ++++--- .../api/jellyfin/jellyfin-controller.ts | 21 ++++++++++--------- .../api/navidrome/navidrome-controller.ts | 2 +- .../api/subsonic/subsonic-controller.ts | 4 ++-- .../tag-editor/hooks/use-metadata-editor.ts | 5 ++++- src/shared/types/domain-types.ts | 15 ++++++------- 7 files changed, 32 insertions(+), 29 deletions(-) diff --git a/src/renderer/api/controller.ts b/src/renderer/api/controller.ts index d72f95aab..3398fd67a 100644 --- a/src/renderer/api/controller.ts +++ b/src/renderer/api/controller.ts @@ -864,15 +864,15 @@ export const controller: GeneralController = { server.type, )?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } })); }, - startScan(args) { + refreshItems(args) { const server = getServerById(args.apiClientProps.serverId); if (!server) { - throw new Error(`${i18n.t('error.apiRouteError')}: startScan`); + throw new Error(`${i18n.t('error.apiRouteError')}: refreshItems`); } return apiController( - 'startScan', + 'refreshItems', server.type, )?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } })); }, diff --git a/src/renderer/api/jellyfin/jellyfin-api.ts b/src/renderer/api/jellyfin/jellyfin-api.ts index 57872e125..e2f0797ff 100644 --- a/src/renderer/api/jellyfin/jellyfin-api.ts +++ b/src/renderer/api/jellyfin/jellyfin-api.ts @@ -365,11 +365,13 @@ export const contract = c.router({ 400: jfType._response.error, }, }, - startScan: { + refreshItem: { body: z.null(), method: 'POST', - path: 'Library/Refresh', - query: z.object({}), + path: 'Items/:id/Refresh', + query: z.object({ + MetadataRefreshMode: z.string().optional(), + }), responses: { 204: z.null(), 400: jfType._response.error, diff --git a/src/renderer/api/jellyfin/jellyfin-controller.ts b/src/renderer/api/jellyfin/jellyfin-controller.ts index f736c3fff..4e1e98c9c 100644 --- a/src/renderer/api/jellyfin/jellyfin-controller.ts +++ b/src/renderer/api/jellyfin/jellyfin-controller.ts @@ -1931,17 +1931,18 @@ export const JellyfinController: InternalControllerEndpoint = { return null; }, - startScan: async (args) => { - const { apiClientProps } = args; + refreshItems: async (args) => { + const { apiClientProps, query } = args; - const res = await jfApiClient(apiClientProps).startScan({ - body: null, - query: {}, - }); - - if (res.status !== 204) { - throw new Error('Failed to start scan'); - } + await Promise.all( + query.ids.map((id) => + jfApiClient(apiClientProps).refreshItem({ + body: null, + params: { id }, + query: { MetadataRefreshMode: 'FullRefresh' }, + }), + ), + ); return null; }, diff --git a/src/renderer/api/navidrome/navidrome-controller.ts b/src/renderer/api/navidrome/navidrome-controller.ts index 3b0cd021a..bfb8bc545 100644 --- a/src/renderer/api/navidrome/navidrome-controller.ts +++ b/src/renderer/api/navidrome/navidrome-controller.ts @@ -1261,7 +1261,7 @@ export const NavidromeController: InternalControllerEndpoint = { id: res.body.data.id, }; }, - startScan: SubsonicController.startScan, + refreshItems: SubsonicController.refreshItems, updateInternetRadioStation: async (args) => { const { apiClientProps, body, query } = args; diff --git a/src/renderer/api/subsonic/subsonic-controller.ts b/src/renderer/api/subsonic/subsonic-controller.ts index 3d75d0569..39e08940d 100644 --- a/src/renderer/api/subsonic/subsonic-controller.ts +++ b/src/renderer/api/subsonic/subsonic-controller.ts @@ -2461,7 +2461,7 @@ export const SubsonicController: InternalControllerEndpoint = { return null; }, - startScan: async (args) => { + refreshItems: async (args) => { const { apiClientProps } = args; const res = await ssApiClient(apiClientProps).startScan({ query: {} }); @@ -2470,7 +2470,7 @@ export const SubsonicController: InternalControllerEndpoint = { throw new Error('Failed to start scan'); } - return res.body.scanStatus; + return null; }, updateInternetRadioStation: async (args) => { const { apiClientProps, body, query } = args; diff --git a/src/renderer/features/tag-editor/hooks/use-metadata-editor.ts b/src/renderer/features/tag-editor/hooks/use-metadata-editor.ts index 48099eaf2..37d96e1fb 100644 --- a/src/renderer/features/tag-editor/hooks/use-metadata-editor.ts +++ b/src/renderer/features/tag-editor/hooks/use-metadata-editor.ts @@ -286,7 +286,10 @@ export const useMetadataEditor = ({ browser, songs: songsProp, utils }: UseMetad if (rescan && server) { try { - await controller.startScan({ apiClientProps: { serverId: server.id } }); + await controller.refreshItems({ + apiClientProps: { serverId: server.id }, + query: { ids: resolvedSongs.map((s) => s.id) }, + }); toast.success({ message: t('page.itemDetail.rescanStarted') }); } catch { // non-fatal diff --git a/src/shared/types/domain-types.ts b/src/shared/types/domain-types.ts index 3cf5d1793..4461d050f 100644 --- a/src/shared/types/domain-types.ts +++ b/src/shared/types/domain-types.ts @@ -1418,14 +1418,9 @@ export type SearchSongsQuery = { songStartIndex?: number; }; -export type StartScanArgs = BaseEndpointArgs; +export type RefreshItemsArgs = BaseEndpointArgs & { query: { ids: string[] } }; -export type StartScanResponse = null | { - count: number; - folderCount: number; - lastScan?: string; - scanning: boolean; -}; +export type RefreshItemsResponse = null; export type SynchronizedLyricsArray = Array<[number, string]>; @@ -1532,6 +1527,7 @@ export type ControllerEndpoint = { getUserInfo: (args: UserInfoArgs) => Promise; getUserList?: (args: UserListArgs) => Promise; movePlaylistItem?: (args: MoveItemArgs) => Promise; + refreshItems: (args: RefreshItemsArgs) => Promise; removeFromPlaylist: (args: RemoveFromPlaylistArgs) => Promise; replacePlaylist: (args: ReplacePlaylistArgs) => Promise; savePlayQueue: (args: SaveQueueArgs) => Promise; @@ -1540,7 +1536,6 @@ export type ControllerEndpoint = { setPlaylistSongs: (args: SetPlaylistSongsArgs) => Promise; setRating?: (args: SetRatingArgs) => Promise; shareItem?: (args: ShareItemArgs) => Promise; - startScan: (args: StartScanArgs) => Promise; updateInternetRadioStation: ( args: UpdateInternetRadioStationArgs, ) => Promise; @@ -1695,6 +1690,9 @@ export type InternalControllerEndpoint = { getUserInfo: (args: ReplaceApiClientProps) => Promise; getUserList?: (args: ReplaceApiClientProps) => Promise; movePlaylistItem?: (args: ReplaceApiClientProps) => Promise; + refreshItems: ( + args: ReplaceApiClientProps, + ) => Promise; removeFromPlaylist: ( args: ReplaceApiClientProps, ) => Promise; @@ -1709,7 +1707,6 @@ export type InternalControllerEndpoint = { ) => Promise; setRating?: (args: ReplaceApiClientProps) => Promise; shareItem?: (args: ReplaceApiClientProps) => Promise; - startScan: (args: ReplaceApiClientProps) => Promise; updateInternetRadioStation: ( args: ReplaceApiClientProps, ) => Promise;