feat(api): decouple media requests from media info

This commit is contained in:
sct
2020-09-21 00:01:38 +09:00
parent 4aa74319e0
commit 8577db1be1
21 changed files with 409 additions and 264 deletions

19
server/constants/media.ts Normal file
View File

@@ -0,0 +1,19 @@
export enum MediaRequestStatus {
PENDING = 1,
APPROVED,
DECLINED,
AVAILABLE,
}
export enum MediaType {
MOVIE = 'movie',
TV = 'tv',
}
export enum MediaStatus {
UNKNOWN = 1,
PENDING,
PROCESSING,
PARTIALLY_AVAILABLE,
AVAILABLE,
}

87
server/entity/Media.ts Normal file
View File

@@ -0,0 +1,87 @@
import {
Entity,
PrimaryGeneratedColumn,
Column,
Index,
OneToMany,
CreateDateColumn,
UpdateDateColumn,
getRepository,
In,
} from 'typeorm';
import { MediaRequest } from './MediaRequest';
import { MediaStatus, MediaType } from '../constants/media';
@Entity()
class Media {
public static async getRelatedMedia(
tmdbIds: number | number[]
): Promise<Media[]> {
const mediaRepository = getRepository(Media);
try {
let finalIds: number[];
if (!Array.isArray(tmdbIds)) {
finalIds = [tmdbIds];
} else {
finalIds = tmdbIds;
}
const media = await mediaRepository.find({
tmdbId: In(finalIds),
});
return media;
} catch (e) {
console.error(e.messaage);
return [];
}
}
public static async getMedia(id: number): Promise<Media | undefined> {
const mediaRepository = getRepository(Media);
try {
const media = await mediaRepository.findOneOrFail({
where: { tmdbId: id },
});
return media;
} catch (e) {
console.error(e.messaage);
return undefined;
}
}
@PrimaryGeneratedColumn()
public id: number;
@Column({ type: 'varchar' })
public mediaType: MediaType;
@Column({ unique: true })
@Index()
public tmdbId: number;
@Column({ unique: true, nullable: true })
@Index()
public tvdbId: number;
@Column({ type: 'int', default: MediaStatus.UNKNOWN })
public status: MediaStatus;
@OneToMany(() => MediaRequest, (request) => request.media)
public requests: MediaRequest;
@CreateDateColumn()
public createdAt: Date;
@UpdateDateColumn()
public updatedAt: Date;
constructor(init?: Partial<Media>) {
Object.assign(this, init);
}
}
export default Media;

View File

