refactor(MediaRequestSubscriber): streamline media availability notifications

This commit is contained in:
0xsysr3ll
2025-08-08 15:10:35 +02:00
parent 308f60ac46
commit 461ed7e5d3

View File

@@ -33,21 +33,17 @@ import { EventSubscriber } from 'typeorm';
export class MediaRequestSubscriber
implements EntitySubscriberInterface<MediaRequest>
{
/**
* Get the latest media state using event manager or fallback to repository
*/
private async getLatestMediaState(
private async notifyAvailableMovie(
entity: MediaRequest,
event?: UpdateEvent<MediaRequest>
): Promise<Media | null> {
) {
// Get fresh media state using event manager
let latestMedia: Media | null = null;
if (event?.manager) {
latestMedia = await event.manager.findOne(Media, {
where: { id: entity.media.id },
});
}
if (!latestMedia) {
const mediaRepository = getRepository(Media);
latestMedia = await mediaRepository.findOne({
@@ -55,93 +51,59 @@ export class MediaRequestSubscriber
});
}
return latestMedia;
}
/**
* Check if media is available for notification
*/
private isMediaAvailable(entity: MediaRequest, media: Media): boolean {
const mediaStatus = media[entity.is4k ? 'status4k' : 'status'];
return mediaStatus === MediaStatus.AVAILABLE;
}
/**
* Send notification for available media
*/
private async sendAvailableNotification(
entity: MediaRequest,
media: Media,
notificationData: {
event: string;
subject: string;
message: string;
image: string;
extra?: { name: string; value: string }[];
// Check availability using fresh media state
if (
!latestMedia ||
latestMedia[entity.is4k ? 'status4k' : 'status'] !== MediaStatus.AVAILABLE
) {
return;
}
): Promise<void> {
const tmdb = new TheMovieDb();
try {
const movie = await tmdb.getMovie({
movieId: entity.media.tmdbId,
});
notificationManager.sendNotification(Notification.MEDIA_AVAILABLE, {
...notificationData,
event: `${entity.is4k ? '4K ' : ''}Movie Request Now Available`,
notifyAdmin: false,
notifySystem: true,
notifyUser: entity.requestedBy,
media,
subject: `${movie.title}${
movie.release_date ? ` (${movie.release_date.slice(0, 4)})` : ''
}`,
message: truncate(movie.overview, {
length: 500,
separator: /\s/,
omission: '…',
}),
media: latestMedia,
image: `https://image.tmdb.org/t/p/w600_and_h900_bestv2${movie.poster_path}`,
request: entity,
});
} catch (e) {
logger.error('Something went wrong sending media notification(s)', {
label: 'Notifications',
errorMessage: e.message,
requestId: entity.id,
mediaId: entity.media.id,
mediaId: entity.id,
});
}
}
private async notifyAvailableMovie(
private async notifyAvailableSeries(
entity: MediaRequest,
event?: UpdateEvent<MediaRequest>
) {
const latestMedia = await this.getLatestMediaState(entity, event);
if (!latestMedia || !this.isMediaAvailable(entity, latestMedia)) {
return;
}
const tmdb = new TheMovieDb();
const movie = await tmdb.getMovie({ movieId: entity.media.tmdbId });
await this.sendAvailableNotification(entity, latestMedia, {
event: `${entity.is4k ? '4K ' : ''}Movie Request Now Available`,
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}`,
});
}
/**
* Get the latest media state with seasons for series
*/
private async getLatestMediaStateWithSeasons(
entity: MediaRequest,
event?: UpdateEvent<MediaRequest>
): Promise<Media | null> {
// Get fresh media state with seasons using event manager
let latestMedia: Media | null = null;
if (event?.manager) {
latestMedia = await event.manager.findOne(Media, {
where: { id: entity.media.id },
relations: { seasons: true },
});
}
if (!latestMedia) {
const mediaRepository = getRepository(Media);
latestMedia = await mediaRepository.findOne({
@@ -150,78 +112,63 @@ export class MediaRequestSubscriber
});
}
return latestMedia;
}
/**
* Check if series is available for notification
*/
private isSeriesAvailable(
entity: MediaRequest,
media: Media
): {
isAvailable: boolean;
requestedSeasons: number[];
availableSeasons: number[];
} {
const requestedSeasons =
entity.seasons?.map((entitySeason) => entitySeason.seasonNumber) ?? [];
const availableSeasons = media.seasons.filter(
(season) =>
season[entity.is4k ? 'status4k' : 'status'] === MediaStatus.AVAILABLE &&
requestedSeasons.includes(season.seasonNumber)
);
const isAvailable =
availableSeasons.length > 0 &&
availableSeasons.length === requestedSeasons.length;
return {
isAvailable,
requestedSeasons,
availableSeasons: availableSeasons.map((s) => s.seasonNumber),
};
}
private async notifyAvailableSeries(
entity: MediaRequest,
event?: UpdateEvent<MediaRequest>
) {
const latestMedia = await this.getLatestMediaStateWithSeasons(
entity,
event
);
if (!latestMedia) {
return;
}
const { isAvailable } = this.isSeriesAvailable(entity, latestMedia);
// Check availability using fresh media state
const requestedSeasons =
entity.seasons?.map((entitySeason) => entitySeason.seasonNumber) ?? [];
const availableSeasons = latestMedia.seasons.filter(
(season) =>
season[entity.is4k ? 'status4k' : 'status'] === MediaStatus.AVAILABLE &&
requestedSeasons.includes(season.seasonNumber)
);
const isMediaAvailable =
availableSeasons.length > 0 &&
availableSeasons.length === requestedSeasons.length;
if (!isAvailable) {
if (!isMediaAvailable) {
return;
}
const tmdb = new TheMovieDb();
const tv = await tmdb.getTvShow({ tvId: entity.media.tmdbId });
await this.sendAvailableNotification(entity, latestMedia, {
event: `${entity.is4k ? '4K ' : ''}Series Request Now Available`,
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: [
{
name: 'Requested Seasons',
value: entity.seasons.map((season) => season.seasonNumber).join(', '),
},
],
});
try {
const tv = await tmdb.getTvShow({ tvId: entity.media.tmdbId });
notificationManager.sendNotification(Notification.MEDIA_AVAILABLE, {
event: `${entity.is4k ? '4K ' : ''}Series Request Now Available`,
subject: `${tv.name}${
tv.first_air_date ? ` (${tv.first_air_date.slice(0, 4)})` : ''
}`,
message: truncate(tv.overview, {
length: 500,
separator: /\s/,
omission: '…',
}),
notifyAdmin: false,
notifySystem: true,
notifyUser: entity.requestedBy,
image: `https://image.tmdb.org/t/p/w600_and_h900_bestv2${tv.poster_path}`,
media: latestMedia,
extra: [
{
name: 'Requested Seasons',
value: entity.seasons
.map((season) => season.seasonNumber)
.join(', '),
},
],
request: entity,
});
} catch (e) {
logger.error('Something went wrong sending media notification(s)', {
label: 'Notifications',
errorMessage: e.message,
mediaId: entity.id,
});
}
}
public async sendToRadarr(entity: MediaRequest): Promise<void> {