import NodePlexAPI from 'plex-api'; import { getSettings, PlexSettings } from '../lib/settings'; export interface PlexLibraryItem { ratingKey: string; parentRatingKey?: string; grandparentRatingKey?: string; title: string; guid: string; parentGuid?: string; grandparentGuid?: string; addedAt: number; updatedAt: number; type: 'movie' | 'show' | 'season' | 'episode'; } interface PlexLibraryResponse { MediaContainer: { Metadata: PlexLibraryItem[]; }; } export interface PlexLibrary { type: 'show' | 'movie'; key: string; title: string; agent: string; } interface PlexLibrariesResponse { MediaContainer: { Directory: PlexLibrary[]; }; } export interface PlexMetadata { ratingKey: string; parentRatingKey?: string; guid: string; type: 'movie' | 'show' | 'season'; title: string; Guid: { id: string; }[]; Children?: { size: 12; Metadata: PlexMetadata[]; }; index: number; parentIndex?: number; leafCount: number; viewedLeafCount: number; addedAt: number; updatedAt: number; Media: Media[]; } interface Media { id: number; duration: number; bitrate: number; width: number; height: number; aspectRatio: number; audioChannels: number; audioCodec: string; videoCodec: string; videoResolution: string; container: string; videoFrameRate: string; videoProfile: string; } interface PlexMetadataResponse { MediaContainer: { Metadata: PlexMetadata[]; }; } class PlexAPI { private plexClient: NodePlexAPI; constructor({ plexToken, plexSettings, timeout, }: { plexToken?: string; plexSettings?: PlexSettings; timeout?: number; }) { const settings = getSettings(); let settingsPlex: PlexSettings | undefined; plexSettings ? (settingsPlex = plexSettings) : (settingsPlex = getSettings().plex); this.plexClient = new NodePlexAPI({ hostname: settingsPlex.ip, port: settingsPlex.port, https: settingsPlex.useSsl, timeout: timeout, token: plexToken, authenticator: { authenticate: ( _plexApi, cb: (err?: string, token?: string) => void ) => { if (!plexToken) { return cb('Plex Token not found!'); } cb(undefined, plexToken); }, }, // requestOptions: { // includeChildren: 1, // }, options: { identifier: settings.clientId, product: 'Overseerr', deviceName: settings.main.applicationTitle, platform: 'Overseerr', }, }); } // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types public async getStatus() { return await this.plexClient.query('/'); } public async getLibraries(): Promise { const response = await this.plexClient.query( '/library/sections' ); return response.MediaContainer.Directory; } public async getLibraryContents(id: string): Promise { const response = await this.plexClient.query( `/library/sections/${id}/all` ); return response.MediaContainer.Metadata; } public async getMetadata( key: string, options: { includeChildren?: boolean } = {} ): Promise { const response = await this.plexClient.query( `/library/metadata/${key}${ options.includeChildren ? '?includeChildren=1' : '' }` ); return response.MediaContainer.Metadata[0]; } public async getChildrenMetadata(key: string): Promise { const response = await this.plexClient.query( `/library/metadata/${key}/children` ); return response.MediaContainer.Metadata; } public async getRecentlyAdded(id: string): Promise { const response = await this.plexClient.query( `/library/sections/${id}/recentlyAdded` ); return response.MediaContainer.Metadata; } } export default PlexAPI;