Files
Homarr/src/pages/api/modules/media-requests/index.ts

166 lines
4.6 KiB
TypeScript
Raw Normal View History

2023-04-04 22:32:08 +02:00
import { getCookie } from 'cookies-next';
import Consola from 'consola';
import { NextApiRequest, NextApiResponse } from 'next';
import { getConfig } from '../../../../tools/config/getConfig';
import { MediaRequest } from '../../../../widgets/media-requests/media-request-types';
import { ConfigAppType } from '../../../../types/app';
2023-04-04 22:32:08 +02:00
const Get = async (request: NextApiRequest, response: NextApiResponse) => {
const configName = getCookie('config-name', { req: request });
const config = getConfig(configName?.toString() ?? 'default');
const apps = config.apps.filter((app) =>
['overseerr', 'jellyseerr'].includes(app.integration?.type ?? '')
);
2023-04-07 20:06:27 +02:00
Consola.log(`Retrieving media requests from ${apps.length} apps`);
2023-04-04 22:32:08 +02:00
const promises = apps.map((app): Promise<MediaRequest[]> => {
const apiKey = app.integration?.properties.find((prop) => prop.field === 'apiKey')?.value ?? '';
const headers: HeadersInit = { 'X-Api-Key': apiKey };
return fetch(`${app.url}/api/v1/request?take=25&skip=0&sort=added`, {
headers,
})
.then(async (response) => {
const body = (await response.json()) as OverseerrResponse;
const requests = await Promise.all(
body.results.map(async (item): Promise<MediaRequest> => {
const genericItem = await retrieveDetailsForItem(
app.url,
item.type,
headers,
item.media.tmdbId
);
return {
appId: app.id,
createdAt: item.createdAt,
id: item.id,
rootFolder: item.rootFolder,
type: item.type,
name: genericItem.name,
userName: item.requestedBy.displayName,
2023-04-07 20:06:27 +02:00
userProfilePicture: constructAvatarUrl(app, item),
userLink: `${app.url}/users/${item.requestedBy.id}`,
2023-04-04 22:32:08 +02:00
airDate: genericItem.airDate,
status: item.status,
backdropPath: `https://image.tmdb.org/t/p/original/${genericItem.backdropPath}`,
posterPath: `https://image.tmdb.org/t/p/w600_and_h900_bestv2/${genericItem.posterPath}`,
href: `${app.url}/movie/${item.media.tmdbId}`,
};
})
);
return Promise.resolve(requests);
})
.catch((err) => {
Consola.error(`Failed to request data from Overseerr: ${err}`);
return Promise.resolve([]);
});
});
2023-04-11 17:38:10 +02:00
const mediaRequests = (await Promise.all(promises)).reduce((prev, cur) => prev.concat(cur), []);
2023-04-04 22:32:08 +02:00
return response.status(200).json(mediaRequests);
};
const constructAvatarUrl = (app: ConfigAppType, item: OverseerrResponseItem) => {
2023-04-07 20:06:27 +02:00
const isAbsolute =
item.requestedBy.avatar.startsWith('http://') || item.requestedBy.avatar.startsWith('https://');
if (isAbsolute) {
return item.requestedBy.avatar;
}
2023-04-07 20:06:27 +02:00
return `${app.url}/${item.requestedBy.avatar}`;
};
2023-04-04 22:32:08 +02:00
const retrieveDetailsForItem = async (
baseUrl: string,
type: OverseerrResponseItem['type'],
headers: HeadersInit,
id: number
): Promise<GenericOverseerrItem> => {
if (type === 'tv') {
const tvResponse = await fetch(`${baseUrl}/api/v1/tv/${id}`, {
headers,
});
const series = (await tvResponse.json()) as OverseerrSeries;
return {
name: series.name,
airDate: series.firstAirDate,
backdropPath: series.backdropPath,
posterPath: series.backdropPath,
};
}
const movieResponse = await fetch(`${baseUrl}/api/v1/movie/${id}`, {
headers,
});
const movie = (await movieResponse.json()) as OverseerrMovie;
return {
name: movie.originalTitle,
airDate: movie.releaseDate,
backdropPath: movie.backdropPath,
posterPath: movie.posterPath,
};
};
type GenericOverseerrItem = {
name: string;
airDate: string;
backdropPath: string;
posterPath: string;
};
type OverseerrMovie = {
originalTitle: string;
releaseDate: string;
backdropPath: string;
posterPath: string;
};
type OverseerrSeries = {
name: string;
firstAirDate: string;
backdropPath: string;
posterPath: string;
};
type OverseerrResponse = {
results: OverseerrResponseItem[];
};
type OverseerrResponseItem = {
id: number;
status: number;
createdAt: string;
type: 'movie' | 'tv';
rootFolder: string;
requestedBy: OverseerrResponseItemUser;
media: OverseerrResponseItemMedia;
};
type OverseerrResponseItemMedia = {
tmdbId: number;
};
type OverseerrResponseItemUser = {
id: number;
displayName: string;
avatar: string;
};
export default async (request: NextApiRequest, response: NextApiResponse) => {
if (request.method === 'GET') {
return Get(request, response);
}
return response.status(405);
};