mirror of
https://github.com/sct/overseerr.git
synced 2025-09-17 17:24:35 +02:00
feat(notif): 4K media notifications (#2324)
This commit is contained in:
@@ -142,7 +142,7 @@ export class MediaRequest {
|
|||||||
if (this.type === MediaType.MOVIE) {
|
if (this.type === MediaType.MOVIE) {
|
||||||
const movie = await tmdb.getMovie({ movieId: media.tmdbId });
|
const movie = await tmdb.getMovie({ movieId: media.tmdbId });
|
||||||
notificationManager.sendNotification(Notification.MEDIA_PENDING, {
|
notificationManager.sendNotification(Notification.MEDIA_PENDING, {
|
||||||
event: 'New Movie Request',
|
event: `New ${this.is4k ? '4K ' : ''}Movie Request`,
|
||||||
subject: `${movie.title}${
|
subject: `${movie.title}${
|
||||||
movie.release_date ? ` (${movie.release_date.slice(0, 4)})` : ''
|
movie.release_date ? ` (${movie.release_date.slice(0, 4)})` : ''
|
||||||
}`,
|
}`,
|
||||||
@@ -161,7 +161,7 @@ export class MediaRequest {
|
|||||||
if (this.type === MediaType.TV) {
|
if (this.type === MediaType.TV) {
|
||||||
const tv = await tmdb.getTvShow({ tvId: media.tmdbId });
|
const tv = await tmdb.getTvShow({ tvId: media.tmdbId });
|
||||||
notificationManager.sendNotification(Notification.MEDIA_PENDING, {
|
notificationManager.sendNotification(Notification.MEDIA_PENDING, {
|
||||||
event: 'New Series Request',
|
event: `New ${this.is4k ? '4K ' : ''}Series Request`,
|
||||||
subject: `${tv.name}${
|
subject: `${tv.name}${
|
||||||
tv.first_air_date ? ` (${tv.first_air_date.slice(0, 4)})` : ''
|
tv.first_air_date ? ` (${tv.first_air_date.slice(0, 4)})` : ''
|
||||||
}`,
|
}`,
|
||||||
@@ -226,7 +226,7 @@ export class MediaRequest {
|
|||||||
: Notification.MEDIA_APPROVED
|
: Notification.MEDIA_APPROVED
|
||||||
: Notification.MEDIA_DECLINED,
|
: Notification.MEDIA_DECLINED,
|
||||||
{
|
{
|
||||||
event: `Movie Request ${
|
event: `${this.is4k ? '4K ' : ''}Movie Request ${
|
||||||
this.status === MediaRequestStatus.APPROVED
|
this.status === MediaRequestStatus.APPROVED
|
||||||
? autoApproved
|
? autoApproved
|
||||||
? 'Automatically Approved'
|
? 'Automatically Approved'
|
||||||
@@ -257,7 +257,7 @@ export class MediaRequest {
|
|||||||
: Notification.MEDIA_APPROVED
|
: Notification.MEDIA_APPROVED
|
||||||
: Notification.MEDIA_DECLINED,
|
: Notification.MEDIA_DECLINED,
|
||||||
{
|
{
|
||||||
event: `Series Request ${
|
event: `${this.is4k ? '4K ' : ''}Series Request ${
|
||||||
this.status === MediaRequestStatus.APPROVED
|
this.status === MediaRequestStatus.APPROVED
|
||||||
? autoApproved
|
? autoApproved
|
||||||
? 'Automatically Approved'
|
? 'Automatically Approved'
|
||||||
@@ -528,7 +528,7 @@ export class MediaRequest {
|
|||||||
);
|
);
|
||||||
|
|
||||||
notificationManager.sendNotification(Notification.MEDIA_FAILED, {
|
notificationManager.sendNotification(Notification.MEDIA_FAILED, {
|
||||||
event: `Movie Request Failed`,
|
event: `${this.is4k ? '4K ' : ''}Movie Request Failed`,
|
||||||
subject: `${movie.title}${
|
subject: `${movie.title}${
|
||||||
movie.release_date ? ` (${movie.release_date.slice(0, 4)})` : ''
|
movie.release_date ? ` (${movie.release_date.slice(0, 4)})` : ''
|
||||||
}`,
|
}`,
|
||||||
@@ -744,7 +744,7 @@ export class MediaRequest {
|
|||||||
);
|
);
|
||||||
|
|
||||||
notificationManager.sendNotification(Notification.MEDIA_FAILED, {
|
notificationManager.sendNotification(Notification.MEDIA_FAILED, {
|
||||||
event: `Series Request Failed`,
|
event: `${this.is4k ? '4K ' : ''}Series Request Failed`,
|
||||||
subject: `${series.name}${
|
subject: `${series.name}${
|
||||||
series.first_air_date
|
series.first_air_date
|
||||||
? ` (${series.first_air_date.slice(0, 4)})`
|
? ` (${series.first_air_date.slice(0, 4)})`
|
||||||
|
@@ -72,28 +72,41 @@ class EmailAgent
|
|||||||
? 'movie'
|
? 'movie'
|
||||||
: 'series'
|
: 'series'
|
||||||
: undefined;
|
: undefined;
|
||||||
|
const is4k = payload.request?.is4k;
|
||||||
|
|
||||||
if (payload.request) {
|
if (payload.request) {
|
||||||
let body = '';
|
let body = '';
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case Notification.MEDIA_PENDING:
|
case Notification.MEDIA_PENDING:
|
||||||
body = `A new request for the following ${mediaType} is pending approval:`;
|
body = `A new request for the following ${mediaType} ${
|
||||||
|
is4k ? 'in 4K ' : ''
|
||||||
|
}is pending approval:`;
|
||||||
break;
|
break;
|
||||||
case Notification.MEDIA_APPROVED:
|
case Notification.MEDIA_APPROVED:
|
||||||
body = `Your request for the following ${mediaType} has been approved:`;
|
body = `Your request for the following ${mediaType} ${
|
||||||
|
is4k ? 'in 4K ' : ''
|
||||||
|
}has been approved:`;
|
||||||
break;
|
break;
|
||||||
case Notification.MEDIA_AUTO_APPROVED:
|
case Notification.MEDIA_AUTO_APPROVED:
|
||||||
body = `A new request for the following ${mediaType} has been automatically approved:`;
|
body = `A new request for the following ${mediaType} ${
|
||||||
|
is4k ? 'in 4K ' : ''
|
||||||
|
}has been automatically approved:`;
|
||||||
break;
|
break;
|
||||||
case Notification.MEDIA_AVAILABLE:
|
case Notification.MEDIA_AVAILABLE:
|
||||||
body = `Your request for the following ${mediaType} is now available:`;
|
body = `Your request for the following ${mediaType} ${
|
||||||
|
is4k ? 'in 4K ' : ''
|
||||||
|
}is now available:`;
|
||||||
break;
|
break;
|
||||||
case Notification.MEDIA_DECLINED:
|
case Notification.MEDIA_DECLINED:
|
||||||
body = `Your request for the following ${mediaType} was declined:`;
|
body = `Your request for the following ${mediaType} ${
|
||||||
|
is4k ? 'in 4K ' : ''
|
||||||
|
}was declined:`;
|
||||||
break;
|
break;
|
||||||
case Notification.MEDIA_FAILED:
|
case Notification.MEDIA_FAILED:
|
||||||
body = `A request for the following ${mediaType} failed to be added to ${
|
body = `A request for the following ${mediaType} ${
|
||||||
|
is4k ? 'in 4K ' : ''
|
||||||
|
}failed to be added to ${
|
||||||
payload.media?.mediaType === MediaType.MOVIE ? 'Radarr' : 'Sonarr'
|
payload.media?.mediaType === MediaType.MOVIE ? 'Radarr' : 'Sonarr'
|
||||||
}:`;
|
}:`;
|
||||||
break;
|
break;
|
||||||
|
@@ -46,6 +46,7 @@ class WebPushAgent
|
|||||||
? 'movie'
|
? 'movie'
|
||||||
: 'series'
|
: 'series'
|
||||||
: undefined;
|
: undefined;
|
||||||
|
const is4k = payload.request?.is4k;
|
||||||
|
|
||||||
const issueType = payload.issue
|
const issueType = payload.issue
|
||||||
? payload.issue.issueType !== IssueType.OTHER
|
? payload.issue.issueType !== IssueType.OTHER
|
||||||
@@ -59,22 +60,34 @@ class WebPushAgent
|
|||||||
message = payload.message;
|
message = payload.message;
|
||||||
break;
|
break;
|
||||||
case Notification.MEDIA_APPROVED:
|
case Notification.MEDIA_APPROVED:
|
||||||
message = `Your ${mediaType} request has been approved.`;
|
message = `Your ${
|
||||||
|
is4k ? '4K ' : ''
|
||||||
|
}${mediaType} request has been approved.`;
|
||||||
break;
|
break;
|
||||||
case Notification.MEDIA_AUTO_APPROVED:
|
case Notification.MEDIA_AUTO_APPROVED:
|
||||||
message = `Automatically approved a new ${mediaType} request from ${payload.request?.requestedBy.displayName}.`;
|
message = `Automatically approved a new ${
|
||||||
|
is4k ? '4K ' : ''
|
||||||
|
}${mediaType} request from ${
|
||||||
|
payload.request?.requestedBy.displayName
|
||||||
|
}.`;
|
||||||
break;
|
break;
|
||||||
case Notification.MEDIA_AVAILABLE:
|
case Notification.MEDIA_AVAILABLE:
|
||||||
message = `Your ${mediaType} request is now available!`;
|
message = `Your ${
|
||||||
|
is4k ? '4K ' : ''
|
||||||
|
}${mediaType} request is now available!`;
|
||||||
break;
|
break;
|
||||||
case Notification.MEDIA_DECLINED:
|
case Notification.MEDIA_DECLINED:
|
||||||
message = `Your ${mediaType} request was declined.`;
|
message = `Your ${is4k ? '4K ' : ''}${mediaType} request was declined.`;
|
||||||
break;
|
break;
|
||||||
case Notification.MEDIA_FAILED:
|
case Notification.MEDIA_FAILED:
|
||||||
message = `Failed to process ${mediaType} request.`;
|
message = `Failed to process ${is4k ? '4K ' : ''}${mediaType} request.`;
|
||||||
break;
|
break;
|
||||||
case Notification.MEDIA_PENDING:
|
case Notification.MEDIA_PENDING:
|
||||||
message = `Approval required for a new ${mediaType} request from ${payload.request?.requestedBy.displayName}.`;
|
message = `Approval required for a new ${
|
||||||
|
is4k ? '4K ' : ''
|
||||||
|
}${mediaType} request from ${
|
||||||
|
payload.request?.requestedBy.displayName
|
||||||
|
}.`;
|
||||||
break;
|
break;
|
||||||
case Notification.ISSUE_CREATED:
|
case Notification.ISSUE_CREATED:
|
||||||
message = `A new ${issueType} was reported by ${payload.issue?.createdBy.displayName}.`;
|
message = `A new ${issueType} was reported by ${payload.issue?.createdBy.displayName}.`;
|
||||||
|
@@ -14,15 +14,19 @@ import notificationManager, { Notification } from '../lib/notifications';
|
|||||||
|
|
||||||
@EventSubscriber()
|
@EventSubscriber()
|
||||||
export class MediaSubscriber implements EntitySubscriberInterface<Media> {
|
export class MediaSubscriber implements EntitySubscriberInterface<Media> {
|
||||||
private async notifyAvailableMovie(entity: Media, dbEntity?: Media) {
|
private async notifyAvailableMovie(
|
||||||
|
entity: Media,
|
||||||
|
dbEntity: Media,
|
||||||
|
is4k: boolean
|
||||||
|
) {
|
||||||
if (
|
if (
|
||||||
entity.status === MediaStatus.AVAILABLE &&
|
entity[is4k ? 'status4k' : 'status'] === MediaStatus.AVAILABLE &&
|
||||||
dbEntity?.status !== MediaStatus.AVAILABLE
|
dbEntity[is4k ? 'status4k' : 'status'] !== MediaStatus.AVAILABLE
|
||||||
) {
|
) {
|
||||||
if (entity.mediaType === MediaType.MOVIE) {
|
if (entity.mediaType === MediaType.MOVIE) {
|
||||||
const requestRepository = getRepository(MediaRequest);
|
const requestRepository = getRepository(MediaRequest);
|
||||||
const relatedRequests = await requestRepository.find({
|
const relatedRequests = await requestRepository.find({
|
||||||
where: { media: entity, is4k: false },
|
where: { media: entity, is4k },
|
||||||
});
|
});
|
||||||
|
|
||||||
if (relatedRequests.length > 0) {
|
if (relatedRequests.length > 0) {
|
||||||
@@ -31,7 +35,7 @@ export class MediaSubscriber implements EntitySubscriberInterface<Media> {
|
|||||||
|
|
||||||
relatedRequests.forEach((request) => {
|
relatedRequests.forEach((request) => {
|
||||||
notificationManager.sendNotification(Notification.MEDIA_AVAILABLE, {
|
notificationManager.sendNotification(Notification.MEDIA_AVAILABLE, {
|
||||||
event: 'Movie Now Available',
|
event: `${is4k ? '4K ' : ''}Movie Request Now Available`,
|
||||||
notifyAdmin: false,
|
notifyAdmin: false,
|
||||||
notifyUser: request.requestedBy,
|
notifyUser: request.requestedBy,
|
||||||
subject: `${movie.title}${
|
subject: `${movie.title}${
|
||||||
@@ -52,15 +56,25 @@ export class MediaSubscriber implements EntitySubscriberInterface<Media> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async notifyAvailableSeries(entity: Media, dbEntity: Media) {
|
private async notifyAvailableSeries(
|
||||||
|
entity: Media,
|
||||||
|
dbEntity: Media,
|
||||||
|
is4k: boolean
|
||||||
|
) {
|
||||||
const seasonRepository = getRepository(Season);
|
const seasonRepository = getRepository(Season);
|
||||||
const newAvailableSeasons = entity.seasons
|
const newAvailableSeasons = entity.seasons
|
||||||
.filter((season) => season.status === MediaStatus.AVAILABLE)
|
.filter(
|
||||||
|
(season) =>
|
||||||
|
season[is4k ? 'status4k' : 'status'] === MediaStatus.AVAILABLE
|
||||||
|
)
|
||||||
.map((season) => season.seasonNumber);
|
.map((season) => season.seasonNumber);
|
||||||
const oldSeasonIds = dbEntity.seasons.map((season) => season.id);
|
const oldSeasonIds = dbEntity.seasons.map((season) => season.id);
|
||||||
const oldSeasons = await seasonRepository.findByIds(oldSeasonIds);
|
const oldSeasons = await seasonRepository.findByIds(oldSeasonIds);
|
||||||
const oldAvailableSeasons = oldSeasons
|
const oldAvailableSeasons = oldSeasons
|
||||||
.filter((season) => season.status === MediaStatus.AVAILABLE)
|
.filter(
|
||||||
|
(season) =>
|
||||||
|
season[is4k ? 'status4k' : 'status'] === MediaStatus.AVAILABLE
|
||||||
|
)
|
||||||
.map((season) => season.seasonNumber);
|
.map((season) => season.seasonNumber);
|
||||||
|
|
||||||
const changedSeasons = newAvailableSeasons.filter(
|
const changedSeasons = newAvailableSeasons.filter(
|
||||||
@@ -74,7 +88,7 @@ export class MediaSubscriber implements EntitySubscriberInterface<Media> {
|
|||||||
|
|
||||||
for (const changedSeasonNumber of changedSeasons) {
|
for (const changedSeasonNumber of changedSeasons) {
|
||||||
const requests = await requestRepository.find({
|
const requests = await requestRepository.find({
|
||||||
where: { media: entity, is4k: false },
|
where: { media: entity, is4k: true },
|
||||||
});
|
});
|
||||||
const request = requests.find(
|
const request = requests.find(
|
||||||
(request) =>
|
(request) =>
|
||||||
@@ -93,7 +107,7 @@ export class MediaSubscriber implements EntitySubscriberInterface<Media> {
|
|||||||
);
|
);
|
||||||
const tv = await tmdb.getTvShow({ tvId: entity.tmdbId });
|
const tv = await tmdb.getTvShow({ tvId: entity.tmdbId });
|
||||||
notificationManager.sendNotification(Notification.MEDIA_AVAILABLE, {
|
notificationManager.sendNotification(Notification.MEDIA_AVAILABLE, {
|
||||||
event: 'Series Now Available',
|
event: `${is4k ? '4K ' : ''}Series Request Now Available`,
|
||||||
subject: `${tv.name}${
|
subject: `${tv.name}${
|
||||||
tv.first_air_date ? ` (${tv.first_air_date.slice(0, 4)})` : ''
|
tv.first_air_date ? ` (${tv.first_air_date.slice(0, 4)})` : ''
|
||||||
}`,
|
}`,
|
||||||
@@ -148,7 +162,22 @@ export class MediaSubscriber implements EntitySubscriberInterface<Media> {
|
|||||||
event.entity.mediaType === MediaType.MOVIE &&
|
event.entity.mediaType === MediaType.MOVIE &&
|
||||||
event.entity.status === MediaStatus.AVAILABLE
|
event.entity.status === MediaStatus.AVAILABLE
|
||||||
) {
|
) {
|
||||||
this.notifyAvailableMovie(event.entity as Media, event.databaseEntity);
|
this.notifyAvailableMovie(
|
||||||
|
event.entity as Media,
|
||||||
|
event.databaseEntity,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
event.entity.mediaType === MediaType.MOVIE &&
|
||||||
|
event.entity.status4k === MediaStatus.AVAILABLE
|
||||||
|
) {
|
||||||
|
this.notifyAvailableMovie(
|
||||||
|
event.entity as Media,
|
||||||
|
event.databaseEntity,
|
||||||
|
true
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@@ -156,7 +185,23 @@ export class MediaSubscriber implements EntitySubscriberInterface<Media> {
|
|||||||
(event.entity.status === MediaStatus.AVAILABLE ||
|
(event.entity.status === MediaStatus.AVAILABLE ||
|
||||||
event.entity.status === MediaStatus.PARTIALLY_AVAILABLE)
|
event.entity.status === MediaStatus.PARTIALLY_AVAILABLE)
|
||||||
) {
|
) {
|
||||||
this.notifyAvailableSeries(event.entity as Media, event.databaseEntity);
|
this.notifyAvailableSeries(
|
||||||
|
event.entity as Media,
|
||||||
|
event.databaseEntity,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
event.entity.mediaType === MediaType.TV &&
|
||||||
|
(event.entity.status4k === MediaStatus.AVAILABLE ||
|
||||||
|
event.entity.status4k === MediaStatus.PARTIALLY_AVAILABLE)
|
||||||
|
) {
|
||||||
|
this.notifyAvailableSeries(
|
||||||
|
event.entity as Media,
|
||||||
|
event.databaseEntity,
|
||||||
|
true
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
Reference in New Issue
Block a user