|
|
@ -13,8 +13,11 @@ import {
|
|
|
|
RelationCount,
|
|
|
|
RelationCount,
|
|
|
|
UpdateDateColumn,
|
|
|
|
UpdateDateColumn,
|
|
|
|
} from 'typeorm';
|
|
|
|
} from 'typeorm';
|
|
|
|
import RadarrAPI from '../api/servarr/radarr';
|
|
|
|
import RadarrAPI, { RadarrMovieOptions } from '../api/servarr/radarr';
|
|
|
|
import SonarrAPI, { SonarrSeries } from '../api/servarr/sonarr';
|
|
|
|
import SonarrAPI, {
|
|
|
|
|
|
|
|
AddSeriesOptions,
|
|
|
|
|
|
|
|
SonarrSeries,
|
|
|
|
|
|
|
|
} from '../api/servarr/sonarr';
|
|
|
|
import TheMovieDb from '../api/themoviedb';
|
|
|
|
import TheMovieDb from '../api/themoviedb';
|
|
|
|
import { ANIME_KEYWORD_ID } from '../api/themoviedb/constants';
|
|
|
|
import { ANIME_KEYWORD_ID } from '../api/themoviedb/constants';
|
|
|
|
import { MediaRequestStatus, MediaStatus, MediaType } from '../constants/media';
|
|
|
|
import { MediaRequestStatus, MediaStatus, MediaType } from '../constants/media';
|
|
|
@ -135,55 +138,15 @@ export class MediaRequest {
|
|
|
|
where: { id: this.media.id },
|
|
|
|
where: { id: this.media.id },
|
|
|
|
});
|
|
|
|
});
|
|
|
|
if (!media) {
|
|
|
|
if (!media) {
|
|
|
|
logger.error('No parent media!', { label: 'Media Request' });
|
|
|
|
logger.error('Media data not found', {
|
|
|
|
return;
|
|
|
|
label: 'Media Request',
|
|
|
|
}
|
|
|
|
requestId: this.id,
|
|
|
|
const tmdb = new TheMovieDb();
|
|
|
|
mediaId: this.media.id,
|
|
|
|
if (this.type === MediaType.MOVIE) {
|
|
|
|
|
|
|
|
const movie = await tmdb.getMovie({ movieId: media.tmdbId });
|
|
|
|
|
|
|
|
notificationManager.sendNotification(Notification.MEDIA_PENDING, {
|
|
|
|
|
|
|
|
event: `New ${this.is4k ? '4K ' : ''}Movie Request`,
|
|
|
|
|
|
|
|
subject: `${movie.title}${
|
|
|
|
|
|
|
|
movie.release_date ? ` (${movie.release_date.slice(0, 4)})` : ''
|
|
|
|
|
|
|
|
}`,
|
|
|
|
|
|
|
|
message: truncate(movie.overview, {
|
|
|
|
|
|
|
|
length: 500,
|
|
|
|
|
|
|
|
separator: /\s/,
|
|
|
|
|
|
|
|
omission: '…',
|
|
|
|
|
|
|
|
}),
|
|
|
|
|
|
|
|
image: `https://image.tmdb.org/t/p/w600_and_h900_bestv2${movie.poster_path}`,
|
|
|
|
|
|
|
|
media,
|
|
|
|
|
|
|
|
request: this,
|
|
|
|
|
|
|
|
notifyAdmin: true,
|
|
|
|
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (this.type === MediaType.TV) {
|
|
|
|
this.sendNotification(media, Notification.MEDIA_PENDING);
|
|
|
|
const tv = await tmdb.getTvShow({ tvId: media.tmdbId });
|
|
|
|
|
|
|
|
notificationManager.sendNotification(Notification.MEDIA_PENDING, {
|
|
|
|
|
|
|
|
event: `New ${this.is4k ? '4K ' : ''}Series Request`,
|
|
|
|
|
|
|
|
subject: `${tv.name}${
|
|
|
|
|
|
|
|
tv.first_air_date ? ` (${tv.first_air_date.slice(0, 4)})` : ''
|
|
|
|
|
|
|
|
}`,
|
|
|
|
|
|
|
|
message: truncate(tv.overview, {
|
|
|
|
|
|
|
|
length: 500,
|
|
|
|
|
|
|
|
separator: /\s/,
|
|
|
|
|
|
|
|
omission: '…',
|
|
|
|
|
|
|
|
}),
|
|
|
|
|
|
|
|
image: `https://image.tmdb.org/t/p/w600_and_h900_bestv2${tv.poster_path}`,
|
|
|
|
|
|
|
|
media,
|
|
|
|
|
|
|
|
extra: [
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
name: 'Requested Seasons',
|
|
|
|
|
|
|
|
value: this.seasons
|
|
|
|
|
|
|
|
.map((season) => season.seasonNumber)
|
|
|
|
|
|
|
|
.join(', '),
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
],
|
|
|
|
|
|
|
|
request: this,
|
|
|
|
|
|
|
|
notifyAdmin: true,
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -204,92 +167,32 @@ export class MediaRequest {
|
|
|
|
where: { id: this.media.id },
|
|
|
|
where: { id: this.media.id },
|
|
|
|
});
|
|
|
|
});
|
|
|
|
if (!media) {
|
|
|
|
if (!media) {
|
|
|
|
logger.error('No parent media!', { label: 'Media Request' });
|
|
|
|
logger.error('Media data not found', {
|
|
|
|
|
|
|
|
label: 'Media Request',
|
|
|
|
|
|
|
|
requestId: this.id,
|
|
|
|
|
|
|
|
mediaId: this.media.id,
|
|
|
|
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (media[this.is4k ? 'status4k' : 'status'] === MediaStatus.AVAILABLE) {
|
|
|
|
if (media[this.is4k ? 'status4k' : 'status'] === MediaStatus.AVAILABLE) {
|
|
|
|
logger.warn(
|
|
|
|
logger.warn(
|
|
|
|
'Media became available before request was approved. Approval notification will be skipped.',
|
|
|
|
'Media became available before request was approved. Skipping approval notification',
|
|
|
|
{ label: 'Media Request' }
|
|
|
|
{ label: 'Media Request', requestId: this.id, mediaId: this.media.id }
|
|
|
|
);
|
|
|
|
);
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const tmdb = new TheMovieDb();
|
|
|
|
this.sendNotification(
|
|
|
|
if (this.media.mediaType === MediaType.MOVIE) {
|
|
|
|
|
|
|
|
const movie = await tmdb.getMovie({ movieId: this.media.tmdbId });
|
|
|
|
|
|
|
|
notificationManager.sendNotification(
|
|
|
|
|
|
|
|
this.status === MediaRequestStatus.APPROVED
|
|
|
|
|
|
|
|
? autoApproved
|
|
|
|
|
|
|
|
? Notification.MEDIA_AUTO_APPROVED
|
|
|
|
|
|
|
|
: Notification.MEDIA_APPROVED
|
|
|
|
|
|
|
|
: Notification.MEDIA_DECLINED,
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
event: `${this.is4k ? '4K ' : ''}Movie Request ${
|
|
|
|
|
|
|
|
this.status === MediaRequestStatus.APPROVED
|
|
|
|
|
|
|
|
? autoApproved
|
|
|
|
|
|
|
|
? 'Automatically Approved'
|
|
|
|
|
|
|
|
: 'Approved'
|
|
|
|
|
|
|
|
: 'Declined'
|
|
|
|
|
|
|
|
}`,
|
|
|
|
|
|
|
|
subject: `${movie.title}${
|
|
|
|
|
|
|
|
movie.release_date ? ` (${movie.release_date.slice(0, 4)})` : ''
|
|
|
|
|
|
|
|
}`,
|
|
|
|
|
|
|
|
message: truncate(movie.overview, {
|
|
|
|
|
|
|
|
length: 500,
|
|
|
|
|
|
|
|
separator: /\s/,
|
|
|
|
|
|
|
|
omission: '…',
|
|
|
|
|
|
|
|
}),
|
|
|
|
|
|
|
|
image: `https://image.tmdb.org/t/p/w600_and_h900_bestv2${movie.poster_path}`,
|
|
|
|
|
|
|
|
notifyAdmin: autoApproved,
|
|
|
|
|
|
|
|
notifyUser: autoApproved ? undefined : this.requestedBy,
|
|
|
|
|
|
|
|
media,
|
|
|
|
media,
|
|
|
|
request: this,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
} else if (this.media.mediaType === MediaType.TV) {
|
|
|
|
|
|
|
|
const tv = await tmdb.getTvShow({ tvId: this.media.tmdbId });
|
|
|
|
|
|
|
|
notificationManager.sendNotification(
|
|
|
|
|
|
|
|
this.status === MediaRequestStatus.APPROVED
|
|
|
|
this.status === MediaRequestStatus.APPROVED
|
|
|
|
? autoApproved
|
|
|
|
? autoApproved
|
|
|
|
? Notification.MEDIA_AUTO_APPROVED
|
|
|
|
? Notification.MEDIA_AUTO_APPROVED
|
|
|
|
: Notification.MEDIA_APPROVED
|
|
|
|
: Notification.MEDIA_APPROVED
|
|
|
|
: Notification.MEDIA_DECLINED,
|
|
|
|
: Notification.MEDIA_DECLINED
|
|
|
|
{
|
|
|
|
|
|
|
|
event: `${this.is4k ? '4K ' : ''}Series Request ${
|
|
|
|
|
|
|
|
this.status === MediaRequestStatus.APPROVED
|
|
|
|
|
|
|
|
? autoApproved
|
|
|
|
|
|
|
|
? 'Automatically Approved'
|
|
|
|
|
|
|
|
: 'Approved'
|
|
|
|
|
|
|
|
: 'Declined'
|
|
|
|
|
|
|
|
}`,
|
|
|
|
|
|
|
|
subject: `${tv.name}${
|
|
|
|
|
|
|
|
tv.first_air_date ? ` (${tv.first_air_date.slice(0, 4)})` : ''
|
|
|
|
|
|
|
|
}`,
|
|
|
|
|
|
|
|
message: truncate(tv.overview, {
|
|
|
|
|
|
|
|
length: 500,
|
|
|
|
|
|
|
|
separator: /\s/,
|
|
|
|
|
|
|
|
omission: '…',
|
|
|
|
|
|
|
|
}),
|
|
|
|
|
|
|
|
image: `https://image.tmdb.org/t/p/w600_and_h900_bestv2${tv.poster_path}`,
|
|
|
|
|
|
|
|
notifyAdmin: autoApproved,
|
|
|
|
|
|
|
|
notifyUser: autoApproved ? undefined : this.requestedBy,
|
|
|
|
|
|
|
|
media,
|
|
|
|
|
|
|
|
extra: [
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
name: 'Requested Seasons',
|
|
|
|
|
|
|
|
value: this.seasons
|
|
|
|
|
|
|
|
.map((season) => season.seasonNumber)
|
|
|
|
|
|
|
|
.join(', '),
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
],
|
|
|
|
|
|
|
|
request: this,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@AfterInsert()
|
|
|
|
@AfterInsert()
|
|
|
|
public async autoapprovalNotification(): Promise<void> {
|
|
|
|
public async autoapprovalNotification(): Promise<void> {
|
|
|
@ -307,7 +210,11 @@ export class MediaRequest {
|
|
|
|
relations: ['requests'],
|
|
|
|
relations: ['requests'],
|
|
|
|
});
|
|
|
|
});
|
|
|
|
if (!media) {
|
|
|
|
if (!media) {
|
|
|
|
logger.error('No parent media!', { label: 'Media Request' });
|
|
|
|
logger.error('Media data not found', {
|
|
|
|
|
|
|
|
label: 'Media Request',
|
|
|
|
|
|
|
|
requestId: this.id,
|
|
|
|
|
|
|
|
mediaId: this.media.id,
|
|
|
|
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
const seasonRequestRepository = getRepository(SeasonRequest);
|
|
|
|
const seasonRequestRepository = getRepository(SeasonRequest);
|
|
|
@ -395,8 +302,12 @@ export class MediaRequest {
|
|
|
|
const settings = getSettings();
|
|
|
|
const settings = getSettings();
|
|
|
|
if (settings.radarr.length === 0 && !settings.radarr[0]) {
|
|
|
|
if (settings.radarr.length === 0 && !settings.radarr[0]) {
|
|
|
|
logger.info(
|
|
|
|
logger.info(
|
|
|
|
'Skipped Radarr request as there is no Radarr server configured',
|
|
|
|
'No Radarr server configured, skipping request processing',
|
|
|
|
{ label: 'Media Request' }
|
|
|
|
{
|
|
|
|
|
|
|
|
label: 'Media Request',
|
|
|
|
|
|
|
|
requestId: this.id,
|
|
|
|
|
|
|
|
mediaId: this.media.id,
|
|
|
|
|
|
|
|
}
|
|
|
|
);
|
|
|
|
);
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -415,18 +326,26 @@ export class MediaRequest {
|
|
|
|
);
|
|
|
|
);
|
|
|
|
logger.info(
|
|
|
|
logger.info(
|
|
|
|
`Request has an override server: ${radarrSettings?.name}`,
|
|
|
|
`Request has an override server: ${radarrSettings?.name}`,
|
|
|
|
{ label: 'Media Request' }
|
|
|
|
{
|
|
|
|
|
|
|
|
label: 'Media Request',
|
|
|
|
|
|
|
|
requestId: this.id,
|
|
|
|
|
|
|
|
mediaId: this.media.id,
|
|
|
|
|
|
|
|
}
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!radarrSettings) {
|
|
|
|
if (!radarrSettings) {
|
|
|
|
logger.info(
|
|
|
|
logger.warn(
|
|
|
|
`There is no default ${
|
|
|
|
`There is no default ${
|
|
|
|
this.is4k ? '4K ' : ''
|
|
|
|
this.is4k ? '4K ' : ''
|
|
|
|
}Radarr server configured. Did you set any of your ${
|
|
|
|
}Radarr server configured. Did you set any of your ${
|
|
|
|
this.is4k ? '4K ' : ''
|
|
|
|
this.is4k ? '4K ' : ''
|
|
|
|
}Radarr servers as default?`,
|
|
|
|
}Radarr servers as default?`,
|
|
|
|
{ label: 'Media Request' }
|
|
|
|
{
|
|
|
|
|
|
|
|
label: 'Media Request',
|
|
|
|
|
|
|
|
requestId: this.id,
|
|
|
|
|
|
|
|
mediaId: this.media.id,
|
|
|
|
|
|
|
|
}
|
|
|
|
);
|
|
|
|
);
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -443,6 +362,8 @@ export class MediaRequest {
|
|
|
|
rootFolder = this.rootFolder;
|
|
|
|
rootFolder = this.rootFolder;
|
|
|
|
logger.info(`Request has an override root folder: ${rootFolder}`, {
|
|
|
|
logger.info(`Request has an override root folder: ${rootFolder}`, {
|
|
|
|
label: 'Media Request',
|
|
|
|
label: 'Media Request',
|
|
|
|
|
|
|
|
requestId: this.id,
|
|
|
|
|
|
|
|
mediaId: this.media.id,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -451,15 +372,22 @@ export class MediaRequest {
|
|
|
|
this.profileId !== radarrSettings.activeProfileId
|
|
|
|
this.profileId !== radarrSettings.activeProfileId
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
qualityProfile = this.profileId;
|
|
|
|
qualityProfile = this.profileId;
|
|
|
|
logger.info(`Request has an override profile id: ${qualityProfile}`, {
|
|
|
|
logger.info(
|
|
|
|
|
|
|
|
`Request has an override quality profile ID: ${qualityProfile}`,
|
|
|
|
|
|
|
|
{
|
|
|
|
label: 'Media Request',
|
|
|
|
label: 'Media Request',
|
|
|
|
});
|
|
|
|
requestId: this.id,
|
|
|
|
|
|
|
|
mediaId: this.media.id,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (this.tags && !isEqual(this.tags, radarrSettings.tags)) {
|
|
|
|
if (this.tags && !isEqual(this.tags, radarrSettings.tags)) {
|
|
|
|
tags = this.tags;
|
|
|
|
tags = this.tags;
|
|
|
|
logger.info(`Request has override tags`, {
|
|
|
|
logger.info(`Request has override tags`, {
|
|
|
|
label: 'Media Request',
|
|
|
|
label: 'Media Request',
|
|
|
|
|
|
|
|
requestId: this.id,
|
|
|
|
|
|
|
|
mediaId: this.media.id,
|
|
|
|
tagIds: tags,
|
|
|
|
tagIds: tags,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -476,7 +404,11 @@ export class MediaRequest {
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
if (!media) {
|
|
|
|
if (!media) {
|
|
|
|
logger.error('Media not present');
|
|
|
|
logger.error('Media data not found', {
|
|
|
|
|
|
|
|
label: 'Media Request',
|
|
|
|
|
|
|
|
requestId: this.id,
|
|
|
|
|
|
|
|
mediaId: this.media.id,
|
|
|
|
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -486,9 +418,7 @@ export class MediaRequest {
|
|
|
|
throw new Error('Media already available');
|
|
|
|
throw new Error('Media already available');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Run this asynchronously so we don't wait for it on the UI side
|
|
|
|
const radarrMovieOptions: RadarrMovieOptions = {
|
|
|
|
radarr
|
|
|
|
|
|
|
|
.addMovie({
|
|
|
|
|
|
|
|
profileId: qualityProfile,
|
|
|
|
profileId: qualityProfile,
|
|
|
|
qualityProfileId: qualityProfile,
|
|
|
|
qualityProfileId: qualityProfile,
|
|
|
|
rootFolderPath: rootFolder,
|
|
|
|
rootFolderPath: rootFolder,
|
|
|
@ -499,7 +429,11 @@ export class MediaRequest {
|
|
|
|
monitored: true,
|
|
|
|
monitored: true,
|
|
|
|
tags,
|
|
|
|
tags,
|
|
|
|
searchNow: !radarrSettings.preventSearch,
|
|
|
|
searchNow: !radarrSettings.preventSearch,
|
|
|
|
})
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Run this asynchronously so we don't wait for it on the UI side
|
|
|
|
|
|
|
|
radarr
|
|
|
|
|
|
|
|
.addMovie(radarrMovieOptions)
|
|
|
|
.then(async (radarrMovie) => {
|
|
|
|
.then(async (radarrMovie) => {
|
|
|
|
// We grab media again here to make sure we have the latest version of it
|
|
|
|
// We grab media again here to make sure we have the latest version of it
|
|
|
|
const media = await mediaRepository.findOne({
|
|
|
|
const media = await mediaRepository.findOne({
|
|
|
@ -507,7 +441,7 @@ export class MediaRequest {
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
if (!media) {
|
|
|
|
if (!media) {
|
|
|
|
throw new Error('Media data is missing');
|
|
|
|
throw new Error('Media data not found');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
media[this.is4k ? 'externalServiceId4k' : 'externalServiceId'] =
|
|
|
|
media[this.is4k ? 'externalServiceId4k' : 'externalServiceId'] =
|
|
|
@ -521,36 +455,30 @@ export class MediaRequest {
|
|
|
|
media[this.is4k ? 'status4k' : 'status'] = MediaStatus.UNKNOWN;
|
|
|
|
media[this.is4k ? 'status4k' : 'status'] = MediaStatus.UNKNOWN;
|
|
|
|
await mediaRepository.save(media);
|
|
|
|
await mediaRepository.save(media);
|
|
|
|
logger.warn(
|
|
|
|
logger.warn(
|
|
|
|
'Newly added movie request failed to add to Radarr, marking as unknown',
|
|
|
|
'Something went wrong sending movie request to Radarr, marking status as UNKNOWN',
|
|
|
|
{
|
|
|
|
{
|
|
|
|
label: 'Media Request',
|
|
|
|
label: 'Media Request',
|
|
|
|
|
|
|
|
requestId: this.id,
|
|
|
|
|
|
|
|
mediaId: this.media.id,
|
|
|
|
|
|
|
|
radarrMovieOptions,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
notificationManager.sendNotification(Notification.MEDIA_FAILED, {
|
|
|
|
this.sendNotification(media, Notification.MEDIA_FAILED);
|
|
|
|
event: `${this.is4k ? '4K ' : ''}Movie Request Failed`,
|
|
|
|
|
|
|
|
subject: `${movie.title}${
|
|
|
|
|
|
|
|
movie.release_date ? ` (${movie.release_date.slice(0, 4)})` : ''
|
|
|
|
|
|
|
|
}`,
|
|
|
|
|
|
|
|
message: truncate(movie.overview, {
|
|
|
|
|
|
|
|
length: 500,
|
|
|
|
|
|
|
|
separator: /\s/,
|
|
|
|
|
|
|
|
omission: '…',
|
|
|
|
|
|
|
|
}),
|
|
|
|
|
|
|
|
media,
|
|
|
|
|
|
|
|
image: `https://image.tmdb.org/t/p/w600_and_h900_bestv2${movie.poster_path}`,
|
|
|
|
|
|
|
|
request: this,
|
|
|
|
|
|
|
|
notifyAdmin: true,
|
|
|
|
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
logger.info('Sent request to Radarr', {
|
|
|
|
|
|
|
|
label: 'Media Request',
|
|
|
|
|
|
|
|
requestId: this.id,
|
|
|
|
|
|
|
|
mediaId: this.media.id,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
logger.info('Sent request to Radarr', { label: 'Media Request' });
|
|
|
|
|
|
|
|
} catch (e) {
|
|
|
|
} catch (e) {
|
|
|
|
const errorMessage = `Request failed to send to Radarr: ${e.message}`;
|
|
|
|
logger.error('Something went wrong sending request to Radarr', {
|
|
|
|
logger.error('Request failed to send to Radarr', {
|
|
|
|
|
|
|
|
label: 'Media Request',
|
|
|
|
label: 'Media Request',
|
|
|
|
errorMessage,
|
|
|
|
errorMessage: e.message,
|
|
|
|
|
|
|
|
requestId: this.id,
|
|
|
|
|
|
|
|
mediaId: this.media.id,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
throw new Error(errorMessage);
|
|
|
|
throw new Error(e.message);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -564,9 +492,13 @@ export class MediaRequest {
|
|
|
|
const mediaRepository = getRepository(Media);
|
|
|
|
const mediaRepository = getRepository(Media);
|
|
|
|
const settings = getSettings();
|
|
|
|
const settings = getSettings();
|
|
|
|
if (settings.sonarr.length === 0 && !settings.sonarr[0]) {
|
|
|
|
if (settings.sonarr.length === 0 && !settings.sonarr[0]) {
|
|
|
|
logger.info(
|
|
|
|
logger.warn(
|
|
|
|
'Skipped Sonarr request as there is no Sonarr server configured',
|
|
|
|
'No Sonarr server configured, skipping request processing',
|
|
|
|
{ label: 'Media Request' }
|
|
|
|
{
|
|
|
|
|
|
|
|
label: 'Media Request',
|
|
|
|
|
|
|
|
requestId: this.id,
|
|
|
|
|
|
|
|
mediaId: this.media.id,
|
|
|
|
|
|
|
|
}
|
|
|
|
);
|
|
|
|
);
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -585,18 +517,26 @@ export class MediaRequest {
|
|
|
|
);
|
|
|
|
);
|
|
|
|
logger.info(
|
|
|
|
logger.info(
|
|
|
|
`Request has an override server: ${sonarrSettings?.name}`,
|
|
|
|
`Request has an override server: ${sonarrSettings?.name}`,
|
|
|
|
{ label: 'Media Request' }
|
|
|
|
{
|
|
|
|
|
|
|
|
label: 'Media Request',
|
|
|
|
|
|
|
|
requestId: this.id,
|
|
|
|
|
|
|
|
mediaId: this.media.id,
|
|
|
|
|
|
|
|
}
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!sonarrSettings) {
|
|
|
|
if (!sonarrSettings) {
|
|
|
|
logger.info(
|
|
|
|
logger.warn(
|
|
|
|
`There is no default ${
|
|
|
|
`There is no default ${
|
|
|
|
this.is4k ? '4K ' : ''
|
|
|
|
this.is4k ? '4K ' : ''
|
|
|
|
}Sonarr server configured. Did you set any of your ${
|
|
|
|
}Sonarr server configured. Did you set any of your ${
|
|
|
|
this.is4k ? '4K ' : ''
|
|
|
|
this.is4k ? '4K ' : ''
|
|
|
|
}Sonarr servers as default?`,
|
|
|
|
}Sonarr servers as default?`,
|
|
|
|
{ label: 'Media Request' }
|
|
|
|
{
|
|
|
|
|
|
|
|
label: 'Media Request',
|
|
|
|
|
|
|
|
requestId: this.id,
|
|
|
|
|
|
|
|
mediaId: this.media.id,
|
|
|
|
|
|
|
|
}
|
|
|
|
);
|
|
|
|
);
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -607,7 +547,7 @@ export class MediaRequest {
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
if (!media) {
|
|
|
|
if (!media) {
|
|
|
|
throw new Error('Media data is missing');
|
|
|
|
throw new Error('Media data not found');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
if (
|
|
|
@ -628,7 +568,7 @@ export class MediaRequest {
|
|
|
|
const requestRepository = getRepository(MediaRequest);
|
|
|
|
const requestRepository = getRepository(MediaRequest);
|
|
|
|
await mediaRepository.remove(media);
|
|
|
|
await mediaRepository.remove(media);
|
|
|
|
await requestRepository.remove(this);
|
|
|
|
await requestRepository.remove(this);
|
|
|
|
throw new Error('Series was missing tvdb id');
|
|
|
|
throw new Error('TVDB ID not found');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let seriesType: SonarrSeries['seriesType'] = 'standard';
|
|
|
|
let seriesType: SonarrSeries['seriesType'] = 'standard';
|
|
|
@ -650,12 +590,10 @@ export class MediaRequest {
|
|
|
|
seriesType === 'anime' && sonarrSettings.activeAnimeProfileId
|
|
|
|
seriesType === 'anime' && sonarrSettings.activeAnimeProfileId
|
|
|
|
? sonarrSettings.activeAnimeProfileId
|
|
|
|
? sonarrSettings.activeAnimeProfileId
|
|
|
|
: sonarrSettings.activeProfileId;
|
|
|
|
: sonarrSettings.activeProfileId;
|
|
|
|
|
|
|
|
|
|
|
|
let languageProfile =
|
|
|
|
let languageProfile =
|
|
|
|
seriesType === 'anime' && sonarrSettings.activeAnimeLanguageProfileId
|
|
|
|
seriesType === 'anime' && sonarrSettings.activeAnimeLanguageProfileId
|
|
|
|
? sonarrSettings.activeAnimeLanguageProfileId
|
|
|
|
? sonarrSettings.activeAnimeLanguageProfileId
|
|
|
|
: sonarrSettings.activeLanguageProfileId;
|
|
|
|
: sonarrSettings.activeLanguageProfileId;
|
|
|
|
|
|
|
|
|
|
|
|
let tags =
|
|
|
|
let tags =
|
|
|
|
seriesType === 'anime'
|
|
|
|
seriesType === 'anime'
|
|
|
|
? sonarrSettings.animeTags
|
|
|
|
? sonarrSettings.animeTags
|
|
|
@ -669,14 +607,21 @@ export class MediaRequest {
|
|
|
|
rootFolder = this.rootFolder;
|
|
|
|
rootFolder = this.rootFolder;
|
|
|
|
logger.info(`Request has an override root folder: ${rootFolder}`, {
|
|
|
|
logger.info(`Request has an override root folder: ${rootFolder}`, {
|
|
|
|
label: 'Media Request',
|
|
|
|
label: 'Media Request',
|
|
|
|
|
|
|
|
requestId: this.id,
|
|
|
|
|
|
|
|
mediaId: this.media.id,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (this.profileId && this.profileId !== qualityProfile) {
|
|
|
|
if (this.profileId && this.profileId !== qualityProfile) {
|
|
|
|
qualityProfile = this.profileId;
|
|
|
|
qualityProfile = this.profileId;
|
|
|
|
logger.info(`Request has an override profile ID: ${qualityProfile}`, {
|
|
|
|
logger.info(
|
|
|
|
|
|
|
|
`Request has an override quality profile ID: ${qualityProfile}`,
|
|
|
|
|
|
|
|
{
|
|
|
|
label: 'Media Request',
|
|
|
|
label: 'Media Request',
|
|
|
|
});
|
|
|
|
requestId: this.id,
|
|
|
|
|
|
|
|
mediaId: this.media.id,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
if (
|
|
|
@ -685,9 +630,11 @@ export class MediaRequest {
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
languageProfile = this.languageProfileId;
|
|
|
|
languageProfile = this.languageProfileId;
|
|
|
|
logger.info(
|
|
|
|
logger.info(
|
|
|
|
`Request has an override Language Profile: ${languageProfile}`,
|
|
|
|
`Request has an override language profile ID: ${languageProfile}`,
|
|
|
|
{
|
|
|
|
{
|
|
|
|
label: 'Media Request',
|
|
|
|
label: 'Media Request',
|
|
|
|
|
|
|
|
requestId: this.id,
|
|
|
|
|
|
|
|
mediaId: this.media.id,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -696,13 +643,13 @@ export class MediaRequest {
|
|
|
|
tags = this.tags;
|
|
|
|
tags = this.tags;
|
|
|
|
logger.info(`Request has override tags`, {
|
|
|
|
logger.info(`Request has override tags`, {
|
|
|
|
label: 'Media Request',
|
|
|
|
label: 'Media Request',
|
|
|
|
|
|
|
|
requestId: this.id,
|
|
|
|
|
|
|
|
mediaId: this.media.id,
|
|
|
|
tagIds: tags,
|
|
|
|
tagIds: tags,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Run this asynchronously so we don't wait for it on the UI side
|
|
|
|
const sonarrSeriesOptions: AddSeriesOptions = {
|
|
|
|
sonarr
|
|
|
|
|
|
|
|
.addSeries({
|
|
|
|
|
|
|
|
profileId: qualityProfile,
|
|
|
|
profileId: qualityProfile,
|
|
|
|
languageProfileId: languageProfile,
|
|
|
|
languageProfileId: languageProfile,
|
|
|
|
rootFolderPath: rootFolder,
|
|
|
|
rootFolderPath: rootFolder,
|
|
|
@ -714,7 +661,11 @@ export class MediaRequest {
|
|
|
|
tags,
|
|
|
|
tags,
|
|
|
|
monitored: true,
|
|
|
|
monitored: true,
|
|
|
|
searchNow: !sonarrSettings.preventSearch,
|
|
|
|
searchNow: !sonarrSettings.preventSearch,
|
|
|
|
})
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Run this asynchronously so we don't wait for it on the UI side
|
|
|
|
|
|
|
|
sonarr
|
|
|
|
|
|
|
|
.addSeries(sonarrSeriesOptions)
|
|
|
|
.then(async (sonarrSeries) => {
|
|
|
|
.then(async (sonarrSeries) => {
|
|
|
|
// We grab media again here to make sure we have the latest version of it
|
|
|
|
// We grab media again here to make sure we have the latest version of it
|
|
|
|
const media = await mediaRepository.findOne({
|
|
|
|
const media = await mediaRepository.findOne({
|
|
|
@ -723,7 +674,7 @@ export class MediaRequest {
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
if (!media) {
|
|
|
|
if (!media) {
|
|
|
|
throw new Error('Media data is missing');
|
|
|
|
throw new Error('Media data not found');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
media[this.is4k ? 'externalServiceId4k' : 'externalServiceId'] =
|
|
|
|
media[this.is4k ? 'externalServiceId4k' : 'externalServiceId'] =
|
|
|
@ -737,26 +688,99 @@ export class MediaRequest {
|
|
|
|
media[this.is4k ? 'status4k' : 'status'] = MediaStatus.UNKNOWN;
|
|
|
|
media[this.is4k ? 'status4k' : 'status'] = MediaStatus.UNKNOWN;
|
|
|
|
await mediaRepository.save(media);
|
|
|
|
await mediaRepository.save(media);
|
|
|
|
logger.warn(
|
|
|
|
logger.warn(
|
|
|
|
'Newly added series request failed to add to Sonarr, marking as unknown',
|
|
|
|
'Something went wrong sending series request to Sonarr, marking status as UNKNOWN',
|
|
|
|
{
|
|
|
|
{
|
|
|
|
label: 'Media Request',
|
|
|
|
label: 'Media Request',
|
|
|
|
|
|
|
|
requestId: this.id,
|
|
|
|
|
|
|
|
mediaId: this.media.id,
|
|
|
|
|
|
|
|
sonarrSeriesOptions,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
notificationManager.sendNotification(Notification.MEDIA_FAILED, {
|
|
|
|
this.sendNotification(media, Notification.MEDIA_FAILED);
|
|
|
|
event: `${this.is4k ? '4K ' : ''}Series Request Failed`,
|
|
|
|
});
|
|
|
|
subject: `${series.name}${
|
|
|
|
logger.info('Sent request to Sonarr', {
|
|
|
|
series.first_air_date
|
|
|
|
label: 'Media Request',
|
|
|
|
? ` (${series.first_air_date.slice(0, 4)})`
|
|
|
|
requestId: this.id,
|
|
|
|
: ''
|
|
|
|
mediaId: this.media.id,
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
|
|
logger.error('Something went wrong sending request to Sonarr', {
|
|
|
|
|
|
|
|
label: 'Media Request',
|
|
|
|
|
|
|
|
errorMessage: e.message,
|
|
|
|
|
|
|
|
requestId: this.id,
|
|
|
|
|
|
|
|
mediaId: this.media.id,
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
throw new Error(e.message);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private async sendNotification(media: Media, type: Notification) {
|
|
|
|
|
|
|
|
const tmdb = new TheMovieDb();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
const mediaType = this.type === MediaType.MOVIE ? 'Movie' : 'Series';
|
|
|
|
|
|
|
|
let event: string | undefined;
|
|
|
|
|
|
|
|
let notifyAdmin = true;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
|
|
|
|
case Notification.MEDIA_APPROVED:
|
|
|
|
|
|
|
|
event = `${this.is4k ? '4K ' : ''}${mediaType} Request Approved`;
|
|
|
|
|
|
|
|
notifyAdmin = false;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Notification.MEDIA_DECLINED:
|
|
|
|
|
|
|
|
event = `${this.is4k ? '4K ' : ''}${mediaType} Request Declined`;
|
|
|
|
|
|
|
|
notifyAdmin = false;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Notification.MEDIA_PENDING:
|
|
|
|
|
|
|
|
event = `New ${this.is4k ? '4K ' : ''}${mediaType} Request`;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Notification.MEDIA_AUTO_APPROVED:
|
|
|
|
|
|
|
|
event = `${
|
|
|
|
|
|
|
|
this.is4k ? '4K ' : ''
|
|
|
|
|
|
|
|
}${mediaType} Request Automatically Approved`;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Notification.MEDIA_FAILED:
|
|
|
|
|
|
|
|
event = `${this.is4k ? '4K ' : ''}${mediaType} Request Failed`;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (this.type === MediaType.MOVIE) {
|
|
|
|
|
|
|
|
const movie = await tmdb.getMovie({ movieId: media.tmdbId });
|
|
|
|
|
|
|
|
notificationManager.sendNotification(type, {
|
|
|
|
|
|
|
|
media,
|
|
|
|
|
|
|
|
request: this,
|
|
|
|
|
|
|
|
notifyAdmin,
|
|
|
|
|
|
|
|
notifyUser: notifyAdmin ? undefined : this.requestedBy,
|
|
|
|
|
|
|
|
event,
|
|
|
|
|
|
|
|
subject: `${movie.title}${
|
|
|
|
|
|
|
|
movie.release_date ? ` (${movie.release_date.slice(0, 4)})` : ''
|
|
|
|
}`,
|
|
|
|
}`,
|
|
|
|
message: truncate(series.overview, {
|
|
|
|
message: truncate(movie.overview, {
|
|
|
|
length: 500,
|
|
|
|
length: 500,
|
|
|
|
separator: /\s/,
|
|
|
|
separator: /\s/,
|
|
|
|
omission: '…',
|
|
|
|
omission: '…',
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
image: `https://image.tmdb.org/t/p/w600_and_h900_bestv2${series.poster_path}`,
|
|
|
|
image: `https://image.tmdb.org/t/p/w600_and_h900_bestv2${movie.poster_path}`,
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
} else if (this.type === MediaType.TV) {
|
|
|
|
|
|
|
|
const tv = await tmdb.getTvShow({ tvId: media.tmdbId });
|
|
|
|
|
|
|
|
notificationManager.sendNotification(type, {
|
|
|
|
media,
|
|
|
|
media,
|
|
|
|
|
|
|
|
request: this,
|
|
|
|
|
|
|
|
notifyAdmin,
|
|
|
|
|
|
|
|
notifyUser: notifyAdmin ? undefined : this.requestedBy,
|
|
|
|
|
|
|
|
event,
|
|
|
|
|
|
|
|
subject: `${tv.name}${
|
|
|
|
|
|
|
|
tv.first_air_date ? ` (${tv.first_air_date.slice(0, 4)})` : ''
|
|
|
|
|
|
|
|
}`,
|
|
|
|
|
|
|
|
message: truncate(tv.overview, {
|
|
|
|
|
|
|
|
length: 500,
|
|
|
|
|
|
|
|
separator: /\s/,
|
|
|
|
|
|
|
|
omission: '…',
|
|
|
|
|
|
|
|
}),
|
|
|
|
|
|
|
|
image: `https://image.tmdb.org/t/p/w600_and_h900_bestv2${tv.poster_path}`,
|
|
|
|
extra: [
|
|
|
|
extra: [
|
|
|
|
{
|
|
|
|
{
|
|
|
|
name: 'Requested Seasons',
|
|
|
|
name: 'Requested Seasons',
|
|
|
@ -765,19 +789,15 @@ export class MediaRequest {
|
|
|
|
.join(', '),
|
|
|
|
.join(', '),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
],
|
|
|
|
request: this,
|
|
|
|
|
|
|
|
notifyAdmin: true,
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
});
|
|
|
|
});
|
|
|
|
logger.info('Sent request to Sonarr', { label: 'Media Request' });
|
|
|
|
}
|
|
|
|
} catch (e) {
|
|
|
|
} catch (e) {
|
|
|
|
const errorMessage = `Request failed to send to Sonarr: ${e.message}`;
|
|
|
|
logger.error('Something went wrong sending media notification(s)', {
|
|
|
|
logger.error('Request failed to send to Sonarr', {
|
|
|
|
label: 'Notifications',
|
|
|
|
label: 'Media Request',
|
|
|
|
errorMessage: e.message,
|
|
|
|
errorMessage,
|
|
|
|
requestId: this.id,
|
|
|
|
|
|
|
|
mediaId: this.media.id,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
throw new Error(errorMessage);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|