parent
14b9cb610c
commit
9385592362
@ -0,0 +1,226 @@
|
||||
import Axios, { AxiosInstance } from 'axios';
|
||||
import logger from '../logger';
|
||||
|
||||
interface SonarrSeason {
|
||||
seasonNumber: number;
|
||||
monitored: boolean;
|
||||
}
|
||||
|
||||
interface SonarrSeries {
|
||||
title: string;
|
||||
sortTitle: string;
|
||||
seasonCount: number;
|
||||
status: string;
|
||||
overview: string;
|
||||
network: string;
|
||||
airTime: string;
|
||||
images: {
|
||||
coverType: string;
|
||||
url: string;
|
||||
}[];
|
||||
remotePoster: string;
|
||||
seasons: SonarrSeason[];
|
||||
year: number;
|
||||
path: string;
|
||||
profileId: number;
|
||||
languageProfileId: number;
|
||||
seasonFolder: boolean;
|
||||
monitored: boolean;
|
||||
useSceneNumbering: boolean;
|
||||
runtime: number;
|
||||
tvdbId: number;
|
||||
tvRageId: number;
|
||||
tvMazeId: number;
|
||||
firstAired: string;
|
||||
lastInfoSync?: string;
|
||||
seriesType: string;
|
||||
cleanTitle: string;
|
||||
imdbId: string;
|
||||
titleSlug: string;
|
||||
certification: string;
|
||||
genres: string[];
|
||||
tags: string[];
|
||||
added: string;
|
||||
ratings: {
|
||||
votes: number;
|
||||
value: number;
|
||||
};
|
||||
qualityProfileId: number;
|
||||
id?: number;
|
||||
rootFolderPath?: string;
|
||||
addOptions?: {
|
||||
ignoreEpisodesWithFiles?: boolean;
|
||||
ignoreEpisodesWithoutFiles?: boolean;
|
||||
searchForMissingEpisodes?: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
interface SonarrProfile {
|
||||
id: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
interface SonarrRootFolder {
|
||||
id: number;
|
||||
path: string;
|
||||
freeSpace: number;
|
||||
totalSpace: number;
|
||||
unmappedFolders: {
|
||||
name: string;
|
||||
path: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
interface AddSeriesOptions {
|
||||
tvdbid: number;
|
||||
title: string;
|
||||
profileId: number;
|
||||
seasons: number[];
|
||||
rootFolderPath: string;
|
||||
monitored?: boolean;
|
||||
searchNow?: boolean;
|
||||
}
|
||||
|
||||
class SonarrAPI {
|
||||
private axios: AxiosInstance;
|
||||
constructor({ url, apiKey }: { url: string; apiKey: string }) {
|
||||
this.axios = Axios.create({
|
||||
baseURL: url,
|
||||
params: {
|
||||
apikey: apiKey,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
public async getSeriesByTvdbId(id: number): Promise<SonarrSeries> {
|
||||
try {
|
||||
const response = await this.axios.get<SonarrSeries[]>('/series/lookup', {
|
||||
params: {
|
||||
term: `tvdb:${id}`,
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.data[0]) {
|
||||
throw new Error('Series not found');
|
||||
}
|
||||
|
||||
return response.data[0];
|
||||
} catch (e) {
|
||||
logger.error('Error retrieving series by tvdb ID', {
|
||||
label: 'Sonarr API',
|
||||
message: e.message,
|
||||
});
|
||||
throw new Error('Series not found');
|
||||
}
|
||||
}
|
||||
|
||||
public async addSeries(options: AddSeriesOptions): Promise<SonarrSeries> {
|
||||
try {
|
||||
const series = await this.getSeriesByTvdbId(options.tvdbid);
|
||||
|
||||
// If the series already exists, we will simply just update it
|
||||
if (series.id) {
|
||||
series.seasons = this.buildSeasonList(options.seasons, series.seasons);
|
||||
|
||||
series.addOptions = {
|
||||
ignoreEpisodesWithFiles: true,
|
||||
searchForMissingEpisodes: true,
|
||||
};
|
||||
|
||||
const newSeriesResponse = await this.axios.put<SonarrSeries>(
|
||||
'/series',
|
||||
series
|
||||
);
|
||||
|
||||
return newSeriesResponse.data;
|
||||
}
|
||||
|
||||
const createdSeriesResponse = await this.axios.post<SonarrSeries>(
|
||||
'/series',
|
||||
{
|
||||
tvdbId: options.tvdbid,
|
||||
title: options.title,
|
||||
profileId: options.profileId,
|
||||
seasons: this.buildSeasonList(
|
||||
options.seasons,
|
||||
series.seasons.map((season) => ({
|
||||
seasonNumber: season.seasonNumber,
|
||||
// We force all seasons to false if its the first request
|
||||
monitored: false,
|
||||
}))
|
||||
),
|
||||
monitored: options.monitored,
|
||||
rootFolderPath: options.rootFolderPath,
|
||||
addOptions: {
|
||||
ignoreEpisodesWithFiles: true,
|
||||
searchForMissingEpisodes: options.searchNow,
|
||||
},
|
||||
} as Partial<SonarrSeries>
|
||||
);
|
||||
|
||||
return createdSeriesResponse.data;
|
||||
} catch (e) {
|
||||
logger.error('Something went wrong adding a series to Sonarr', {
|
||||
label: 'Sonarr API',
|
||||
message: e.message,
|
||||
error: e,
|
||||
});
|
||||
throw new Error('Failed to add series');
|
||||
}
|
||||
}
|
||||
|
||||
public async getProfiles(): Promise<SonarrProfile[]> {
|
||||
try {
|
||||
const response = await this.axios.get<SonarrProfile[]>('/profile');
|
||||
|
||||
return response.data;
|
||||
} catch (e) {
|
||||
logger.error('Something went wrong retrieving Sonarr profiles', {
|
||||
label: 'Sonarr API',
|
||||
message: e.message,
|
||||
});
|
||||
throw new Error('Failed to get profiles');
|
||||
}
|
||||
}
|
||||
|
||||
public async getRootFolders(): Promise<SonarrRootFolder[]> {
|
||||
try {
|
||||
const response = await this.axios.get<SonarrRootFolder[]>('/rootfolder');
|
||||
|
||||
return response.data;
|
||||
} catch (e) {
|
||||
logger.error('Something went wrong retrieving Sonarr root folders', {
|
||||
label: 'Sonarr API',
|
||||
message: e.message,
|
||||
});
|
||||
throw new Error('Failed to get root folders');
|
||||
}
|
||||
}
|
||||
|
||||
private buildSeasonList(
|
||||
seasons: number[],
|
||||
existingSeasons?: SonarrSeason[]
|
||||
): SonarrSeason[] {
|
||||
if (existingSeasons) {
|
||||
const newSeasons = existingSeasons.map((season) => {
|
||||
if (seasons.includes(season.seasonNumber)) {
|
||||
season.monitored = true;
|
||||
}
|
||||
return season;
|
||||
});
|
||||
|
||||
return newSeasons;
|
||||
}
|
||||
|
||||
const newSeasons = seasons.map(
|
||||
(seasonNumber): SonarrSeason => ({
|
||||
seasonNumber,
|
||||
monitored: true,
|
||||
})
|
||||
);
|
||||
|
||||
return newSeasons;
|
||||
}
|
||||
}
|
||||
|
||||
export default SonarrAPI;
|
Loading…
Reference in new issue