mirror of
https://github.com/sct/overseerr.git
synced 2025-09-17 17:24:35 +02:00
feat(api): decouple media requests from media info
This commit is contained in:
19
server/constants/media.ts
Normal file
19
server/constants/media.ts
Normal 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
87
server/entity/Media.ts
Normal 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;
|
@@ -5,144 +5,53 @@ import {
|
|||||||
Column,
|
Column,
|
||||||
CreateDateColumn,
|
CreateDateColumn,
|
||||||
UpdateDateColumn,
|
UpdateDateColumn,
|
||||||
getRepository,
|
TableInheritance,
|
||||||
In,
|
|
||||||
Index,
|
|
||||||
AfterUpdate,
|
AfterUpdate,
|
||||||
AfterInsert,
|
AfterInsert,
|
||||||
|
getRepository,
|
||||||
} from 'typeorm';
|
} from 'typeorm';
|
||||||
import { User } from './User';
|
import { User } from './User';
|
||||||
import RadarrAPI from '../api/radarr';
|
import Media from './Media';
|
||||||
import { getSettings } from '../lib/settings';
|
import { MediaStatus, MediaRequestStatus, MediaType } from '../constants/media';
|
||||||
import TheMovieDb from '../api/themoviedb';
|
|
||||||
|
|
||||||
export enum MediaRequestStatus {
|
|
||||||
PENDING = 1,
|
|
||||||
APPROVED,
|
|
||||||
DECLINED,
|
|
||||||
AVAILABLE,
|
|
||||||
}
|
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
|
@TableInheritance({ column: { type: 'varchar', name: 'type' } })
|
||||||
export class MediaRequest {
|
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()
|
@PrimaryGeneratedColumn()
|
||||||
public id: number;
|
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' })
|
@Column({ type: 'integer' })
|
||||||
public status: MediaRequestStatus;
|
public status: MediaRequestStatus;
|
||||||
|
|
||||||
|
@ManyToOne(() => Media, (media) => media.requests, { eager: true })
|
||||||
|
public media: Media;
|
||||||
|
|
||||||
@ManyToOne(() => User, (user) => user.requests, { eager: true })
|
@ManyToOne(() => User, (user) => user.requests, { eager: true })
|
||||||
public requestedBy: User;
|
public requestedBy: User;
|
||||||
|
|
||||||
@ManyToOne(() => User, { nullable: true })
|
@ManyToOne(() => User, { nullable: true })
|
||||||
public modifiedBy?: User;
|
public modifiedBy?: User;
|
||||||
|
|
||||||
@CreateDateColumn()
|
@CreateDateColumn()
|
||||||
public createdAt: Date;
|
public createdAt: Date;
|
||||||
|
|
||||||
@UpdateDateColumn()
|
@UpdateDateColumn()
|
||||||
public updatedAt: Date;
|
public updatedAt: Date;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
public type: MediaType;
|
||||||
|
|
||||||
constructor(init?: Partial<MediaRequest>) {
|
constructor(init?: Partial<MediaRequest>) {
|
||||||
Object.assign(this, init);
|
Object.assign(this, init);
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterUpdate()
|
@AfterUpdate()
|
||||||
@AfterInsert()
|
@AfterInsert()
|
||||||
private async sendToRadarr() {
|
private async updateParentStatus() {
|
||||||
if (
|
const mediaRepository = getRepository(Media);
|
||||||
this.mediaType === 'movie' &&
|
if (this.status === MediaRequestStatus.APPROVED) {
|
||||||
this.status === MediaRequestStatus.APPROVED
|
this.media.status = MediaStatus.PROCESSING;
|
||||||
) {
|
mediaRepository.save(this.media);
|
||||||
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}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
57
server/entity/MovieRequest.ts
Normal file
57
server/entity/MovieRequest.ts
Normal 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;
|
37
server/entity/SeasonRequest.ts
Normal file
37
server/entity/SeasonRequest.ts
Normal 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;
|
16
server/entity/TvRequest.ts
Normal file
16
server/entity/TvRequest.ts
Normal 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;
|
@@ -10,6 +10,7 @@ import {
|
|||||||
ExternalIds,
|
ExternalIds,
|
||||||
mapExternalIds,
|
mapExternalIds,
|
||||||
} from './common';
|
} from './common';
|
||||||
|
import Media from '../entity/Media';
|
||||||
|
|
||||||
export interface MovieDetails {
|
export interface MovieDetails {
|
||||||
id: number;
|
id: number;
|
||||||
@@ -46,13 +47,13 @@ export interface MovieDetails {
|
|||||||
cast: Cast[];
|
cast: Cast[];
|
||||||
crew: Crew[];
|
crew: Crew[];
|
||||||
};
|
};
|
||||||
request?: MediaRequest;
|
mediaInfo?: Media;
|
||||||
externalIds: ExternalIds;
|
externalIds: ExternalIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const mapMovieDetails = (
|
export const mapMovieDetails = (
|
||||||
movie: TmdbMovieDetails,
|
movie: TmdbMovieDetails,
|
||||||
request?: MediaRequest
|
media?: Media
|
||||||
): MovieDetails => ({
|
): MovieDetails => ({
|
||||||
id: movie.id,
|
id: movie.id,
|
||||||
adult: movie.adult,
|
adult: movie.adult,
|
||||||
@@ -88,5 +89,5 @@ export const mapMovieDetails = (
|
|||||||
crew: movie.credits.crew.map(mapCrew),
|
crew: movie.credits.crew.map(mapCrew),
|
||||||
},
|
},
|
||||||
externalIds: mapExternalIds(movie.external_ids),
|
externalIds: mapExternalIds(movie.external_ids),
|
||||||
request,
|
mediaInfo: media,
|
||||||
});
|
});
|
||||||
|
@@ -4,6 +4,7 @@ import type {
|
|||||||
TmdbTvResult,
|
TmdbTvResult,
|
||||||
} from '../api/themoviedb';
|
} from '../api/themoviedb';
|
||||||
import type { MediaRequest } from '../entity/MediaRequest';
|
import type { MediaRequest } from '../entity/MediaRequest';
|
||||||
|
import Media from '../entity/Media';
|
||||||
|
|
||||||
export type MediaType = 'tv' | 'movie' | 'person';
|
export type MediaType = 'tv' | 'movie' | 'person';
|
||||||
|
|
||||||
@@ -18,7 +19,7 @@ interface SearchResult {
|
|||||||
genreIds: number[];
|
genreIds: number[];
|
||||||
overview: string;
|
overview: string;
|
||||||
originalLanguage: string;
|
originalLanguage: string;
|
||||||
request?: MediaRequest;
|
mediaInfo?: Media;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MovieResult extends SearchResult {
|
export interface MovieResult extends SearchResult {
|
||||||
@@ -28,7 +29,7 @@ export interface MovieResult extends SearchResult {
|
|||||||
releaseDate: string;
|
releaseDate: string;
|
||||||
adult: boolean;
|
adult: boolean;
|
||||||
video: boolean;
|
video: boolean;
|
||||||
request?: MediaRequest;
|
mediaInfo?: Media;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TvResult extends SearchResult {
|
export interface TvResult extends SearchResult {
|
||||||
@@ -53,7 +54,7 @@ export type Results = MovieResult | TvResult | PersonResult;
|
|||||||
|
|
||||||
export const mapMovieResult = (
|
export const mapMovieResult = (
|
||||||
movieResult: TmdbMovieResult,
|
movieResult: TmdbMovieResult,
|
||||||
request?: MediaRequest
|
media?: Media
|
||||||
): MovieResult => ({
|
): MovieResult => ({
|
||||||
id: movieResult.id,
|
id: movieResult.id,
|
||||||
mediaType: 'movie',
|
mediaType: 'movie',
|
||||||
@@ -70,12 +71,12 @@ export const mapMovieResult = (
|
|||||||
voteCount: movieResult.vote_count,
|
voteCount: movieResult.vote_count,
|
||||||
backdropPath: movieResult.backdrop_path,
|
backdropPath: movieResult.backdrop_path,
|
||||||
posterPath: movieResult.poster_path,
|
posterPath: movieResult.poster_path,
|
||||||
request,
|
mediaInfo: media,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const mapTvResult = (
|
export const mapTvResult = (
|
||||||
tvResult: TmdbTvResult,
|
tvResult: TmdbTvResult,
|
||||||
request?: MediaRequest
|
media?: Media
|
||||||
): TvResult => ({
|
): TvResult => ({
|
||||||
id: tvResult.id,
|
id: tvResult.id,
|
||||||
firstAirDate: tvResult.first_air_Date,
|
firstAirDate: tvResult.first_air_Date,
|
||||||
@@ -92,7 +93,7 @@ export const mapTvResult = (
|
|||||||
voteCount: tvResult.vote_count,
|
voteCount: tvResult.vote_count,
|
||||||
backdropPath: tvResult.backdrop_path,
|
backdropPath: tvResult.backdrop_path,
|
||||||
posterPath: tvResult.poster_path,
|
posterPath: tvResult.poster_path,
|
||||||
request,
|
mediaInfo: media,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const mapPersonResult = (
|
export const mapPersonResult = (
|
||||||
@@ -115,19 +116,19 @@ export const mapPersonResult = (
|
|||||||
|
|
||||||
export const mapSearchResults = (
|
export const mapSearchResults = (
|
||||||
results: (TmdbMovieResult | TmdbTvResult | TmdbPersonResult)[],
|
results: (TmdbMovieResult | TmdbTvResult | TmdbPersonResult)[],
|
||||||
requests?: MediaRequest[]
|
media?: Media[]
|
||||||
): Results[] =>
|
): Results[] =>
|
||||||
results.map((result) => {
|
results.map((result) => {
|
||||||
switch (result.media_type) {
|
switch (result.media_type) {
|
||||||
case 'movie':
|
case 'movie':
|
||||||
return mapMovieResult(
|
return mapMovieResult(
|
||||||
result,
|
result,
|
||||||
requests?.find((req) => req.mediaId === result.id)
|
media?.find((req) => req.tmdbId === result.id)
|
||||||
);
|
);
|
||||||
case 'tv':
|
case 'tv':
|
||||||
return mapTvResult(
|
return mapTvResult(
|
||||||
result,
|
result,
|
||||||
requests?.find((req) => req.mediaId === result.id)
|
media?.find((req) => req.tmdbId === result.id)
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
return mapPersonResult(result);
|
return mapPersonResult(result);
|
||||||
|
@@ -8,12 +8,12 @@ import {
|
|||||||
ExternalIds,
|
ExternalIds,
|
||||||
mapExternalIds,
|
mapExternalIds,
|
||||||
} from './common';
|
} from './common';
|
||||||
import { MediaRequest } from '../entity/MediaRequest';
|
|
||||||
import {
|
import {
|
||||||
TmdbTvEpisodeDetails,
|
TmdbTvEpisodeDetails,
|
||||||
TmdbTvSeasonDetails,
|
TmdbTvSeasonDetails,
|
||||||
TmdbTvDetails,
|
TmdbTvDetails,
|
||||||
} from '../api/themoviedb';
|
} from '../api/themoviedb';
|
||||||
|
import type Media from '../entity/Media';
|
||||||
|
|
||||||
interface Episode {
|
interface Episode {
|
||||||
id: number;
|
id: number;
|
||||||
@@ -78,7 +78,7 @@ export interface TvDetails {
|
|||||||
crew: Crew[];
|
crew: Crew[];
|
||||||
};
|
};
|
||||||
externalIds: ExternalIds;
|
externalIds: ExternalIds;
|
||||||
request?: MediaRequest;
|
mediaInfo?: Media;
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapEpisodeDetails = (episode: TmdbTvEpisodeDetails): Episode => ({
|
const mapEpisodeDetails = (episode: TmdbTvEpisodeDetails): Episode => ({
|
||||||
@@ -107,7 +107,7 @@ const mapSeasonDetails = (season: TmdbTvSeasonDetails): Season => ({
|
|||||||
|
|
||||||
export const mapTvDetails = (
|
export const mapTvDetails = (
|
||||||
show: TmdbTvDetails,
|
show: TmdbTvDetails,
|
||||||
request?: MediaRequest
|
media?: Media
|
||||||
): TvDetails => ({
|
): TvDetails => ({
|
||||||
createdBy: show.created_by,
|
createdBy: show.created_by,
|
||||||
episodeRunTime: show.episode_run_time,
|
episodeRunTime: show.episode_run_time,
|
||||||
@@ -159,5 +159,5 @@ export const mapTvDetails = (
|
|||||||
crew: show.credits.crew.map(mapCrew),
|
crew: show.credits.crew.map(mapCrew),
|
||||||
},
|
},
|
||||||
externalIds: mapExternalIds(show.external_ids),
|
externalIds: mapExternalIds(show.external_ids),
|
||||||
request,
|
mediaInfo: media,
|
||||||
});
|
});
|
||||||
|
@@ -263,8 +263,8 @@ components:
|
|||||||
video:
|
video:
|
||||||
type: boolean
|
type: boolean
|
||||||
example: false
|
example: false
|
||||||
request:
|
mediaInfo:
|
||||||
$ref: '#/components/schemas/MediaRequest'
|
$ref: '#/components/schemas/MediaInfo'
|
||||||
TvResult:
|
TvResult:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
@@ -306,8 +306,8 @@ components:
|
|||||||
type: string
|
type: string
|
||||||
firstAirDate:
|
firstAirDate:
|
||||||
type: string
|
type: string
|
||||||
request:
|
mediaInfo:
|
||||||
$ref: '#/components/schemas/MediaRequest'
|
$ref: '#/components/schemas/MediaInfo'
|
||||||
PersonResult:
|
PersonResult:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
@@ -435,8 +435,8 @@ components:
|
|||||||
$ref: '#/components/schemas/Crew'
|
$ref: '#/components/schemas/Crew'
|
||||||
externalIds:
|
externalIds:
|
||||||
$ref: '#/components/schemas/ExternalIds'
|
$ref: '#/components/schemas/ExternalIds'
|
||||||
request:
|
mediaInfo:
|
||||||
$ref: '#/components/schemas/MediaRequest'
|
$ref: '#/components/schemas/MediaInfo'
|
||||||
Episode:
|
Episode:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
@@ -577,8 +577,8 @@ components:
|
|||||||
$ref: '#/components/schemas/Crew'
|
$ref: '#/components/schemas/Crew'
|
||||||
externalIds:
|
externalIds:
|
||||||
$ref: '#/components/schemas/ExternalIds'
|
$ref: '#/components/schemas/ExternalIds'
|
||||||
request:
|
mediaInfo:
|
||||||
$ref: '#/components/schemas/MediaRequest'
|
$ref: '#/components/schemas/MediaInfo'
|
||||||
MediaRequest:
|
MediaRequest:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
@@ -586,18 +586,13 @@ components:
|
|||||||
type: number
|
type: number
|
||||||
example: 123
|
example: 123
|
||||||
readOnly: true
|
readOnly: true
|
||||||
mediaId:
|
|
||||||
type: number
|
|
||||||
example: 123
|
|
||||||
description: TMDB Movie ID
|
|
||||||
mediaType:
|
|
||||||
type: string
|
|
||||||
enum: [movie, tv]
|
|
||||||
status:
|
status:
|
||||||
type: number
|
type: number
|
||||||
example: 0
|
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
|
readOnly: true
|
||||||
|
media:
|
||||||
|
$ref: '#/components/schemas/MediaInfo'
|
||||||
createdAt:
|
createdAt:
|
||||||
type: string
|
type: string
|
||||||
example: '2020-09-12T10:00:27.000Z'
|
example: '2020-09-12T10:00:27.000Z'
|
||||||
@@ -616,9 +611,34 @@ components:
|
|||||||
nullable: true
|
nullable: true
|
||||||
required:
|
required:
|
||||||
- id
|
- id
|
||||||
- mediaId
|
|
||||||
- mediaType
|
|
||||||
- status
|
- 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:
|
Cast:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { Router } from 'express';
|
import { Router } from 'express';
|
||||||
import TheMovieDb from '../api/themoviedb';
|
import TheMovieDb from '../api/themoviedb';
|
||||||
import { mapMovieResult, mapTvResult } from '../models/Search';
|
import { mapMovieResult, mapTvResult } from '../models/Search';
|
||||||
import { MediaRequest } from '../entity/MediaRequest';
|
import Media from '../entity/Media';
|
||||||
|
|
||||||
const discoverRoutes = Router();
|
const discoverRoutes = Router();
|
||||||
|
|
||||||
@@ -13,7 +13,7 @@ discoverRoutes.get('/movies', async (req, res) => {
|
|||||||
language: req.query.language as string,
|
language: req.query.language as string,
|
||||||
});
|
});
|
||||||
|
|
||||||
const requests = await MediaRequest.getRelatedRequests(
|
const media = await Media.getRelatedMedia(
|
||||||
data.results.map((result) => result.id)
|
data.results.map((result) => result.id)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@ discoverRoutes.get('/movies', async (req, res) => {
|
|||||||
results: data.results.map((result) =>
|
results: data.results.map((result) =>
|
||||||
mapMovieResult(
|
mapMovieResult(
|
||||||
result,
|
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,
|
language: req.query.language as string,
|
||||||
});
|
});
|
||||||
|
|
||||||
const requests = await MediaRequest.getRelatedRequests(
|
const media = await Media.getRelatedMedia(
|
||||||
data.results.map((result) => result.id)
|
data.results.map((result) => result.id)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ discoverRoutes.get('/tv', async (req, res) => {
|
|||||||
results: data.results.map((result) =>
|
results: data.results.map((result) =>
|
||||||
mapTvResult(
|
mapTvResult(
|
||||||
result,
|
result,
|
||||||
requests.find((req) => req.mediaId === result.id)
|
media.find((req) => req.tmdbId === result.id)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
@@ -3,6 +3,7 @@ import TheMovieDb from '../api/themoviedb';
|
|||||||
import { mapMovieDetails } from '../models/Movie';
|
import { mapMovieDetails } from '../models/Movie';
|
||||||
import { MediaRequest } from '../entity/MediaRequest';
|
import { MediaRequest } from '../entity/MediaRequest';
|
||||||
import { mapMovieResult } from '../models/Search';
|
import { mapMovieResult } from '../models/Search';
|
||||||
|
import Media from '../entity/Media';
|
||||||
|
|
||||||
const movieRoutes = Router();
|
const movieRoutes = Router();
|
||||||
|
|
||||||
@@ -14,9 +15,9 @@ movieRoutes.get('/:id', async (req, res) => {
|
|||||||
language: req.query.language as string,
|
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) => {
|
movieRoutes.get('/:id/recommendations', async (req, res) => {
|
||||||
@@ -28,7 +29,7 @@ movieRoutes.get('/:id/recommendations', async (req, res) => {
|
|||||||
language: req.query.language as string,
|
language: req.query.language as string,
|
||||||
});
|
});
|
||||||
|
|
||||||
const requests = await MediaRequest.getRelatedRequests(
|
const media = await Media.getRelatedMedia(
|
||||||
results.results.map((result) => result.id)
|
results.results.map((result) => result.id)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -39,7 +40,7 @@ movieRoutes.get('/:id/recommendations', async (req, res) => {
|
|||||||
results: results.results.map((result) =>
|
results: results.results.map((result) =>
|
||||||
mapMovieResult(
|
mapMovieResult(
|
||||||
result,
|
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,
|
language: req.query.language as string,
|
||||||
});
|
});
|
||||||
|
|
||||||
const requests = await MediaRequest.getRelatedRequests(
|
const media = await Media.getRelatedMedia(
|
||||||
results.results.map((result) => result.id)
|
results.results.map((result) => result.id)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -65,7 +66,7 @@ movieRoutes.get('/:id/similar', async (req, res) => {
|
|||||||
results: results.results.map((result) =>
|
results: results.results.map((result) =>
|
||||||
mapMovieResult(
|
mapMovieResult(
|
||||||
result,
|
result,
|
||||||
requests.find((req) => req.mediaId === result.id)
|
media.find((req) => req.tmdbId === result.id)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
@@ -2,8 +2,12 @@ import { Router } from 'express';
|
|||||||
import { isAuthenticated } from '../middleware/auth';
|
import { isAuthenticated } from '../middleware/auth';
|
||||||
import { Permission } from '../lib/permissions';
|
import { Permission } from '../lib/permissions';
|
||||||
import { getRepository } from 'typeorm';
|
import { getRepository } from 'typeorm';
|
||||||
import { MediaRequest, MediaRequestStatus } from '../entity/MediaRequest';
|
import { MediaRequest } from '../entity/MediaRequest';
|
||||||
import TheMovieDb from '../api/themoviedb';
|
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();
|
const requestRoutes = Router();
|
||||||
|
|
||||||
@@ -15,10 +19,12 @@ requestRoutes.get('/', async (req, res, next) => {
|
|||||||
order: {
|
order: {
|
||||||
id: 'DESC',
|
id: 'DESC',
|
||||||
},
|
},
|
||||||
|
relations: ['media'],
|
||||||
take: 20,
|
take: 20,
|
||||||
})
|
})
|
||||||
: await requestRepository.find({
|
: await requestRepository.find({
|
||||||
where: { requestedBy: { id: req.user?.id } },
|
where: { requestedBy: { id: req.user?.id } },
|
||||||
|
relations: ['media'],
|
||||||
order: {
|
order: {
|
||||||
id: 'DESC',
|
id: 'DESC',
|
||||||
},
|
},
|
||||||
@@ -36,16 +42,33 @@ requestRoutes.post(
|
|||||||
isAuthenticated(Permission.REQUEST),
|
isAuthenticated(Permission.REQUEST),
|
||||||
async (req, res, next) => {
|
async (req, res, next) => {
|
||||||
const tmdb = new TheMovieDb();
|
const tmdb = new TheMovieDb();
|
||||||
const requestRepository = getRepository(MediaRequest);
|
const mediaRepository = getRepository(Media);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const media =
|
const tmdbMedia =
|
||||||
req.body.mediaType === 'movie'
|
req.body.mediaType === 'movie'
|
||||||
? await tmdb.getMovie({ movieId: req.body.mediaId })
|
? await tmdb.getMovie({ movieId: req.body.mediaId })
|
||||||
: await tmdb.getTvShow({ tvId: req.body.mediaId });
|
: await tmdb.getTvShow({ tvId: req.body.mediaId });
|
||||||
const request = new MediaRequest({
|
|
||||||
mediaId: media.id,
|
let media = await mediaRepository.findOne({
|
||||||
|
where: { tmdbId: req.body.mediaId },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!media) {
|
||||||
|
media = new Media({
|
||||||
|
tmdbId: tmdbMedia.id,
|
||||||
|
tvdbId: tmdbMedia.external_ids.tvdb_id,
|
||||||
|
status: MediaStatus.PENDING,
|
||||||
mediaType: req.body.mediaType,
|
mediaType: req.body.mediaType,
|
||||||
|
});
|
||||||
|
await mediaRepository.save(media);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req.body.mediaType === 'movie') {
|
||||||
|
const requestRepository = getRepository(MovieRequest);
|
||||||
|
|
||||||
|
const request = new MovieRequest({
|
||||||
|
media,
|
||||||
requestedBy: req.user,
|
requestedBy: req.user,
|
||||||
// If the user is an admin or has the "auto approve" permission, automatically approve the request
|
// If the user is an admin or has the "auto approve" permission, automatically approve the request
|
||||||
status: req.user?.hasPermission(Permission.AUTO_APPROVE)
|
status: req.user?.hasPermission(Permission.AUTO_APPROVE)
|
||||||
@@ -54,8 +77,24 @@ requestRoutes.post(
|
|||||||
});
|
});
|
||||||
|
|
||||||
await requestRepository.save(request);
|
await requestRepository.save(request);
|
||||||
|
|
||||||
return res.status(201).json(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) {
|
} catch (e) {
|
||||||
next({ message: e.message, status: 500 });
|
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);
|
return res.status(200).json(request);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -106,7 +145,7 @@ requestRoutes.delete('/:requestId', async (req, res, next) => {
|
|||||||
|
|
||||||
requestRoutes.get<{
|
requestRoutes.get<{
|
||||||
requestId: string;
|
requestId: string;
|
||||||
status: 'pending' | 'approve' | 'decline' | 'available';
|
status: 'pending' | 'approve' | 'decline';
|
||||||
}>(
|
}>(
|
||||||
'/:requestId/:status',
|
'/:requestId/:status',
|
||||||
isAuthenticated(Permission.MANAGE_REQUESTS),
|
isAuthenticated(Permission.MANAGE_REQUESTS),
|
||||||
@@ -131,9 +170,6 @@ requestRoutes.get<{
|
|||||||
case 'decline':
|
case 'decline':
|
||||||
newStatus = MediaRequestStatus.DECLINED;
|
newStatus = MediaRequestStatus.DECLINED;
|
||||||
break;
|
break;
|
||||||
case 'available':
|
|
||||||
newStatus = MediaRequestStatus.AVAILABLE;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
request.status = newStatus;
|
request.status = newStatus;
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { Router } from 'express';
|
import { Router } from 'express';
|
||||||
import TheMovieDb from '../api/themoviedb';
|
import TheMovieDb from '../api/themoviedb';
|
||||||
import { mapSearchResults } from '../models/Search';
|
import { mapSearchResults } from '../models/Search';
|
||||||
import { MediaRequest } from '../entity/MediaRequest';
|
import Media from '../entity/Media';
|
||||||
|
|
||||||
const searchRoutes = Router();
|
const searchRoutes = Router();
|
||||||
|
|
||||||
@@ -14,7 +14,7 @@ searchRoutes.get('/', async (req, res) => {
|
|||||||
language: req.query.language as string,
|
language: req.query.language as string,
|
||||||
});
|
});
|
||||||
|
|
||||||
const requests = await MediaRequest.getRelatedRequests(
|
const media = await Media.getRelatedMedia(
|
||||||
results.results.map((result) => result.id)
|
results.results.map((result) => result.id)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ searchRoutes.get('/', async (req, res) => {
|
|||||||
page: results.page,
|
page: results.page,
|
||||||
totalPages: results.total_pages,
|
totalPages: results.total_pages,
|
||||||
totalResults: results.total_results,
|
totalResults: results.total_results,
|
||||||
results: mapSearchResults(results.results, requests),
|
results: mapSearchResults(results.results, media),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -3,6 +3,7 @@ import TheMovieDb from '../api/themoviedb';
|
|||||||
import { MediaRequest } from '../entity/MediaRequest';
|
import { MediaRequest } from '../entity/MediaRequest';
|
||||||
import { mapTvDetails } from '../models/Tv';
|
import { mapTvDetails } from '../models/Tv';
|
||||||
import { mapTvResult } from '../models/Search';
|
import { mapTvResult } from '../models/Search';
|
||||||
|
import Media from '../entity/Media';
|
||||||
|
|
||||||
const tvRoutes = Router();
|
const tvRoutes = Router();
|
||||||
|
|
||||||
@@ -14,9 +15,9 @@ tvRoutes.get('/:id', async (req, res) => {
|
|||||||
language: req.query.language as string,
|
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) => {
|
tvRoutes.get('/:id/recommendations', async (req, res) => {
|
||||||
@@ -28,7 +29,7 @@ tvRoutes.get('/:id/recommendations', async (req, res) => {
|
|||||||
language: req.query.language as string,
|
language: req.query.language as string,
|
||||||
});
|
});
|
||||||
|
|
||||||
const requests = await MediaRequest.getRelatedRequests(
|
const media = await Media.getRelatedMedia(
|
||||||
results.results.map((result) => result.id)
|
results.results.map((result) => result.id)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -39,7 +40,7 @@ tvRoutes.get('/:id/recommendations', async (req, res) => {
|
|||||||
results: results.results.map((result) =>
|
results: results.results.map((result) =>
|
||||||
mapTvResult(
|
mapTvResult(
|
||||||
result,
|
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,
|
language: req.query.language as string,
|
||||||
});
|
});
|
||||||
|
|
||||||
const requests = await MediaRequest.getRelatedRequests(
|
const media = await Media.getRelatedMedia(
|
||||||
results.results.map((result) => result.id)
|
results.results.map((result) => result.id)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -65,7 +66,7 @@ tvRoutes.get('/:id/similar', async (req, res) => {
|
|||||||
results: results.results.map((result) =>
|
results: results.results.map((result) =>
|
||||||
mapTvResult(
|
mapTvResult(
|
||||||
result,
|
result,
|
||||||
requests.find((req) => req.mediaId === result.id)
|
media.find((req) => req.tmdbId === result.id)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
@@ -39,13 +39,12 @@ const ListView: React.FC<ListViewProps> = ({
|
|||||||
<TitleCard
|
<TitleCard
|
||||||
id={title.id}
|
id={title.id}
|
||||||
image={title.posterPath}
|
image={title.posterPath}
|
||||||
status={title.request?.status}
|
status={title.mediaInfo?.status}
|
||||||
summary={title.overview}
|
summary={title.overview}
|
||||||
title={title.title}
|
title={title.title}
|
||||||
userScore={title.voteAverage}
|
userScore={title.voteAverage}
|
||||||
year={title.releaseDate}
|
year={title.releaseDate}
|
||||||
mediaType={title.mediaType}
|
mediaType={title.mediaType}
|
||||||
requestId={title.request?.id}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
@@ -54,13 +53,12 @@ const ListView: React.FC<ListViewProps> = ({
|
|||||||
<TitleCard
|
<TitleCard
|
||||||
id={title.id}
|
id={title.id}
|
||||||
image={title.posterPath}
|
image={title.posterPath}
|
||||||
status={title.request?.status}
|
status={title.mediaInfo?.status}
|
||||||
summary={title.overview}
|
summary={title.overview}
|
||||||
title={title.name}
|
title={title.name}
|
||||||
userScore={title.voteAverage}
|
userScore={title.voteAverage}
|
||||||
year={title.firstAirDate}
|
year={title.firstAirDate}
|
||||||
mediaType={title.mediaType}
|
mediaType={title.mediaType}
|
||||||
requestId={title.request?.id}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
@@ -76,8 +76,8 @@ const Discover: React.FC = () => {
|
|||||||
items={requests?.map((request) => (
|
items={requests?.map((request) => (
|
||||||
<RequestCard
|
<RequestCard
|
||||||
key={`request-slider-item-${request.id}`}
|
key={`request-slider-item-${request.id}`}
|
||||||
tmdbId={request.mediaId}
|
tmdbId={request.media.tmdbId}
|
||||||
type={request.mediaType}
|
type={request.media.mediaType}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
/>
|
/>
|
||||||
@@ -115,13 +115,12 @@ const Discover: React.FC = () => {
|
|||||||
key={`popular-movie-slider-${title.id}`}
|
key={`popular-movie-slider-${title.id}`}
|
||||||
id={title.id}
|
id={title.id}
|
||||||
image={title.posterPath}
|
image={title.posterPath}
|
||||||
status={title.request?.status}
|
status={title.mediaInfo?.status}
|
||||||
summary={title.overview}
|
summary={title.overview}
|
||||||
title={title.title}
|
title={title.title}
|
||||||
userScore={title.voteAverage}
|
userScore={title.voteAverage}
|
||||||
year={title.releaseDate}
|
year={title.releaseDate}
|
||||||
mediaType={title.mediaType}
|
mediaType={title.mediaType}
|
||||||
requestId={title.request?.id}
|
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
/>
|
/>
|
||||||
@@ -159,13 +158,12 @@ const Discover: React.FC = () => {
|
|||||||
key={`popular-tv-slider-${title.id}`}
|
key={`popular-tv-slider-${title.id}`}
|
||||||
id={title.id}
|
id={title.id}
|
||||||
image={title.posterPath}
|
image={title.posterPath}
|
||||||
status={title.request?.status}
|
status={title.mediaInfo?.status}
|
||||||
summary={title.overview}
|
summary={title.overview}
|
||||||
title={title.name}
|
title={title.name}
|
||||||
userScore={title.voteAverage}
|
userScore={title.voteAverage}
|
||||||
year={title.firstAirDate}
|
year={title.firstAirDate}
|
||||||
mediaType={title.mediaType}
|
mediaType={title.mediaType}
|
||||||
requestId={title.request?.id}
|
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
/>
|
/>
|
||||||
|
@@ -23,6 +23,7 @@ import { LanguageContext } from '../../context/LanguageContext';
|
|||||||
import LoadingSpinner from '../Common/LoadingSpinner';
|
import LoadingSpinner from '../Common/LoadingSpinner';
|
||||||
import { useUser, Permission } from '../../hooks/useUser';
|
import { useUser, Permission } from '../../hooks/useUser';
|
||||||
import PendingRequest from '../PendingRequest';
|
import PendingRequest from '../PendingRequest';
|
||||||
|
import { MediaStatus } from '../../../server/constants/media';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
releasedate: 'Release Date',
|
releasedate: 'Release Date',
|
||||||
@@ -101,13 +102,7 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const cancelRequest = async () => {
|
const cancelRequest = async () => {
|
||||||
const response = await axios.delete<MediaRequest>(
|
// fix this
|
||||||
`/api/v1/request/${data?.request?.id}`
|
|
||||||
);
|
|
||||||
|
|
||||||
if (response.data.id) {
|
|
||||||
revalidate();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!data && !error) {
|
if (!data && !error) {
|
||||||
@@ -167,7 +162,8 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 flex justify-end mt-4 md:mt-0">
|
<div className="flex-1 flex justify-end mt-4 md:mt-0">
|
||||||
{!data.request && (
|
{(!data.mediaInfo ||
|
||||||
|
data.mediaInfo?.status === MediaStatus.UNKNOWN) && (
|
||||||
<Button
|
<Button
|
||||||
buttonType="primary"
|
buttonType="primary"
|
||||||
onClick={() => setShowRequestModal(true)}
|
onClick={() => setShowRequestModal(true)}
|
||||||
@@ -189,14 +185,8 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
|
|||||||
<FormattedMessage {...messages.request} />
|
<FormattedMessage {...messages.request} />
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{data.request?.status === MediaRequestStatus.PENDING && (
|
{data.mediaInfo?.status === MediaStatus.PENDING && (
|
||||||
<Button
|
<Button buttonType="warning">
|
||||||
buttonType="warning"
|
|
||||||
onClick={() => {
|
|
||||||
if (data.request?.requestedBy.id === user?.id)
|
|
||||||
setShowCancelModal(true);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<svg
|
<svg
|
||||||
className="w-4 mr-2"
|
className="w-4 mr-2"
|
||||||
fill="none"
|
fill="none"
|
||||||
@@ -211,12 +201,10 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
|
|||||||
d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9"
|
d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
{data.request?.requestedBy.id === user?.id
|
<FormattedMessage {...messages.pending} />
|
||||||
? intl.formatMessage(messages.cancelrequest)
|
|
||||||
: intl.formatMessage(messages.pending)}
|
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{data.request?.status === MediaRequestStatus.APPROVED && (
|
{data.mediaInfo?.status === MediaStatus.PROCESSING && (
|
||||||
<Button buttonType="danger">
|
<Button buttonType="danger">
|
||||||
<svg
|
<svg
|
||||||
className="w-5 mr-1"
|
className="w-5 mr-1"
|
||||||
@@ -235,7 +223,7 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
|
|||||||
<FormattedMessage {...messages.unavailable} />
|
<FormattedMessage {...messages.unavailable} />
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{data.request?.status === MediaRequestStatus.AVAILABLE && (
|
{data.mediaInfo?.status === MediaStatus.AVAILABLE && (
|
||||||
<Button buttonType="success">
|
<Button buttonType="success">
|
||||||
<svg
|
<svg
|
||||||
className="w-5 mr-1"
|
className="w-5 mr-1"
|
||||||
@@ -300,13 +288,13 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex pt-8 text-white flex-col md:flex-row pb-4">
|
<div className="flex pt-8 text-white flex-col md:flex-row pb-4">
|
||||||
<div className="flex-1 md:mr-8">
|
<div className="flex-1 md:mr-8">
|
||||||
{data.request?.status === MediaRequestStatus.PENDING &&
|
{/* {data.mediaInfo?.status === MediaStatus.PENDING &&
|
||||||
hasPermission(Permission.MANAGE_REQUESTS) && (
|
hasPermission(Permission.MANAGE_REQUESTS) && (
|
||||||
<PendingRequest
|
<PendingRequest
|
||||||
request={data.request}
|
request={data.request}
|
||||||
onUpdate={() => revalidate()}
|
onUpdate={() => revalidate()}
|
||||||
/>
|
/>
|
||||||
)}
|
)} */}
|
||||||
<h2 className="text-xl md:text-2xl">
|
<h2 className="text-xl md:text-2xl">
|
||||||
<FormattedMessage {...messages.overview} />
|
<FormattedMessage {...messages.overview} />
|
||||||
</h2>
|
</h2>
|
||||||
@@ -463,13 +451,12 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
|
|||||||
key={`recommended-${title.id}`}
|
key={`recommended-${title.id}`}
|
||||||
id={title.id}
|
id={title.id}
|
||||||
image={title.posterPath}
|
image={title.posterPath}
|
||||||
status={title.request?.status}
|
status={title.mediaInfo?.status}
|
||||||
summary={title.overview}
|
summary={title.overview}
|
||||||
title={title.title}
|
title={title.title}
|
||||||
userScore={title.voteAverage}
|
userScore={title.voteAverage}
|
||||||
year={title.releaseDate}
|
year={title.releaseDate}
|
||||||
mediaType={title.mediaType}
|
mediaType={title.mediaType}
|
||||||
requestId={title.request?.id}
|
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
/>
|
/>
|
||||||
@@ -510,13 +497,12 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
|
|||||||
key={`recommended-${title.id}`}
|
key={`recommended-${title.id}`}
|
||||||
id={title.id}
|
id={title.id}
|
||||||
image={title.posterPath}
|
image={title.posterPath}
|
||||||
status={title.request?.status}
|
status={title.mediaInfo?.status}
|
||||||
summary={title.overview}
|
summary={title.overview}
|
||||||
title={title.title}
|
title={title.title}
|
||||||
userScore={title.voteAverage}
|
userScore={title.voteAverage}
|
||||||
year={title.releaseDate}
|
year={title.releaseDate}
|
||||||
mediaType={title.mediaType}
|
mediaType={title.mediaType}
|
||||||
requestId={title.request?.id}
|
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
/>
|
/>
|
||||||
|
@@ -34,25 +34,23 @@ const RequestCard: React.FC<TmdbTitleCardProps> = ({ tmdbId, type }) => {
|
|||||||
<TitleCard
|
<TitleCard
|
||||||
id={title.id}
|
id={title.id}
|
||||||
image={title.posterPath}
|
image={title.posterPath}
|
||||||
status={title.request?.status}
|
status={title.mediaInfo?.status}
|
||||||
summary={title.overview}
|
summary={title.overview}
|
||||||
title={title.title}
|
title={title.title}
|
||||||
userScore={title.voteAverage}
|
userScore={title.voteAverage}
|
||||||
year={title.releaseDate}
|
year={title.releaseDate}
|
||||||
mediaType={'movie'}
|
mediaType={'movie'}
|
||||||
requestId={title.request?.id}
|
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<TitleCard
|
<TitleCard
|
||||||
id={title.id}
|
id={title.id}
|
||||||
image={title.posterPath}
|
image={title.posterPath}
|
||||||
status={title.request?.status}
|
status={title.mediaInfo?.status}
|
||||||
summary={title.overview}
|
summary={title.overview}
|
||||||
title={title.name}
|
title={title.name}
|
||||||
userScore={title.voteAverage}
|
userScore={title.voteAverage}
|
||||||
year={title.firstAirDate}
|
year={title.firstAirDate}
|
||||||
mediaType={'tv'}
|
mediaType={'tv'}
|
||||||
requestId={title.request?.id}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@@ -11,6 +11,7 @@ import axios from 'axios';
|
|||||||
import { MediaRequest } from '../../../server/entity/MediaRequest';
|
import { MediaRequest } from '../../../server/entity/MediaRequest';
|
||||||
import MovieRequestModal from '../RequestModal/MovieRequestModal';
|
import MovieRequestModal from '../RequestModal/MovieRequestModal';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
import { MediaStatus } from '../../../server/constants/media';
|
||||||
|
|
||||||
interface TitleCardProps {
|
interface TitleCardProps {
|
||||||
id: number;
|
id: number;
|
||||||
@@ -20,17 +21,10 @@ interface TitleCardProps {
|
|||||||
title: string;
|
title: string;
|
||||||
userScore: number;
|
userScore: number;
|
||||||
mediaType: MediaType;
|
mediaType: MediaType;
|
||||||
status?: MediaRequestStatus;
|
status?: MediaStatus;
|
||||||
requestId?: number;
|
requestId?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum MediaRequestStatus {
|
|
||||||
PENDING = 1,
|
|
||||||
APPROVED,
|
|
||||||
DECLINED,
|
|
||||||
AVAILABLE,
|
|
||||||
}
|
|
||||||
|
|
||||||
const TitleCard: React.FC<TitleCardProps> = ({
|
const TitleCard: React.FC<TitleCardProps> = ({
|
||||||
id,
|
id,
|
||||||
image,
|
image,
|
||||||
@@ -56,7 +50,7 @@ const TitleCard: React.FC<TitleCardProps> = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (response.data) {
|
if (response.data) {
|
||||||
setCurrentStatus(response.data.status);
|
setCurrentStatus(response.data.media.status);
|
||||||
addToast(
|
addToast(
|
||||||
<span>
|
<span>
|
||||||
<strong>{title}</strong> succesfully requested!
|
<strong>{title}</strong> succesfully requested!
|
||||||
@@ -131,13 +125,13 @@ const TitleCard: React.FC<TitleCardProps> = ({
|
|||||||
right: '-1px',
|
right: '-1px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{currentStatus === MediaRequestStatus.AVAILABLE && (
|
{currentStatus === MediaStatus.AVAILABLE && (
|
||||||
<Available className="rounded-tr-md" />
|
<Available className="rounded-tr-md" />
|
||||||
)}
|
)}
|
||||||
{currentStatus === MediaRequestStatus.PENDING && (
|
{currentStatus === MediaStatus.PENDING && (
|
||||||
<Requested className="rounded-tr-md" />
|
<Requested className="rounded-tr-md" />
|
||||||
)}
|
)}
|
||||||
{currentStatus === MediaRequestStatus.APPROVED && (
|
{currentStatus === MediaStatus.PROCESSING && (
|
||||||
<Unavailable className="rounded-tr-md" />
|
<Unavailable className="rounded-tr-md" />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -251,7 +245,7 @@ const TitleCard: React.FC<TitleCardProps> = ({
|
|||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
{currentStatus === MediaRequestStatus.PENDING && (
|
{currentStatus === MediaStatus.PENDING && (
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowCancelModal(true)}
|
onClick={() => setShowCancelModal(true)}
|
||||||
className="w-full h-7 text-center text-white bg-orange-400 hover:bg-orange-300 rounded-sm ml-1 focus:border-orange-700 focus:shadow-outline-orange active:bg-orange-700 transition ease-in-out duration-150"
|
className="w-full h-7 text-center text-white bg-orange-400 hover:bg-orange-300 rounded-sm ml-1 focus:border-orange-700 focus:shadow-outline-orange active:bg-orange-700 transition ease-in-out duration-150"
|
||||||
@@ -272,7 +266,7 @@ const TitleCard: React.FC<TitleCardProps> = ({
|
|||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
{currentStatus === MediaRequestStatus.APPROVED && (
|
{currentStatus === MediaStatus.AVAILABLE && (
|
||||||
<button className="w-full h-7 text-center text-white bg-red-500 rounded-sm ml-1">
|
<button className="w-full h-7 text-center text-white bg-red-500 rounded-sm ml-1">
|
||||||
<svg
|
<svg
|
||||||
className="w-4 mx-auto"
|
className="w-4 mx-auto"
|
||||||
@@ -290,7 +284,7 @@ const TitleCard: React.FC<TitleCardProps> = ({
|
|||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
{currentStatus === MediaRequestStatus.AVAILABLE && (
|
{currentStatus === MediaStatus.AVAILABLE && (
|
||||||
<button className="w-full h-7 text-center text-white bg-green-400 rounded-sm ml-1">
|
<button className="w-full h-7 text-center text-white bg-green-400 rounded-sm ml-1">
|
||||||
<svg
|
<svg
|
||||||
className="w-4 mx-auto"
|
className="w-4 mx-auto"
|
||||||
|
@@ -17,6 +17,7 @@ import LoadingSpinner from '../Common/LoadingSpinner';
|
|||||||
import { useUser, Permission } from '../../hooks/useUser';
|
import { useUser, Permission } from '../../hooks/useUser';
|
||||||
import PendingRequest from '../PendingRequest';
|
import PendingRequest from '../PendingRequest';
|
||||||
import { TvDetails as TvDetailsType } from '../../../server/models/Tv';
|
import { TvDetails as TvDetailsType } from '../../../server/models/Tv';
|
||||||
|
import { MediaStatus } from '../../../server/constants/media';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
userrating: 'User Rating',
|
userrating: 'User Rating',
|
||||||
@@ -76,7 +77,7 @@ const TvDetails: React.FC<TvDetailsProps> = ({ tv }) => {
|
|||||||
const request = async () => {
|
const request = async () => {
|
||||||
const response = await axios.post<MediaRequest>('/api/v1/request', {
|
const response = await axios.post<MediaRequest>('/api/v1/request', {
|
||||||
mediaId: data?.id,
|
mediaId: data?.id,
|
||||||
mediaType: 'movie',
|
mediaType: 'tv',
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.data) {
|
if (response.data) {
|
||||||
@@ -91,13 +92,7 @@ const TvDetails: React.FC<TvDetailsProps> = ({ tv }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const cancelRequest = async () => {
|
const cancelRequest = async () => {
|
||||||
const response = await axios.delete<MediaRequest>(
|
// fix me
|
||||||
`/api/v1/request/${data?.request?.id}`
|
|
||||||
);
|
|
||||||
|
|
||||||
if (response.data.id) {
|
|
||||||
revalidate();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!data && !error) {
|
if (!data && !error) {
|
||||||
@@ -148,7 +143,8 @@ const TvDetails: React.FC<TvDetailsProps> = ({ tv }) => {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 flex justify-end mt-4 md:mt-0">
|
<div className="flex-1 flex justify-end mt-4 md:mt-0">
|
||||||
{!data.request && (
|
{(!data.mediaInfo ||
|
||||||
|
data.mediaInfo.status === MediaStatus.UNKNOWN) && (
|
||||||
<Button
|
<Button
|
||||||
buttonType="primary"
|
buttonType="primary"
|
||||||
onClick={() => setShowRequestModal(true)}
|
onClick={() => setShowRequestModal(true)}
|
||||||
@@ -170,14 +166,8 @@ const TvDetails: React.FC<TvDetailsProps> = ({ tv }) => {
|
|||||||
<FormattedMessage {...messages.request} />
|
<FormattedMessage {...messages.request} />
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{data.request?.status === MediaRequestStatus.PENDING && (
|
{data.mediaInfo?.status === MediaStatus.PENDING && (
|
||||||
<Button
|
<Button buttonType="warning">
|
||||||
buttonType="warning"
|
|
||||||
onClick={() => {
|
|
||||||
if (data.request?.requestedBy.id === user?.id)
|
|
||||||
setShowCancelModal(true);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<svg
|
<svg
|
||||||
className="w-4 mr-2"
|
className="w-4 mr-2"
|
||||||
fill="none"
|
fill="none"
|
||||||
@@ -192,12 +182,10 @@ const TvDetails: React.FC<TvDetailsProps> = ({ tv }) => {
|
|||||||
d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9"
|
d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
{data.request?.requestedBy.id === user?.id
|
<FormattedMessage {...messages.pending} />
|
||||||
? intl.formatMessage(messages.cancelrequest)
|
|
||||||
: intl.formatMessage(messages.pending)}
|
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{data.request?.status === MediaRequestStatus.APPROVED && (
|
{data.mediaInfo?.status === MediaStatus.PROCESSING && (
|
||||||
<Button buttonType="danger">
|
<Button buttonType="danger">
|
||||||
<svg
|
<svg
|
||||||
className="w-5 mr-1"
|
className="w-5 mr-1"
|
||||||
@@ -216,7 +204,7 @@ const TvDetails: React.FC<TvDetailsProps> = ({ tv }) => {
|
|||||||
<FormattedMessage {...messages.unavailable} />
|
<FormattedMessage {...messages.unavailable} />
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{data.request?.status === MediaRequestStatus.AVAILABLE && (
|
{data.mediaInfo?.status === MediaStatus.AVAILABLE && (
|
||||||
<Button buttonType="success">
|
<Button buttonType="success">
|
||||||
<svg
|
<svg
|
||||||
className="w-5 mr-1"
|
className="w-5 mr-1"
|
||||||
@@ -281,13 +269,13 @@ const TvDetails: React.FC<TvDetailsProps> = ({ tv }) => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex pt-8 text-white flex-col md:flex-row pb-4">
|
<div className="flex pt-8 text-white flex-col md:flex-row pb-4">
|
||||||
<div className="flex-1 md:mr-8">
|
<div className="flex-1 md:mr-8">
|
||||||
{data.request?.status === MediaRequestStatus.PENDING &&
|
{/* {data.mediaInfo?.status === MediaStatus.PENDING &&
|
||||||
hasPermission(Permission.MANAGE_REQUESTS) && (
|
hasPermission(Permission.MANAGE_REQUESTS) && (
|
||||||
<PendingRequest
|
<PendingRequest
|
||||||
request={data.request}
|
request={data.request}
|
||||||
onUpdate={() => revalidate()}
|
onUpdate={() => revalidate()}
|
||||||
/>
|
/>
|
||||||
)}
|
)} */}
|
||||||
<h2 className="text-xl md:text-2xl">
|
<h2 className="text-xl md:text-2xl">
|
||||||
<FormattedMessage {...messages.overview} />
|
<FormattedMessage {...messages.overview} />
|
||||||
</h2>
|
</h2>
|
||||||
@@ -403,13 +391,12 @@ const TvDetails: React.FC<TvDetailsProps> = ({ tv }) => {
|
|||||||
key={`recommended-${title.id}`}
|
key={`recommended-${title.id}`}
|
||||||
id={title.id}
|
id={title.id}
|
||||||
image={title.posterPath}
|
image={title.posterPath}
|
||||||
status={title.request?.status}
|
status={title.mediaInfo?.status}
|
||||||
summary={title.overview}
|
summary={title.overview}
|
||||||
title={title.name}
|
title={title.name}
|
||||||
userScore={title.voteAverage}
|
userScore={title.voteAverage}
|
||||||
year={title.firstAirDate}
|
year={title.firstAirDate}
|
||||||
mediaType={title.mediaType}
|
mediaType={title.mediaType}
|
||||||
requestId={title.request?.id}
|
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
/>
|
/>
|
||||||
@@ -447,13 +434,12 @@ const TvDetails: React.FC<TvDetailsProps> = ({ tv }) => {
|
|||||||
key={`recommended-${title.id}`}
|
key={`recommended-${title.id}`}
|
||||||
id={title.id}
|
id={title.id}
|
||||||
image={title.posterPath}
|
image={title.posterPath}
|
||||||
status={title.request?.status}
|
status={title.mediaInfo?.status}
|
||||||
summary={title.overview}
|
summary={title.overview}
|
||||||
title={title.name}
|
title={title.name}
|
||||||
userScore={title.voteAverage}
|
userScore={title.voteAverage}
|
||||||
year={title.firstAirDate}
|
year={title.firstAirDate}
|
||||||
mediaType={title.mediaType}
|
mediaType={title.mediaType}
|
||||||
requestId={title.request?.id}
|
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
/>
|
/>
|
||||||
|
Reference in New Issue
Block a user