refactor: updated to reflect latest availability sync changes

pull/3460/head
Brandon Cohen 12 months ago
parent 8d96c0ce53
commit 44901b3e4f
No known key found for this signature in database
GPG Key ID: 3E759476348594C9

@ -8,7 +8,6 @@ import { getRepository } from '@server/datasource';
import Media from '@server/entity/Media'; import Media from '@server/entity/Media';
import MediaRequest from '@server/entity/MediaRequest'; import MediaRequest from '@server/entity/MediaRequest';
import type Season from '@server/entity/Season'; import type Season from '@server/entity/Season';
import SeasonRequest from '@server/entity/SeasonRequest';
import { User } from '@server/entity/User'; import { User } from '@server/entity/User';
import type { RadarrSettings, SonarrSettings } from '@server/lib/settings'; import type { RadarrSettings, SonarrSettings } from '@server/lib/settings';
import { getSettings } from '@server/lib/settings'; import { getSettings } from '@server/lib/settings';
@ -203,9 +202,6 @@ class AvailabilitySync {
await this.mediaUpdater(media, true); await this.mediaUpdater(media, true);
} }
} }
if (!mediaExists) {
await mediaRepository.save({ media, ...media });
}
} }
} catch (ex) { } catch (ex) {
logger.error('Failed to complete availability sync.', { logger.error('Failed to complete availability sync.', {
@ -246,87 +242,55 @@ class AvailabilitySync {
} while (mediaPage.length > 0); } while (mediaPage.length > 0);
} }
private findMediaStatus(
requests: MediaRequest[],
is4k: boolean
): MediaStatus {
const filteredRequests = requests.filter(
(request) => request.is4k === is4k
);
let mediaStatus: MediaStatus;
if (
filteredRequests.some(
(request) => request.status === MediaRequestStatus.APPROVED
)
) {
mediaStatus = MediaStatus.PROCESSING;
} else if (
filteredRequests.some(
(request) => request.status === MediaRequestStatus.PENDING
)
) {
mediaStatus = MediaStatus.PENDING;
} else {
mediaStatus = MediaStatus.UNKNOWN;
}
return mediaStatus;
}
private async mediaUpdater(media: Media, is4k: boolean): Promise<void> { private async mediaUpdater(media: Media, is4k: boolean): Promise<void> {
const mediaRepository = getRepository(Media); const mediaRepository = getRepository(Media);
const requestRepository = getRepository(MediaRequest);
try { try {
// Find all related requests only if // If media type is tv, check if a season is processing
// the related media has an available status //to see if we need to keep the external metadata
const requests = await requestRepository let isMediaProcessing = false;
.createQueryBuilder('request')
.leftJoinAndSelect('request.media', 'media')
.where('(media.id = :id)', {
id: media.id,
})
.andWhere(
`(request.is4k = :is4k AND media.${
is4k ? 'status4k' : 'status'
} IN (:...mediaStatus))`,
{
mediaStatus: [
MediaStatus.AVAILABLE,
MediaStatus.PARTIALLY_AVAILABLE,
],
is4k: is4k,
}
)
.getMany();
// Check if a season is processing or pending to
// make sure we set the media to the correct status
let mediaStatus = MediaStatus.UNKNOWN;
if (media.mediaType === 'tv') { if (media.mediaType === 'tv') {
mediaStatus = this.findMediaStatus(requests, is4k); const requestRepository = getRepository(MediaRequest);
const request = await requestRepository
.createQueryBuilder('request')
.leftJoinAndSelect('request.media', 'media')
.where('(media.id = :id)', {
id: media.id,
})
.andWhere(
'(request.is4k = :is4k AND request.status = :requestStatus)',
{
requestStatus: MediaRequestStatus.APPROVED,
is4k: is4k,
}
)
.getOne();
if (request) {
isMediaProcessing = true;
}
} }
media[is4k ? 'status4k' : 'status'] = mediaStatus; // Set the non-4K or 4K media to deleted
media[is4k ? 'serviceId4k' : 'serviceId'] = // and change related columns to null if media
mediaStatus === MediaStatus.PROCESSING // is not processing
? media[is4k ? 'serviceId4k' : 'serviceId'] media[is4k ? 'status4k' : 'status'] = MediaStatus.DELETED;
: null; media[is4k ? 'serviceId4k' : 'serviceId'] = isMediaProcessing
? media[is4k ? 'serviceId4k' : 'serviceId']
: null;
media[is4k ? 'externalServiceId4k' : 'externalServiceId'] = media[is4k ? 'externalServiceId4k' : 'externalServiceId'] =
mediaStatus === MediaStatus.PROCESSING isMediaProcessing
? media[is4k ? 'externalServiceId4k' : 'externalServiceId'] ? media[is4k ? 'externalServiceId4k' : 'externalServiceId']
: null; : null;
media[is4k ? 'externalServiceSlug4k' : 'externalServiceSlug'] = media[is4k ? 'externalServiceSlug4k' : 'externalServiceSlug'] =
mediaStatus === MediaStatus.PROCESSING isMediaProcessing
? media[is4k ? 'externalServiceSlug4k' : 'externalServiceSlug'] ? media[is4k ? 'externalServiceSlug4k' : 'externalServiceSlug']
: null; : null;
media[is4k ? 'ratingKey4k' : 'ratingKey'] = media[is4k ? 'ratingKey4k' : 'ratingKey'] = isMediaProcessing
mediaStatus === MediaStatus.PROCESSING ? media[is4k ? 'ratingKey4k' : 'ratingKey']
? media[is4k ? 'ratingKey4k' : 'ratingKey'] : null;
: null;
logger.info( logger.info(
`The ${is4k ? '4K' : 'non-4K'} ${ `The ${is4k ? '4K' : 'non-4K'} ${
@ -338,13 +302,6 @@ class AvailabilitySync {
); );
await mediaRepository.save({ media, ...media }); await mediaRepository.save({ media, ...media });
// Only delete media request if type is movie.
// Type tv request deletion is handled
// in the season request entity
if (requests.length > 0 && media.mediaType === 'movie') {
await requestRepository.remove(requests);
}
} catch (ex) { } catch (ex) {
logger.debug( logger.debug(
`Failure updating the ${is4k ? '4K' : 'non-4K'} ${ `Failure updating the ${is4k ? '4K' : 'non-4K'} ${
@ -364,35 +321,21 @@ class AvailabilitySync {
is4k: boolean is4k: boolean
): Promise<void> { ): Promise<void> {
const mediaRepository = getRepository(Media); const mediaRepository = getRepository(Media);
const seasonRequestRepository = getRepository(SeasonRequest);
// Filter out only the values that are false
// (media that should be deleted)
const seasonsPendingRemoval = new Map( const seasonsPendingRemoval = new Map(
// Disabled linter as only the value is needed from the filter // Disabled linter as only the value is needed from the filter
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
[...seasons].filter(([_, exists]) => !exists) [...seasons].filter(([_, exists]) => !exists)
); );
// Retrieve the season keys to pass into our log
const seasonKeys = [...seasonsPendingRemoval.keys()]; const seasonKeys = [...seasonsPendingRemoval.keys()];
try { try {
// Need to check and see if there are any related season
// requests. If they are, we will need to delete them.
const seasonRequests = await seasonRequestRepository
.createQueryBuilder('seasonRequest')
.leftJoinAndSelect('seasonRequest.request', 'request')
.leftJoinAndSelect('request.media', 'media')
.where('(media.id = :id)', { id: media.id })
.andWhere(
'(request.is4k = :is4k AND seasonRequest.seasonNumber IN (:...seasonNumbers))',
{
seasonNumbers: seasonKeys,
is4k: is4k,
}
)
.getMany();
for (const mediaSeason of media.seasons) { for (const mediaSeason of media.seasons) {
if (seasonsPendingRemoval.has(mediaSeason.seasonNumber)) { if (seasonsPendingRemoval.has(mediaSeason.seasonNumber)) {
mediaSeason[is4k ? 'status4k' : 'status'] = MediaStatus.UNKNOWN; mediaSeason[is4k ? 'status4k' : 'status'] = MediaStatus.DELETED;
} }
} }
@ -414,16 +357,12 @@ class AvailabilitySync {
await mediaRepository.save({ media, ...media }); await mediaRepository.save({ media, ...media });
if (seasonRequests.length > 0) {
await seasonRequestRepository.remove(seasonRequests);
}
logger.info( logger.info(
`The ${is4k ? '4K' : 'non-4K'} season(s) [${seasonKeys}] [TMDB ID ${ `The ${is4k ? '4K' : 'non-4K'} season(s) [${seasonKeys}] [TMDB ID ${
media.tmdbId media.tmdbId
}] was not found in any ${ }] was not found in any ${
media.mediaType === 'tv' ? 'Sonarr' : 'Radarr' media.mediaType === 'tv' ? 'Sonarr' : 'Radarr'
} and Plex instance. Status will be changed to unknown.`, } and Plex instance. Status will be changed to deleted.`,
{ label: 'AvailabilitySync' } { label: 'AvailabilitySync' }
); );
} catch (ex) { } catch (ex) {
@ -448,53 +387,47 @@ class AvailabilitySync {
// Check for availability in all of the available radarr servers // Check for availability in all of the available radarr servers
// If any find the media, we will assume the media exists // If any find the media, we will assume the media exists
for (const server of this.radarrServers) { for (const server of this.radarrServers) {
const radarrAPI = new RadarrAPI({ if (server.is4k === is4k) {
apiKey: server.apiKey, const radarrAPI = new RadarrAPI({
url: RadarrAPI.buildUrl(server, '/api/v3'), apiKey: server.apiKey,
}); url: RadarrAPI.buildUrl(server, '/api/v3'),
});
try {
let radarr: RadarrMovie | undefined; try {
let radarr: RadarrMovie | undefined;
if (!server.is4k && media.externalServiceId && !is4k) {
radarr = await radarrAPI.getMovie({ if (media.externalServiceId && !is4k) {
id: media.externalServiceId, radarr = await radarrAPI.getMovie({
}); id: media.externalServiceId,
} });
}
if (server.is4k && media.externalServiceId4k && is4k) { if (media.externalServiceId4k && is4k) {
radarr = await radarrAPI.getMovie({ radarr = await radarrAPI.getMovie({
id: media.externalServiceId4k, id: media.externalServiceId4k,
}); });
} }
if (radarr && radarr.hasFile) { if (radarr && radarr.hasFile) {
existsInRadarr = true; existsInRadarr = true;
} }
} catch (ex) { } catch (ex) {
if (!ex.message.includes('404')) { if (!ex.message.includes('404')) {
existsInRadarr = true; existsInRadarr = true;
logger.debug( logger.debug(
`Failure retrieving the ${is4k ? '4K' : 'non-4K'} movie [TMDB ID ${ `Failure retrieving the ${
media.tmdbId is4k ? '4K' : 'non-4K'
}] from Radarr.`, } movie [TMDB ID ${media.tmdbId}] from Radarr.`,
{ {
errorMessage: ex.message, errorMessage: ex.message,
label: 'AvailabilitySync', label: 'AvailabilitySync',
} }
); );
}
} }
} }
} }
if ((!radarr || !radarr.hasFile) && !existsInPlex) {
movieExists = false;
}
if ((!radarr4k || !radarr4k.hasFile) && !existsInPlex4k) {
movieExists4k = false;
}
return existsInRadarr; return existsInRadarr;
} }
@ -508,65 +441,49 @@ class AvailabilitySync {
// Check for availability in all of the available sonarr servers // Check for availability in all of the available sonarr servers
// If any find the media, we will assume the media exists // If any find the media, we will assume the media exists
for (const server of this.sonarrServers) { for (const server of this.sonarrServers) {
const sonarrAPI = new SonarrAPI({ if (server.is4k === is4k) {
apiKey: server.apiKey, const sonarrAPI = new SonarrAPI({
url: SonarrAPI.buildUrl(server, '/api/v3'), apiKey: server.apiKey,
}); url: SonarrAPI.buildUrl(server, '/api/v3'),
});
try {
let sonarr: SonarrSeries | undefined; try {
let sonarr: SonarrSeries | undefined;
if (!server.is4k && media.externalServiceId && !is4k) {
sonarr = await sonarrAPI.getSeriesById(media.externalServiceId); if (media.externalServiceId && !is4k) {
this.sonarrSeasonsCache[`${server.id}-${media.externalServiceId}`] = sonarr = await sonarrAPI.getSeriesById(media.externalServiceId);
sonarr.seasons; this.sonarrSeasonsCache[`${server.id}-${media.externalServiceId}`] =
} sonarr.seasons;
}
if (server.is4k && media.externalServiceId4k && is4k) { if (media.externalServiceId4k && is4k) {
sonarr = await sonarrAPI.getSeriesById(media.externalServiceId4k); sonarr = await sonarrAPI.getSeriesById(media.externalServiceId4k);
this.sonarrSeasonsCache[`${server.id}-${media.externalServiceId4k}`] = this.sonarrSeasonsCache[
sonarr.seasons; `${server.id}-${media.externalServiceId4k}`
} ] = sonarr.seasons;
}
if (sonarr && sonarr.statistics.episodeFileCount > 0) { if (sonarr && sonarr.statistics.episodeFileCount > 0) {
existsInSonarr = true; existsInSonarr = true;
} }
} catch (ex) { } catch (ex) {
if (!ex.message.includes('404')) { if (!ex.message.includes('404')) {
existsInSonarr = true; existsInSonarr = true;
preventSeasonSearch = true; preventSeasonSearch = true;
logger.debug( logger.debug(
`Failure retrieving the ${is4k ? '4K' : 'non-4K'} show [TMDB ID ${ `Failure retrieving the ${is4k ? '4K' : 'non-4K'} show [TMDB ID ${
media.tmdbId media.tmdbId
}] from Sonarr.`, }] from Sonarr.`,
{ {
errorMessage: ex.message, errorMessage: ex.message,
label: 'AvailabilitySync', label: 'AvailabilitySync',
} }
); );
}
} }
} }
} }
if (
(!sonarr || sonarr.statistics.episodeFileCount === 0) &&
!existsInPlex
) {
showExists = false;
}
if (
(!sonarr4k || sonarr4k.statistics.episodeFileCount === 0) &&
!existsInPlex4k
) {
showExists4k = false;
}
// Here we check each season for availability
for (const season of media.seasons) {
await this.seasonExists(media, season, showExists, showExists4k);
}
// Here we check each season for availability // Here we check each season for availability
// If the API returns an error other than a 404, // If the API returns an error other than a 404,
// we will have to prevent the season check from happening // we will have to prevent the season check from happening

Loading…
Cancel
Save