mirror of
https://github.com/sct/overseerr.git
synced 2025-09-17 17:24:35 +02:00
feat: radarr/sonarr tag support (#1366)
This commit is contained in:
169
server/api/servarr/base.ts
Normal file
169
server/api/servarr/base.ts
Normal file
@@ -0,0 +1,169 @@
|
||||
import cacheManager, { AvailableCacheIds } from '../../lib/cache';
|
||||
import { DVRSettings } from '../../lib/settings';
|
||||
import ExternalAPI from '../externalapi';
|
||||
|
||||
export interface RootFolder {
|
||||
id: number;
|
||||
path: string;
|
||||
freeSpace: number;
|
||||
totalSpace: number;
|
||||
unmappedFolders: {
|
||||
name: string;
|
||||
path: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
export interface QualityProfile {
|
||||
id: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
interface QueueItem {
|
||||
size: number;
|
||||
title: string;
|
||||
sizeleft: number;
|
||||
timeleft: string;
|
||||
estimatedCompletionTime: string;
|
||||
status: string;
|
||||
trackedDownloadStatus: string;
|
||||
trackedDownloadState: string;
|
||||
downloadId: string;
|
||||
protocol: string;
|
||||
downloadClient: string;
|
||||
indexer: string;
|
||||
id: number;
|
||||
}
|
||||
|
||||
export interface Tag {
|
||||
id: number;
|
||||
label: string;
|
||||
}
|
||||
|
||||
interface QueueResponse<QueueItemAppendT> {
|
||||
page: number;
|
||||
pageSize: number;
|
||||
sortKey: string;
|
||||
sortDirection: string;
|
||||
totalRecords: number;
|
||||
records: (QueueItem & QueueItemAppendT)[];
|
||||
}
|
||||
|
||||
class ServarrBase<QueueItemAppendT> extends ExternalAPI {
|
||||
static buildUrl(settings: DVRSettings, path?: string): string {
|
||||
return `${settings.useSsl ? 'https' : 'http'}://${settings.hostname}:${
|
||||
settings.port
|
||||
}${settings.baseUrl ?? ''}${path}`;
|
||||
}
|
||||
|
||||
protected apiName: string;
|
||||
|
||||
constructor({
|
||||
url,
|
||||
apiKey,
|
||||
cacheName,
|
||||
apiName,
|
||||
}: {
|
||||
url: string;
|
||||
apiKey: string;
|
||||
cacheName: AvailableCacheIds;
|
||||
apiName: string;
|
||||
}) {
|
||||
super(
|
||||
url,
|
||||
{
|
||||
apikey: apiKey,
|
||||
},
|
||||
{
|
||||
nodeCache: cacheManager.getCache(cacheName).data,
|
||||
}
|
||||
);
|
||||
|
||||
this.apiName = apiName;
|
||||
}
|
||||
|
||||
public getProfiles = async (): Promise<QualityProfile[]> => {
|
||||
try {
|
||||
const data = await this.getRolling<QualityProfile[]>(
|
||||
`/qualityProfile`,
|
||||
undefined,
|
||||
3600
|
||||
);
|
||||
|
||||
return data;
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`[${this.apiName}] Failed to retrieve profiles: ${e.message}`
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
public getRootFolders = async (): Promise<RootFolder[]> => {
|
||||
try {
|
||||
const data = await this.getRolling<RootFolder[]>(
|
||||
`/rootfolder`,
|
||||
undefined,
|
||||
3600
|
||||
);
|
||||
|
||||
return data;
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`[${this.apiName}] Failed to retrieve root folders: ${e.message}`
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
public getQueue = async (): Promise<(QueueItem & QueueItemAppendT)[]> => {
|
||||
try {
|
||||
const response = await this.axios.get<QueueResponse<QueueItemAppendT>>(
|
||||
`/queue`
|
||||
);
|
||||
|
||||
return response.data.records;
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`[${this.apiName}] Failed to retrieve queue: ${e.message}`
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
public getTags = async (): Promise<Tag[]> => {
|
||||
try {
|
||||
const response = await this.axios.get<Tag[]>(`/tag`);
|
||||
|
||||
return response.data;
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`[${this.apiName}] Failed to retrieve tags: ${e.message}`
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
public createTag = async ({ label }: { label: string }): Promise<Tag> => {
|
||||
try {
|
||||
const response = await this.axios.post<Tag>(`/tag`, {
|
||||
label,
|
||||
});
|
||||
|
||||
return response.data;
|
||||
} catch (e) {
|
||||
throw new Error(`[${this.apiName}] Failed to create tag: ${e.message}`);
|
||||
}
|
||||
};
|
||||
|
||||
protected async runCommand(
|
||||
commandName: string,
|
||||
options: Record<string, unknown>
|
||||
): Promise<void> {
|
||||
try {
|
||||
await this.axios.post(`/command`, {
|
||||
name: commandName,
|
||||
...options,
|
||||
});
|
||||
} catch (e) {
|
||||
throw new Error(`[${this.apiName}] Failed to run command: ${e.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default ServarrBase;
|
@@ -1,12 +1,11 @@
|
||||
import cacheManager from '../lib/cache';
|
||||
import { RadarrSettings } from '../lib/settings';
|
||||
import logger from '../logger';
|
||||
import ExternalAPI from './externalapi';
|
||||
import logger from '../../logger';
|
||||
import ServarrBase from './base';
|
||||
|
||||
interface RadarrMovieOptions {
|
||||
title: string;
|
||||
qualityProfileId: number;
|
||||
minimumAvailability: string;
|
||||
tags: number[];
|
||||
profileId: number;
|
||||
year: number;
|
||||
rootFolderPath: string;
|
||||
@@ -32,65 +31,9 @@ export interface RadarrMovie {
|
||||
hasFile: boolean;
|
||||
}
|
||||
|
||||
export interface RadarrRootFolder {
|
||||
id: number;
|
||||
path: string;
|
||||
freeSpace: number;
|
||||
totalSpace: number;
|
||||
unmappedFolders: {
|
||||
name: string;
|
||||
path: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
export interface RadarrProfile {
|
||||
id: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
interface QueueItem {
|
||||
movieId: number;
|
||||
size: number;
|
||||
title: string;
|
||||
sizeleft: number;
|
||||
timeleft: string;
|
||||
estimatedCompletionTime: string;
|
||||
status: string;
|
||||
trackedDownloadStatus: string;
|
||||
trackedDownloadState: string;
|
||||
downloadId: string;
|
||||
protocol: string;
|
||||
downloadClient: string;
|
||||
indexer: string;
|
||||
id: number;
|
||||
}
|
||||
|
||||
interface QueueResponse {
|
||||
page: number;
|
||||
pageSize: number;
|
||||
sortKey: string;
|
||||
sortDirection: string;
|
||||
totalRecords: number;
|
||||
records: QueueItem[];
|
||||
}
|
||||
|
||||
class RadarrAPI extends ExternalAPI {
|
||||
static buildRadarrUrl(radarrSettings: RadarrSettings, path?: string): string {
|
||||
return `${radarrSettings.useSsl ? 'https' : 'http'}://${
|
||||
radarrSettings.hostname
|
||||
}:${radarrSettings.port}${radarrSettings.baseUrl ?? ''}${path}`;
|
||||
}
|
||||
|
||||
class RadarrAPI extends ServarrBase<{ movieId: number }> {
|
||||
constructor({ url, apiKey }: { url: string; apiKey: string }) {
|
||||
super(
|
||||
url,
|
||||
{
|
||||
apikey: apiKey,
|
||||
},
|
||||
{
|
||||
nodeCache: cacheManager.getCache('radarr').data,
|
||||
}
|
||||
);
|
||||
super({ url, apiKey, cacheName: 'radarr', apiName: 'Radarr' });
|
||||
}
|
||||
|
||||
public getMovies = async (): Promise<RadarrMovie[]> => {
|
||||
@@ -162,6 +105,7 @@ class RadarrAPI extends ExternalAPI {
|
||||
minimumAvailability: options.minimumAvailability,
|
||||
tmdbId: options.tmdbId,
|
||||
year: options.year,
|
||||
tags: options.tags,
|
||||
rootFolderPath: options.rootFolderPath,
|
||||
monitored: options.monitored,
|
||||
addOptions: {
|
||||
@@ -206,6 +150,7 @@ class RadarrAPI extends ExternalAPI {
|
||||
year: options.year,
|
||||
rootFolderPath: options.rootFolderPath,
|
||||
monitored: options.monitored,
|
||||
tags: options.tags,
|
||||
addOptions: {
|
||||
searchForMovie: options.searchNow,
|
||||
},
|
||||
@@ -238,44 +183,6 @@ class RadarrAPI extends ExternalAPI {
|
||||
throw new Error('Failed to add movie to Radarr');
|
||||
}
|
||||
};
|
||||
|
||||
public getProfiles = async (): Promise<RadarrProfile[]> => {
|
||||
try {
|
||||
const data = await this.getRolling<RadarrProfile[]>(
|
||||
`/qualityProfile`,
|
||||
undefined,
|
||||
3600
|
||||
);
|
||||
|
||||
return data;
|
||||
} catch (e) {
|
||||
throw new Error(`[Radarr] Failed to retrieve profiles: ${e.message}`);
|
||||
}
|
||||
};
|
||||
|
||||
public getRootFolders = async (): Promise<RadarrRootFolder[]> => {
|
||||
try {
|
||||
const data = await this.getRolling<RadarrRootFolder[]>(
|
||||
`/rootfolder`,
|
||||
undefined,
|
||||
3600
|
||||
);
|
||||
|
||||
return data;
|
||||
} catch (e) {
|
||||
throw new Error(`[Radarr] Failed to retrieve root folders: ${e.message}`);
|
||||
}
|
||||
};
|
||||
|
||||
public getQueue = async (): Promise<QueueItem[]> => {
|
||||
try {
|
||||
const response = await this.axios.get<QueueResponse>(`/queue`);
|
||||
|
||||
return response.data.records;
|
||||
} catch (e) {
|
||||
throw new Error(`[Radarr] Failed to retrieve queue: ${e.message}`);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default RadarrAPI;
|
@@ -1,7 +1,5 @@
|
||||
import cacheManager from '../lib/cache';
|
||||
import { SonarrSettings } from '../lib/settings';
|
||||
import logger from '../logger';
|
||||
import ExternalAPI from './externalapi';
|
||||
import logger from '../../logger';
|
||||
import ServarrBase from './base';
|
||||
|
||||
interface SonarrSeason {
|
||||
seasonNumber: number;
|
||||
@@ -49,7 +47,7 @@ export interface SonarrSeries {
|
||||
titleSlug: string;
|
||||
certification: string;
|
||||
genres: string[];
|
||||
tags: string[];
|
||||
tags: number[];
|
||||
added: string;
|
||||
ratings: {
|
||||
votes: number;
|
||||
@@ -65,49 +63,6 @@ export interface SonarrSeries {
|
||||
};
|
||||
}
|
||||
|
||||
interface QueueItem {
|
||||
seriesId: number;
|
||||
episodeId: number;
|
||||
size: number;
|
||||
title: string;
|
||||
sizeleft: number;
|
||||
timeleft: string;
|
||||
estimatedCompletionTime: string;
|
||||
status: string;
|
||||
trackedDownloadStatus: string;
|
||||
trackedDownloadState: string;
|
||||
downloadId: string;
|
||||
protocol: string;
|
||||
downloadClient: string;
|
||||
indexer: string;
|
||||
id: number;
|
||||
}
|
||||
|
||||
interface QueueResponse {
|
||||
page: number;
|
||||
pageSize: number;
|
||||
sortKey: string;
|
||||
sortDirection: string;
|
||||
totalRecords: number;
|
||||
records: QueueItem[];
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -116,6 +71,7 @@ interface AddSeriesOptions {
|
||||
seasons: number[];
|
||||
seasonFolder: boolean;
|
||||
rootFolderPath: string;
|
||||
tags?: number[];
|
||||
seriesType: SonarrSeries['seriesType'];
|
||||
monitored?: boolean;
|
||||
searchNow?: boolean;
|
||||
@@ -126,23 +82,9 @@ export interface LanguageProfile {
|
||||
name: string;
|
||||
}
|
||||
|
||||
class SonarrAPI extends ExternalAPI {
|
||||
static buildSonarrUrl(sonarrSettings: SonarrSettings, path?: string): string {
|
||||
return `${sonarrSettings.useSsl ? 'https' : 'http'}://${
|
||||
sonarrSettings.hostname
|
||||
}:${sonarrSettings.port}${sonarrSettings.baseUrl ?? ''}${path}`;
|
||||
}
|
||||
|
||||
class SonarrAPI extends ServarrBase<{ seriesId: number; episodeId: number }> {
|
||||
constructor({ url, apiKey }: { url: string; apiKey: string }) {
|
||||
super(
|
||||
url,
|
||||
{
|
||||
apikey: apiKey,
|
||||
},
|
||||
{
|
||||
nodeCache: cacheManager.getCache('sonarr').data,
|
||||
}
|
||||
);
|
||||
super({ url, apiKey, apiName: 'Sonarr', cacheName: 'sonarr' });
|
||||
}
|
||||
|
||||
public async getSeries(): Promise<SonarrSeries[]> {
|
||||
@@ -151,7 +93,7 @@ class SonarrAPI extends ExternalAPI {
|
||||
|
||||
return response.data;
|
||||
} catch (e) {
|
||||
throw new Error(`[Radarr] Failed to retrieve series: ${e.message}`);
|
||||
throw new Error(`[Sonarr] Failed to retrieve series: ${e.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,6 +147,7 @@ class SonarrAPI extends ExternalAPI {
|
||||
|
||||
// If the series already exists, we will simply just update it
|
||||
if (series.id) {
|
||||
series.tags = options.tags ?? series.tags;
|
||||
series.seasons = this.buildSeasonList(options.seasons, series.seasons);
|
||||
|
||||
const newSeriesResponse = await this.axios.put<SonarrSeries>(
|
||||
@@ -249,6 +192,7 @@ class SonarrAPI extends ExternalAPI {
|
||||
monitored: false,
|
||||
}))
|
||||
),
|
||||
tags: options.tags,
|
||||
seasonFolder: options.seasonFolder,
|
||||
monitored: options.monitored,
|
||||
rootFolderPath: options.rootFolderPath,
|
||||
@@ -286,46 +230,6 @@ class SonarrAPI extends ExternalAPI {
|
||||
}
|
||||
}
|
||||
|
||||
public async getProfiles(): Promise<SonarrProfile[]> {
|
||||
try {
|
||||
const data = await this.getRolling<SonarrProfile[]>(
|
||||
'/qualityProfile',
|
||||
undefined,
|
||||
3600
|
||||
);
|
||||
|
||||
return data;
|
||||
} catch (e) {
|
||||
logger.error('Something went wrong while retrieving Sonarr profiles.', {
|
||||
label: 'Sonarr API',
|
||||
message: e.message,
|
||||
});
|
||||
throw new Error('Failed to get profiles');
|
||||
}
|
||||
}
|
||||
|
||||
public async getRootFolders(): Promise<SonarrRootFolder[]> {
|
||||
try {
|
||||
const data = await this.getRolling<SonarrRootFolder[]>(
|
||||
'/rootfolder',
|
||||
undefined,
|
||||
3600
|
||||
);
|
||||
|
||||
return data;
|
||||
} catch (e) {
|
||||
logger.error(
|
||||
'Something went wrong while retrieving Sonarr root folders.',
|
||||
{
|
||||
label: 'Sonarr API',
|
||||
message: e.message,
|
||||
}
|
||||
);
|
||||
|
||||
throw new Error('Failed to get root folders');
|
||||
}
|
||||
}
|
||||
|
||||
public async getLanguageProfiles(): Promise<LanguageProfile[]> {
|
||||
try {
|
||||
const data = await this.getRolling<LanguageProfile[]>(
|
||||
@@ -356,25 +260,6 @@ class SonarrAPI extends ExternalAPI {
|
||||
await this.runCommand('SeriesSearch', { seriesId });
|
||||
}
|
||||
|
||||
private async runCommand(
|
||||
commandName: string,
|
||||
options: Record<string, unknown>
|
||||
): Promise<void> {
|
||||
try {
|
||||
await this.axios.post(`/command`, {
|
||||
name: commandName,
|
||||
...options,
|
||||
});
|
||||
} catch (e) {
|
||||
logger.error('Something went wrong attempting to run a Sonarr command.', {
|
||||
label: 'Sonarr API',
|
||||
message: e.message,
|
||||
});
|
||||
|
||||
throw new Error('Failed to run Sonarr command.');
|
||||
}
|
||||
}
|
||||
|
||||
private buildSeasonList(
|
||||
seasons: number[],
|
||||
existingSeasons?: SonarrSeason[]
|
||||
@@ -399,16 +284,6 @@ class SonarrAPI extends ExternalAPI {
|
||||
|
||||
return newSeasons;
|
||||
}
|
||||
|
||||
public getQueue = async (): Promise<QueueItem[]> => {
|
||||
try {
|
||||
const response = await this.axios.get<QueueResponse>(`/queue`);
|
||||
|
||||
return response.data.records;
|
||||
} catch (e) {
|
||||
throw new Error(`[Radarr] Failed to retrieve queue: ${e.message}`);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default SonarrAPI;
|
@@ -1,23 +1,23 @@
|
||||
import {
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
AfterLoad,
|
||||
Column,
|
||||
Index,
|
||||
OneToMany,
|
||||
CreateDateColumn,
|
||||
UpdateDateColumn,
|
||||
Entity,
|
||||
getRepository,
|
||||
In,
|
||||
AfterLoad,
|
||||
Index,
|
||||
OneToMany,
|
||||
PrimaryGeneratedColumn,
|
||||
UpdateDateColumn,
|
||||
} from 'typeorm';
|
||||
import { MediaRequest } from './MediaRequest';
|
||||
import RadarrAPI from '../api/servarr/radarr';
|
||||
import SonarrAPI from '../api/servarr/sonarr';
|
||||
import { MediaStatus, MediaType } from '../constants/media';
|
||||
import logger from '../logger';
|
||||
import Season from './Season';
|
||||
import { getSettings } from '../lib/settings';
|
||||
import RadarrAPI from '../api/radarr';
|
||||
import downloadTracker, { DownloadingItem } from '../lib/downloadtracker';
|
||||
import SonarrAPI from '../api/sonarr';
|
||||
import { getSettings } from '../lib/settings';
|
||||
import logger from '../logger';
|
||||
import { MediaRequest } from './MediaRequest';
|
||||
import Season from './Season';
|
||||
|
||||
@Entity()
|
||||
class Media {
|
||||
@@ -168,10 +168,7 @@ class Media {
|
||||
if (server) {
|
||||
this.serviceUrl = server.externalUrl
|
||||
? `${server.externalUrl}/movie/${this.externalServiceSlug}`
|
||||
: RadarrAPI.buildRadarrUrl(
|
||||
server,
|
||||
`/movie/${this.externalServiceSlug}`
|
||||
);
|
||||
: RadarrAPI.buildUrl(server, `/movie/${this.externalServiceSlug}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,7 +181,7 @@ class Media {
|
||||
if (server) {
|
||||
this.serviceUrl4k = server.externalUrl
|
||||
? `${server.externalUrl}/movie/${this.externalServiceSlug4k}`
|
||||
: RadarrAPI.buildRadarrUrl(
|
||||
: RadarrAPI.buildUrl(
|
||||
server,
|
||||
`/movie/${this.externalServiceSlug4k}`
|
||||
);
|
||||
@@ -202,10 +199,7 @@ class Media {
|
||||
if (server) {
|
||||
this.serviceUrl = server.externalUrl
|
||||
? `${server.externalUrl}/series/${this.externalServiceSlug}`
|
||||
: SonarrAPI.buildSonarrUrl(
|
||||
server,
|
||||
`/series/${this.externalServiceSlug}`
|
||||
);
|
||||
: SonarrAPI.buildUrl(server, `/series/${this.externalServiceSlug}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,7 +212,7 @@ class Media {
|
||||
if (server) {
|
||||
this.serviceUrl4k = server.externalUrl
|
||||
? `${server.externalUrl}/series/${this.externalServiceSlug4k}`
|
||||
: SonarrAPI.buildSonarrUrl(
|
||||
: SonarrAPI.buildUrl(
|
||||
server,
|
||||
`/series/${this.externalServiceSlug4k}`
|
||||
);
|
||||
|
@@ -1,28 +1,29 @@
|
||||
import { isEqual } from 'lodash';
|
||||
import {
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
ManyToOne,
|
||||
AfterInsert,
|
||||
AfterRemove,
|
||||
AfterUpdate,
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
UpdateDateColumn,
|
||||
AfterUpdate,
|
||||
AfterInsert,
|
||||
Entity,
|
||||
getRepository,
|
||||
ManyToOne,
|
||||
OneToMany,
|
||||
AfterRemove,
|
||||
PrimaryGeneratedColumn,
|
||||
RelationCount,
|
||||
UpdateDateColumn,
|
||||
} from 'typeorm';
|
||||
import { User } from './User';
|
||||
import Media from './Media';
|
||||
import { MediaStatus, MediaRequestStatus, MediaType } from '../constants/media';
|
||||
import { getSettings } from '../lib/settings';
|
||||
import RadarrAPI from '../api/servarr/radarr';
|
||||
import SonarrAPI, { SonarrSeries } from '../api/servarr/sonarr';
|
||||
import TheMovieDb from '../api/themoviedb';
|
||||
import { ANIME_KEYWORD_ID } from '../api/themoviedb/constants';
|
||||
import RadarrAPI from '../api/radarr';
|
||||
import logger from '../logger';
|
||||
import SeasonRequest from './SeasonRequest';
|
||||
import SonarrAPI, { SonarrSeries } from '../api/sonarr';
|
||||
import { MediaRequestStatus, MediaStatus, MediaType } from '../constants/media';
|
||||
import notificationManager, { Notification } from '../lib/notifications';
|
||||
import { getSettings } from '../lib/settings';
|
||||
import logger from '../logger';
|
||||
import Media from './Media';
|
||||
import SeasonRequest from './SeasonRequest';
|
||||
import { User } from './User';
|
||||
|
||||
@Entity()
|
||||
export class MediaRequest {
|
||||
@@ -85,6 +86,37 @@ export class MediaRequest {
|
||||
@Column({ nullable: true })
|
||||
public languageProfileId: number;
|
||||
|
||||
@Column({
|
||||
type: 'text',
|
||||
nullable: true,
|
||||
transformer: {
|
||||
from: (value: string | null): number[] | null => {
|
||||
if (value) {
|
||||
if (value === 'none') {
|
||||
return [];
|
||||
}
|
||||
return value.split(',').map((v) => Number(v));
|
||||
}
|
||||
return null;
|
||||
},
|
||||
to: (value: number[] | null): string | null => {
|
||||
if (value) {
|
||||
const finalValue = value.join(',');
|
||||
|
||||
// We want to keep the actual state of an "empty array" so we use
|
||||
// the keyword "none" to track this.
|
||||
if (!finalValue) {
|
||||
return 'none';
|
||||
}
|
||||
|
||||
return finalValue;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
},
|
||||
})
|
||||
public tags?: number[];
|
||||
|
||||
constructor(init?: Partial<MediaRequest>) {
|
||||
Object.assign(this, init);
|
||||
}
|
||||
@@ -365,6 +397,7 @@ export class MediaRequest {
|
||||
|
||||
let rootFolder = radarrSettings.activeDirectory;
|
||||
let qualityProfile = radarrSettings.activeProfileId;
|
||||
let tags = radarrSettings.tags;
|
||||
|
||||
if (
|
||||
this.rootFolder &&
|
||||
@@ -387,10 +420,22 @@ export class MediaRequest {
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
this.tags &&
|
||||
(radarrSettings.tags.length !== (this.tags?.length ?? 0) ||
|
||||
radarrSettings.tags.every((num) => (this.tags ?? []).includes(num)))
|
||||
) {
|
||||
tags = this.tags;
|
||||
logger.info(`Request has override tags`, {
|
||||
label: 'Media Request',
|
||||
tagIds: tags,
|
||||
});
|
||||
}
|
||||
|
||||
const tmdb = new TheMovieDb();
|
||||
const radarr = new RadarrAPI({
|
||||
apiKey: radarrSettings.apiKey,
|
||||
url: RadarrAPI.buildRadarrUrl(radarrSettings, '/api/v3'),
|
||||
url: RadarrAPI.buildUrl(radarrSettings, '/api/v3'),
|
||||
});
|
||||
const movie = await tmdb.getMovie({ movieId: this.media.tmdbId });
|
||||
|
||||
@@ -420,6 +465,7 @@ export class MediaRequest {
|
||||
tmdbId: movie.id,
|
||||
year: Number(movie.release_date.slice(0, 4)),
|
||||
monitored: true,
|
||||
tags,
|
||||
searchNow: !radarrSettings.preventSearch,
|
||||
})
|
||||
.then(async (radarrMovie) => {
|
||||
@@ -531,7 +577,7 @@ export class MediaRequest {
|
||||
const tmdb = new TheMovieDb();
|
||||
const sonarr = new SonarrAPI({
|
||||
apiKey: sonarrSettings.apiKey,
|
||||
url: SonarrAPI.buildSonarrUrl(sonarrSettings, '/api/v3'),
|
||||
url: SonarrAPI.buildUrl(sonarrSettings, '/api/v3'),
|
||||
});
|
||||
const series = await tmdb.getTvShow({ tvId: media.tmdbId });
|
||||
const tvdbId = series.external_ids.tvdb_id ?? media.tvdbId;
|
||||
@@ -568,6 +614,11 @@ export class MediaRequest {
|
||||
? sonarrSettings.activeAnimeLanguageProfileId
|
||||
: sonarrSettings.activeLanguageProfileId;
|
||||
|
||||
let tags =
|
||||
seriesType === 'anime'
|
||||
? sonarrSettings.animeTags
|
||||
: sonarrSettings.tags;
|
||||
|
||||
if (
|
||||
this.rootFolder &&
|
||||
this.rootFolder !== '' &&
|
||||
@@ -599,6 +650,14 @@ export class MediaRequest {
|
||||
);
|
||||
}
|
||||
|
||||
if (this.tags && !isEqual(this.tags, tags)) {
|
||||
tags = this.tags;
|
||||
logger.info(`Request has override tags`, {
|
||||
label: 'Media Request',
|
||||
tags,
|
||||
});
|
||||
}
|
||||
|
||||
// Run this asynchronously so we don't wait for it on the UI side
|
||||
sonarr
|
||||
.addSeries({
|
||||
@@ -610,6 +669,7 @@ export class MediaRequest {
|
||||
seasons: this.seasons.map((season) => season.seasonNumber),
|
||||
seasonFolder: sonarrSettings.enableSeasonFolders,
|
||||
seriesType,
|
||||
tags,
|
||||
monitored: true,
|
||||
searchNow: !sonarrSettings.preventSearch,
|
||||
})
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { RadarrProfile, RadarrRootFolder } from '../../api/radarr';
|
||||
import { LanguageProfile } from '../../api/sonarr';
|
||||
import { QualityProfile, RootFolder, Tag } from '../../api/servarr/base';
|
||||
import { LanguageProfile } from '../../api/servarr/sonarr';
|
||||
|
||||
export interface ServiceCommonServer {
|
||||
id: number;
|
||||
@@ -12,11 +12,14 @@ export interface ServiceCommonServer {
|
||||
activeAnimeProfileId?: number;
|
||||
activeAnimeDirectory?: string;
|
||||
activeAnimeLanguageProfileId?: number;
|
||||
activeTags: number[];
|
||||
activeAnimeTags?: number[];
|
||||
}
|
||||
|
||||
export interface ServiceCommonServerWithDetails {
|
||||
server: ServiceCommonServer;
|
||||
profiles: RadarrProfile[];
|
||||
rootFolders: Partial<RadarrRootFolder>[];
|
||||
profiles: QualityProfile[];
|
||||
rootFolders: Partial<RootFolder>[];
|
||||
languageProfiles?: LanguageProfile[];
|
||||
tags: Tag[];
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import { uniqWith } from 'lodash';
|
||||
import RadarrAPI from '../api/radarr';
|
||||
import SonarrAPI from '../api/sonarr';
|
||||
import RadarrAPI from '../api/servarr/radarr';
|
||||
import SonarrAPI from '../api/servarr/sonarr';
|
||||
import { MediaType } from '../constants/media';
|
||||
import logger from '../logger';
|
||||
import { getSettings } from './settings';
|
||||
@@ -73,7 +73,7 @@ class DownloadTracker {
|
||||
if (server.syncEnabled) {
|
||||
const radarr = new RadarrAPI({
|
||||
apiKey: server.apiKey,
|
||||
url: RadarrAPI.buildRadarrUrl(server, '/api/v3'),
|
||||
url: RadarrAPI.buildUrl(server, '/api/v3'),
|
||||
});
|
||||
|
||||
const queueItems = await radarr.getQueue();
|
||||
@@ -140,7 +140,7 @@ class DownloadTracker {
|
||||
if (server.syncEnabled) {
|
||||
const radarr = new SonarrAPI({
|
||||
apiKey: server.apiKey,
|
||||
url: SonarrAPI.buildSonarrUrl(server, '/api/v3'),
|
||||
url: SonarrAPI.buildUrl(server, '/api/v3'),
|
||||
});
|
||||
|
||||
const queueItems = await radarr.getQueue();
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { uniqWith } from 'lodash';
|
||||
import RadarrAPI, { RadarrMovie } from '../../../api/radarr';
|
||||
import RadarrAPI, { RadarrMovie } from '../../../api/servarr/radarr';
|
||||
import { getSettings, RadarrSettings } from '../../settings';
|
||||
import BaseScanner, { RunnableScanner, StatusBase } from '../baseScanner';
|
||||
|
||||
@@ -52,7 +52,7 @@ class RadarrScanner
|
||||
|
||||
this.radarrApi = new RadarrAPI({
|
||||
apiKey: server.apiKey,
|
||||
url: RadarrAPI.buildRadarrUrl(server, '/api/v3'),
|
||||
url: RadarrAPI.buildUrl(server, '/api/v3'),
|
||||
});
|
||||
|
||||
this.items = await this.radarrApi.getMovies();
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import { uniqWith } from 'lodash';
|
||||
import { getRepository } from 'typeorm';
|
||||
import SonarrAPI, { SonarrSeries } from '../../../api/sonarr';
|
||||
import SonarrAPI, { SonarrSeries } from '../../../api/servarr/sonarr';
|
||||
import Media from '../../../entity/Media';
|
||||
import { getSettings, SonarrSettings } from '../../settings';
|
||||
import BaseScanner, {
|
||||
@@ -58,7 +58,7 @@ class SonarrScanner
|
||||
|
||||
this.sonarrApi = new SonarrAPI({
|
||||
apiKey: server.apiKey,
|
||||
url: SonarrAPI.buildSonarrUrl(server, '/api/v3'),
|
||||
url: SonarrAPI.buildUrl(server, '/api/v3'),
|
||||
});
|
||||
|
||||
this.items = await this.sonarrApi.getSeries();
|
||||
|
@@ -30,7 +30,7 @@ export interface PlexSettings {
|
||||
libraries: Library[];
|
||||
}
|
||||
|
||||
interface DVRSettings {
|
||||
export interface DVRSettings {
|
||||
id: number;
|
||||
name: string;
|
||||
hostname: string;
|
||||
@@ -41,6 +41,7 @@ interface DVRSettings {
|
||||
activeProfileId: number;
|
||||
activeProfileName: string;
|
||||
activeDirectory: string;
|
||||
tags: number[];
|
||||
is4k: boolean;
|
||||
isDefault: boolean;
|
||||
externalUrl?: string;
|
||||
@@ -58,6 +59,7 @@ export interface SonarrSettings extends DVRSettings {
|
||||
activeAnimeDirectory?: string;
|
||||
activeAnimeLanguageProfileId?: number;
|
||||
activeLanguageProfileId?: number;
|
||||
animeTags?: number[];
|
||||
enableSeasonFolders: boolean;
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,32 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class CreateTagsFieldonMediaRequest1617624225464
|
||||
implements MigrationInterface {
|
||||
name = 'CreateTagsFieldonMediaRequest1617624225464';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE "temporary_media_request" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "status" integer NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "type" varchar NOT NULL, "mediaId" integer, "requestedById" integer, "modifiedById" integer, "is4k" boolean NOT NULL DEFAULT (0), "serverId" integer, "profileId" integer, "rootFolder" varchar, "languageProfileId" integer, "tags" text, CONSTRAINT "FK_f4fc4efa14c3ba2b29c4525fa15" FOREIGN KEY ("modifiedById") REFERENCES "user" ("id") ON DELETE SET NULL ON UPDATE NO ACTION, CONSTRAINT "FK_6997bee94720f1ecb7f31137095" FOREIGN KEY ("requestedById") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_a1aa713f41c99e9d10c48da75a0" FOREIGN KEY ("mediaId") REFERENCES "media" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)`
|
||||
);
|
||||
await queryRunner.query(
|
||||
`INSERT INTO "temporary_media_request"("id", "status", "createdAt", "updatedAt", "type", "mediaId", "requestedById", "modifiedById", "is4k", "serverId", "profileId", "rootFolder", "languageProfileId") SELECT "id", "status", "createdAt", "updatedAt", "type", "mediaId", "requestedById", "modifiedById", "is4k", "serverId", "profileId", "rootFolder", "languageProfileId" FROM "media_request"`
|
||||
);
|
||||
await queryRunner.query(`DROP TABLE "media_request"`);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "temporary_media_request" RENAME TO "media_request"`
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "media_request" RENAME TO "temporary_media_request"`
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE "media_request" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "status" integer NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "type" varchar NOT NULL, "mediaId" integer, "requestedById" integer, "modifiedById" integer, "is4k" boolean NOT NULL DEFAULT (0), "serverId" integer, "profileId" integer, "rootFolder" varchar, "languageProfileId" integer, CONSTRAINT "FK_f4fc4efa14c3ba2b29c4525fa15" FOREIGN KEY ("modifiedById") REFERENCES "user" ("id") ON DELETE SET NULL ON UPDATE NO ACTION, CONSTRAINT "FK_6997bee94720f1ecb7f31137095" FOREIGN KEY ("requestedById") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_a1aa713f41c99e9d10c48da75a0" FOREIGN KEY ("mediaId") REFERENCES "media" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)`
|
||||
);
|
||||
await queryRunner.query(
|
||||
`INSERT INTO "media_request"("id", "status", "createdAt", "updatedAt", "type", "mediaId", "requestedById", "modifiedById", "is4k", "serverId", "profileId", "rootFolder", "languageProfileId") SELECT "id", "status", "createdAt", "updatedAt", "type", "mediaId", "requestedById", "modifiedById", "is4k", "serverId", "profileId", "rootFolder", "languageProfileId" FROM "temporary_media_request"`
|
||||
);
|
||||
await queryRunner.query(`DROP TABLE "temporary_media_request"`);
|
||||
}
|
||||
}
|
@@ -278,6 +278,7 @@ requestRoutes.post(
|
||||
serverId: req.body.serverId,
|
||||
profileId: req.body.profileId,
|
||||
rootFolder: req.body.rootFolder,
|
||||
tags: req.body.tags,
|
||||
});
|
||||
|
||||
await requestRepository.save(request);
|
||||
@@ -356,6 +357,7 @@ requestRoutes.post(
|
||||
profileId: req.body.profileId,
|
||||
rootFolder: req.body.rootFolder,
|
||||
languageProfileId: req.body.languageProfileId,
|
||||
tags: req.body.tags,
|
||||
seasons: finalSeasons.map(
|
||||
(sn) =>
|
||||
new SeasonRequest({
|
||||
@@ -497,6 +499,7 @@ requestRoutes.put<{ requestId: string }>(
|
||||
request.serverId = req.body.serverId;
|
||||
request.profileId = req.body.profileId;
|
||||
request.rootFolder = req.body.rootFolder;
|
||||
request.tags = req.body.tags;
|
||||
request.requestedBy = requestUser as User;
|
||||
|
||||
requestRepository.save(request);
|
||||
@@ -505,6 +508,8 @@ requestRoutes.put<{ requestId: string }>(
|
||||
request.serverId = req.body.serverId;
|
||||
request.profileId = req.body.profileId;
|
||||
request.rootFolder = req.body.rootFolder;
|
||||
request.languageProfileId = req.body.languageProfileId;
|
||||
request.tags = req.body.tags;
|
||||
request.requestedBy = requestUser as User;
|
||||
|
||||
const requestedSeasons = req.body.seasons as number[] | undefined;
|
||||
|
@@ -1,12 +1,12 @@
|
||||
import { Router } from 'express';
|
||||
import RadarrAPI from '../api/radarr';
|
||||
import SonarrAPI from '../api/sonarr';
|
||||
import RadarrAPI from '../api/servarr/radarr';
|
||||
import SonarrAPI from '../api/servarr/sonarr';
|
||||
import TheMovieDb from '../api/themoviedb';
|
||||
import {
|
||||
ServiceCommonServer,
|
||||
ServiceCommonServerWithDetails,
|
||||
} from '../interfaces/api/serviceInterfaces';
|
||||
import { getSettings } from '../lib/settings';
|
||||
import TheMovieDb from '../api/themoviedb';
|
||||
import logger from '../logger';
|
||||
|
||||
const serviceRoutes = Router();
|
||||
@@ -22,6 +22,7 @@ serviceRoutes.get('/radarr', async (req, res) => {
|
||||
isDefault: radarr.isDefault,
|
||||
activeDirectory: radarr.activeDirectory,
|
||||
activeProfileId: radarr.activeProfileId,
|
||||
activeTags: radarr.tags ?? [],
|
||||
})
|
||||
);
|
||||
|
||||
@@ -46,11 +47,12 @@ serviceRoutes.get<{ radarrId: string }>(
|
||||
|
||||
const radarr = new RadarrAPI({
|
||||
apiKey: radarrSettings.apiKey,
|
||||
url: RadarrAPI.buildRadarrUrl(radarrSettings, '/api/v3'),
|
||||
url: RadarrAPI.buildUrl(radarrSettings, '/api/v3'),
|
||||
});
|
||||
|
||||
const profiles = await radarr.getProfiles();
|
||||
const rootFolders = await radarr.getRootFolders();
|
||||
const tags = await radarr.getTags();
|
||||
|
||||
return res.status(200).json({
|
||||
server: {
|
||||
@@ -60,6 +62,7 @@ serviceRoutes.get<{ radarrId: string }>(
|
||||
isDefault: radarrSettings.isDefault,
|
||||
activeDirectory: radarrSettings.activeDirectory,
|
||||
activeProfileId: radarrSettings.activeProfileId,
|
||||
activeTags: radarrSettings.tags,
|
||||
},
|
||||
profiles: profiles.map((profile) => ({
|
||||
id: profile.id,
|
||||
@@ -71,6 +74,7 @@ serviceRoutes.get<{ radarrId: string }>(
|
||||
path: folder.path,
|
||||
totalSpace: folder.totalSpace,
|
||||
})),
|
||||
tags,
|
||||
} as ServiceCommonServerWithDetails);
|
||||
}
|
||||
);
|
||||
@@ -90,6 +94,7 @@ serviceRoutes.get('/sonarr', async (req, res) => {
|
||||
activeAnimeDirectory: sonarr.activeAnimeDirectory,
|
||||
activeLanguageProfileId: sonarr.activeLanguageProfileId,
|
||||
activeAnimeLanguageProfileId: sonarr.activeAnimeLanguageProfileId,
|
||||
activeTags: [],
|
||||
})
|
||||
);
|
||||
|
||||
@@ -114,13 +119,14 @@ serviceRoutes.get<{ sonarrId: string }>(
|
||||
|
||||
const sonarr = new SonarrAPI({
|
||||
apiKey: sonarrSettings.apiKey,
|
||||
url: SonarrAPI.buildSonarrUrl(sonarrSettings, '/api/v3'),
|
||||
url: SonarrAPI.buildUrl(sonarrSettings, '/api/v3'),
|
||||
});
|
||||
|
||||
try {
|
||||
const profiles = await sonarr.getProfiles();
|
||||
const rootFolders = await sonarr.getRootFolders();
|
||||
const languageProfiles = await sonarr.getLanguageProfiles();
|
||||
const tags = await sonarr.getTags();
|
||||
|
||||
return res.status(200).json({
|
||||
server: {
|
||||
@@ -135,6 +141,8 @@ serviceRoutes.get<{ sonarrId: string }>(
|
||||
activeLanguageProfileId: sonarrSettings.activeLanguageProfileId,
|
||||
activeAnimeLanguageProfileId:
|
||||
sonarrSettings.activeAnimeLanguageProfileId,
|
||||
activeTags: sonarrSettings.tags,
|
||||
activeAnimeTags: sonarrSettings.animeTags,
|
||||
},
|
||||
profiles: profiles.map((profile) => ({
|
||||
id: profile.id,
|
||||
@@ -147,6 +155,7 @@ serviceRoutes.get<{ sonarrId: string }>(
|
||||
totalSpace: folder.totalSpace,
|
||||
})),
|
||||
languageProfiles: languageProfiles,
|
||||
tags,
|
||||
} as ServiceCommonServerWithDetails);
|
||||
} catch (e) {
|
||||
next({ status: 500, message: e.message });
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { Router } from 'express';
|
||||
import RadarrAPI from '../../api/radarr';
|
||||
import RadarrAPI from '../../api/servarr/radarr';
|
||||
import { getSettings, RadarrSettings } from '../../lib/settings';
|
||||
import logger from '../../logger';
|
||||
|
||||
@@ -35,15 +35,20 @@ radarrRoutes.post('/', (req, res) => {
|
||||
return res.status(201).json(newRadarr);
|
||||
});
|
||||
|
||||
radarrRoutes.post('/test', async (req, res, next) => {
|
||||
radarrRoutes.post<
|
||||
undefined,
|
||||
Record<string, unknown>,
|
||||
RadarrSettings & { tagLabel?: string }
|
||||
>('/test', async (req, res, next) => {
|
||||
try {
|
||||
const radarr = new RadarrAPI({
|
||||
apiKey: req.body.apiKey,
|
||||
url: RadarrAPI.buildRadarrUrl(req.body, '/api/v3'),
|
||||
url: RadarrAPI.buildUrl(req.body, '/api/v3'),
|
||||
});
|
||||
|
||||
const profiles = await radarr.getProfiles();
|
||||
const folders = await radarr.getRootFolders();
|
||||
const tags = await radarr.getTags();
|
||||
|
||||
return res.status(200).json({
|
||||
profiles,
|
||||
@@ -51,6 +56,7 @@ radarrRoutes.post('/test', async (req, res, next) => {
|
||||
id: folder.id,
|
||||
path: folder.path,
|
||||
})),
|
||||
tags,
|
||||
});
|
||||
} catch (e) {
|
||||
logger.error('Failed to test Radarr', {
|
||||
@@ -62,40 +68,41 @@ radarrRoutes.post('/test', async (req, res, next) => {
|
||||
}
|
||||
});
|
||||
|
||||
radarrRoutes.put<{ id: string }>('/:id', (req, res) => {
|
||||
const settings = getSettings();
|
||||
radarrRoutes.put<{ id: string }, RadarrSettings, RadarrSettings>(
|
||||
'/:id',
|
||||
(req, res, next) => {
|
||||
const settings = getSettings();
|
||||
|
||||
const radarrIndex = settings.radarr.findIndex(
|
||||
(r) => r.id === Number(req.params.id)
|
||||
);
|
||||
const radarrIndex = settings.radarr.findIndex(
|
||||
(r) => r.id === Number(req.params.id)
|
||||
);
|
||||
|
||||
if (radarrIndex === -1) {
|
||||
return res
|
||||
.status(404)
|
||||
.json({ status: '404', message: 'Settings instance not found' });
|
||||
if (radarrIndex === -1) {
|
||||
return next({ status: '404', message: 'Settings instance not found' });
|
||||
}
|
||||
|
||||
// If we are setting this as the default, clear any previous defaults for the same type first
|
||||
// ex: if is4k is true, it will only remove defaults for other servers that have is4k set to true
|
||||
// and are the default
|
||||
if (req.body.isDefault) {
|
||||
settings.radarr
|
||||
.filter((radarrInstance) => radarrInstance.is4k === req.body.is4k)
|
||||
.forEach((radarrInstance) => {
|
||||
radarrInstance.isDefault = false;
|
||||
});
|
||||
}
|
||||
|
||||
settings.radarr[radarrIndex] = {
|
||||
...req.body,
|
||||
id: Number(req.params.id),
|
||||
} as RadarrSettings;
|
||||
settings.save();
|
||||
|
||||
return res.status(200).json(settings.radarr[radarrIndex]);
|
||||
}
|
||||
);
|
||||
|
||||
// If we are setting this as the default, clear any previous defaults for the same type first
|
||||
// ex: if is4k is true, it will only remove defaults for other servers that have is4k set to true
|
||||
// and are the default
|
||||
if (req.body.isDefault) {
|
||||
settings.radarr
|
||||
.filter((radarrInstance) => radarrInstance.is4k === req.body.is4k)
|
||||
.forEach((radarrInstance) => {
|
||||
radarrInstance.isDefault = false;
|
||||
});
|
||||
}
|
||||
|
||||
settings.radarr[radarrIndex] = {
|
||||
...req.body,
|
||||
id: Number(req.params.id),
|
||||
} as RadarrSettings;
|
||||
settings.save();
|
||||
|
||||
return res.status(200).json(settings.radarr[radarrIndex]);
|
||||
});
|
||||
|
||||
radarrRoutes.get<{ id: string }>('/:id/profiles', async (req, res) => {
|
||||
radarrRoutes.get<{ id: string }>('/:id/profiles', async (req, res, next) => {
|
||||
const settings = getSettings();
|
||||
|
||||
const radarrSettings = settings.radarr.find(
|
||||
@@ -103,14 +110,12 @@ radarrRoutes.get<{ id: string }>('/:id/profiles', async (req, res) => {
|
||||
);
|
||||
|
||||
if (!radarrSettings) {
|
||||
return res
|
||||
.status(404)
|
||||
.json({ status: '404', message: 'Settings instance not found' });
|
||||
return next({ status: '404', message: 'Settings instance not found' });
|
||||
}
|
||||
|
||||
const radarr = new RadarrAPI({
|
||||
apiKey: radarrSettings.apiKey,
|
||||
url: RadarrAPI.buildRadarrUrl(radarrSettings, '/api/v3'),
|
||||
url: RadarrAPI.buildUrl(radarrSettings, '/api/v3'),
|
||||
});
|
||||
|
||||
const profiles = await radarr.getProfiles();
|
||||
@@ -123,7 +128,7 @@ radarrRoutes.get<{ id: string }>('/:id/profiles', async (req, res) => {
|
||||
);
|
||||
});
|
||||
|
||||
radarrRoutes.delete<{ id: string }>('/:id', (req, res) => {
|
||||
radarrRoutes.delete<{ id: string }>('/:id', (req, res, next) => {
|
||||
const settings = getSettings();
|
||||
|
||||
const radarrIndex = settings.radarr.findIndex(
|
||||
@@ -131,9 +136,7 @@ radarrRoutes.delete<{ id: string }>('/:id', (req, res) => {
|
||||
);
|
||||
|
||||
if (radarrIndex === -1) {
|
||||
return res
|
||||
.status(404)
|
||||
.json({ status: '404', message: 'Settings instance not found' });
|
||||
return next({ status: '404', message: 'Settings instance not found' });
|
||||
}
|
||||
|
||||
const removed = settings.radarr.splice(radarrIndex, 1);
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { Router } from 'express';
|
||||
import SonarrAPI from '../../api/sonarr';
|
||||
import SonarrAPI from '../../api/servarr/sonarr';
|
||||
import { getSettings, SonarrSettings } from '../../lib/settings';
|
||||
import logger from '../../logger';
|
||||
|
||||
@@ -39,12 +39,13 @@ sonarrRoutes.post('/test', async (req, res, next) => {
|
||||
try {
|
||||
const sonarr = new SonarrAPI({
|
||||
apiKey: req.body.apiKey,
|
||||
url: SonarrAPI.buildSonarrUrl(req.body, '/api/v3'),
|
||||
url: SonarrAPI.buildUrl(req.body, '/api/v3'),
|
||||
});
|
||||
|
||||
const profiles = await sonarr.getProfiles();
|
||||
const folders = await sonarr.getRootFolders();
|
||||
const languageProfiles = await sonarr.getLanguageProfiles();
|
||||
const tags = await sonarr.getTags();
|
||||
|
||||
return res.status(200).json({
|
||||
profiles,
|
||||
@@ -53,6 +54,7 @@ sonarrRoutes.post('/test', async (req, res, next) => {
|
||||
path: folder.path,
|
||||
})),
|
||||
languageProfiles,
|
||||
tags,
|
||||
});
|
||||
} catch (e) {
|
||||
logger.error('Failed to test Sonarr', {
|
||||
|
Reference in New Issue
Block a user