@@ -5,6 +5,8 @@ import PlexAPI from '@server/api/plexapi';
55import RadarrAPI , { type RadarrMovie } from '@server/api/servarr/radarr' ;
66import type { SonarrSeason , SonarrSeries } from '@server/api/servarr/sonarr' ;
77import SonarrAPI from '@server/api/servarr/sonarr' ;
8+ import TheMovieDb from '@server/api/themoviedb' ;
9+ import type { TmdbTvDetails } from '@server/api/themoviedb/interfaces' ;
810import { MediaRequestStatus , MediaStatus } from '@server/constants/media' ;
911import { MediaServerType } from '@server/constants/server' ;
1012import { getRepository } from '@server/datasource' ;
@@ -31,6 +33,8 @@ class AvailabilitySync {
3133 private radarrServers : RadarrSettings [ ] ;
3234 private sonarrServers : SonarrSettings [ ] ;
3335
36+ readonly tmdb = new TheMovieDb ( ) ;
37+
3438 async run ( ) {
3539 const settings = getSettings ( ) ;
3640 const mediaServerType = getSettings ( ) . main . mediaServerType ;
@@ -45,7 +49,7 @@ class AvailabilitySync {
4549
4650 try {
4751 logger . info ( `Starting availability sync...` , {
48- label : 'Availability Sync ' ,
52+ label : 'AvailabilitySync ' ,
4953 } ) ;
5054 const pageSize = 50 ;
5155
@@ -149,7 +153,7 @@ class AvailabilitySync {
149153
150154 if ( existsInPlex || existsInRadarr ) {
151155 movieExists = true ;
152- logger . info (
156+ logger . debug (
153157 `The non-4K movie [TMDB ID ${ media . tmdbId } ] still exists. Preventing removal.` ,
154158 {
155159 label : 'AvailabilitySync' ,
@@ -159,7 +163,7 @@ class AvailabilitySync {
159163
160164 if ( existsInPlex4k || existsInRadarr4k ) {
161165 movieExists4k = true ;
162- logger . info (
166+ logger . debug (
163167 `The 4K movie [TMDB ID ${ media . tmdbId } ] still exists. Preventing removal.` ,
164168 {
165169 label : 'AvailabilitySync' ,
@@ -182,7 +186,7 @@ class AvailabilitySync {
182186
183187 if ( existsInJellyfin || existsInRadarr ) {
184188 movieExists = true ;
185- logger . info (
189+ logger . debug (
186190 `The non-4K movie [TMDB ID ${ media . tmdbId } ] still exists. Preventing removal.` ,
187191 {
188192 label : 'AvailabilitySync' ,
@@ -192,7 +196,7 @@ class AvailabilitySync {
192196
193197 if ( existsInJellyfin4k || existsInRadarr4k ) {
194198 movieExists4k = true ;
195- logger . info (
199+ logger . debug (
196200 `The 4K movie [TMDB ID ${ media . tmdbId } ] still exists. Preventing removal.` ,
197201 {
198202 label : 'AvailabilitySync' ,
@@ -246,7 +250,7 @@ class AvailabilitySync {
246250 if ( mediaServerType === MediaServerType . PLEX ) {
247251 if ( existsInPlex || existsInSonarr ) {
248252 showExists = true ;
249- logger . info (
253+ logger . debug (
250254 `The non-4K show [TMDB ID ${ media . tmdbId } ] still exists. Preventing removal.` ,
251255 {
252256 label : 'AvailabilitySync' ,
@@ -258,7 +262,7 @@ class AvailabilitySync {
258262 if ( mediaServerType === MediaServerType . PLEX ) {
259263 if ( existsInPlex4k || existsInSonarr4k ) {
260264 showExists4k = true ;
261- logger . info (
265+ logger . debug (
262266 `The 4K show [TMDB ID ${ media . tmdbId } ] still exists. Preventing removal.` ,
263267 {
264268 label : 'AvailabilitySync' ,
@@ -274,7 +278,7 @@ class AvailabilitySync {
274278 ) {
275279 if ( existsInJellyfin || existsInSonarr ) {
276280 showExists = true ;
277- logger . info (
281+ logger . debug (
278282 `The non-4K show [TMDB ID ${ media . tmdbId } ] still exists. Preventing removal.` ,
279283 {
280284 label : 'AvailabilitySync' ,
@@ -289,7 +293,7 @@ class AvailabilitySync {
289293 ) {
290294 if ( existsInJellyfin4k || existsInSonarr4k ) {
291295 showExists4k = true ;
292- logger . info (
296+ logger . debug (
293297 `The 4K show [TMDB ID ${ media . tmdbId } ] still exists. Preventing removal.` ,
294298 {
295299 label : 'AvailabilitySync' ,
@@ -353,6 +357,40 @@ class AvailabilitySync {
353357 ] ) ;
354358 }
355359
360+ // We need to fetch from TMDB to get the episode count for each season
361+ let tvShow : TmdbTvDetails ;
362+ if ( media . tmdbId ) {
363+ tvShow = await this . tmdb . getTvShow ( {
364+ tvId : Number ( media . tmdbId ) ,
365+ } ) ;
366+ } else if ( media . tvdbId ) {
367+ tvShow = await this . tmdb . getShowByTvdbId ( {
368+ tvdbId : Number ( media . tvdbId ) ,
369+ } ) ;
370+ } else {
371+ throw new Error ( 'No ID provided' ) ;
372+ }
373+
374+ // fill the finalSeasons and finalSeasons4k maps with false for missing seasons
375+ media . seasons . forEach ( ( season ) => {
376+ if (
377+ ! finalSeasons . has ( season . seasonNumber ) &&
378+ tvShow . seasons . find (
379+ ( s ) => s . season_number === season . seasonNumber
380+ ) ?. episode_count
381+ ) {
382+ finalSeasons . set ( season . seasonNumber , false ) ;
383+ }
384+ if (
385+ ! finalSeasons4k . has ( season . seasonNumber ) &&
386+ tvShow . seasons . find (
387+ ( s ) => s . season_number === season . seasonNumber
388+ ) ?. episode_count
389+ ) {
390+ finalSeasons4k . set ( season . seasonNumber , false ) ;
391+ }
392+ } ) ;
393+
356394 if (
357395 ! showExists &&
358396 ( media . status === MediaStatus . AVAILABLE ||
@@ -405,11 +443,11 @@ class AvailabilitySync {
405443 } catch ( ex ) {
406444 logger . error ( 'Failed to complete availability sync.' , {
407445 errorMessage : ex . message ,
408- label : 'Availability Sync ' ,
446+ label : 'AvailabilitySync ' ,
409447 } ) ;
410448 } finally {
411449 logger . info ( `Availability sync complete.` , {
412- label : 'Availability Sync ' ,
450+ label : 'AvailabilitySync ' ,
413451 } ) ;
414452 this . running = false ;
415453 }
@@ -508,7 +546,7 @@ class AvailabilitySync {
508546 ? media [ is4k ? 'jellyfinMediaId4k' : 'jellyfinMediaId' ]
509547 : null ;
510548 }
511- logger . info (
549+ logger . debug (
512550 `The ${ is4k ? '4K' : 'non-4K' } ${
513551 media . mediaType === 'movie' ? 'movie' : 'show'
514552 } [TMDB ID ${ media . tmdbId } ] was not found in any ${
@@ -531,7 +569,7 @@ class AvailabilitySync {
531569 } [TMDB ID ${ media . tmdbId } ].`,
532570 {
533571 errorMessage : ex . message ,
534- label : 'Availability Sync ' ,
572+ label : 'AvailabilitySync ' ,
535573 }
536574 ) ;
537575 }
@@ -555,56 +593,44 @@ class AvailabilitySync {
555593 // Retrieve the season keys to pass into our log
556594 const seasonKeys = [ ...seasonsPendingRemoval . keys ( ) ] ;
557595
558- // let isSeasonRemoved = false;
559-
560596 try {
561597 for ( const mediaSeason of media . seasons ) {
562- if ( seasonsPendingRemoval . has ( mediaSeason . seasonNumber ) ) {
598+ if (
599+ seasonsPendingRemoval . has ( mediaSeason . seasonNumber ) &&
600+ mediaSeason [ is4k ? 'status4k' : 'status' ] !== MediaStatus . UNKNOWN
601+ ) {
563602 mediaSeason [ is4k ? 'status4k' : 'status' ] = MediaStatus . DELETED ;
564603 }
565604 }
566605
567- if ( media . status === MediaStatus . AVAILABLE && ! is4k ) {
568- media . status = MediaStatus . PARTIALLY_AVAILABLE ;
569- logger . info (
570- `Marking the non-4K show [TMDB ID ${ media . tmdbId } ] as PARTIALLY_AVAILABLE because season removal has occurred.` ,
571- { label : 'Availability Sync' }
572- ) ;
573- }
574-
575- if ( media . status4k === MediaStatus . AVAILABLE && is4k ) {
576- media . status4k = MediaStatus . PARTIALLY_AVAILABLE ;
577- logger . info (
578- `Marking the 4K show [TMDB ID ${ media . tmdbId } ] as PARTIALLY_AVAILABLE because season removal has occurred.` ,
579- { label : 'Availability Sync' }
606+ if ( media [ is4k ? 'status4k' : 'status' ] === MediaStatus . AVAILABLE ) {
607+ media [ is4k ? 'status4k' : 'status' ] = MediaStatus . PARTIALLY_AVAILABLE ;
608+ logger . debug (
609+ `Marking the ${
610+ is4k ? '4K' : 'non-4K'
611+ } show [TMDB ID ${ media . tmdbId } ] as PARTIALLY_AVAILABLE because season(s) [${ seasonKeys } ] was not found in any ${
612+ media . mediaType === 'tv' ? 'Sonarr' : 'Radarr'
613+ } and ${
614+ mediaServerType === MediaServerType . PLEX
615+ ? 'plex'
616+ : mediaServerType === MediaServerType . JELLYFIN
617+ ? 'jellyfin'
618+ : 'emby'
619+ } instance.`,
620+ { label : 'AvailabilitySync' }
580621 ) ;
581622 }
582623
583624 media . lastSeasonChange = new Date ( ) ;
584625 await mediaRepository . save ( media ) ;
585-
586- logger . info (
587- `The ${ is4k ? '4K' : 'non-4K' } season(s) [${ seasonKeys } ] [TMDB ID ${
588- media . tmdbId
589- } ] was not found in any ${
590- media . mediaType === 'tv' ? 'Sonarr' : 'Radarr'
591- } and ${
592- mediaServerType === MediaServerType . PLEX
593- ? 'plex'
594- : mediaServerType === MediaServerType . JELLYFIN
595- ? 'jellyfin'
596- : 'emby'
597- } instance. Status will be changed to deleted.`,
598- { label : 'AvailabilitySync' }
599- ) ;
600626 } catch ( ex ) {
601627 logger . debug (
602628 `Failure updating the ${
603629 is4k ? '4K' : 'non-4K'
604630 } season(s) [${ seasonKeys } ], TMDB ID ${ media . tmdbId } .`,
605631 {
606632 errorMessage : ex . message ,
607- label : 'Availability Sync ' ,
633+ label : 'AvailabilitySync ' ,
608634 }
609635 ) ;
610636 }
@@ -671,7 +697,7 @@ class AvailabilitySync {
671697 } ] from Radarr.`,
672698 {
673699 errorMessage : ex . message ,
674- label : 'Availability Sync ' ,
700+ label : 'AvailabilitySync ' ,
675701 }
676702 ) ;
677703 }
@@ -728,7 +754,7 @@ class AvailabilitySync {
728754 } ] from Sonarr.`,
729755 {
730756 errorMessage : ex . message ,
731- label : 'Availability Sync ' ,
757+ label : 'AvailabilitySync ' ,
732758 }
733759 ) ;
734760 }
@@ -895,7 +921,7 @@ class AvailabilitySync {
895921 } [TMDB ID ${ media . tmdbId } ] from Plex.`,
896922 {
897923 errorMessage : ex . message ,
898- label : 'Availability Sync ' ,
924+ label : 'AvailabilitySync ' ,
899925 }
900926 ) ;
901927 }
0 commit comments