diff --git a/server/constants/media.ts b/server/constants/media.ts new file mode 100644 index 000000000..a15dd7ee8 --- /dev/null +++ b/server/constants/media.ts @@ -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, +} diff --git a/server/entity/Media.ts b/server/entity/Media.ts new file mode 100644 index 000000000..0e79153d6 --- /dev/null +++ b/server/entity/Media.ts @@ -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 { + 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 { + 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) { + Object.assign(this, init); + } +} + +export default Media; diff --git a/server/entity/MediaRequest.ts b/server/entity/MediaRequest.ts index 952b5cb97..0e6282c9a 100644 --- a/server/entity/MediaRequest.ts +++ b/server/entity/MediaRequest.ts @@ -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 { - 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 { - 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) { 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); } } } diff --git a/server/entity/MovieRequest.ts b/server/entity/MovieRequest.ts new file mode 100644 index 000000000..f3591f46a --- /dev/null +++ b/server/entity/MovieRequest.ts @@ -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) { + 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; diff --git a/server/entity/SeasonRequest.ts b/server/entity/SeasonRequest.ts new file mode 100644 index 000000000..2f841863d --- /dev/null +++ b/server/entity/SeasonRequest.ts @@ -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) { + Object.assign(this, init); + } +} + +export default SeasonRequest; diff --git a/server/entity/TvRequest.ts b/server/entity/TvRequest.ts new file mode 100644 index 000000000..d6faa6b0f --- /dev/null +++ b/server/entity/TvRequest.ts @@ -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) { + super(init); + } +} + +export default TvRequest; diff --git a/server/models/Movie.ts b/server/models/Movie.ts index 329e7ae05..da5f23558 100644 --- a/server/models/Movie.ts +++ b/server/models/Movie.ts @@ -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, }); diff --git a/server/models/Search.ts b/server/models/Search.ts index ec93995b9..5830b2a11 100644 --- a/server/models/Search.ts +++ b/server/models/Search.ts @@ -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); diff --git a/server/models/Tv.ts b/server/models/Tv.ts index 246b56b2e..7edd8ba20 100644 --- a/server/models/Tv.ts +++ b/server/models/Tv.ts @@ -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, }); diff --git a/server/overseerr-api.yml b/server/overseerr-api.yml index 58997b00d..8d58522e5 100644 --- a/server/overseerr-api.yml +++ b/server/overseerr-api.yml @@ -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: diff --git a/server/routes/discover.ts b/server/routes/discover.ts index 797796f0f..78b2020a4 100644 --- a/server/routes/discover.ts +++ b/server/routes/discover.ts @@ -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) ) ), }); diff --git a/server/routes/movie.ts b/server/routes/movie.ts index 8e50bf0e7..b16e523d7 100644 --- a/server/routes/movie.ts +++ b/server/routes/movie.ts @@ -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) ) ), }); diff --git a/server/routes/request.ts b/server/routes/request.ts index 3f6c2184f..29ef44ea7 100644 --- a/server/routes/request.ts +++ b/server/routes/request.ts @@ -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); + } + + if (req.body.mediaType === 'movie') { + const requestRepository = getRepository(MovieRequest); - return res.status(201).json(request); + 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; diff --git a/server/routes/search.ts b/server/routes/search.ts index d76a61eec..622e54693 100644 --- a/server/routes/search.ts +++ b/server/routes/search.ts @@ -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), }); }); diff --git a/server/routes/tv.ts b/server/routes/tv.ts index d24a91116..a814fe2d7 100644 --- a/server/routes/tv.ts +++ b/server/routes/tv.ts @@ -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) ) ), }); diff --git a/src/components/Common/ListView/index.tsx b/src/components/Common/ListView/index.tsx index 96817966d..62076c017 100644 --- a/src/components/Common/ListView/index.tsx +++ b/src/components/Common/ListView/index.tsx @@ -39,13 +39,12 @@ const ListView: React.FC = ({ ); break; @@ -54,13 +53,12 @@ const ListView: React.FC = ({ ); break; diff --git a/src/components/Discover/index.tsx b/src/components/Discover/index.tsx index 324afa971..07cb5443e 100644 --- a/src/components/Discover/index.tsx +++ b/src/components/Discover/index.tsx @@ -76,8 +76,8 @@ const Discover: React.FC = () => { items={requests?.map((request) => ( ))} /> @@ -115,13 +115,12 @@ const Discover: React.FC = () => { key={`popular-movie-slider-${title.id}`} id={title.id} image={title.posterPath} - status={title.request?.status} + status={title.mediaInfo?.status} summary={title.overview} title={title.title} userScore={title.voteAverage} year={title.releaseDate} mediaType={title.mediaType} - requestId={title.request?.id} /> ))} /> @@ -159,13 +158,12 @@ const Discover: React.FC = () => { key={`popular-tv-slider-${title.id}`} id={title.id} image={title.posterPath} - status={title.request?.status} + status={title.mediaInfo?.status} summary={title.overview} title={title.name} userScore={title.voteAverage} year={title.firstAirDate} mediaType={title.mediaType} - requestId={title.request?.id} /> ))} /> diff --git a/src/components/MovieDetails/index.tsx b/src/components/MovieDetails/index.tsx index d34681047..69d20e1cb 100644 --- a/src/components/MovieDetails/index.tsx +++ b/src/components/MovieDetails/index.tsx @@ -23,6 +23,7 @@ import { LanguageContext } from '../../context/LanguageContext'; import LoadingSpinner from '../Common/LoadingSpinner'; import { useUser, Permission } from '../../hooks/useUser'; import PendingRequest from '../PendingRequest'; +import { MediaStatus } from '../../../server/constants/media'; const messages = defineMessages({ releasedate: 'Release Date', @@ -101,13 +102,7 @@ const MovieDetails: React.FC = ({ movie }) => { }; const cancelRequest = async () => { - const response = await axios.delete( - `/api/v1/request/${data?.request?.id}` - ); - - if (response.data.id) { - revalidate(); - } + // fix this }; if (!data && !error) { @@ -167,7 +162,8 @@ const MovieDetails: React.FC = ({ movie }) => {
- {!data.request && ( + {(!data.mediaInfo || + data.mediaInfo?.status === MediaStatus.UNKNOWN) && ( )} - {data.request?.status === MediaRequestStatus.PENDING && ( - )} - {data.request?.status === MediaRequestStatus.APPROVED && ( + {data.mediaInfo?.status === MediaStatus.PROCESSING && ( )} - {data.request?.status === MediaRequestStatus.AVAILABLE && ( + {data.mediaInfo?.status === MediaStatus.AVAILABLE && (
- {data.request?.status === MediaRequestStatus.PENDING && + {/* {data.mediaInfo?.status === MediaStatus.PENDING && hasPermission(Permission.MANAGE_REQUESTS) && ( revalidate()} /> - )} + )} */}

@@ -463,13 +451,12 @@ const MovieDetails: React.FC = ({ movie }) => { key={`recommended-${title.id}`} id={title.id} image={title.posterPath} - status={title.request?.status} + status={title.mediaInfo?.status} summary={title.overview} title={title.title} userScore={title.voteAverage} year={title.releaseDate} mediaType={title.mediaType} - requestId={title.request?.id} /> ))} /> @@ -510,13 +497,12 @@ const MovieDetails: React.FC = ({ movie }) => { key={`recommended-${title.id}`} id={title.id} image={title.posterPath} - status={title.request?.status} + status={title.mediaInfo?.status} summary={title.overview} title={title.title} userScore={title.voteAverage} year={title.releaseDate} mediaType={title.mediaType} - requestId={title.request?.id} /> ))} /> diff --git a/src/components/TitleCard/RequestCard.tsx b/src/components/TitleCard/RequestCard.tsx index f81275080..87b2e2741 100644 --- a/src/components/TitleCard/RequestCard.tsx +++ b/src/components/TitleCard/RequestCard.tsx @@ -34,25 +34,23 @@ const RequestCard: React.FC = ({ tmdbId, type }) => { ) : ( ); }; diff --git a/src/components/TitleCard/index.tsx b/src/components/TitleCard/index.tsx index 2853d258a..69e362e65 100644 --- a/src/components/TitleCard/index.tsx +++ b/src/components/TitleCard/index.tsx @@ -11,6 +11,7 @@ import axios from 'axios'; import { MediaRequest } from '../../../server/entity/MediaRequest'; import MovieRequestModal from '../RequestModal/MovieRequestModal'; import Link from 'next/link'; +import { MediaStatus } from '../../../server/constants/media'; interface TitleCardProps { id: number; @@ -20,17 +21,10 @@ interface TitleCardProps { title: string; userScore: number; mediaType: MediaType; - status?: MediaRequestStatus; + status?: MediaStatus; requestId?: number; } -enum MediaRequestStatus { - PENDING = 1, - APPROVED, - DECLINED, - AVAILABLE, -} - const TitleCard: React.FC = ({ id, image, @@ -56,7 +50,7 @@ const TitleCard: React.FC = ({ }); if (response.data) { - setCurrentStatus(response.data.status); + setCurrentStatus(response.data.media.status); addToast( {title} succesfully requested! @@ -131,13 +125,13 @@ const TitleCard: React.FC = ({ right: '-1px', }} > - {currentStatus === MediaRequestStatus.AVAILABLE && ( + {currentStatus === MediaStatus.AVAILABLE && ( )} - {currentStatus === MediaRequestStatus.PENDING && ( + {currentStatus === MediaStatus.PENDING && ( )} - {currentStatus === MediaRequestStatus.APPROVED && ( + {currentStatus === MediaStatus.PROCESSING && ( )}
@@ -251,7 +245,7 @@ const TitleCard: React.FC = ({ )} - {currentStatus === MediaRequestStatus.PENDING && ( + {currentStatus === MediaStatus.PENDING && ( )} - {currentStatus === MediaRequestStatus.APPROVED && ( + {currentStatus === MediaStatus.AVAILABLE && ( )} - {currentStatus === MediaRequestStatus.AVAILABLE && ( + {currentStatus === MediaStatus.AVAILABLE && (
- {!data.request && ( + {(!data.mediaInfo || + data.mediaInfo.status === MediaStatus.UNKNOWN) && ( )} - {data.request?.status === MediaRequestStatus.PENDING && ( - )} - {data.request?.status === MediaRequestStatus.APPROVED && ( + {data.mediaInfo?.status === MediaStatus.PROCESSING && ( )} - {data.request?.status === MediaRequestStatus.AVAILABLE && ( + {data.mediaInfo?.status === MediaStatus.AVAILABLE && (
- {data.request?.status === MediaRequestStatus.PENDING && + {/* {data.mediaInfo?.status === MediaStatus.PENDING && hasPermission(Permission.MANAGE_REQUESTS) && ( revalidate()} /> - )} + )} */}

@@ -403,13 +391,12 @@ const TvDetails: React.FC = ({ tv }) => { key={`recommended-${title.id}`} id={title.id} image={title.posterPath} - status={title.request?.status} + status={title.mediaInfo?.status} summary={title.overview} title={title.name} userScore={title.voteAverage} year={title.firstAirDate} mediaType={title.mediaType} - requestId={title.request?.id} /> ))} /> @@ -447,13 +434,12 @@ const TvDetails: React.FC = ({ tv }) => { key={`recommended-${title.id}`} id={title.id} image={title.posterPath} - status={title.request?.status} + status={title.mediaInfo?.status} summary={title.overview} title={title.name} userScore={title.voteAverage} year={title.firstAirDate} mediaType={title.mediaType} - requestId={title.request?.id} /> ))} />