mirror of
https://github.com/sct/overseerr.git
synced 2025-09-17 17:24:35 +02:00
feat: notifications for media_available and media_approved
This commit is contained in:
@@ -8,11 +8,14 @@ import {
|
||||
UpdateDateColumn,
|
||||
getRepository,
|
||||
In,
|
||||
AfterUpdate,
|
||||
} from 'typeorm';
|
||||
import { MediaRequest } from './MediaRequest';
|
||||
import { MediaStatus, MediaType } from '../constants/media';
|
||||
import logger from '../logger';
|
||||
import Season from './Season';
|
||||
import notificationManager, { Notification } from '../lib/notifications';
|
||||
import TheMovieDb from '../api/themoviedb';
|
||||
|
||||
@Entity()
|
||||
class Media {
|
||||
@@ -95,6 +98,32 @@ class Media {
|
||||
constructor(init?: Partial<Media>) {
|
||||
Object.assign(this, init);
|
||||
}
|
||||
|
||||
@AfterUpdate()
|
||||
private async notifyAvailable() {
|
||||
if (this.status === MediaStatus.AVAILABLE) {
|
||||
if (this.mediaType === MediaType.MOVIE) {
|
||||
const requestRepository = getRepository(MediaRequest);
|
||||
const relatedRequests = await requestRepository.find({
|
||||
where: { media: this },
|
||||
});
|
||||
|
||||
if (relatedRequests.length > 0) {
|
||||
const tmdb = new TheMovieDb();
|
||||
const movie = await tmdb.getMovie({ movieId: this.tmdbId });
|
||||
|
||||
relatedRequests.forEach((request) => {
|
||||
notificationManager.sendNotification(Notification.MEDIA_AVAILABLE, {
|
||||
notifyUser: request.requestedBy,
|
||||
subject: movie.title,
|
||||
message: movie.overview,
|
||||
image: `https://image.tmdb.org/t/p/w600_and_h900_bestv2${movie.poster_path}`,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Media;
|
||||
|
@@ -64,14 +64,86 @@ export class MediaRequest {
|
||||
@AfterInsert()
|
||||
private async notifyNewRequest() {
|
||||
if (this.status === MediaRequestStatus.PENDING) {
|
||||
const mediaRepository = getRepository(Media);
|
||||
const media = await mediaRepository.findOne({
|
||||
where: { id: this.media.id },
|
||||
});
|
||||
if (!media) {
|
||||
logger.error('No parent media!', { label: 'Media Request' });
|
||||
return;
|
||||
}
|
||||
const tmdb = new TheMovieDb();
|
||||
if (this.type === MediaType.MOVIE) {
|
||||
const movie = await tmdb.getMovie({ movieId: media.tmdbId });
|
||||
notificationManager.sendNotification(Notification.MEDIA_PENDING, {
|
||||
subject: movie.title,
|
||||
message: movie.overview,
|
||||
image: `https://image.tmdb.org/t/p/w600_and_h900_bestv2${movie.poster_path}`,
|
||||
notifyUser: this.requestedBy,
|
||||
});
|
||||
}
|
||||
|
||||
if (this.type === MediaType.TV) {
|
||||
const tv = await tmdb.getTvShow({ tvId: media.tmdbId });
|
||||
notificationManager.sendNotification(Notification.MEDIA_PENDING, {
|
||||
subject: tv.name,
|
||||
message: tv.overview,
|
||||
image: `https://image.tmdb.org/t/p/w600_and_h900_bestv2${tv.poster_path}`,
|
||||
notifyUser: this.requestedBy,
|
||||
extra: [
|
||||
{
|
||||
name: 'Seasons',
|
||||
value: this.seasons
|
||||
.map((season) => season.seasonNumber)
|
||||
.join(', '),
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification for approval
|
||||
*
|
||||
* We only check on AfterUpdate as to not trigger this for
|
||||
* auto approved content
|
||||
*/
|
||||
@AfterUpdate()
|
||||
private async notifyApproved() {
|
||||
if (this.status === MediaRequestStatus.APPROVED) {
|
||||
const mediaRepository = getRepository(Media);
|
||||
const media = await mediaRepository.findOne({
|
||||
where: { id: this.media.id },
|
||||
});
|
||||
if (!media) {
|
||||
logger.error('No parent media!', { label: 'Media Request' });
|
||||
return;
|
||||
}
|
||||
const tmdb = new TheMovieDb();
|
||||
if (this.media.mediaType === MediaType.MOVIE) {
|
||||
const movie = await tmdb.getMovie({ movieId: this.media.tmdbId });
|
||||
notificationManager.sendNotification(Notification.MEDIA_ADDED, {
|
||||
subject: `New Request: ${movie.title}`,
|
||||
notificationManager.sendNotification(Notification.MEDIA_APPROVED, {
|
||||
subject: movie.title,
|
||||
message: movie.overview,
|
||||
image: `https://image.tmdb.org/t/p/w600_and_h900_bestv2${movie.poster_path}`,
|
||||
username: this.requestedBy.username,
|
||||
notifyUser: this.requestedBy,
|
||||
});
|
||||
} else if (this.media.mediaType === MediaType.TV) {
|
||||
const tv = await tmdb.getTvShow({ tvId: this.media.tmdbId });
|
||||
notificationManager.sendNotification(Notification.MEDIA_APPROVED, {
|
||||
subject: tv.name,
|
||||
message: tv.overview,
|
||||
image: `https://image.tmdb.org/t/p/w600_and_h900_bestv2${tv.poster_path}`,
|
||||
notifyUser: this.requestedBy,
|
||||
extra: [
|
||||
{
|
||||
name: 'Seasons',
|
||||
value: this.seasons
|
||||
.map((season) => season.seasonNumber)
|
||||
.join(', '),
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -5,9 +5,16 @@ import {
|
||||
ManyToOne,
|
||||
CreateDateColumn,
|
||||
UpdateDateColumn,
|
||||
AfterInsert,
|
||||
AfterUpdate,
|
||||
getRepository,
|
||||
RelationId,
|
||||
} from 'typeorm';
|
||||
import { MediaStatus } from '../constants/media';
|
||||
import Media from './Media';
|
||||
import logger from '../logger';
|
||||
import TheMovieDb from '../api/themoviedb';
|
||||
import notificationManager, { Notification } from '../lib/notifications';
|
||||
|
||||
@Entity()
|
||||
class Season {
|
||||
@@ -20,8 +27,8 @@ class Season {
|
||||
@Column({ type: 'int', default: MediaStatus.UNKNOWN })
|
||||
public status: MediaStatus;
|
||||
|
||||
@ManyToOne(() => Media, (media) => media.seasons)
|
||||
public media: Media;
|
||||
@ManyToOne(() => Media, (media) => media.seasons, { onDelete: 'CASCADE' })
|
||||
public media: Promise<Media>;
|
||||
|
||||
@CreateDateColumn()
|
||||
public createdAt: Date;
|
||||
@@ -32,6 +39,60 @@ class Season {
|
||||
constructor(init?: Partial<Season>) {
|
||||
Object.assign(this, init);
|
||||
}
|
||||
|
||||
@AfterInsert()
|
||||
@AfterUpdate()
|
||||
private async sendSeasonAvailableNotification() {
|
||||
if (this.status === MediaStatus.AVAILABLE) {
|
||||
try {
|
||||
const lazyMedia = await this.media;
|
||||
const tmdb = new TheMovieDb();
|
||||
const mediaRepository = getRepository(Media);
|
||||
const media = await mediaRepository.findOneOrFail({
|
||||
where: { id: lazyMedia.id },
|
||||
relations: ['requests'],
|
||||
});
|
||||
|
||||
const availableSeasons = media.seasons.map(
|
||||
(season) => season.seasonNumber
|
||||
);
|
||||
|
||||
const request = media.requests.find(
|
||||
(request) =>
|
||||
// Check if the season is complete AND it contains the current season that was just marked available
|
||||
request.seasons.every((season) =>
|
||||
availableSeasons.includes(season.seasonNumber)
|
||||
) &&
|
||||
request.seasons.some(
|
||||
(season) => season.seasonNumber === this.seasonNumber
|
||||
)
|
||||
);
|
||||
|
||||
if (request) {
|
||||
const tv = await tmdb.getTvShow({ tvId: media.tmdbId });
|
||||
notificationManager.sendNotification(Notification.MEDIA_AVAILABLE, {
|
||||
subject: tv.name,
|
||||
message: tv.overview,
|
||||
notifyUser: request.requestedBy,
|
||||
image: `https://image.tmdb.org/t/p/w600_and_h900_bestv2${tv.poster_path}`,
|
||||
extra: [
|
||||
{
|
||||
name: 'Seasons',
|
||||
value: request.seasons
|
||||
.map((season) => season.seasonNumber)
|
||||
.join(', '),
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
logger.error('Something went wrong sending season available notice', {
|
||||
label: 'Notifications',
|
||||
message: e.message,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Season;
|
||||
|
Reference in New Issue
Block a user