mirror of
https://github.com/sct/overseerr.git
synced 2025-09-17 17:24:35 +02:00
feat(api): radarr api wrapper / send to radarr when requests approved (#93)
This commit is contained in:
122
server/api/radarr.ts
Normal file
122
server/api/radarr.ts
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
import Axios, { AxiosInstance } from 'axios';
|
||||||
|
|
||||||
|
interface RadarrMovieOptions {
|
||||||
|
title: string;
|
||||||
|
qualityProfileId: number;
|
||||||
|
profileId: number;
|
||||||
|
year: number;
|
||||||
|
rootFolderPath: string;
|
||||||
|
tmdbId: number;
|
||||||
|
monitored?: boolean;
|
||||||
|
searchNow?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RadarrMovie {
|
||||||
|
id: number;
|
||||||
|
title: string;
|
||||||
|
isAvailable: boolean;
|
||||||
|
monitored: boolean;
|
||||||
|
tmdbId: number;
|
||||||
|
titleSlug: string;
|
||||||
|
folderName: string;
|
||||||
|
path: string;
|
||||||
|
profileId: number;
|
||||||
|
qualityProfileId: number;
|
||||||
|
added: string;
|
||||||
|
downloaded: boolean;
|
||||||
|
hasFile: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RadarrRootFolder {
|
||||||
|
id: number;
|
||||||
|
path: string;
|
||||||
|
freeSpace: number;
|
||||||
|
totalSpace: number;
|
||||||
|
unmappedFolders: {
|
||||||
|
name: string;
|
||||||
|
path: string;
|
||||||
|
}[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RadarrProfile {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
class RadarrAPI {
|
||||||
|
private axios: AxiosInstance;
|
||||||
|
constructor({ url, apiKey }: { url: string; apiKey: string }) {
|
||||||
|
this.axios = Axios.create({
|
||||||
|
baseURL: url,
|
||||||
|
params: {
|
||||||
|
apikey: apiKey,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public getMovies = async (): Promise<RadarrMovie[]> => {
|
||||||
|
try {
|
||||||
|
const response = await this.axios.get<RadarrMovie[]>('/movie');
|
||||||
|
|
||||||
|
return response.data;
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error(`[Radarr] Failed to retrieve movies: ${e.message}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public getMovie = async ({ id }: { id: number }): Promise<RadarrMovie> => {
|
||||||
|
try {
|
||||||
|
const response = await this.axios.get<RadarrMovie>(`/movie/${id}`);
|
||||||
|
|
||||||
|
return response.data;
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error(`[Radarr] Failed to retrieve movie: ${e.message}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public addMovie = async (
|
||||||
|
options: RadarrMovieOptions
|
||||||
|
): Promise<RadarrMovie> => {
|
||||||
|
try {
|
||||||
|
const response = await this.axios.post<RadarrMovie>(`/movie`, {
|
||||||
|
title: options.title,
|
||||||
|
qualityProfileId: options.qualityProfileId,
|
||||||
|
profileId: options.profileId,
|
||||||
|
titleSlug: options.tmdbId.toString(),
|
||||||
|
tmdbId: options.tmdbId,
|
||||||
|
year: options.year,
|
||||||
|
rootFolderPath: options.rootFolderPath,
|
||||||
|
monitored: options.monitored,
|
||||||
|
addOptions: {
|
||||||
|
searchForMovie: options.searchNow,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return response.data;
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error(`[Radarr] Failed to add movie: ${e.message}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public getProfiles = async (): Promise<RadarrProfile[]> => {
|
||||||
|
try {
|
||||||
|
const response = await this.axios.get<RadarrProfile[]>(`/profile`);
|
||||||
|
|
||||||
|
return response.data;
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error(`[Radarr] Failed to retrieve profiles: ${e.message}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public getRootFolders = async (): Promise<RadarrRootFolder[]> => {
|
||||||
|
try {
|
||||||
|
const response = await this.axios.get<RadarrRootFolder[]>(`/rootfolder`);
|
||||||
|
|
||||||
|
return response.data;
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error(`[Radarr] Failed to retrieve root folders: ${e.message}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default RadarrAPI;
|
@@ -8,8 +8,13 @@ import {
|
|||||||
getRepository,
|
getRepository,
|
||||||
In,
|
In,
|
||||||
Index,
|
Index,
|
||||||
|
AfterUpdate,
|
||||||
|
AfterInsert,
|
||||||
} from 'typeorm';
|
} from 'typeorm';
|
||||||
import { User } from './User';
|
import { User } from './User';
|
||||||
|
import RadarrAPI from '../api/radarr';
|
||||||
|
import { getSettings } from '../lib/settings';
|
||||||
|
import TheMovieDb from '../api/themoviedb';
|
||||||
|
|
||||||
export enum MediaRequestStatus {
|
export enum MediaRequestStatus {
|
||||||
PENDING = 1,
|
PENDING = 1,
|
||||||
@@ -60,7 +65,6 @@ export class MediaRequest {
|
|||||||
|
|
||||||
@ManyToOne(() => User, { nullable: true })
|
@ManyToOne(() => User, { nullable: true })
|
||||||
public modifiedBy?: User;
|
public modifiedBy?: User;
|
||||||
|
|
||||||
@CreateDateColumn()
|
@CreateDateColumn()
|
||||||
public createdAt: Date;
|
public createdAt: Date;
|
||||||
|
|
||||||
@@ -70,4 +74,49 @@ export class MediaRequest {
|
|||||||
constructor(init?: Partial<MediaRequest>) {
|
constructor(init?: Partial<MediaRequest>) {
|
||||||
Object.assign(this, init);
|
Object.assign(this, init);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@AfterUpdate()
|
||||||
|
@AfterInsert()
|
||||||
|
private async sendToRadarr() {
|
||||||
|
if (
|
||||||
|
this.mediaType === 'movie' &&
|
||||||
|
this.status === MediaRequestStatus.APPROVED
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
const settings = getSettings();
|
||||||
|
if (settings.radarr.length === 0 && !settings.radarr[0]) {
|
||||||
|
console.log(
|
||||||
|
'[MediaRequest] Skipped radarr request as there is no radarr configured'
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tmdb = new TheMovieDb();
|
||||||
|
const radarrSettings = settings.radarr[0];
|
||||||
|
const radarr = new RadarrAPI({
|
||||||
|
apiKey: radarrSettings.apiKey,
|
||||||
|
url: `${radarrSettings.useSsl ? 'https' : 'http'}://${
|
||||||
|
radarrSettings.hostname
|
||||||
|
}:${radarrSettings.port}/api`,
|
||||||
|
});
|
||||||
|
const movie = await tmdb.getMovie({ movieId: this.mediaId });
|
||||||
|
|
||||||
|
await radarr.addMovie({
|
||||||
|
profileId: radarrSettings.activeProfileId,
|
||||||
|
qualityProfileId: radarrSettings.activeProfileId,
|
||||||
|
rootFolderPath: radarrSettings.activeDirectory,
|
||||||
|
title: movie.title,
|
||||||
|
tmdbId: movie.id,
|
||||||
|
year: Number(movie.release_date.slice(0, 4)),
|
||||||
|
monitored: true,
|
||||||
|
searchNow: true,
|
||||||
|
});
|
||||||
|
console.log('[MediaRequest] Sent request to Radarr');
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error(
|
||||||
|
`[MediaRequest] Request failed to send to radarr: ${e.message}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -23,7 +23,7 @@ interface DVRSettings {
|
|||||||
apiKey: string;
|
apiKey: string;
|
||||||
useSsl: boolean;
|
useSsl: boolean;
|
||||||
baseUrl?: string;
|
baseUrl?: string;
|
||||||
activeProfile: string;
|
activeProfileId: number;
|
||||||
activeDirectory: string;
|
activeDirectory: string;
|
||||||
is4k: boolean;
|
is4k: boolean;
|
||||||
}
|
}
|
||||||
|
@@ -110,9 +110,9 @@ components:
|
|||||||
example: false
|
example: false
|
||||||
baseUrl:
|
baseUrl:
|
||||||
type: string
|
type: string
|
||||||
activeProfile:
|
activeProfileId:
|
||||||
type: string
|
type: number
|
||||||
example: '1080p'
|
example: 1
|
||||||
activeDirectory:
|
activeDirectory:
|
||||||
type: string
|
type: string
|
||||||
example: '/movies'
|
example: '/movies'
|
||||||
|
Reference in New Issue
Block a user