@@ -5,144 +5,53 @@ import {
Column,
CreateDateColumn,
UpdateDateColumn,
getRepository,
In,
Index,
TableInheritance,
AfterUpdate,
AfterInsert,
getRepository,
} from 'typeorm';
import { User } from './User';
import RadarrAPI from '../api/radarr';
import { getSettings } from '../lib/settings';
import TheMovieDb from '../api/themoviedb';
export enum MediaRequestStatus {
PENDING = 1,
APPROVED,
DECLINED,
AVAILABLE,
}
import Media from './Media';
import { MediaStatus, MediaRequestStatus, MediaType } from '../constants/media';
@Entity()
@TableInheritance({ column: { type: 'varchar', name: 'type' } })
export class MediaRequest {
public static async getRelatedRequests(
mediaIds: number | number[]
): Promise<MediaRequest[]> {
const requestRepository = getRepository(MediaRequest);
try {
let finalIds: number[];
if (!Array.isArray(mediaIds)) {
finalIds = [mediaIds];
} else {
finalIds = mediaIds;
}
const requests = await requestRepository.find({
mediaId: In(finalIds),
});
return requests;
} catch (e) {
console.error(e.messaage);
return [];
}
}
public static async getRequest(
id: number
): Promise<MediaRequest | undefined> {
const requestRepository = getRepository(MediaRequest);
try {
const request = await requestRepository.findOneOrFail({
where: { mediaId: id },
});
return request;
} catch (e) {
console.error(e.messaage);
return undefined;
}
}
@PrimaryGeneratedColumn()
public id: number;
@Column({ unique: true })
@Index()
public mediaId: number;
@Column({ unique: true, nullable: true })
@Index()
public tvdbId: number;
@Column({ nullable: true })
public seasons?: string;
@Column()
public mediaType: 'movie' | 'tv';
@Column({ type: 'integer' })
public status: MediaRequestStatus;
@ManyToOne(() => Media, (media) => media.requests, { eager: true })
public media: Media;
@ManyToOne(() => User, (user) => user.requests, { eager: true })
public requestedBy: User;
@ManyToOne(() => User, { nullable: true })
public modifiedBy?: User;
@CreateDateColumn()
public createdAt: Date;
@UpdateDateColumn()
public updatedAt: Date;
@Column()
public type: MediaType;
constructor(init?: Partial<MediaRequest>) {
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}`
);
}
private async updateParentStatus() {
const mediaRepository = getRepository(Media);
if (this.status === MediaRequestStatus.APPROVED) {
this.media.status = MediaStatus.PROCESSING;
mediaRepository.save(this.media);
}
}
}

View File

@@ -0,0 +1,57 @@
import { MediaRequest } from './MediaRequest';
import { ChildEntity, AfterUpdate, AfterInsert } from 'typeorm';
import TheMovieDb from '../api/themoviedb';
import RadarrAPI from '../api/radarr';
import { getSettings } from '../lib/settings';
import { MediaType, MediaRequestStatus } from '../constants/media';
@ChildEntity(MediaType.MOVIE)
class MovieRequest extends MediaRequest {
constructor(init?: Partial<MovieRequest>) {
super(init);
}
@AfterUpdate()
@AfterInsert()
private async sendToRadarr() {
if (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.media.tmdbId });
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}`
);
}
}
}
}
export default MovieRequest;

View File

@@ -0,0 +1,37 @@
import {
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
UpdateDateColumn,
ManyToOne,
} from 'typeorm';
import TvRequest from './TvRequest';
import { MediaRequestStatus } from '../constants/media';
@Entity()
class SeasonRequest {
@PrimaryGeneratedColumn()
public id: number;
@Column()
public seasonNumber: number;
@Column({ type: 'int', default: MediaRequestStatus.PENDING })
public status: MediaRequestStatus;
@ManyToOne(() => TvRequest, (request) => request.seasons)
public request: TvRequest;
@CreateDateColumn()
public createdAt: Date;
@UpdateDateColumn()
public updatedAt: Date;
constructor(init?: Partial<SeasonRequest>) {
Object.assign(this, init);
}
}
export default SeasonRequest;

View File

@@ -0,0 +1,16 @@
import { MediaRequest } from './MediaRequest';
import { ChildEntity, OneToMany } from 'typeorm';
import SeasonRequest from './SeasonRequest';
import { MediaType } from '../constants/media';
@ChildEntity(MediaType.TV)
class TvRequest extends MediaRequest {
@OneToMany(() => SeasonRequest, (season) => season.request)
public seasons: SeasonRequest[];
constructor(init?: Partial<TvRequest>) {
super(init);
}
}
export default TvRequest;

View File

@@ -10,6 +10,7 @@ import {
ExternalIds,
mapExternalIds,
} from './common';
import Media from '../entity/Media';
export interface MovieDetails {
id: number;
@@ -46,13 +47,13 @@ export interface MovieDetails {
cast: Cast[];
crew: Crew[];
};
request?: MediaRequest;
mediaInfo?: Media;
externalIds: ExternalIds;
}
export const mapMovieDetails = (
movie: TmdbMovieDetails,
request?: MediaRequest
media?: Media
): MovieDetails => ({
id: movie.id,
adult: movie.adult,
@@ -88,5 +89,5 @@ export const mapMovieDetails = (
crew: movie.credits.crew.map(mapCrew),
},
externalIds: mapExternalIds(movie.external_ids),
request,
mediaInfo: media,
});

View File

