1+ import type { SonarrSeason } from '@server/api/servarr/sonarr' ;
12import TheMovieDb from '@server/api/themoviedb' ;
23import { MediaStatus , MediaType } from '@server/constants/media' ;
34import { getRepository } from '@server/datasource' ;
45import Media from '@server/entity/Media' ;
6+ import { MediaRequest } from '@server/entity/MediaRequest' ;
57import Season from '@server/entity/Season' ;
8+ import SeasonRequest from '@server/entity/SeasonRequest' ;
69import { getSettings } from '@server/lib/settings' ;
710import logger from '@server/logger' ;
811import AsyncLock from '@server/utils/asyncLock' ;
@@ -48,6 +51,7 @@ export interface ProcessableSeason {
4851 episodes4k : number ;
4952 is4kOverride ?: boolean ;
5053 processing ?: boolean ;
54+ monitored ?: boolean ;
5155}
5256
5357class BaseScanner < T > {
@@ -211,7 +215,7 @@ class BaseScanner<T> {
211215
212216 /**
213217 * processShow takes a TMDB ID and an array of ProcessableSeasons, which
214- * should include the total episodes a sesaon has + the total available
218+ * should include the total episodes a season has + the total available
215219 * episodes that each season currently has. Unlike processMovie, this method
216220 * does not take an `is4k` option. We handle both the 4k _and_ non 4k status
217221 * in one method.
@@ -234,6 +238,7 @@ class BaseScanner<T> {
234238 } : ProcessOptions = { }
235239 ) : Promise < void > {
236240 const mediaRepository = getRepository ( Media ) ;
241+ const settings = getSettings ( ) ;
237242
238243 await this . asyncLock . dispatch ( tmdbId , async ( ) => {
239244 const media = await this . getExisting ( tmdbId , MediaType . TV ) ;
@@ -277,25 +282,35 @@ class BaseScanner<T> {
277282 // force it to stay available (to avoid competing scanners)
278283 existingSeason . status =
279284 ( season . totalEpisodes === season . episodes && season . episodes > 0 ) ||
280- existingSeason . status === MediaStatus . AVAILABLE
285+ ( existingSeason . status === MediaStatus . AVAILABLE &&
286+ season . episodes > 0 )
281287 ? MediaStatus . AVAILABLE
282288 : season . episodes > 0
283289 ? MediaStatus . PARTIALLY_AVAILABLE
284290 : ! season . is4kOverride && season . processing
285291 ? MediaStatus . PROCESSING
292+ : settings . main . removeUnmonitoredEnabled &&
293+ ! season . monitored &&
294+ season . episodes == 0
295+ ? MediaStatus . UNKNOWN
286296 : existingSeason . status ;
287297
288298 // Same thing here, except we only do updates if 4k is enabled
289299 existingSeason . status4k =
290300 ( this . enable4kShow &&
291301 season . episodes4k === season . totalEpisodes &&
292302 season . episodes4k > 0 ) ||
293- existingSeason . status4k === MediaStatus . AVAILABLE
303+ ( existingSeason . status4k === MediaStatus . AVAILABLE &&
304+ season . episodes > 0 )
294305 ? MediaStatus . AVAILABLE
295306 : this . enable4kShow && season . episodes4k > 0
296307 ? MediaStatus . PARTIALLY_AVAILABLE
297308 : season . is4kOverride && season . processing
298309 ? MediaStatus . PROCESSING
310+ : settings . main . removeUnmonitoredEnabled &&
311+ ! season . monitored &&
312+ season . episodes4k == 0
313+ ? MediaStatus . UNKNOWN
299314 : existingSeason . status4k ;
300315 } else {
301316 newSeasons . push (
@@ -618,6 +633,56 @@ class BaseScanner<T> {
618633 get protectedBundleSize ( ) : number {
619634 return this . bundleSize ;
620635 }
636+
637+ protected async processUnmonitoredMovie ( tmdbId : number ) : Promise < void > {
638+ const mediaRepository = getRepository ( Media ) ;
639+ await this . asyncLock . dispatch ( tmdbId , async ( ) => {
640+ const existing = await this . getExisting ( tmdbId , MediaType . MOVIE ) ;
641+ if ( existing && existing . status === MediaStatus . PROCESSING ) {
642+ existing . status = MediaStatus . UNKNOWN ;
643+ await mediaRepository . save ( existing ) ;
644+ this . log (
645+ `Movie TMDB ID ${ tmdbId } unmonitored from Radarr. Media status set to UNKNOWN.` ,
646+ 'info'
647+ ) ;
648+ }
649+ } ) ;
650+ }
651+
652+ protected async processUnmonitoredSeason (
653+ tmdbId : number ,
654+ season : SonarrSeason
655+ ) : Promise < void > {
656+ // Remove unmonitored seasons from Requests
657+ const requestRepository = getRepository ( MediaRequest ) ;
658+ const seasonRequestRepository = getRepository ( SeasonRequest ) ;
659+
660+ const existingRequests = await requestRepository
661+ . createQueryBuilder ( 'request' )
662+ . innerJoinAndSelect ( 'request.media' , 'media' )
663+ . innerJoinAndSelect ( 'request.seasons' , 'seasons' )
664+ . where ( 'media.tmdbId = :tmdbId' , { tmdbId : tmdbId } )
665+ . andWhere ( 'media.mediaType = :mediaType' , {
666+ mediaType : MediaType . TV ,
667+ } )
668+ . andWhere ( 'seasons.seasonNumber = :seasonNumber' , {
669+ seasonNumber : season . seasonNumber ,
670+ } )
671+ . getMany ( ) ;
672+
673+ if ( existingRequests && existingRequests . length > 0 ) {
674+ for ( const existingRequest of existingRequests ) {
675+ for ( const requestedSeason of existingRequest . seasons ) {
676+ if ( requestedSeason . seasonNumber === season . seasonNumber ) {
677+ this . log (
678+ `Removing request for Season ${ season . seasonNumber } of tmdbId ${ tmdbId } as it is unmonitored`
679+ ) ;
680+ await seasonRequestRepository . remove ( requestedSeason ) ;
681+ }
682+ }
683+ }
684+ }
685+ }
621686}
622687
623688export default BaseScanner ;
0 commit comments