diff --git a/server/entity/MediaRequest.ts b/server/entity/MediaRequest.ts index cf0903e8f..3b64f4727 100644 --- a/server/entity/MediaRequest.ts +++ b/server/entity/MediaRequest.ts @@ -151,6 +151,15 @@ export class MediaRequest { logger.error('No parent media!', { label: 'Media Request' }); return; } + + if (media[this.is4k ? 'status4k' : 'status'] === MediaStatus.AVAILABLE) { + logger.warn( + 'Media became available before request was approved. Approval notification will be skipped.', + { label: 'Media Request' } + ); + return; + } + const tmdb = new TheMovieDb(); if (this.media.mediaType === MediaType.MOVIE) { const movie = await tmdb.getMovie({ movieId: this.media.tmdbId }); @@ -205,7 +214,13 @@ export class MediaRequest { return; } const seasonRequestRepository = getRepository(SeasonRequest); - if (this.status === MediaRequestStatus.APPROVED) { + if ( + this.status === MediaRequestStatus.APPROVED && + // Do not update the status if the item is already partially available or available + media[this.is4k ? 'status4k' : 'status'] !== MediaStatus.AVAILABLE && + media[this.is4k ? 'status4k' : 'status'] !== + MediaStatus.PARTIALLY_AVAILABLE + ) { if (this.is4k) { media.status4k = MediaStatus.PROCESSING; } else { @@ -358,6 +373,21 @@ export class MediaRequest { }); const movie = await tmdb.getMovie({ movieId: this.media.tmdbId }); + const media = await mediaRepository.findOne({ + where: { id: this.media.id }, + }); + + if (!media) { + logger.error('Media not present'); + return; + } + + if ( + media[this.is4k ? 'status4k' : 'status'] === MediaStatus.AVAILABLE + ) { + throw new Error('Media already available'); + } + // Run this asynchronously so we don't wait for it on the UI side radarr .addMovie({ @@ -373,13 +403,6 @@ export class MediaRequest { }) .then(async (success) => { if (!success) { - const media = await mediaRepository.findOne({ - where: { id: this.media.id }, - }); - if (!media) { - logger.error('Media not present'); - return; - } media.status = MediaStatus.UNKNOWN; await mediaRepository.save(media); logger.warn( @@ -464,6 +487,12 @@ export class MediaRequest { throw new Error('Media data is missing'); } + if ( + media[this.is4k ? 'status4k' : 'status'] === MediaStatus.AVAILABLE + ) { + throw new Error('Media already available'); + } + const tmdb = new TheMovieDb(); const sonarr = new SonarrAPI({ apiKey: sonarrSettings.apiKey, diff --git a/server/subscriber/MediaSubscriber.ts b/server/subscriber/MediaSubscriber.ts index 92c4803ec..6e5e285ca 100644 --- a/server/subscriber/MediaSubscriber.ts +++ b/server/subscriber/MediaSubscriber.ts @@ -5,7 +5,7 @@ import { UpdateEvent, } from 'typeorm'; import TheMovieDb from '../api/themoviedb'; -import { MediaStatus, MediaType } from '../constants/media'; +import { MediaRequestStatus, MediaStatus, MediaType } from '../constants/media'; import Media from '../entity/Media'; import { MediaRequest } from '../entity/MediaRequest'; import Season from '../entity/Season'; @@ -102,6 +102,21 @@ export class MediaSubscriber implements EntitySubscriberInterface { } } + private async updateChildRequestStatus(event: Media, is4k: boolean) { + const requestRepository = getRepository(MediaRequest); + + const requests = await requestRepository.find({ + where: { media: event.id }, + }); + + for (const request of requests) { + if (request.is4k === is4k) { + request.status = MediaRequestStatus.APPROVED; + await requestRepository.save(request); + } + } + } + public beforeUpdate(event: UpdateEvent): void { if (!event.entity) { return; @@ -121,5 +136,19 @@ export class MediaSubscriber implements EntitySubscriberInterface { ) { this.notifyAvailableSeries(event.entity, event.databaseEntity); } + + if ( + event.entity.status === MediaStatus.AVAILABLE && + event.databaseEntity.status === MediaStatus.PENDING + ) { + this.updateChildRequestStatus(event.entity, false); + } + + if ( + event.entity.status4k === MediaStatus.AVAILABLE && + event.databaseEntity.status4k === MediaStatus.PENDING + ) { + this.updateChildRequestStatus(event.entity, true); + } } }