Add Lidarr/Readarr backend support
- Add MUSIC and BOOK to MediaType enum - Add permission flags for music/book requests - Create Lidarr API adapter (artist/album search, add, remove) - Create Readarr API adapter (book/author search, add, remove) - Add Lidarr/Readarr settings interfaces and routes - Add music and book API routes for search/detail - Register all new routes in main router and settings router
This commit is contained in:
@@ -41,7 +41,9 @@ import semver from 'semver';
|
||||
import { URL } from 'url';
|
||||
import metadataRoutes from './metadata';
|
||||
import notificationRoutes from './notifications';
|
||||
import lidarrRoutes from './lidarr';
|
||||
import radarrRoutes from './radarr';
|
||||
import readarrRoutes from './readarr';
|
||||
import sonarrRoutes from './sonarr';
|
||||
|
||||
const settingsRoutes = Router();
|
||||
@@ -49,6 +51,8 @@ const settingsRoutes = Router();
|
||||
settingsRoutes.use('/notifications', notificationRoutes);
|
||||
settingsRoutes.use('/radarr', radarrRoutes);
|
||||
settingsRoutes.use('/sonarr', sonarrRoutes);
|
||||
settingsRoutes.use('/lidarr', lidarrRoutes);
|
||||
settingsRoutes.use('/readarr', readarrRoutes);
|
||||
settingsRoutes.use('/discover', discoverSettingRoutes);
|
||||
settingsRoutes.use('/metadatas', metadataRoutes);
|
||||
|
||||
|
||||
106
server/routes/settings/lidarr.ts
Normal file
106
server/routes/settings/lidarr.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
import LidarrAPI from '@server/api/servarr/lidarr';
|
||||
import type { LidarrSettings } from '@server/lib/settings';
|
||||
import { getSettings } from '@server/lib/settings';
|
||||
import logger from '@server/logger';
|
||||
import { Router } from 'express';
|
||||
|
||||
const lidarrRoutes = Router();
|
||||
|
||||
lidarrRoutes.get('/', (_req, res) => {
|
||||
const settings = getSettings();
|
||||
res.status(200).json(settings.lidarr);
|
||||
});
|
||||
|
||||
lidarrRoutes.post('/', async (req, res) => {
|
||||
const settings = getSettings();
|
||||
const newLidarr = req.body as LidarrSettings;
|
||||
const lastItem = settings.lidarr[settings.lidarr.length - 1];
|
||||
newLidarr.id = lastItem ? lastItem.id + 1 : 0;
|
||||
|
||||
if (req.body.isDefault) {
|
||||
settings.lidarr.forEach((instance) => {
|
||||
instance.isDefault = false;
|
||||
});
|
||||
}
|
||||
|
||||
settings.lidarr = [...settings.lidarr, newLidarr];
|
||||
await settings.save();
|
||||
return res.status(201).json(newLidarr);
|
||||
});
|
||||
|
||||
lidarrRoutes.post<
|
||||
undefined,
|
||||
Record<string, unknown>,
|
||||
LidarrSettings
|
||||
>('/test', async (req, res, next) => {
|
||||
try {
|
||||
const lidarr = new LidarrAPI({
|
||||
apiKey: req.body.apiKey,
|
||||
url: LidarrAPI.buildUrl(req.body, '/api/v1'),
|
||||
});
|
||||
|
||||
const profiles = await lidarr.getProfiles();
|
||||
const folders = await lidarr.getRootFolders();
|
||||
const tags = await lidarr.getTags();
|
||||
const metadataProfiles = await lidarr.getMetadataProfiles();
|
||||
|
||||
return res.status(200).json({
|
||||
profiles,
|
||||
rootFolders: folders.map((folder) => ({
|
||||
id: folder.id,
|
||||
path: folder.path,
|
||||
})),
|
||||
tags,
|
||||
metadataProfiles,
|
||||
});
|
||||
} catch (e) {
|
||||
logger.error('Failed to test Lidarr', {
|
||||
label: 'Lidarr',
|
||||
message: e.message,
|
||||
});
|
||||
next({ status: 500, message: 'Failed to connect to Lidarr' });
|
||||
}
|
||||
});
|
||||
|
||||
lidarrRoutes.put<{ id: string }>('/:id', async (req, res, next) => {
|
||||
const settings = getSettings();
|
||||
const lidarrIndex = settings.lidarr.findIndex(
|
||||
(r) => r.id === Number(req.params.id)
|
||||
);
|
||||
|
||||
if (lidarrIndex === -1) {
|
||||
return next({ status: 404, message: 'Lidarr server not found.' });
|
||||
}
|
||||
|
||||
if (req.body.isDefault) {
|
||||
settings.lidarr.forEach((instance) => {
|
||||
instance.isDefault = false;
|
||||
});
|
||||
}
|
||||
|
||||
settings.lidarr[lidarrIndex] = {
|
||||
...settings.lidarr[lidarrIndex],
|
||||
...req.body,
|
||||
id: Number(req.params.id),
|
||||
} as LidarrSettings;
|
||||
|
||||
await settings.save();
|
||||
return res.status(200).json(settings.lidarr[lidarrIndex]);
|
||||
});
|
||||
|
||||
lidarrRoutes.delete<{ id: string }>('/:id', async (req, res, next) => {
|
||||
const settings = getSettings();
|
||||
const lidarrIndex = settings.lidarr.findIndex(
|
||||
(r) => r.id === Number(req.params.id)
|
||||
);
|
||||
|
||||
if (lidarrIndex === -1) {
|
||||
return next({ status: 404, message: 'Lidarr server not found.' });
|
||||
}
|
||||
|
||||
const removed = settings.lidarr.splice(lidarrIndex, 1);
|
||||
await settings.save();
|
||||
return res.status(200).json(removed[0]);
|
||||
});
|
||||
|
||||
export default lidarrRoutes;
|
||||
106
server/routes/settings/readarr.ts
Normal file
106
server/routes/settings/readarr.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
import ReadarrAPI from '@server/api/servarr/readarr';
|
||||
import type { ReadarrSettings } from '@server/lib/settings';
|
||||
import { getSettings } from '@server/lib/settings';
|
||||
import logger from '@server/logger';
|
||||
import { Router } from 'express';
|
||||
|
||||
const readarrRoutes = Router();
|
||||
|
||||
readarrRoutes.get('/', (_req, res) => {
|
||||
const settings = getSettings();
|
||||
res.status(200).json(settings.readarr);
|
||||
});
|
||||
|
||||
readarrRoutes.post('/', async (req, res) => {
|
||||
const settings = getSettings();
|
||||
const newReadarr = req.body as ReadarrSettings;
|
||||
const lastItem = settings.readarr[settings.readarr.length - 1];
|
||||
newReadarr.id = lastItem ? lastItem.id + 1 : 0;
|
||||
|
||||
if (req.body.isDefault) {
|
||||
settings.readarr.forEach((instance) => {
|
||||
instance.isDefault = false;
|
||||
});
|
||||
}
|
||||
|
||||
settings.readarr = [...settings.readarr, newReadarr];
|
||||
await settings.save();
|
||||
return res.status(201).json(newReadarr);
|
||||
});
|
||||
|
||||
readarrRoutes.post<
|
||||
undefined,
|
||||
Record<string, unknown>,
|
||||
ReadarrSettings
|
||||
>('/test', async (req, res, next) => {
|
||||
try {
|
||||
const readarr = new ReadarrAPI({
|
||||
apiKey: req.body.apiKey,
|
||||
url: ReadarrAPI.buildUrl(req.body, '/api/v1'),
|
||||
});
|
||||
|
||||
const profiles = await readarr.getProfiles();
|
||||
const folders = await readarr.getRootFolders();
|
||||
const tags = await readarr.getTags();
|
||||
const metadataProfiles = await readarr.getMetadataProfiles();
|
||||
|
||||
return res.status(200).json({
|
||||
profiles,
|
||||
rootFolders: folders.map((folder) => ({
|
||||
id: folder.id,
|
||||
path: folder.path,
|
||||
})),
|
||||
tags,
|
||||
metadataProfiles,
|
||||
});
|
||||
} catch (e) {
|
||||
logger.error('Failed to test Readarr', {
|
||||
label: 'Readarr',
|
||||
message: e.message,
|
||||
});
|
||||
next({ status: 500, message: 'Failed to connect to Readarr' });
|
||||
}
|
||||
});
|
||||
|
||||
readarrRoutes.put<{ id: string }>('/:id', async (req, res, next) => {
|
||||
const settings = getSettings();
|
||||
const readarrIndex = settings.readarr.findIndex(
|
||||
(r) => r.id === Number(req.params.id)
|
||||
);
|
||||
|
||||
if (readarrIndex === -1) {
|
||||
return next({ status: 404, message: 'Readarr server not found.' });
|
||||
}
|
||||
|
||||
if (req.body.isDefault) {
|
||||
settings.readarr.forEach((instance) => {
|
||||
instance.isDefault = false;
|
||||
});
|
||||
}
|
||||
|
||||
settings.readarr[readarrIndex] = {
|
||||
...settings.readarr[readarrIndex],
|
||||
...req.body,
|
||||
id: Number(req.params.id),
|
||||
} as ReadarrSettings;
|
||||
|
||||
await settings.save();
|
||||
return res.status(200).json(settings.readarr[readarrIndex]);
|
||||
});
|
||||
|
||||
readarrRoutes.delete<{ id: string }>('/:id', async (req, res, next) => {
|
||||
const settings = getSettings();
|
||||
const readarrIndex = settings.readarr.findIndex(
|
||||
(r) => r.id === Number(req.params.id)
|
||||
);
|
||||
|
||||
if (readarrIndex === -1) {
|
||||
return next({ status: 404, message: 'Readarr server not found.' });
|
||||
}
|
||||
|
||||
const removed = settings.readarr.splice(readarrIndex, 1);
|
||||
await settings.save();
|
||||
return res.status(200).json(removed[0]);
|
||||
});
|
||||
|
||||
export default readarrRoutes;
|
||||
Reference in New Issue
Block a user