Skip to content

Commit d4691f9

Browse files
committed
feat(notifications): added Play On Media Server in notifications when media is available
In the email notification users receive when their media request is now available, I have added a Play on media server (e.g. Play on Plex) button that takes them directly to the media on their media server. feat(notifications): play On Media Server in Discord & Slack Notifications In the Discord & Slack notification users receive when their media request is now available, I have added a Play on media server (e.g. Play on Plex) button that takes them directly to the media on their media server.
1 parent 33a5d9a commit d4691f9

File tree

4 files changed

+111
-3
lines changed

4 files changed

+111
-3
lines changed

server/lib/notifications/agents/email.ts

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { IssueType, IssueTypeName } from '@server/constants/issue';
22
import { MediaType } from '@server/constants/media';
3+
import { MediaServerType } from '@server/constants/server';
34
import { getRepository } from '@server/datasource';
45
import { User } from '@server/entity/User';
56
import PreparedEmail from '@server/lib/email';
@@ -49,9 +50,33 @@ class EmailAgent
4950
recipientName?: string
5051
): EmailOptions | undefined {
5152
const settings = getSettings();
52-
const { applicationUrl, applicationTitle } = settings.main;
53+
const { applicationUrl, applicationTitle, mediaServerType } = settings.main;
5354
const { embedPoster } = settings.notifications.agents.email;
5455

56+
function getAvailableMediaServerName() {
57+
if (mediaServerType === MediaServerType.EMBY) {
58+
return 'Emby';
59+
}
60+
61+
if (mediaServerType === MediaServerType.PLEX) {
62+
return 'Plex';
63+
}
64+
65+
return 'Jellyfin';
66+
}
67+
68+
const mediaServerName = getAvailableMediaServerName();
69+
70+
function getAvailableMediaServerUrl(): string | undefined {
71+
const wants4k = payload.request?.is4k;
72+
const url4k = (payload.media as any)?.mediaUrl4k as string | undefined;
73+
const url = (payload.media as any)?.mediaUrl as string | undefined;
74+
75+
return (wants4k ? (url4k ?? url) : (url ?? url4k)) || undefined;
76+
}
77+
78+
const mediaServerUrl = getAvailableMediaServerUrl();
79+
5580
if (type === Notification.TEST_NOTIFICATION) {
5681
return {
5782
template: path.join(__dirname, '../../../templates/email/test-email'),
@@ -141,6 +166,10 @@ class EmailAgent
141166
applicationTitle,
142167
recipientName,
143168
recipientEmail,
169+
mediaServerActionUrl: mediaServerUrl
170+
? `${mediaServerUrl}`
171+
: undefined,
172+
mediaServerName,
144173
},
145174
};
146175
} else if (payload.issue) {

server/lib/notifications/agents/slack.ts

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { IssueStatus, IssueTypeName } from '@server/constants/issue';
2+
import { MediaServerType } from '@server/constants/server';
23
import type { NotificationAgentSlack } from '@server/lib/settings';
34
import { getSettings } from '@server/lib/settings';
45
import logger from '@server/logger';
@@ -64,9 +65,29 @@ class SlackAgent
6465
payload: NotificationPayload
6566
): SlackBlockEmbed {
6667
const settings = getSettings();
67-
const { applicationUrl, applicationTitle } = settings.main;
68+
const { applicationUrl, applicationTitle, mediaServerType } = settings.main;
6869
const { embedPoster } = settings.notifications.agents.slack;
6970

71+
function getAvailableMediaServerName() {
72+
if (mediaServerType === MediaServerType.EMBY) {
73+
return 'Emby';
74+
}
75+
76+
if (mediaServerType === MediaServerType.PLEX) {
77+
return 'Plex';
78+
}
79+
80+
return 'Jellyfin';
81+
}
82+
83+
function getAvailableMediaServerUrl(): string | undefined {
84+
const wants4k = payload.request?.is4k;
85+
const url4k = (payload.media as any)?.mediaUrl4k as string | undefined;
86+
const url = (payload.media as any)?.mediaUrl as string | undefined;
87+
88+
return (wants4k ? (url4k ?? url) : (url ?? url4k)) || undefined;
89+
}
90+
7091
const fields: EmbedField[] = [];
7192

7293
if (payload.request) {
@@ -206,6 +227,28 @@ class SlackAgent
206227
});
207228
}
208229