@@ -4,6 +4,7 @@ import type {
TmdbTvResult,
} from '../api/themoviedb';
import type { MediaRequest } from '../entity/MediaRequest';
import Media from '../entity/Media';
export type MediaType = 'tv' | 'movie' | 'person';
@@ -18,7 +19,7 @@ interface SearchResult {
genreIds: number[];
overview: string;
originalLanguage: string;
request?: MediaRequest;
mediaInfo?: Media;
}
export interface MovieResult extends SearchResult {
@@ -28,7 +29,7 @@ export interface MovieResult extends SearchResult {
releaseDate: string;
adult: boolean;
video: boolean;
request?: MediaRequest;
mediaInfo?: Media;
}
export interface TvResult extends SearchResult {
@@ -53,7 +54,7 @@ export type Results = MovieResult | TvResult | PersonResult;
export const mapMovieResult = (
movieResult: TmdbMovieResult,
request?: MediaRequest
media?: Media
): MovieResult => ({
id: movieResult.id,
mediaType: 'movie',
@@ -70,12 +71,12 @@ export const mapMovieResult = (
voteCount: movieResult.vote_count,
backdropPath: movieResult.backdrop_path,
posterPath: movieResult.poster_path,
request,
mediaInfo: media,
});
export const mapTvResult = (
tvResult: TmdbTvResult,
request?: MediaRequest
media?: Media
): TvResult => ({
id: tvResult.id,
firstAirDate: tvResult.first_air_Date,
@@ -92,7 +93,7 @@ export const mapTvResult = (
voteCount: tvResult.vote_count,
backdropPath: tvResult.backdrop_path,
posterPath: tvResult.poster_path,
request,
mediaInfo: media,
});
export const mapPersonResult = (
@@ -115,19 +116,19 @@ export const mapPersonResult = (
export const mapSearchResults = (
results: (TmdbMovieResult | TmdbTvResult | TmdbPersonResult)[],
requests?: MediaRequest[]
media?: Media[]
): Results[] =>
results.map((result) => {
switch (result.media_type) {
case 'movie':
return mapMovieResult(
result,
requests?.find((req) => req.mediaId === result.id)
media?.find((req) => req.tmdbId === result.id)
);
case 'tv':
return mapTvResult(
result,
requests?.find((req) => req.mediaId === result.id)
media?.find((req) => req.tmdbId === result.id)
);
default:
return mapPersonResult(result);

View File

@@ -8,12 +8,12 @@ import {
ExternalIds,
mapExternalIds,
} from './common';
import { MediaRequest } from '../entity/MediaRequest';
import {
TmdbTvEpisodeDetails,
TmdbTvSeasonDetails,
TmdbTvDetails,
} from '../api/themoviedb';
import type Media from '../entity/Media';
interface Episode {
id: number;
@@ -78,7 +78,7 @@ export interface TvDetails {
crew: Crew[];
};
externalIds: ExternalIds;
request?: MediaRequest;
mediaInfo?: Media;
}
const mapEpisodeDetails = (episode: TmdbTvEpisodeDetails): Episode => ({
@@ -107,7 +107,7 @@ const mapSeasonDetails = (season: TmdbTvSeasonDetails): Season => ({
export const mapTvDetails = (
show: TmdbTvDetails,
request?: MediaRequest
media?: Media
): TvDetails => ({
createdBy: show.created_by,
episodeRunTime: show.episode_run_time,
@@ -159,5 +159,5 @@ export const mapTvDetails = (
crew: show.credits.crew.map(mapCrew),
},
externalIds: mapExternalIds(show.external_ids),
request,
mediaInfo: media,
});

View File

@@ -263,8 +263,8 @@ components:
video:
type: boolean
example: false
request:
$ref: '#/components/schemas/MediaRequest'
mediaInfo:
$ref: '#/components/schemas/MediaInfo'
TvResult:
type: object
properties:
@@ -306,8 +306,8 @@ components:
type: string
firstAirDate:
type: string
request:
$ref: '#/components/schemas/MediaRequest'
mediaInfo:
$ref: '#/components/schemas/MediaInfo'
PersonResult:
type: object
properties:
@@ -435,8 +435,8 @@ components:
$ref: '#/components/schemas/Crew'
externalIds:
$ref: '#/components/schemas/ExternalIds'
request:
$ref: '#/components/schemas/MediaRequest'
mediaInfo:
$ref: '#/components/schemas/MediaInfo'
Episode:
type: object
properties:
@@ -577,8 +577,8 @@ components:
$ref: '#/components/schemas/Crew'
externalIds:
$ref: '#/components/schemas/ExternalIds'
request:
$ref: '#/components/schemas/MediaRequest'
mediaInfo:
$ref: '#/components/schemas/MediaInfo'
MediaRequest:
type: object
properties:
@@ -586,18 +586,13 @@ components:
type: number
example: 123
readOnly: true
mediaId:
type: number
example: 123
description: TMDB Movie ID
mediaType:
type: string
enum: [movie, tv]
status:
type: number
example: 0
description: Status of the request. 0 = PENDING APPROVAL, 1 = APPROVED, 2 = DECLINED, 3 = AVAILABLE
description: Status of the request. 1 = PENDING APPROVAL, 2 = APPROVED, 3 = DECLINED, 4 = AVAILABLE
readOnly: true
media:
$ref: '#/components/schemas/MediaInfo'
createdAt:
type: string
example: '2020-09-12T10:00:27.000Z'
@@ -616,9 +611,34 @@ components:
nullable: true
required:
- id
- mediaId
- mediaType
- status
MediaInfo:
type: object
properties:
id:
type: number
readOnly: true
tmdbId:
type: number
readOnly: true
tvdbId:
type: number
readOnly: true
status:
type: number
requests:
type: array
readOnly: true
items:
$ref: '#/components/schemas/MediaRequest'
createdAt:
type: string
example: '2020-09-12T10:00:27.000Z'
readOnly: true
updatedAt:
type: string
example: '2020-09-12T10:00:27.000Z'
readOnly: true
Cast:
type: object
properties:

View File

@@ -1,7 +1,7 @@
import { Router } from 'express';
import TheMovieDb from '../api/themoviedb';
import { mapMovieResult, mapTvResult } from '../models/Search';
import { MediaRequest } from '../entity/MediaRequest';
import Media from '../entity/Media';
const discoverRoutes = Router();
@@ -13,7 +13,7 @@ discoverRoutes.get('/movies', async (req, res) => {
language: req.query.language as string,
});
const requests = await MediaRequest.getRelatedRequests(
const media = await Media.getRelatedMedia(
data.results.map((result) => result.id)
);
@@ -24,7 +24,7 @@ discoverRoutes.get('/movies', async (req, res) => {
results: data.results.map((result) =>
mapMovieResult(
result,
requests.find((req) => req.mediaId === result.id)
media.find((req) => req.tmdbId === result.id)
)
),
});
@@ -38,7 +38,7 @@ discoverRoutes.get('/tv', async (req, res) => {
language: req.query.language as string,
});
const requests = await MediaRequest.getRelatedRequests(
const media = await Media.getRelatedMedia(
data.results.map((result) => result.id)
);
@@ -49,7 +49,7 @@ discoverRoutes.get('/tv', async (req, res) => {
results: data.results.map((result) =>
mapTvResult(
result,
requests.find((req) => req.mediaId === result.id)
media.find((req) => req.tmdbId === result.id)
)
),
});

View File

@@ -3,6 +3,7 @@ import TheMovieDb from '../api/themoviedb';
import { mapMovieDetails } from '../models/Movie';
import { MediaRequest } from '../entity/MediaRequest';
import { mapMovieResult } from '../models/Search';
import Media from '../entity/Media';
const movieRoutes = Router();
@@ -14,9 +15,9 @@ movieRoutes.get('/:id', async (req, res) => {
language: req.query.language as string,
});
const request = await MediaRequest.getRequest(movie.id);
const media = await Media.getMedia(movie.id);
return res.status(200).json(mapMovieDetails(movie, request));
return res.status(200).json(mapMovieDetails(movie, media));
});
movieRoutes.get('/:id/recommendations', async (req, res) => {
@@ -28,7 +29,7 @@ movieRoutes.get('/:id/recommendations', async (req, res) => {
language: req.query.language as string,
});
const requests = await MediaRequest.getRelatedRequests(
const media = await Media.getRelatedMedia(
results.results.map((result) => result.id)
);
@@ -39,7 +40,7 @@ movieRoutes.get('/:id/recommendations', async (req, res) => {
results: results.results.map((result) =>
mapMovieResult(
result,
requests.find((req) => req.mediaId === result.id)
media.find((req) => req.tmdbId === result.id)
)
),
});
@@ -54,7 +55,7 @@ movieRoutes.get('/:id/similar', async (req, res) => {
language: req.query.language as string,
});
const requests = await MediaRequest.getRelatedRequests(
const media = await Media.getRelatedMedia(
results.results.map((result) => result.id)
);
@@ -65,7 +66,7 @@ movieRoutes.get('/:id/similar', async (req, res) => {
results: results.results.map((result) =>
mapMovieResult(
result,
requests.find((req) => req.mediaId === result.id)
media.find((req) => req.tmdbId === result.id)
)
),
});

View File

@@ -2,8 +2,12 @@ import { Router } from 'express';
import { isAuthenticated } from '../middleware/auth';
import { Permission } from '../lib/permissions';
import { getRepository } from 'typeorm';
import { MediaRequest, MediaRequestStatus } from '../entity/MediaRequest';
import { MediaRequest } from '../entity/MediaRequest';
import TheMovieDb from '../api/themoviedb';
import Media from '../entity/Media';
import MovieRequest from '../entity/MovieRequest';
import { MediaStatus, MediaRequestStatus, MediaType } from '../constants/media';
import TvRequest from '../entity/TvRequest';
const requestRoutes = Router();
@@ -15,10 +19,12 @@ requestRoutes.get('/', async (req, res, next) => {
order: {
id: 'DESC',
},
relations: ['media'],
take: 20,
})
: await requestRepository.find({
where: { requestedBy: { id: req.user?.id } },
relations: ['media'],
order: {
id: 'DESC',
},
@@ -36,26 +42,59 @@ requestRoutes.post(
isAuthenticated(Permission.REQUEST),
async (req, res, next) => {
const tmdb = new TheMovieDb();
const requestRepository = getRepository(MediaRequest);
const mediaRepository = getRepository(Media);
try {
const media =
const tmdbMedia =
req.body.mediaType === 'movie'
? await tmdb.getMovie({ movieId: req.body.mediaId })
: await tmdb.getTvShow({ tvId: req.body.mediaId });
const request = new MediaRequest({
mediaId: media.id,
mediaType: req.body.mediaType,
requestedBy: req.user,
// If the user is an admin or has the "auto approve" permission, automatically approve the request
status: req.user?.hasPermission(Permission.AUTO_APPROVE)
? MediaRequestStatus.APPROVED
: MediaRequestStatus.PENDING,
let media = await mediaRepository.findOne({
where: { tmdbId: req.body.mediaId },
});
await requestRepository.save(request);
if (!media) {
media = new Media({
tmdbId: tmdbMedia.id,
tvdbId: tmdbMedia.external_ids.tvdb_id,
status: MediaStatus.PENDING,
mediaType: req.body.mediaType,
});
await mediaRepository.save(media);
}
return res.status(201).json(request);
if (req.body.mediaType === 'movie') {
const requestRepository = getRepository(MovieRequest);
const request = new MovieRequest({
media,
requestedBy: req.user,
// If the user is an admin or has the "auto approve" permission, automatically approve the request
status: req.user?.hasPermission(Permission.AUTO_APPROVE)
? MediaRequestStatus.APPROVED
: MediaRequestStatus.PENDING,
});
await requestRepository.save(request);
return res.status(201).json(request);
} else if (req.body.mediaType === 'tv') {
const requestRepository = getRepository(TvRequest);
const request = new TvRequest({
media,
requestedBy: req.user,
// If the user is an admin or has the "auto approve" permission, automatically approve the request
status: req.user?.hasPermission(Permission.AUTO_APPROVE)
? MediaRequestStatus.APPROVED
: MediaRequestStatus.PENDING,
});
await requestRepository.save(request);
return res.status(201).json(request);
}
next({ status: 500, message: 'Invalid media type' });
} catch (e) {
next({ message: e.message, status: 500 });
}
@@ -96,7 +135,7 @@ requestRoutes.delete('/:requestId', async (req, res, next) => {
});
}
requestRepository.delete(request.id);
await requestRepository.delete(request.id);
return res.status(200).json(request);
} catch (e) {
@@ -106,7 +145,7 @@ requestRoutes.delete('/:requestId', async (req, res, next) => {
requestRoutes.get<{
requestId: string;
status: 'pending' | 'approve' | 'decline' | 'available';
status: 'pending' | 'approve' | 'decline';
}>(
'/:requestId/:status',
isAuthenticated(Permission.MANAGE_REQUESTS),
@@ -131,9 +170,6 @@ requestRoutes.get<{
case 'decline':
newStatus = MediaRequestStatus.DECLINED;
break;
case 'available':
newStatus = MediaRequestStatus.AVAILABLE;
break;
}
request.status = newStatus;

View File

@@ -1,7 +1,7 @@
import { Router } from 'express';
import TheMovieDb from '../api/themoviedb';
import { mapSearchResults } from '../models/Search';
import { MediaRequest } from '../entity/MediaRequest';
import Media from '../entity/Media';
const searchRoutes = Router();
@@ -14,7 +14,7 @@ searchRoutes.get('/', async (req, res) => {
language: req.query.language as string,
});
const requests = await MediaRequest.getRelatedRequests(
const media = await Media.getRelatedMedia(
results.results.map((result) => result.id)
);
@@ -22,7 +22,7 @@ searchRoutes.get('/', async (req, res) => {
page: results.page,
totalPages: results.total_pages,
totalResults: results.total_results,
results: mapSearchResults(results.results, requests),
results: mapSearchResults(results.results, media),
});
});

View File

@@ -3,6 +3,7 @@ import TheMovieDb from '../api/themoviedb';
import { MediaRequest } from '../entity/MediaRequest';
import { mapTvDetails } from '../models/Tv';
import { mapTvResult } from '../models/Search';
import Media from '../entity/Media';
const tvRoutes = Router();
@@ -14,9 +15,9 @@ tvRoutes.get('/:id', async (req, res) => {
language: req.query.language as string,
});
const request = await MediaRequest.getRequest(tv.id);
const media = await Media.getMedia(tv.id);
return res.status(200).json(mapTvDetails(tv, request));
return res.status(200).json(mapTvDetails(tv, media));
});
tvRoutes.get('/:id/recommendations', async (req, res) => {
@@ -28,7 +29,7 @@ tvRoutes.get('/:id/recommendations', async (req, res) => {
language: req.query.language as string,
});
const requests = await MediaRequest.getRelatedRequests(
const media = await Media.getRelatedMedia(
results.results.map((result) => result.id)
);
@@ -39,7 +40,7 @@ tvRoutes.get('/:id/recommendations', async (req, res) => {
results: results.results.map((result) =>
mapTvResult(
result,
requests.find((req) => req.mediaId === result.id)
media.find((req) => req.tmdbId === result.id)
)
),
});
@@ -54,7 +55,7 @@ tvRoutes.get('/:id/similar', async (req, res) => {
language: req.query.language as string,
});
const requests = await MediaRequest.getRelatedRequests(
const media = await Media.getRelatedMedia(
results.results.map((result) => result.id)
);
@@ -65,7 +66,7 @@ tvRoutes.get('/:id/similar', async (req, res) => {
results: results.results.map((result) =>
mapTvResult(
result,
requests.find((req) => req.mediaId === result.id)
media.find((req) => req.tmdbId === result.id)
)
),
});