Compare commits

...

1 Commits

Author SHA1 Message Date
fallenbagel
c51479cc8e fix(api): make item endpoints user-independent
Fix Jellyfin/Emby item retrieval to work properly with api tokens by using user-independent item
endpoints. This will resolve the issue where disabled libraries for jellyseerr owner's would fail to
scan even when we migrated to API tokens due to using the user-dependent item view endpoints.
2025-03-01 02:28:32 +08:00

View File

@@ -1,7 +1,9 @@
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
import ExternalAPI from '@server/api/externalapi'; import ExternalAPI from '@server/api/externalapi';
import { ApiErrorCode } from '@server/constants/error'; import { ApiErrorCode } from '@server/constants/error';
import { MediaServerType } from '@server/constants/server';
import availabilitySync from '@server/lib/availabilitySync'; import availabilitySync from '@server/lib/availabilitySync';
import { getSettings } from '@server/lib/settings';
import logger from '@server/logger'; import logger from '@server/logger';
import { ApiError } from '@server/types/error'; import { ApiError } from '@server/types/error';
import { getAppVersion } from '@server/utils/appVersion'; import { getAppVersion } from '@server/utils/appVersion';
@@ -92,14 +94,22 @@ export interface JellyfinLibraryItemExtended extends JellyfinLibraryItem {
DateCreated?: string; DateCreated?: string;
} }
export interface JellyfinItemsReponse {
Items: JellyfinLibraryItemExtended[];
TotalRecordCount: number;
StartIndex: number;
}
class JellyfinAPI extends ExternalAPI { class JellyfinAPI extends ExternalAPI {
private userId?: string; private userId?: string;
private mediaServerType: MediaServerType;
constructor( constructor(
jellyfinHost: string, jellyfinHost: string,
authToken?: string | null, authToken?: string | null,
deviceId?: string | null deviceId?: string | null
) { ) {
const settings = getSettings();
let authHeaderVal: string; let authHeaderVal: string;
if (authToken) { if (authToken) {
authHeaderVal = `MediaBrowser Client="Jellyseerr", Device="Jellyseerr", DeviceId="${deviceId}", Version="${getAppVersion()}", Token="${authToken}"`; authHeaderVal = `MediaBrowser Client="Jellyseerr", Device="Jellyseerr", DeviceId="${deviceId}", Version="${getAppVersion()}", Token="${authToken}"`;
@@ -116,6 +126,8 @@ class JellyfinAPI extends ExternalAPI {
}, },
} }
); );
this.mediaServerType = settings.main.mediaServerType;
} }
public async login( public async login(
@@ -296,18 +308,15 @@ class JellyfinAPI extends ExternalAPI {
public async getLibraryContents(id: string): Promise<JellyfinLibraryItem[]> { public async getLibraryContents(id: string): Promise<JellyfinLibraryItem[]> {
try { try {
const libraryItemsResponse = await this.get<any>( const libraryItemsResponse = await this.get<any>(`/Items`, {
`/Users/${this.userId}/Items`, SortBy: 'SortName',
{ SortOrder: 'Ascending',
SortBy: 'SortName', IncludeItemTypes: 'Series,Movie,Others',
SortOrder: 'Ascending', Recursive: 'true',
IncludeItemTypes: 'Series,Movie,Others', StartIndex: '0',
Recursive: 'true', ParentId: id,
StartIndex: '0', collapseBoxSetItems: 'false',
ParentId: id, });
collapseBoxSetItems: 'false',
}
);
return libraryItemsResponse.Items.filter( return libraryItemsResponse.Items.filter(
(item: JellyfinLibraryItem) => item.LocationType !== 'Virtual' (item: JellyfinLibraryItem) => item.LocationType !== 'Virtual'
@@ -324,13 +333,22 @@ class JellyfinAPI extends ExternalAPI {
public async getRecentlyAdded(id: string): Promise<JellyfinLibraryItem[]> { public async getRecentlyAdded(id: string): Promise<JellyfinLibraryItem[]> {
try { try {
const itemResponse = await this.get<any>( const endpoint =
`/Users/${this.userId}/Items/Latest`, this.mediaServerType === MediaServerType.JELLYFIN
{ ? `/Items/Latest`
Limit: '12', : `/Users/${this.userId}/Items/Latest`;
ParentId: id,
} const baseParams = {
); Limit: '12',
ParentId: id,
};
const params =
this.mediaServerType === MediaServerType.JELLYFIN
? { ...baseParams, userId: this.userId ?? `Me` }
: baseParams;
const itemResponse = await this.get<any>(endpoint, params);
return itemResponse; return itemResponse;
} catch (e) { } catch (e) {
@@ -347,11 +365,12 @@ class JellyfinAPI extends ExternalAPI {
id: string id: string
): Promise<JellyfinLibraryItemExtended | undefined> { ): Promise<JellyfinLibraryItemExtended | undefined> {
try { try {
const itemResponse = await this.get<any>( const itemResponse = await this.get<JellyfinItemsReponse>(`/Items`, {
`/Users/${this.userId}/Items/${id}` ids: id,
); fields: 'ProviderIds,MediaSources,Width,Height,IsHD,DateCreated',
});
return itemResponse; return itemResponse.Items?.[0];
} catch (e) { } catch (e) {
if (availabilitySync.running) { if (availabilitySync.running) {
if (e.cause?.status === 500) { if (e.cause?.status === 500) {