Skip to content

Commit 1d0fd23

Browse files
committed
fix: prevent season deletion in preference of setting season to unknown
1 parent cae9649 commit 1d0fd23

File tree

2 files changed

+94
-79
lines changed

2 files changed

+94
-79
lines changed

server/job/schedule.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ export const startJobs = (): void => {
110110
id: 'availability-sync',
111111
name: 'Media Availability Sync',
112112
type: 'process',
113-
interval: 'long',
113+
interval: 'hours',
114114
cronSchedule: jobs['availability-sync'].schedule,
115115
job: schedule.scheduleJob(jobs['availability-sync'].schedule, () => {
116116
logger.info('Starting scheduled job: Media Availability Sync', {

server/lib/availabilitySync.ts

+93-78
Original file line numberDiff line numberDiff line change
@@ -46,72 +46,104 @@ class AvailabilitySync {
4646
const pageSize = 50;
4747

4848
try {
49-
for await (const mediaPage of this.loadAvailableMediaPaginated(
50-
pageSize
51-
)) {
49+
for await (const media of this.loadAvailableMediaPaginated(pageSize)) {
5250
try {
5351
if (!this.running) {
5452
throw new Error('Job aborted');
5553
}
5654

57-
for (const media of mediaPage) {
58-
const mediaExists = await this.mediaExists(media);
55+
const mediaExists = await this.mediaExists(media);
56+
57+
//We can not delete media so if both versions do not exist, we will change both columns to unknown or null
58+
if (!mediaExists) {
59+
if (
60+
media.status !== MediaStatus.UNKNOWN ||
61+
media.status4k !== MediaStatus.UNKNOWN
62+
) {
63+
const request = await requestRepository.find({
64+
relations: {
65+
media: true,
66+
},
67+
where: { media: { id: media.id } },
68+
});
5969

60-
//We can not delete media so if both versions do not exist, we will change both columns to unknown or null
61-
if (!mediaExists) {
62-
if (
63-
media.status !== MediaStatus.UNKNOWN &&
64-
media.status4k !== MediaStatus.UNKNOWN
65-
) {
66-
const request = await requestRepository.find({
67-
relations: {
68-
media: true,
69-
},
70-
where: { media: { id: media.id } },
71-
});
72-
73-
logger.debug(
74-
`${media.tmdbId} does not exist in any of your media instances. We will change its status to unknown.`,
75-
{ label: 'AvailabilitySync' }
76-
);
70+
logger.debug(
71+
`${
72+
media.mediaType === 'tv' ? media.tvdbId : media.tmdbId
73+
} does not exist in any of your media instances. We will change its status to unknown.`,
74+
{ label: 'AvailabilitySync' }
75+
);
76+
77+
await mediaRepository.update(media.id, {
78+
status: MediaStatus.UNKNOWN,
79+
status4k: MediaStatus.UNKNOWN,
80+
serviceId: null,
81+
serviceId4k: null,
82+
externalServiceId: null,
83+
externalServiceId4k: null,
84+
externalServiceSlug: null,
85+
externalServiceSlug4k: null,
86+
ratingKey: null,
87+
ratingKey4k: null,
88+
});
7789

78-
await mediaRepository.update(media.id, {
79-
status: MediaStatus.UNKNOWN,
80-
status4k: MediaStatus.UNKNOWN,
81-
serviceId: null,
82-
serviceId4k: null,
83-
externalServiceId: null,
84-
externalServiceId4k: null,
85-
externalServiceSlug: null,
86-
externalServiceSlug4k: null,
87-
ratingKey: null,
88-
ratingKey4k: null,
89-
});
90-
91-
await requestRepository.remove(request);
92-
}
93-
continue;
90+
await requestRepository.remove(request);
9491
}
92+
}
9593

96-
if (media.mediaType === 'tv') {
97-
// ok, the show itself exists, but do all it's seasons?
98-
const seasons = await seasonRepository.find({
99-
where: [
100-
{ status: MediaStatus.AVAILABLE, media: { id: media.id } },
101-
{ status4k: MediaStatus.AVAILABLE, media: { id: media.id } },
102-
],
103-
});
104-
105-
let didDeleteSeasons = false;
106-
for (const season of seasons) {
94+
if (media.mediaType === 'tv') {
95+
// ok, the show itself exists, but do all it's seasons?
96+
const seasons = await seasonRepository.find({
97+
where: [
98+
{ status: MediaStatus.AVAILABLE, media: { id: media.id } },
99+
{
100+
status: MediaStatus.PARTIALLY_AVAILABLE,
101+
media: { id: media.id },
102+
},
103+
{ status4k: MediaStatus.AVAILABLE, media: { id: media.id } },
104+
{
105+
status4k: MediaStatus.PARTIALLY_AVAILABLE,
106+
media: { id: media.id },
107+
},
108+
],
109+
});
110+
111+
let didDeleteSeasons = false;
112+
for (const season of seasons) {
113+
if (
114+
!mediaExists &&
115+
(season.status !== MediaStatus.UNKNOWN ||
116+
season.status4k !== MediaStatus.UNKNOWN)
117+
) {
118+
await seasonRepository.update(
119+
{ id: season.id },
120+
{
121+
status: MediaStatus.UNKNOWN,
122+
status4k: MediaStatus.UNKNOWN,
123+
}
124+
);
125+
} else {
107126
const seasonExists = await this.seasonExists(media, season);
108127

109128
if (!seasonExists) {
110129
logger.debug(
111-
`Removing season ${season.seasonNumber}, media id: ${media.tmdbId} because it doesn't appear in any library.`,
130+
`Removing season ${season.seasonNumber}, media id: ${media.tvdbId} because it does not exist in any of your media instances.`,
112131
{ label: 'AvailabilitySync' }
113132
);
114133

134+
if (
135+
season.status !== MediaStatus.UNKNOWN ||
136+
season.status4k !== MediaStatus.UNKNOWN
137+
) {
138+
await seasonRepository.update(
139+
{ id: season.id },
140+
{
141+
status: MediaStatus.UNKNOWN,
142+
status4k: MediaStatus.UNKNOWN,
143+
}
144+
);
145+
}
146+
115147
const seasonToBeDeleted =
116148
await seasonRequestRepository.findOne({
117149
relations: {
@@ -129,8 +161,6 @@ class AvailabilitySync {
129161
},
130162
});
131163

132-
await seasonRepository.delete(season.id);
133-
134164
if (seasonToBeDeleted) {
135165
await seasonRequestRepository.remove(seasonToBeDeleted);
136166
}
@@ -145,7 +175,7 @@ class AvailabilitySync {
145175
media.status4k === MediaStatus.AVAILABLE
146176
) {
147177
logger.debug(
148-
`Marking media id: ${media.tmdbId} as PARTIALLY_AVAILABLE because we deleted some of its seasons.`,
178+
`Marking media id: ${media.tvdbId} as PARTIALLY_AVAILABLE because we deleted some of its seasons.`,
149179
{ label: 'AvailabilitySync' }
150180
);
151181

@@ -198,20 +228,15 @@ class AvailabilitySync {
198228
{ status4k: MediaStatus.PARTIALLY_AVAILABLE },
199229
];
200230

201-
let mediaPage = await mediaRepository.find({
202-
where: whereOptions,
203-
skip: offset,
204-
take: pageSize,
205-
});
231+
let mediaPage: Media[];
206232

207233
do {
208-
yield mediaPage;
209-
offset += pageSize;
210-
mediaPage = await mediaRepository.find({
234+
yield* (mediaPage = await mediaRepository.find({
211235
where: whereOptions,
212236
skip: offset,
213237
take: pageSize,
214-
});
238+
}));
239+
offset += pageSize;
215240
} while (mediaPage.length > 0);
216241
}
217242

@@ -285,7 +310,6 @@ class AvailabilitySync {
285310

286311
//check if both exist or if a single non-4k or 4k exists
287312
//if both do not exist we will return false
288-
289313
if (!server.is4k && !meta.id) {
290314
existsInRadarr = false;
291315
}
@@ -309,7 +333,6 @@ class AvailabilitySync {
309333

310334
//if only a single non-4k or 4k exists, then change entity columns accordingly
311335
//related media request will then be deleted
312-
313336
if (!existsInRadarr && existsInRadarr4k && !existsInPlex) {
314337
if (media.status !== MediaStatus.UNKNOWN) {
315338
this.mediaUpdater(media, false);
@@ -353,7 +376,6 @@ class AvailabilitySync {
353376

354377
//check if both exist or if a single non-4k or 4k exists
355378
//if both do not exist we will return false
356-
357379
if (!server.is4k && !meta.id) {
358380
existsInSonarr = false;
359381
}
@@ -377,7 +399,6 @@ class AvailabilitySync {
377399

378400
//if only a single non-4k or 4k exists, then change entity columns accordingly
379401
//related media request will then be deleted
380-
381402
if (!existsInSonarr && existsInSonarr4k && !existsInPlex) {
382403
if (media.status !== MediaStatus.UNKNOWN) {
383404
this.mediaUpdater(media, false);
@@ -402,7 +423,7 @@ class AvailabilitySync {
402423
season: Season,
403424
seasonExistsInPlex: boolean,
404425
seasonExistsInPlex4k: boolean
405-
) {
426+
): Promise<boolean> {
406427
if (!media.tvdbId) {
407428
return false;
408429
}
@@ -470,7 +491,6 @@ class AvailabilitySync {
470491

471492
//if season does not exist, we will change status to unknown and delete related season request
472493
//if parent media request is empty(all related seasons have been removed), parent is automatically deleted
473-
474494
if (
475495
!seasonExistsInSonarr &&
476496
seasonExistsInSonarr4k &&
@@ -491,7 +511,7 @@ class AvailabilitySync {
491511

492512
if (media.status === MediaStatus.AVAILABLE) {
493513
logger.debug(
494-
`Marking media id: ${media.tmdbId} as PARTIALLY_AVAILABLE because we deleted one of its seasons.`,
514+
`Marking media id: ${media.tvdbId} as PARTIALLY_AVAILABLE because we deleted one of its seasons.`,
495515
{ label: 'AvailabilitySync' }
496516
);
497517
await mediaRepository.update(media.id, {
@@ -521,7 +541,7 @@ class AvailabilitySync {
521541

522542
if (media.status4k === MediaStatus.AVAILABLE) {
523543
logger.debug(
524-
`Marking media id: ${media.tmdbId} as PARTIALLY_AVAILABLE because we deleted one of its seasons.`,
544+
`Marking media id: ${media.tvdbId} as PARTIALLY_AVAILABLE because we deleted one of its seasons.`,
525545
{ label: 'AvailabilitySync' }
526546
);
527547
await mediaRepository.update(media.id, {
@@ -546,7 +566,6 @@ class AvailabilitySync {
546566
let existsInPlex4k = false;
547567

548568
//check each plex instance to see if media exists
549-
550569
try {
551570
if (ratingKey) {
552571
const meta = await this.plexClient?.getMetadata(ratingKey);
@@ -566,16 +585,13 @@ class AvailabilitySync {
566585
throw ex;
567586
}
568587
}
569-
570-
//base case for if both exist in plex
571-
588+
//base case for if both media versions exist in plex
572589
if (existsInPlex && existsInPlex4k) {
573590
return true;
574591
}
575592

576593
//we then check radarr or sonarr has that specific media. If not, then we will move to delete
577594
//if a non-4k or 4k version exists in at least one of the instances, we will only update that specific version
578-
579595
if (media.mediaType === 'movie') {
580596
const existsInRadarr = await this.mediaExistsInRadarr(
581597
media,
@@ -584,7 +600,6 @@ class AvailabilitySync {
584600
);
585601

586602
//if true, media exists in at least one radarr or plex instance.
587-
588603
if (existsInRadarr) {
589604
logger.warn(
590605
`${media.tmdbId} exists in at least one radarr or plex instance. Media will be updated if set to available.`,
@@ -605,10 +620,9 @@ class AvailabilitySync {
605620
);
606621

607622
//if true, media exists in at least one sonarr or plex instance.
608-
609623
if (existsInSonarr) {
610624
logger.warn(
611-
`${media.tmdbId} exists in at least one sonarr or plex instance. Media will be updated if set to available.`,
625+
`${media.tvdbId} exists in at least one sonarr or plex instance. Media will be updated if set to available.`,
612626
{
613627
label: 'AvailabilitySync',
614628
}
@@ -658,6 +672,7 @@ class AvailabilitySync {
658672
}
659673
}
660674

675+
//base case for if both season versions exist in plex
661676
if (seasonExistsInPlex && seasonExistsInPlex4k) {
662677
return true;
663678
}

0 commit comments

Comments
 (0)