feat: notifications for media_available and media_approved

This commit is contained in:
sct
2020-11-23 10:34:53 +00:00
parent d8e542e5fe
commit a6c5e65bbf
8 changed files with 234 additions and 29 deletions

View File

@@ -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;

View File

@@ -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(', '),
},
],
});
}
}

View File

@@ -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;