diff --git a/server/entity/Media.ts b/server/entity/Media.ts index 8daf36e83..f01bf3a6f 100644 --- a/server/entity/Media.ts +++ b/server/entity/Media.ts @@ -1,12 +1,7 @@ import RadarrAPI from '@server/api/servarr/radarr'; import SonarrAPI from '@server/api/servarr/sonarr'; -import { - MediaRequestStatus, - MediaStatus, - MediaType, -} from '@server/constants/media'; +import { MediaStatus, MediaType } from '@server/constants/media'; import { getRepository } from '@server/datasource'; -import SeasonRequest from '@server/entity/SeasonRequest'; import type { DownloadingItem } from '@server/lib/downloadtracker'; import downloadTracker from '@server/lib/downloadtracker'; import { getSettings } from '@server/lib/settings'; @@ -15,7 +10,6 @@ import { DbAwareColumn } from '@server/utils/DbColumnHelper'; import { getHostname } from '@server/utils/getHostname'; import { AfterLoad, - AfterUpdate, Column, Entity, Index, @@ -377,111 +371,6 @@ class Media { } } } - - @AfterUpdate() - public async updateRelatedMediaRequest(): Promise { - const requestRepository = getRepository(MediaRequest); - const seasonRequestRepository = getRepository(SeasonRequest); - - const validStatuses = [ - MediaStatus.PARTIALLY_AVAILABLE, - MediaStatus.AVAILABLE, - MediaStatus.DELETED, - ]; - - if ( - validStatuses.includes(this.status) || - validStatuses.includes(this.status4k) - ) { - const relatedRequests = await requestRepository.find({ - relations: { - media: true, - }, - where: { - media: { id: this.id }, - status: MediaRequestStatus.APPROVED, - }, - }); - - // Check the media entity status and if available - // or deleted, set the related request to completed - if (relatedRequests.length > 0) { - const completedRequests: MediaRequest[] = []; - - relatedRequests.forEach((request) => { - let shouldComplete = false; - - if ( - this[request.is4k ? 'status4k' : 'status'] === - MediaStatus.AVAILABLE || - this[request.is4k ? 'status4k' : 'status'] === MediaStatus.DELETED - ) { - shouldComplete = true; - } else if (this.mediaType === 'tv') { - // For TV, check if all requested seasons are available or deleted - const allSeasonsReady = request.seasons.every((requestSeason) => { - const matchingSeason = this.seasons.find( - (mediaSeason) => - mediaSeason.seasonNumber === requestSeason.seasonNumber - ); - - if (!matchingSeason) { - return false; - } - - return ( - matchingSeason[request.is4k ? 'status4k' : 'status'] === - MediaStatus.AVAILABLE || - matchingSeason[request.is4k ? 'status4k' : 'status'] === - MediaStatus.DELETED - ); - }); - - shouldComplete = allSeasonsReady; - } - - if (shouldComplete) { - request.status = MediaRequestStatus.COMPLETED; - completedRequests.push(request); - } - }); - - await requestRepository.save(completedRequests); - - // Handle season requests and mark them completed when - // that specific season becomes available - if (this.mediaType === 'tv') { - const seasonsToUpdate = relatedRequests.flatMap((request) => { - return request.seasons.filter((requestSeason) => { - const matchingSeason = this.seasons.find( - (mediaSeason) => - mediaSeason.seasonNumber === requestSeason.seasonNumber - ); - - if (!matchingSeason) { - return false; - } - - return ( - matchingSeason[request.is4k ? 'status4k' : 'status'] === - MediaStatus.AVAILABLE || - matchingSeason[request.is4k ? 'status4k' : 'status'] === - MediaStatus.DELETED - ); - }); - }); - - await Promise.all( - seasonsToUpdate.map((season) => - seasonRequestRepository.update(season.id, { - status: MediaRequestStatus.COMPLETED, - }) - ) - ); - } - } - } - } } export default Media; diff --git a/server/subscriber/MediaSubscriber.ts b/server/subscriber/MediaSubscriber.ts index ec70621a5..9ec002fe2 100644 --- a/server/subscriber/MediaSubscriber.ts +++ b/server/subscriber/MediaSubscriber.ts @@ -1,7 +1,13 @@ -import { MediaRequestStatus, MediaStatus } from '@server/constants/media'; +import { + MediaRequestStatus, + MediaStatus, + MediaType, +} from '@server/constants/media'; import { getRepository } from '@server/datasource'; import Media from '@server/entity/Media'; import { MediaRequest } from '@server/entity/MediaRequest'; +import Season from '@server/entity/Season'; +import SeasonRequest from '@server/entity/SeasonRequest'; import type { EntitySubscriberInterface, UpdateEvent } from 'typeorm'; import { EventSubscriber } from 'typeorm'; @@ -25,11 +31,7 @@ export class MediaSubscriber implements EntitySubscriberInterface { } } - private async updateRelatedMediaRequest( - event: Media, - databaseEvent: Media, - is4k: boolean - ) { + private async updateRelatedMediaRequest(event: Media, is4k: boolean) { const requestRepository = getRepository(MediaRequest); const seasonRequestRepository = getRepository(SeasonRequest); @@ -49,59 +51,35 @@ export class MediaSubscriber implements EntitySubscriberInterface { if (relatedRequests.length > 0) { const completedRequests: MediaRequest[] = []; - for (const request of relatedRequests) { + relatedRequests.forEach((request) => { let shouldComplete = false; if ( - (event[request.is4k ? 'status4k' : 'status'] === + event[request.is4k ? 'status4k' : 'status'] === MediaStatus.AVAILABLE || - event[request.is4k ? 'status4k' : 'status'] === - MediaStatus.DELETED) && - event.mediaType === MediaType.MOVIE + event[request.is4k ? 'status4k' : 'status'] === MediaStatus.DELETED ) { shouldComplete = true; } else if (event.mediaType === 'tv') { - const allSeasonResults = await Promise.all( - request.seasons.map(async (requestSeason) => { - const matchingSeason = event.seasons.find( - (mediaSeason) => - mediaSeason.seasonNumber === requestSeason.seasonNumber - ); - const matchingOldSeason = databaseEvent.seasons.find( - (oldSeason) => - oldSeason.seasonNumber === requestSeason.seasonNumber - ); - - if (!matchingSeason) { - return false; - } - - const currentSeasonStatus = - matchingSeason[request.is4k ? 'status4k' : 'status']; - const previousSeasonStatus = - matchingOldSeason?.[request.is4k ? 'status4k' : 'status']; - - const hasStatusChanged = - currentSeasonStatus !== previousSeasonStatus; - - const shouldUpdate = - (hasStatusChanged || - requestSeason.status === MediaRequestStatus.COMPLETED) && - (currentSeasonStatus === MediaStatus.AVAILABLE || - currentSeasonStatus === MediaStatus.DELETED); - - if (shouldUpdate) { - requestSeason.status = MediaRequestStatus.COMPLETED; - await seasonRequestRepository.save(requestSeason); - - return true; - } + // For TV, check if all requested seasons are available or deleted + const allSeasonsReady = request.seasons.every((requestSeason) => { + const matchingSeason = event.seasons.find( + (mediaSeason) => + mediaSeason.seasonNumber === requestSeason.seasonNumber + ); + if (!matchingSeason) { return false; - }) - ); + } + + return ( + matchingSeason[request.is4k ? 'status4k' : 'status'] === + MediaStatus.AVAILABLE || + matchingSeason[request.is4k ? 'status4k' : 'status'] === + MediaStatus.DELETED + ); + }); - const allSeasonsReady = allSeasonResults.every((result) => result); shouldComplete = allSeasonsReady; } @@ -109,9 +87,41 @@ export class MediaSubscriber implements EntitySubscriberInterface { request.status = MediaRequestStatus.COMPLETED; completedRequests.push(request); } - } + }); await requestRepository.save(completedRequests); + + // Handle season requests and mark them completed when + // that specific season becomes available + if (event.mediaType === 'tv') { + const seasonsToUpdate = relatedRequests.flatMap((request) => { + return request.seasons.filter((requestSeason) => { + const matchingSeason = event.seasons.find( + (mediaSeason) => + mediaSeason.seasonNumber === requestSeason.seasonNumber + ); + + if (!matchingSeason) { + return false; + } + + return ( + matchingSeason[request.is4k ? 'status4k' : 'status'] === + MediaStatus.AVAILABLE || + matchingSeason[request.is4k ? 'status4k' : 'status'] === + MediaStatus.DELETED + ); + }); + }); + + await Promise.all( + seasonsToUpdate.map((season) => + seasonRequestRepository.update(season.id, { + status: MediaRequestStatus.COMPLETED, + }) + ) + ); + } } } @@ -163,7 +173,7 @@ export class MediaSubscriber implements EntitySubscriberInterface { return ( season[is4k ? 'status4k' : 'status'] !== - previousSeason?.[is4k ? 'status4k' : 'status'] + previousSeason[is4k ? 'status4k' : 'status'] ); }); }; @@ -174,11 +184,7 @@ export class MediaSubscriber implements EntitySubscriberInterface { seasonStatusCheck(false))) && validStatuses.includes(event.entity.status) ) { - this.updateRelatedMediaRequest( - event.entity as Media, - event.databaseEntity as Media, - false - ); + this.updateRelatedMediaRequest(event.entity as Media, false); } if ( @@ -186,11 +192,7 @@ export class MediaSubscriber implements EntitySubscriberInterface { (event.entity.mediaType === MediaType.TV && seasonStatusCheck(true))) && validStatuses.includes(event.entity.status4k) ) { - this.updateRelatedMediaRequest( - event.entity as Media, - event.databaseEntity as Media, - true - ); + this.updateRelatedMediaRequest(event.entity as Media, true); } }