import logger from '@server/logger'; import ServarrBase from './base'; export interface LidarrArtistOptions { artistName: string; qualityProfileId: number; metadataProfileId: number; tags: number[]; rootFolderPath: string; foreignArtistId: string; // MusicBrainz ID monitored?: boolean; searchNow?: boolean; } export interface LidarrAlbumOptions { foreignAlbumId: string; // MusicBrainz Album ID monitored?: boolean; searchNow?: boolean; } export interface LidarrArtist { id: number; artistName: string; foreignArtistId: string; monitored: boolean; path: string; qualityProfileId: number; metadataProfileId: number; rootFolderPath: string; tags: number[]; added: string; status: string; ended: boolean; artistType: string; disambiguation: string; images: { coverType: string; url: string; remoteUrl: string; }[]; genres: string[]; statistics?: { albumCount: number; trackFileCount: number; trackCount: number; totalTrackCount: number; sizeOnDisk: number; percentOfTracks: number; }; } export interface LidarrAlbum { id: number; title: string; foreignAlbumId: string; artistId: number; monitored: boolean; albumType: string; duration: number; releaseDate: string; genres: string[]; images: { coverType: string; url: string; remoteUrl: string; }[]; artist: LidarrArtist; statistics?: { trackFileCount: number; trackCount: number; totalTrackCount: number; sizeOnDisk: number; percentOfTracks: number; }; } export interface MetadataProfile { id: number; name: string; } class LidarrAPI extends ServarrBase<{ artistId: number }> { constructor({ url, apiKey }: { url: string; apiKey: string }) { super({ url, apiKey, cacheName: 'lidarr', apiName: 'Lidarr' }); } public getArtists = async (): Promise => { try { const response = await this.axios.get('/artist'); return response.data; } catch (e) { throw new Error(`[Lidarr] Failed to retrieve artists: ${e.message}`, { cause: e, }); } }; public getArtist = async ({ id }: { id: number }): Promise => { try { const response = await this.axios.get(`/artist/${id}`); return response.data; } catch (e) { throw new Error(`[Lidarr] Failed to retrieve artist: ${e.message}`, { cause: e, }); } }; public async getArtistByMbId(mbId: string): Promise { try { const artists = await this.getArtists(); return artists.find((a) => a.foreignArtistId === mbId) || null; } catch (e) { logger.error('Error retrieving artist by MusicBrainz ID', { label: 'Lidarr API', errorMessage: e.message, mbId, }); return null; } } public searchArtist = async (term: string): Promise => { try { const response = await this.axios.get( '/artist/lookup', { params: { term } } ); return response.data; } catch (e) { throw new Error(`[Lidarr] Failed to search artists: ${e.message}`, { cause: e, }); } }; public searchAlbum = async (term: string): Promise => { try { const response = await this.axios.get('/album/lookup', { params: { term }, }); return response.data; } catch (e) { throw new Error(`[Lidarr] Failed to search albums: ${e.message}`, { cause: e, }); } }; public getAlbums = async (artistId: number): Promise => { try { const response = await this.axios.get('/album', { params: { artistId }, }); return response.data; } catch (e) { throw new Error(`[Lidarr] Failed to retrieve albums: ${e.message}`, { cause: e, }); } }; public getAlbum = async ({ id }: { id: number }): Promise => { try { const response = await this.axios.get(`/album/${id}`); return response.data; } catch (e) { throw new Error(`[Lidarr] Failed to retrieve album: ${e.message}`, { cause: e, }); } }; public addArtist = async ( options: LidarrArtistOptions ): Promise => { try { // Check if artist already exists const existing = await this.getArtistByMbId(options.foreignArtistId); if (existing) { logger.info('Artist already exists in Lidarr.', { label: 'Lidarr', artistId: existing.id, artistName: existing.artistName, }); return existing; } // Look up artist details const lookupResults = await this.searchArtist( `lidarr:${options.foreignArtistId}` ); const lookupArtist = lookupResults[0]; const response = await this.axios.post('/artist', { ...lookupArtist, artistName: options.artistName, qualityProfileId: options.qualityProfileId, metadataProfileId: options.metadataProfileId, rootFolderPath: options.rootFolderPath, monitored: options.monitored ?? true, tags: options.tags, addOptions: { monitor: 'all', searchForMissingAlbums: options.searchNow ?? true, }, }); if (response.data.id) { logger.info('Lidarr accepted request', { label: 'Lidarr', artistId: response.data.id, artistName: response.data.artistName, }); } return response.data; } catch (e) { logger.error('Failed to add artist to Lidarr', { label: 'Lidarr', errorMessage: e.message, options, }); throw new Error('Failed to add artist to Lidarr', { cause: e }); } }; public async searchArtistCommand(artistId: number): Promise { logger.info('Executing artist search command', { label: 'Lidarr API', artistId, }); try { await this.runCommand('ArtistSearch', { artistId }); } catch (e) { logger.error('Something went wrong executing Lidarr artist search.', { label: 'Lidarr API', errorMessage: e.message, artistId, }); } } public getMetadataProfiles = async (): Promise => { try { const response = await this.axios.get('/metadataprofile'); return response.data; } catch (e) { throw new Error( `[Lidarr] Failed to retrieve metadata profiles: ${e.message}`, { cause: e } ); } }; public removeArtist = async (artistId: number): Promise => { try { await this.axios.delete(`/artist/${artistId}`, { params: { deleteFiles: true, addImportListExclusion: false }, }); logger.info(`[Lidarr] Removed artist ${artistId}`); } catch (e) { throw new Error(`[Lidarr] Failed to remove artist: ${e.message}`, { cause: e, }); } }; } export default LidarrAPI;