230+
if (!payload.issue) {
231+
const mediaServerName = getAvailableMediaServerName();
232+
const mediaServerUrl = getAvailableMediaServerUrl();
233+
234+
if (mediaServerUrl) {
235+
blocks.push({
236+
type: 'actions',
237+
elements: [
238+
{
239+
action_id: 'open-in-mediaServer',
240+
type: 'button',
241+
url: mediaServerUrl,
242+
text: {
243+
type: 'plain_text',
244+
text: `Play on ${mediaServerName}`,
245+
},
246+
},
247+
],
248+
});
249+
}
250+
}
251+
209252
return {
210253
text: payload.event ?? payload.subject,
211254
blocks,

server/lib/notifications/agents/telegram.ts

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { IssueStatus, IssueTypeName } from '@server/constants/issue';
22
import { MediaStatus } from '@server/constants/media';
3+
import { MediaServerType } from '@server/constants/server';
34
import { getRepository } from '@server/datasource';
45
import { User } from '@server/entity/User';
56
import type { NotificationAgentTelegram } from '@server/lib/settings';
@@ -66,9 +67,29 @@ class TelegramAgent
6667
payload: NotificationPayload
6768
): Partial<TelegramMessagePayload | TelegramPhotoPayload> {
6869
const settings = getSettings();
69-
const { applicationUrl, applicationTitle } = settings.main;
70+
const { applicationUrl, applicationTitle, mediaServerType } = settings.main;
7071
const { embedPoster } = settings.notifications.agents.telegram;
7172

73+
function getAvailableMediaServerName() {
74+
if (mediaServerType === MediaServerType.EMBY) {
75+
return 'Emby';
76+
}
77+
78+
if (mediaServerType === MediaServerType.PLEX) {
79+
return 'Plex';
80+
}
81+
82+
return 'Jellyfin';
83+
}
84+
85+
function getAvailableMediaServerUrl(): string | undefined {
86+
const wants4k = payload.request?.is4k;
87+
const url4k = (payload.media as any)?.mediaUrl4k as string | undefined;
88+
const url = (payload.media as any)?.mediaUrl as string | undefined;
89+
90+
return (wants4k ? (url4k ?? url) : (url ?? url4k)) || undefined;
91+
}
92+
7293
/* eslint-disable no-useless-escape */
7394
let message = `\*${this.escapeText(
7495
payload.event ? `${payload.event} - ${payload.subject}` : payload.subject
@@ -142,6 +163,15 @@ class TelegramAgent
142163
payload.issue ? 'Issue' : 'Media'
143164
} in ${this.escapeText(applicationTitle)}\]\(${url}\)`;
144165
}
166+
167+
if (!payload.issue) {
168+
const mediaServerName = getAvailableMediaServerName();
169+
const mediaServerUrl = getAvailableMediaServerUrl();
170+
171+
if (mediaServerUrl) {
172+
message += `\n\[Play on ${this.escapeText(mediaServerName)}\]\(${mediaServerUrl}\)`;
173+
}
174+
}
145175
/* eslint-enable */
146176

147177
return embedPoster && payload.image

server/templates/email/media-request/html.pug

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,9 @@ div(style='display: block; background-color: #111827; padding: 2.5rem 0;')
6868
a(href=actionUrl style='display: block; margin: 1.5rem 3rem 0; text-decoration: none; font-size: 1.0em; line-height: 2.25em;')
6969
span(style='padding: 0.2rem; font-weight: 500; text-align: center; border-radius: 10px; background-color: rgb(99,102,241); color: #fff; display: block; border: 1px solid rgba(255,255,255,0.2);')
7070
| View Media in #{applicationTitle}
71+
if mediaServerActionUrl
72+
tr
73+
td
74+
a(href=mediaServerActionUrl style='display: block; margin: 1.5rem 3rem 0; text-decoration: none; font-size: 1.0em; line-height: 2.25em;')
75+
span(style='padding: 0.2rem; font-weight: 500; text-align: center; border-radius: 10px; background-color: rgb(99,102,241); color: #fff; display: block; border: 1px solid rgba(255,255,255,0.2);')
76+
| Play on #{mediaServerName}

0 commit comments

Comments
 (0)