Add notification on subscription live stream
This commit is contained in:
parent
4300cc1ee1
commit
a012d6c2a9
|
@ -30,7 +30,7 @@ export class MyAccountNotificationPreferencesComponent implements OnInit {
|
||||||
private notifier: Notifier
|
private notifier: Notifier
|
||||||
) {
|
) {
|
||||||
this.labelNotifications = {
|
this.labelNotifications = {
|
||||||
newVideoFromSubscription: $localize`New video from your subscriptions`,
|
newVideoFromSubscription: $localize`New video or live from your subscriptions`,
|
||||||
newCommentOnMyVideo: $localize`New comment on your video`,
|
newCommentOnMyVideo: $localize`New comment on your video`,
|
||||||
abuseAsModerator: $localize`New abuse`,
|
abuseAsModerator: $localize`New abuse`,
|
||||||
videoAutoBlacklistAsModerator: $localize`An automatically blocked video is awaiting review`,
|
videoAutoBlacklistAsModerator: $localize`An automatically blocked video is awaiting review`,
|
||||||
|
|
|
@ -150,6 +150,7 @@ export class UserNotification implements UserNotificationServer {
|
||||||
|
|
||||||
switch (this.type) {
|
switch (this.type) {
|
||||||
case UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION:
|
case UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION:
|
||||||
|
case UserNotificationType.NEW_LIVE_FROM_SUBSCRIPTION:
|
||||||
this.videoUrl = this.buildVideoUrl(this.video)
|
this.videoUrl = this.buildVideoUrl(this.video)
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
|
@ -6,9 +6,7 @@
|
||||||
|
|
||||||
<ng-container [ngSwitch]="notification.type">
|
<ng-container [ngSwitch]="notification.type">
|
||||||
<ng-container *ngSwitchCase="1"> <!-- UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION -->
|
<ng-container *ngSwitchCase="1"> <!-- UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION -->
|
||||||
<ng-container *ngIf="notification.video; then hasVideo; else noVideo"></ng-container>
|
@if (notification.video) {
|
||||||
|
|
||||||
<ng-template #hasVideo>
|
|
||||||
<a (click)="markAsRead(notification)" [routerLink]="notification.accountUrl">
|
<a (click)="markAsRead(notification)" [routerLink]="notification.accountUrl">
|
||||||
<img alt="" aria-labelledby="avatar" class="avatar" [src]="notification.video.channel.avatarUrl" />
|
<img alt="" aria-labelledby="avatar" class="avatar" [src]="notification.video.channel.avatarUrl" />
|
||||||
</a>
|
</a>
|
||||||
|
@ -16,15 +14,13 @@
|
||||||
<div class="message" i18n>
|
<div class="message" i18n>
|
||||||
{{ notification.video.channel.displayName }} published a new video: <a (click)="markAsRead(notification)" [routerLink]="notification.videoUrl">{{ notification.video.name }}</a>
|
{{ notification.video.channel.displayName }} published a new video: <a (click)="markAsRead(notification)" [routerLink]="notification.videoUrl">{{ notification.video.name }}</a>
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
} @else {
|
||||||
|
|
||||||
<ng-template #noVideo>
|
|
||||||
<my-global-icon iconName="alert" aria-hidden="true"></my-global-icon>
|
<my-global-icon iconName="alert" aria-hidden="true"></my-global-icon>
|
||||||
|
|
||||||
<div class="message" i18n>
|
<div class="message" i18n>
|
||||||
The notification concerns a video now unavailable
|
The notification concerns a video now unavailable
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
}
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container *ngSwitchCase="5"> <!-- UserNotificationType.UNBLACKLIST_ON_MY_VIDEO -->
|
<ng-container *ngSwitchCase="5"> <!-- UserNotificationType.UNBLACKLIST_ON_MY_VIDEO -->
|
||||||
|
@ -224,6 +220,24 @@
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container *ngSwitchCase="21"> <!-- UserNotificationType.NEW_LIVE_FROM_SUBSCRIPTION -->
|
||||||
|
@if (notification.video) {
|
||||||
|
<a (click)="markAsRead(notification)" [routerLink]="notification.accountUrl">
|
||||||
|
<img alt="" aria-labelledby="avatar" class="avatar" [src]="notification.video.channel.avatarUrl" />
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<div class="message" i18n>
|
||||||
|
{{ notification.video.channel.displayName }} is live streaming in <a (click)="markAsRead(notification)" [routerLink]="notification.videoUrl">{{ notification.video.name }}</a>
|
||||||
|
</div>
|
||||||
|
} @else {
|
||||||
|
<my-global-icon iconName="alert" aria-hidden="true"></my-global-icon>
|
||||||
|
|
||||||
|
<div class="message" i18n>
|
||||||
|
The notification concerns a video now unavailable
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<ng-container *ngSwitchDefault>
|
<ng-container *ngSwitchDefault>
|
||||||
<my-global-icon iconName="alert" aria-hidden="true"></my-global-icon>
|
<my-global-icon iconName="alert" aria-hidden="true"></my-global-icon>
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,9 @@ export const UserNotificationType = {
|
||||||
|
|
||||||
MY_VIDEO_STUDIO_EDITION_FINISHED: 19,
|
MY_VIDEO_STUDIO_EDITION_FINISHED: 19,
|
||||||
|
|
||||||
NEW_USER_REGISTRATION_REQUEST: 20
|
NEW_USER_REGISTRATION_REQUEST: 20,
|
||||||
|
|
||||||
|
NEW_LIVE_FROM_SUBSCRIPTION: 21
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
export type UserNotificationType_Type = typeof UserNotificationType[keyof typeof UserNotificationType]
|
export type UserNotificationType_Type = typeof UserNotificationType[keyof typeof UserNotificationType]
|
||||||
|
|
|
@ -119,12 +119,13 @@ export class LiveCommand extends AbstractCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
async quickCreate (options: OverrideCommandOptions & {
|
async quickCreate (options: OverrideCommandOptions & {
|
||||||
|
name: string
|
||||||
saveReplay: boolean
|
saveReplay: boolean
|
||||||
permanentLive: boolean
|
permanentLive: boolean
|
||||||
privacy?: VideoPrivacyType
|
privacy?: VideoPrivacyType
|
||||||
videoPasswords?: string[]
|
videoPasswords?: string[]
|
||||||
}) {
|
}) {
|
||||||
const { saveReplay, permanentLive, privacy = VideoPrivacy.PUBLIC, videoPasswords } = options
|
const { name = 'live', saveReplay, permanentLive, privacy = VideoPrivacy.PUBLIC, videoPasswords } = options
|
||||||
|
|
||||||
const replaySettings = privacy === VideoPrivacy.PASSWORD_PROTECTED
|
const replaySettings = privacy === VideoPrivacy.PASSWORD_PROTECTED
|
||||||
? { privacy: VideoPrivacy.PRIVATE }
|
? { privacy: VideoPrivacy.PRIVATE }
|
||||||
|
@ -134,7 +135,7 @@ export class LiveCommand extends AbstractCommand {
|
||||||
...options,
|
...options,
|
||||||
|
|
||||||
fields: {
|
fields: {
|
||||||
name: 'live',
|
name,
|
||||||
permanentLive,
|
permanentLive,
|
||||||
saveReplay,
|
saveReplay,
|
||||||
replaySettings,
|
replaySettings,
|
||||||
|
|
|
@ -18,7 +18,7 @@ import {
|
||||||
checkNewInstanceFollower,
|
checkNewInstanceFollower,
|
||||||
checkAutoInstanceFollowing,
|
checkAutoInstanceFollowing,
|
||||||
checkVideoAutoBlacklistForModerators,
|
checkVideoAutoBlacklistForModerators,
|
||||||
checkVideoIsPublished,
|
checkMyVideoIsPublished,
|
||||||
checkNewVideoFromSubscription
|
checkNewVideoFromSubscription
|
||||||
} from '@tests/shared/notifications.js'
|
} from '@tests/shared/notifications.js'
|
||||||
|
|
||||||
|
@ -487,7 +487,7 @@ describe('Test moderation notifications', function () {
|
||||||
it('Should not send video publish notification if auto-blacklisted', async function () {
|
it('Should not send video publish notification if auto-blacklisted', async function () {
|
||||||
this.timeout(120000)
|
this.timeout(120000)
|
||||||
|
|
||||||
await checkVideoIsPublished({ ...userBaseParams, videoName, shortUUID, checkType: 'absence' })
|
await checkMyVideoIsPublished({ ...userBaseParams, videoName, shortUUID, checkType: 'absence' })
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should not send a local user subscription notification if auto-blacklisted', async function () {
|
it('Should not send a local user subscription notification if auto-blacklisted', async function () {
|
||||||
|
@ -576,7 +576,7 @@ describe('Test moderation notifications', function () {
|
||||||
const { shortUUID } = await servers[0].videos.upload({ token: userToken1, attributes })
|
const { shortUUID } = await servers[0].videos.upload({ token: userToken1, attributes })
|
||||||
|
|
||||||
await wait(6000)
|
await wait(6000)
|
||||||
await checkVideoIsPublished({ ...userBaseParams, videoName: name, shortUUID, checkType: 'absence' })
|
await checkMyVideoIsPublished({ ...userBaseParams, videoName: name, shortUUID, checkType: 'absence' })
|
||||||
await checkNewVideoFromSubscription({ ...adminBaseParamsServer1, videoName: name, shortUUID, checkType: 'absence' })
|
await checkNewVideoFromSubscription({ ...adminBaseParamsServer1, videoName: name, shortUUID, checkType: 'absence' })
|
||||||
await checkNewVideoFromSubscription({ ...adminBaseParamsServer2, videoName: name, shortUUID, checkType: 'absence' })
|
await checkNewVideoFromSubscription({ ...adminBaseParamsServer2, videoName: name, shortUUID, checkType: 'absence' })
|
||||||
})
|
})
|
||||||
|
|
|
@ -10,10 +10,12 @@ import {
|
||||||
prepareNotificationsTest,
|
prepareNotificationsTest,
|
||||||
CheckerBaseParams,
|
CheckerBaseParams,
|
||||||
checkNewVideoFromSubscription,
|
checkNewVideoFromSubscription,
|
||||||
checkVideoIsPublished,
|
checkMyVideoIsPublished,
|
||||||
checkVideoStudioEditionIsFinished,
|
checkVideoStudioEditionIsFinished,
|
||||||
checkMyVideoImportIsFinished,
|
checkMyVideoImportIsFinished,
|
||||||
checkNewActorFollow
|
checkNewActorFollow,
|
||||||
|
checkNewLiveFromSubscription,
|
||||||
|
waitUntilNotification
|
||||||
} from '@tests/shared/notifications.js'
|
} from '@tests/shared/notifications.js'
|
||||||
import { FIXTURE_URLS } from '@tests/shared/tests.js'
|
import { FIXTURE_URLS } from '@tests/shared/tests.js'
|
||||||
import { uploadRandomVideoOnServers } from '@tests/shared/videos.js'
|
import { uploadRandomVideoOnServers } from '@tests/shared/videos.js'
|
||||||
|
@ -209,6 +211,82 @@ describe('Test user notifications', function () {
|
||||||
|
|
||||||
await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID: video.shortUUID, checkType: 'presence' })
|
await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID: video.shortUUID, checkType: 'presence' })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('New live from my subscription notification', function () {
|
||||||
|
let baseParams: CheckerBaseParams
|
||||||
|
|
||||||
|
async function createAndStreamLive (server: PeerTubeServer) {
|
||||||
|
const name = 'video live ' + buildUUID()
|
||||||
|
|
||||||
|
const streamDate = new Date()
|
||||||
|
const { video } = await server.live.quickCreate({ name, permanentLive: true, saveReplay: false })
|
||||||
|
await waitJobs(servers)
|
||||||
|
|
||||||
|
const ffmpegCommand = await server.live.sendRTMPStreamInVideo({ videoId: video.uuid })
|
||||||
|
|
||||||
|
return { name, video, ffmpegCommand, streamDate }
|
||||||
|
}
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
baseParams = {
|
||||||
|
server: servers[0],
|
||||||
|
emails,
|
||||||
|
socketNotifications: userNotifications,
|
||||||
|
token: userAccessToken
|
||||||
|
}
|
||||||
|
|
||||||
|
await servers[0].config.enableLive({ allowReplay: false })
|
||||||
|
|
||||||
|
await servers[0].subscriptions.add({ token: userAccessToken, targetUri: 'root_channel@' + servers[0].host })
|
||||||
|
await waitJobs(servers)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should not send a notification when a live is created', async function () {
|
||||||
|
this.timeout(100000)
|
||||||
|
|
||||||
|
const name = 'video live ' + buildUUID()
|
||||||
|
|
||||||
|
const { video } = await servers[0].live.quickCreate({ name, permanentLive: true, saveReplay: false })
|
||||||
|
await waitJobs(servers)
|
||||||
|
await checkNewLiveFromSubscription({ ...baseParams, videoName: name, shortUUID: video.shortUUID, checkType: 'absence' })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should send a local notification when streaming in the live', async function () {
|
||||||
|
this.timeout(100000)
|
||||||
|
|
||||||
|
const { name, video, ffmpegCommand, streamDate } = await createAndStreamLive(servers[0])
|
||||||
|
|
||||||
|
await waitUntilNotification({
|
||||||
|
server: servers[0],
|
||||||
|
token: userAccessToken,
|
||||||
|
notificationType: UserNotificationType.NEW_LIVE_FROM_SUBSCRIPTION,
|
||||||
|
fromDate: streamDate
|
||||||
|
})
|
||||||
|
|
||||||
|
await checkNewLiveFromSubscription({ ...baseParams, videoName: name, shortUUID: video.shortUUID, checkType: 'presence' })
|
||||||
|
|
||||||
|
await stopFfmpeg(ffmpegCommand)
|
||||||
|
await waitJobs(servers)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should send a remote notification when streaming in the live ', async function () {
|
||||||
|
this.timeout(100000)
|
||||||
|
|
||||||
|
const { name, video, ffmpegCommand, streamDate } = await createAndStreamLive(servers[1])
|
||||||
|
|
||||||
|
await waitUntilNotification({
|
||||||
|
server: servers[0],
|
||||||
|
token: userAccessToken,
|
||||||
|
notificationType: UserNotificationType.NEW_LIVE_FROM_SUBSCRIPTION,
|
||||||
|
fromDate: streamDate
|
||||||
|
})
|
||||||
|
await checkNewLiveFromSubscription({ ...baseParams, videoName: name, shortUUID: video.shortUUID, checkType: 'presence' })
|
||||||
|
|
||||||
|
await stopFfmpeg(ffmpegCommand)
|
||||||
|
await waitJobs(servers)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('My video is published', function () {
|
describe('My video is published', function () {
|
||||||
|
@ -229,7 +307,7 @@ describe('Test user notifications', function () {
|
||||||
const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 1)
|
const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 1)
|
||||||
await waitJobs(servers)
|
await waitJobs(servers)
|
||||||
|
|
||||||
await checkVideoIsPublished({ ...baseParams, videoName: name, shortUUID, checkType: 'absence' })
|
await checkMyVideoIsPublished({ ...baseParams, videoName: name, shortUUID, checkType: 'absence' })
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should not send a notification if the wait transcoding is false', async function () {
|
it('Should not send a notification if the wait transcoding is false', async function () {
|
||||||
|
@ -250,7 +328,7 @@ describe('Test user notifications', function () {
|
||||||
const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 2, { waitTranscoding: true, fixture: 'video_short_240p.mp4' })
|
const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 2, { waitTranscoding: true, fixture: 'video_short_240p.mp4' })
|
||||||
await waitJobs(servers)
|
await waitJobs(servers)
|
||||||
|
|
||||||
await checkVideoIsPublished({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
|
await checkMyVideoIsPublished({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should send a notification with a transcoded video', async function () {
|
it('Should send a notification with a transcoded video', async function () {
|
||||||
|
@ -259,7 +337,7 @@ describe('Test user notifications', function () {
|
||||||
const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 2, { waitTranscoding: true })
|
const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 2, { waitTranscoding: true })
|
||||||
await waitJobs(servers)
|
await waitJobs(servers)
|
||||||
|
|
||||||
await checkVideoIsPublished({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
|
await checkMyVideoIsPublished({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should send a notification when an imported video is transcoded', async function () {
|
it('Should send a notification when an imported video is transcoded', async function () {
|
||||||
|
@ -277,7 +355,7 @@ describe('Test user notifications', function () {
|
||||||
const { video } = await servers[1].imports.importVideo({ attributes })
|
const { video } = await servers[1].imports.importVideo({ attributes })
|
||||||
|
|
||||||
await waitJobs(servers)
|
await waitJobs(servers)
|
||||||
await checkVideoIsPublished({ ...baseParams, videoName: name, shortUUID: video.shortUUID, checkType: 'presence' })
|
await checkMyVideoIsPublished({ ...baseParams, videoName: name, shortUUID: video.shortUUID, checkType: 'presence' })
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should send a notification when the scheduled update has been proceeded', async function () {
|
it('Should send a notification when the scheduled update has been proceeded', async function () {
|
||||||
|
@ -296,7 +374,7 @@ describe('Test user notifications', function () {
|
||||||
const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 2, data)
|
const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 2, data)
|
||||||
|
|
||||||
await wait(6000)
|
await wait(6000)
|
||||||
await checkVideoIsPublished({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
|
await checkMyVideoIsPublished({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should not send a notification before the video is published', async function () {
|
it('Should not send a notification before the video is published', async function () {
|
||||||
|
@ -314,7 +392,7 @@ describe('Test user notifications', function () {
|
||||||
const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 2, data)
|
const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 2, data)
|
||||||
|
|
||||||
await wait(6000)
|
await wait(6000)
|
||||||
await checkVideoIsPublished({ ...baseParams, videoName: name, shortUUID, checkType: 'absence' })
|
await checkMyVideoIsPublished({ ...baseParams, videoName: name, shortUUID, checkType: 'absence' })
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -354,7 +432,7 @@ describe('Test user notifications', function () {
|
||||||
await servers[1].live.waitUntilReplacedByReplay({ videoId: shortUUID })
|
await servers[1].live.waitUntilReplacedByReplay({ videoId: shortUUID })
|
||||||
|
|
||||||
await waitJobs(servers)
|
await waitJobs(servers)
|
||||||
await checkVideoIsPublished({ ...baseParams, videoName: 'non permanent live', shortUUID, checkType: 'presence' })
|
await checkMyVideoIsPublished({ ...baseParams, videoName: 'non permanent live', shortUUID, checkType: 'presence' })
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should send a notification is a live replay of a permanent live is published', async function () {
|
it('Should send a notification is a live replay of a permanent live is published', async function () {
|
||||||
|
@ -386,7 +464,7 @@ describe('Test user notifications', function () {
|
||||||
const video = await findExternalSavedVideo(servers[1], liveDetails)
|
const video = await findExternalSavedVideo(servers[1], liveDetails)
|
||||||
expect(video).to.exist
|
expect(video).to.exist
|
||||||
|
|
||||||
await checkVideoIsPublished({ ...baseParams, videoName: video.name, shortUUID: video.shortUUID, checkType: 'presence' })
|
await checkMyVideoIsPublished({ ...baseParams, videoName: video.name, shortUUID: video.shortUUID, checkType: 'presence' })
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -408,7 +486,7 @@ describe('Test user notifications', function () {
|
||||||
const { name, shortUUID, id } = await uploadRandomVideoOnServers(servers, 2, { waitTranscoding: true })
|
const { name, shortUUID, id } = await uploadRandomVideoOnServers(servers, 2, { waitTranscoding: true })
|
||||||
|
|
||||||
await waitJobs(servers)
|
await waitJobs(servers)
|
||||||
await checkVideoIsPublished({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
|
await checkMyVideoIsPublished({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
|
||||||
|
|
||||||
const tasks: VideoStudioTask[] = [
|
const tasks: VideoStudioTask[] = [
|
||||||
{
|
{
|
||||||
|
|
|
@ -7,7 +7,8 @@ import {
|
||||||
UserNotification,
|
UserNotification,
|
||||||
UserNotificationSetting,
|
UserNotificationSetting,
|
||||||
UserNotificationSettingValue,
|
UserNotificationSettingValue,
|
||||||
UserNotificationType
|
UserNotificationType,
|
||||||
|
UserNotificationType_Type
|
||||||
} from '@peertube/peertube-models'
|
} from '@peertube/peertube-models'
|
||||||
import {
|
import {
|
||||||
ConfigCommand,
|
ConfigCommand,
|
||||||
|
@ -17,11 +18,13 @@ import {
|
||||||
setAccessTokensToServers,
|
setAccessTokensToServers,
|
||||||
setDefaultAccountAvatar,
|
setDefaultAccountAvatar,
|
||||||
setDefaultChannelAvatar,
|
setDefaultChannelAvatar,
|
||||||
setDefaultVideoChannel
|
setDefaultVideoChannel,
|
||||||
|
waitJobs
|
||||||
} from '@peertube/peertube-server-commands'
|
} from '@peertube/peertube-server-commands'
|
||||||
import { expect } from 'chai'
|
import { expect } from 'chai'
|
||||||
import { inspect } from 'util'
|
import { inspect } from 'util'
|
||||||
import { MockSmtpServer } from './mock-servers/index.js'
|
import { MockSmtpServer } from './mock-servers/index.js'
|
||||||
|
import { wait } from '@peertube/peertube-core-utils'
|
||||||
|
|
||||||
type CheckerBaseParams = {
|
type CheckerBaseParams = {
|
||||||
server: PeerTubeServer
|
server: PeerTubeServer
|
||||||
|
@ -55,6 +58,24 @@ function getAllNotificationsSettings (): UserNotificationSetting {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function waitUntilNotification (options: {
|
||||||
|
server: PeerTubeServer
|
||||||
|
notificationType: UserNotificationType_Type
|
||||||
|
token: string
|
||||||
|
fromDate: Date
|
||||||
|
}) {
|
||||||
|
const { server, fromDate, notificationType, token } = options
|
||||||
|
|
||||||
|
do {
|
||||||
|
const { data } = await server.notifications.list({ start: 0, count: 5, token })
|
||||||
|
if (data.some(n => n.type === notificationType && new Date(n.createdAt) >= fromDate)) break
|
||||||
|
|
||||||
|
await wait(500)
|
||||||
|
} while (true)
|
||||||
|
|
||||||
|
await waitJobs([ server ])
|
||||||
|
}
|
||||||
|
|
||||||
async function checkNewVideoFromSubscription (options: CheckerBaseParams & {
|
async function checkNewVideoFromSubscription (options: CheckerBaseParams & {
|
||||||
videoName: string
|
videoName: string
|
||||||
shortUUID: string
|
shortUUID: string
|
||||||
|
@ -85,7 +106,37 @@ async function checkNewVideoFromSubscription (options: CheckerBaseParams & {
|
||||||
await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
|
await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
|
||||||
}
|
}
|
||||||
|
|
||||||
async function checkVideoIsPublished (options: CheckerBaseParams & {
|
async function checkNewLiveFromSubscription (options: CheckerBaseParams & {
|
||||||
|
videoName: string
|
||||||
|
shortUUID: string
|
||||||
|
checkType: CheckerType
|
||||||
|
}) {
|
||||||
|
const { videoName, shortUUID } = options
|
||||||
|
const notificationType = UserNotificationType.NEW_LIVE_FROM_SUBSCRIPTION
|
||||||
|
|
||||||
|
function notificationChecker (notification: UserNotification, checkType: CheckerType) {
|
||||||
|
if (checkType === 'presence') {
|
||||||
|
expect(notification).to.not.be.undefined
|
||||||
|
expect(notification.type).to.equal(notificationType)
|
||||||
|
|
||||||
|
checkVideo(notification.video, videoName, shortUUID)
|
||||||
|
checkActor(notification.video.channel)
|
||||||
|
} else {
|
||||||
|
expect(notification).to.satisfy((n: UserNotification) => {
|
||||||
|
return n === undefined || n.type !== UserNotificationType.NEW_LIVE_FROM_SUBSCRIPTION || n.video.name !== videoName
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function emailNotificationFinder (email: object) {
|
||||||
|
const text = email['text']
|
||||||
|
return text.indexOf(shortUUID) !== -1 && text.indexOf('Your subscription') !== -1
|
||||||
|
}
|
||||||
|
|
||||||
|
await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
|
||||||
|
}
|
||||||
|
|
||||||
|
async function checkMyVideoIsPublished (options: CheckerBaseParams & {
|
||||||
videoName: string
|
videoName: string
|
||||||
shortUUID: string
|
shortUUID: string
|
||||||
checkType: CheckerType
|
checkType: CheckerType
|
||||||
|
@ -780,10 +831,13 @@ export {
|
||||||
|
|
||||||
getAllNotificationsSettings,
|
getAllNotificationsSettings,
|
||||||
|
|
||||||
|
waitUntilNotification,
|
||||||
|
|
||||||
checkMyVideoImportIsFinished,
|
checkMyVideoImportIsFinished,
|
||||||
checkUserRegistered,
|
checkUserRegistered,
|
||||||
checkAutoInstanceFollowing,
|
checkAutoInstanceFollowing,
|
||||||
checkVideoIsPublished,
|
checkMyVideoIsPublished,
|
||||||
|
checkNewLiveFromSubscription,
|
||||||
checkNewVideoFromSubscription,
|
checkNewVideoFromSubscription,
|
||||||
checkNewActorFollow,
|
checkNewActorFollow,
|
||||||
checkNewCommentOnMyVideo,
|
checkNewCommentOnMyVideo,
|
||||||
|
@ -841,10 +895,9 @@ async function checkNotification (options: CheckerBaseParams & {
|
||||||
|
|
||||||
if (check.mail) {
|
if (check.mail) {
|
||||||
// Last email
|
// Last email
|
||||||
const email = emails
|
const email = emails.slice()
|
||||||
.slice()
|
.reverse()
|
||||||
.reverse()
|
.find(e => emailNotificationFinder(e))
|
||||||
.find(e => emailNotificationFinder(e))
|
|
||||||
|
|
||||||
if (checkType === 'presence') {
|
if (checkType === 'presence') {
|
||||||
const texts = emails.map(e => e.text)
|
const texts = emails.map(e => e.text)
|
||||||
|
|
|
@ -61,5 +61,5 @@ async function processVideoShare (actorAnnouncer: MActorSignature, activity: Act
|
||||||
return undefined
|
return undefined
|
||||||
})
|
})
|
||||||
|
|
||||||
if (videoCreated && notify) Notifier.Instance.notifyOnNewVideoIfNeeded(video)
|
if (videoCreated && notify) Notifier.Instance.notifyOnNewVideoOrLiveIfNeeded(video)
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,7 +73,7 @@ async function processCreateVideo (videoToCreateData: VideoObject, notify: boole
|
||||||
const syncParam = { rates: false, shares: false, comments: false, refreshVideo: false }
|
const syncParam = { rates: false, shares: false, comments: false, refreshVideo: false }
|
||||||
const { video, created } = await getOrCreateAPVideo({ videoObject: videoToCreateData, syncParam })
|
const { video, created } = await getOrCreateAPVideo({ videoObject: videoToCreateData, syncParam })
|
||||||
|
|
||||||
if (created && notify) Notifier.Instance.notifyOnNewVideoIfNeeded(video)
|
if (created && notify) Notifier.Instance.notifyOnNewVideoOrLiveIfNeeded(video)
|
||||||
|
|
||||||
return video
|
return video
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,11 +93,12 @@ export class APVideoUpdater extends APVideoAbstractBuilder {
|
||||||
|
|
||||||
// Notify our users?
|
// Notify our users?
|
||||||
if (this.wasPrivateVideo || this.wasUnlistedVideo) {
|
if (this.wasPrivateVideo || this.wasUnlistedVideo) {
|
||||||
Notifier.Instance.notifyOnNewVideoIfNeeded(videoUpdated)
|
Notifier.Instance.notifyOnNewVideoOrLiveIfNeeded(videoUpdated)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (videoUpdated.isLive && oldState !== videoUpdated.state) {
|
if (videoUpdated.isLive && oldState !== videoUpdated.state) {
|
||||||
PeerTubeSocket.Instance.sendVideoLiveNewState(videoUpdated)
|
PeerTubeSocket.Instance.sendVideoLiveNewState(videoUpdated)
|
||||||
|
Notifier.Instance.notifyOnNewVideoOrLiveIfNeeded(videoUpdated)
|
||||||
}
|
}
|
||||||
|
|
||||||
Hooks.runAction('action:activity-pub.remote-video.updated', { video: videoUpdated, videoAPObject: this.videoObject })
|
Hooks.runAction('action:activity-pub.remote-video.updated', { video: videoUpdated, videoAPObject: this.videoObject })
|
||||||
|
|
|
@ -23,5 +23,5 @@ async function doNotifyNewVideo (payload: NotifyPayload & { action: 'new-video'
|
||||||
const refreshedVideo = await VideoModel.loadFull(payload.videoUUID)
|
const refreshedVideo = await VideoModel.loadFull(payload.videoUUID)
|
||||||
if (!refreshedVideo) return
|
if (!refreshedVideo) return
|
||||||
|
|
||||||
Notifier.Instance.notifyOnNewVideoIfNeeded(refreshedVideo)
|
Notifier.Instance.notifyOnNewVideoOrLiveIfNeeded(refreshedVideo)
|
||||||
}
|
}
|
||||||
|
|
|
@ -303,7 +303,7 @@ async function afterImportSuccess (options: {
|
||||||
|
|
||||||
Notifier.Instance.notifyOnVideoAutoBlacklist(videoBlacklist)
|
Notifier.Instance.notifyOnVideoAutoBlacklist(videoBlacklist)
|
||||||
} else {
|
} else {
|
||||||
Notifier.Instance.notifyOnNewVideoIfNeeded(video)
|
Notifier.Instance.notifyOnNewVideoOrLiveIfNeeded(video)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate the storyboard in the job queue, and don't forget to federate an update after
|
// Generate the storyboard in the job queue, and don't forget to federate an update after
|
||||||
|
|
|
@ -35,6 +35,7 @@ import { computeResolutionsToTranscode } from '../transcoding/transcoding-resolu
|
||||||
import { LiveQuotaStore } from './live-quota-store.js'
|
import { LiveQuotaStore } from './live-quota-store.js'
|
||||||
import { cleanupAndDestroyPermanentLive, getLiveSegmentTime } from './live-utils.js'
|
import { cleanupAndDestroyPermanentLive, getLiveSegmentTime } from './live-utils.js'
|
||||||
import { MuxingSession } from './shared/index.js'
|
import { MuxingSession } from './shared/index.js'
|
||||||
|
import { Notifier } from '../notifier/notifier.js'
|
||||||
|
|
||||||
// Disable node media server logs
|
// Disable node media server logs
|
||||||
nodeMediaServerLogger.setLogType(0)
|
nodeMediaServerLogger.setLogType(0)
|
||||||
|
@ -417,6 +418,7 @@ class LiveManager {
|
||||||
logger.error('Cannot federate live video %s.', video.url, { err, ...localLTags })
|
logger.error('Cannot federate live video %s.', video.url, { err, ...localLTags })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Notifier.Instance.notifyOnNewVideoOrLiveIfNeeded(video)
|
||||||
PeerTubeSocket.Instance.sendVideoLiveNewState(video)
|
PeerTubeSocket.Instance.sendVideoLiveNewState(video)
|
||||||
|
|
||||||
Hooks.runAction('action:live.video.state.updated', { video })
|
Hooks.runAction('action:live.video.state.updated', { video })
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { UserNotificationSettingValue, UserNotificationSettingValueType } from '@peertube/peertube-models'
|
import { UserNotificationSettingValue, UserNotificationSettingValueType } from '@peertube/peertube-models'
|
||||||
import { MRegistration, MUser, MUserDefault } from '@server/types/models/user/index.js'
|
import { MRegistration, MUser, MUserDefault } from '@server/types/models/user/index.js'
|
||||||
import { MVideoBlacklistLightVideo, MVideoBlacklistVideo } from '@server/types/models/video/video-blacklist.js'
|
import { MVideoBlacklistLightVideo, MVideoBlacklistVideo } from '@server/types/models/video/video-blacklist.js'
|
||||||
import { logger } from '../../helpers/logger.js'
|
import { logger, loggerTagsFactory } from '../../helpers/logger.js'
|
||||||
import { CONFIG } from '../../initializers/config.js'
|
import { CONFIG } from '../../initializers/config.js'
|
||||||
import {
|
import {
|
||||||
MAbuseFull,
|
MAbuseFull,
|
||||||
|
@ -35,7 +35,7 @@ import {
|
||||||
NewCommentForVideoOwner,
|
NewCommentForVideoOwner,
|
||||||
NewPeerTubeVersionForAdmins,
|
NewPeerTubeVersionForAdmins,
|
||||||
NewPluginVersionForAdmins,
|
NewPluginVersionForAdmins,
|
||||||
NewVideoForSubscribers,
|
NewVideoOrLiveForSubscribers,
|
||||||
OwnedPublicationAfterAutoUnblacklist,
|
OwnedPublicationAfterAutoUnblacklist,
|
||||||
OwnedPublicationAfterScheduleUpdate,
|
OwnedPublicationAfterScheduleUpdate,
|
||||||
OwnedPublicationAfterTranscoding,
|
OwnedPublicationAfterTranscoding,
|
||||||
|
@ -44,10 +44,12 @@ import {
|
||||||
UnblacklistForOwner
|
UnblacklistForOwner
|
||||||
} from './shared/index.js'
|
} from './shared/index.js'
|
||||||
|
|
||||||
|
const lTags = loggerTagsFactory('notifier')
|
||||||
|
|
||||||
class Notifier {
|
class Notifier {
|
||||||
|
|
||||||
private readonly notificationModels = {
|
private readonly notificationModels = {
|
||||||
newVideo: [ NewVideoForSubscribers ],
|
newVideoOrLive: [ NewVideoOrLiveForSubscribers ],
|
||||||
publicationAfterTranscoding: [ OwnedPublicationAfterTranscoding ],
|
publicationAfterTranscoding: [ OwnedPublicationAfterTranscoding ],
|
||||||
publicationAfterScheduleUpdate: [ OwnedPublicationAfterScheduleUpdate ],
|
publicationAfterScheduleUpdate: [ OwnedPublicationAfterScheduleUpdate ],
|
||||||
publicationAfterAutoUnblacklist: [ OwnedPublicationAfterAutoUnblacklist ],
|
publicationAfterAutoUnblacklist: [ OwnedPublicationAfterAutoUnblacklist ],
|
||||||
|
@ -74,8 +76,10 @@ class Notifier {
|
||||||
private constructor () {
|
private constructor () {
|
||||||
}
|
}
|
||||||
|
|
||||||
notifyOnNewVideoIfNeeded (video: MVideoAccountLight): void {
|
notifyOnNewVideoOrLiveIfNeeded (video: MVideoAccountLight): void {
|
||||||
const models = this.notificationModels.newVideo
|
const models = this.notificationModels.newVideoOrLive
|
||||||
|
|
||||||
|
logger.debug('Notify on new video or live if needed', { video: video.url, ...lTags() })
|
||||||
|
|
||||||
this.sendNotifications(models, video)
|
this.sendNotifications(models, video)
|
||||||
.catch(err => logger.error('Cannot notify subscribers of new video %s.', video.url, { err }))
|
.catch(err => logger.error('Cannot notify subscribers of new video %s.', video.url, { err }))
|
||||||
|
@ -84,6 +88,8 @@ class Notifier {
|
||||||
notifyOnVideoPublishedAfterTranscoding (video: MVideoFullLight): void {
|
notifyOnVideoPublishedAfterTranscoding (video: MVideoFullLight): void {
|
||||||
const models = this.notificationModels.publicationAfterTranscoding
|
const models = this.notificationModels.publicationAfterTranscoding
|
||||||
|
|
||||||
|
logger.debug('Notify on published video after transcoding', { video: video.url, ...lTags() })
|
||||||
|
|
||||||
this.sendNotifications(models, video)
|
this.sendNotifications(models, video)
|
||||||
.catch(err => logger.error('Cannot notify owner that its video %s has been published after transcoding.', video.url, { err }))
|
.catch(err => logger.error('Cannot notify owner that its video %s has been published after transcoding.', video.url, { err }))
|
||||||
}
|
}
|
||||||
|
@ -91,6 +97,8 @@ class Notifier {
|
||||||
notifyOnVideoPublishedAfterScheduledUpdate (video: MVideoFullLight): void {
|
notifyOnVideoPublishedAfterScheduledUpdate (video: MVideoFullLight): void {
|
||||||
const models = this.notificationModels.publicationAfterScheduleUpdate
|
const models = this.notificationModels.publicationAfterScheduleUpdate
|
||||||
|
|
||||||
|
logger.debug('Notify on published video after scheduled update', { video: video.url, ...lTags() })
|
||||||
|
|
||||||
this.sendNotifications(models, video)
|
this.sendNotifications(models, video)
|
||||||
.catch(err => logger.error('Cannot notify owner that its video %s has been published after scheduled update.', video.url, { err }))
|
.catch(err => logger.error('Cannot notify owner that its video %s has been published after scheduled update.', video.url, { err }))
|
||||||
}
|
}
|
||||||
|
@ -98,6 +106,8 @@ class Notifier {
|
||||||
notifyOnVideoPublishedAfterRemovedFromAutoBlacklist (video: MVideoFullLight): void {
|
notifyOnVideoPublishedAfterRemovedFromAutoBlacklist (video: MVideoFullLight): void {
|
||||||
const models = this.notificationModels.publicationAfterAutoUnblacklist
|
const models = this.notificationModels.publicationAfterAutoUnblacklist
|
||||||
|
|
||||||
|
logger.debug('Notify on published video after being removed from auto blacklist', { video: video.url, ...lTags() })
|
||||||
|
|
||||||
this.sendNotifications(models, video)
|
this.sendNotifications(models, video)
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
logger.error('Cannot notify owner that its video %s has been published after removed from auto-blacklist.', video.url, { err })
|
logger.error('Cannot notify owner that its video %s has been published after removed from auto-blacklist.', video.url, { err })
|
||||||
|
@ -107,6 +117,8 @@ class Notifier {
|
||||||
notifyOnNewComment (comment: MCommentOwnerVideo): void {
|
notifyOnNewComment (comment: MCommentOwnerVideo): void {
|
||||||
const models = this.notificationModels.newComment
|
const models = this.notificationModels.newComment
|
||||||
|
|
||||||
|
logger.debug('Notify on new comment', { comment: comment.url, ...lTags() })
|
||||||
|
|
||||||
this.sendNotifications(models, comment)
|
this.sendNotifications(models, comment)
|
||||||
.catch(err => logger.error('Cannot notify of new comment %s.', comment.url, { err }))
|
.catch(err => logger.error('Cannot notify of new comment %s.', comment.url, { err }))
|
||||||
}
|
}
|
||||||
|
@ -114,6 +126,8 @@ class Notifier {
|
||||||
notifyOnNewAbuse (payload: NewAbusePayload): void {
|
notifyOnNewAbuse (payload: NewAbusePayload): void {
|
||||||
const models = this.notificationModels.newAbuse
|
const models = this.notificationModels.newAbuse
|
||||||
|
|
||||||
|
logger.debug('Notify on new abuse', { abuse: payload.abuseInstance.id, ...lTags() })
|
||||||
|
|
||||||
this.sendNotifications(models, payload)
|
this.sendNotifications(models, payload)
|
||||||
.catch(err => logger.error('Cannot notify of new abuse %d.', payload.abuseInstance.id, { err }))
|
.catch(err => logger.error('Cannot notify of new abuse %d.', payload.abuseInstance.id, { err }))
|
||||||
}
|
}
|
||||||
|
@ -121,6 +135,8 @@ class Notifier {
|
||||||
notifyOnVideoAutoBlacklist (videoBlacklist: MVideoBlacklistLightVideo): void {
|
notifyOnVideoAutoBlacklist (videoBlacklist: MVideoBlacklistLightVideo): void {
|
||||||
const models = this.notificationModels.newAutoBlacklist
|
const models = this.notificationModels.newAutoBlacklist
|
||||||
|
|
||||||
|
logger.debug('Notify on video auto blacklist', { video: videoBlacklist?.Video?.url, ...lTags() })
|
||||||
|
|
||||||
this.sendNotifications(models, videoBlacklist)
|
this.sendNotifications(models, videoBlacklist)
|
||||||
.catch(err => logger.error('Cannot notify of auto-blacklist of video %s.', videoBlacklist.Video.url, { err }))
|
.catch(err => logger.error('Cannot notify of auto-blacklist of video %s.', videoBlacklist.Video.url, { err }))
|
||||||
}
|
}
|
||||||
|
@ -128,6 +144,8 @@ class Notifier {
|
||||||
notifyOnVideoBlacklist (videoBlacklist: MVideoBlacklistVideo): void {
|
notifyOnVideoBlacklist (videoBlacklist: MVideoBlacklistVideo): void {
|
||||||
const models = this.notificationModels.newBlacklist
|
const models = this.notificationModels.newBlacklist
|
||||||
|
|
||||||
|
logger.debug('Notify on video manual blacklist', { video: videoBlacklist?.Video?.url, ...lTags() })
|
||||||
|
|
||||||
this.sendNotifications(models, videoBlacklist)
|
this.sendNotifications(models, videoBlacklist)
|
||||||
.catch(err => logger.error('Cannot notify video owner of new video blacklist of %s.', videoBlacklist.Video.url, { err }))
|
.catch(err => logger.error('Cannot notify video owner of new video blacklist of %s.', videoBlacklist.Video.url, { err }))
|
||||||
}
|
}
|
||||||
|
@ -135,6 +153,8 @@ class Notifier {
|
||||||
notifyOnVideoUnblacklist (video: MVideoFullLight): void {
|
notifyOnVideoUnblacklist (video: MVideoFullLight): void {
|
||||||
const models = this.notificationModels.unblacklist
|
const models = this.notificationModels.unblacklist
|
||||||
|
|
||||||
|
logger.debug('Notify on video unblacklist', { video: video.url, ...lTags() })
|
||||||
|
|
||||||
this.sendNotifications(models, video)
|
this.sendNotifications(models, video)
|
||||||
.catch(err => logger.error('Cannot notify video owner of unblacklist of %s.', video.url, { err }))
|
.catch(err => logger.error('Cannot notify video owner of unblacklist of %s.', video.url, { err }))
|
||||||
}
|
}
|
||||||
|
@ -142,6 +162,8 @@ class Notifier {
|
||||||
notifyOnFinishedVideoImport (payload: ImportFinishedForOwnerPayload): void {
|
notifyOnFinishedVideoImport (payload: ImportFinishedForOwnerPayload): void {
|
||||||
const models = this.notificationModels.importFinished
|
const models = this.notificationModels.importFinished
|
||||||
|
|
||||||
|
logger.debug('Notify on finished video import', { import: payload.videoImport.getTargetIdentifier(), ...lTags() })
|
||||||
|
|
||||||
this.sendNotifications(models, payload)
|
this.sendNotifications(models, payload)
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
logger.error('Cannot notify owner that its video import %s is finished.', payload.videoImport.getTargetIdentifier(), { err })
|
logger.error('Cannot notify owner that its video import %s is finished.', payload.videoImport.getTargetIdentifier(), { err })
|
||||||
|
@ -151,6 +173,8 @@ class Notifier {
|
||||||
notifyOnNewDirectRegistration (user: MUserDefault): void {
|
notifyOnNewDirectRegistration (user: MUserDefault): void {
|
||||||
const models = this.notificationModels.directRegistration
|
const models = this.notificationModels.directRegistration
|
||||||
|
|
||||||
|
logger.debug('Notify on new direct registration', { user: user.username, ...lTags() })
|
||||||
|
|
||||||
this.sendNotifications(models, user)
|
this.sendNotifications(models, user)
|
||||||
.catch(err => logger.error('Cannot notify moderators of new user registration (%s).', user.username, { err }))
|
.catch(err => logger.error('Cannot notify moderators of new user registration (%s).', user.username, { err }))
|
||||||
}
|
}
|
||||||
|
@ -158,6 +182,8 @@ class Notifier {
|
||||||
notifyOnNewRegistrationRequest (registration: MRegistration): void {
|
notifyOnNewRegistrationRequest (registration: MRegistration): void {
|
||||||
const models = this.notificationModels.registrationRequest
|
const models = this.notificationModels.registrationRequest
|
||||||
|
|
||||||
|
logger.debug('Notify on new registration request', { registration: registration.username, ...lTags() })
|
||||||
|
|
||||||
this.sendNotifications(models, registration)
|
this.sendNotifications(models, registration)
|
||||||
.catch(err => logger.error('Cannot notify moderators of new registration request (%s).', registration.username, { err }))
|
.catch(err => logger.error('Cannot notify moderators of new registration request (%s).', registration.username, { err }))
|
||||||
}
|
}
|
||||||
|
@ -165,20 +191,22 @@ class Notifier {
|
||||||
notifyOfNewUserFollow (actorFollow: MActorFollowFull): void {
|
notifyOfNewUserFollow (actorFollow: MActorFollowFull): void {
|
||||||
const models = this.notificationModels.userFollow
|
const models = this.notificationModels.userFollow
|
||||||
|
|
||||||
|
const following = actorFollow?.ActorFollowing?.VideoChannel?.getDisplayName()
|
||||||
|
const follower = actorFollow?.ActorFollower?.Account?.getDisplayName()
|
||||||
|
|
||||||
|
logger.debug('Notify on new user follow', { following, follower, ...lTags() })
|
||||||
|
|
||||||
this.sendNotifications(models, actorFollow)
|
this.sendNotifications(models, actorFollow)
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
logger.error(
|
logger.error('Cannot notify owner of channel %s of a new follow by %s.', following, follower, { err })
|
||||||
'Cannot notify owner of channel %s of a new follow by %s.',
|
|
||||||
actorFollow.ActorFollowing.VideoChannel.getDisplayName(),
|
|
||||||
actorFollow.ActorFollower.Account.getDisplayName(),
|
|
||||||
{ err }
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
notifyOfNewInstanceFollow (actorFollow: MActorFollowFull): void {
|
notifyOfNewInstanceFollow (actorFollow: MActorFollowFull): void {
|
||||||
const models = this.notificationModels.instanceFollow
|
const models = this.notificationModels.instanceFollow
|
||||||
|
|
||||||
|
logger.debug('Notify on new instance follow', { follower: actorFollow.ActorFollower.url, ...lTags() })
|
||||||
|
|
||||||
this.sendNotifications(models, actorFollow)
|
this.sendNotifications(models, actorFollow)
|
||||||
.catch(err => logger.error('Cannot notify administrators of new follower %s.', actorFollow.ActorFollower.url, { err }))
|
.catch(err => logger.error('Cannot notify administrators of new follower %s.', actorFollow.ActorFollower.url, { err }))
|
||||||
}
|
}
|
||||||
|
@ -186,6 +214,8 @@ class Notifier {
|
||||||
notifyOfAutoInstanceFollowing (actorFollow: MActorFollowFull): void {
|
notifyOfAutoInstanceFollowing (actorFollow: MActorFollowFull): void {
|
||||||
const models = this.notificationModels.autoInstanceFollow
|
const models = this.notificationModels.autoInstanceFollow
|
||||||
|
|
||||||
|
logger.debug('Notify on new instance auto following', { following: actorFollow.ActorFollowing.url, ...lTags() })
|
||||||
|
|
||||||
this.sendNotifications(models, actorFollow)
|
this.sendNotifications(models, actorFollow)
|
||||||
.catch(err => logger.error('Cannot notify administrators of auto instance following %s.', actorFollow.ActorFollowing.url, { err }))
|
.catch(err => logger.error('Cannot notify administrators of auto instance following %s.', actorFollow.ActorFollowing.url, { err }))
|
||||||
}
|
}
|
||||||
|
@ -193,6 +223,8 @@ class Notifier {
|
||||||
notifyOnAbuseStateChange (abuse: MAbuseFull): void {
|
notifyOnAbuseStateChange (abuse: MAbuseFull): void {
|
||||||
const models = this.notificationModels.abuseStateChange
|
const models = this.notificationModels.abuseStateChange
|
||||||
|
|
||||||
|
logger.debug('Notify on abuse state change', { abuse: abuse.id, ...lTags() })
|
||||||
|
|
||||||
this.sendNotifications(models, abuse)
|
this.sendNotifications(models, abuse)
|
||||||
.catch(err => logger.error('Cannot notify of abuse %d state change.', abuse.id, { err }))
|
.catch(err => logger.error('Cannot notify of abuse %d state change.', abuse.id, { err }))
|
||||||
}
|
}
|
||||||
|
@ -200,6 +232,8 @@ class Notifier {
|
||||||
notifyOnAbuseMessage (abuse: MAbuseFull, message: MAbuseMessage): void {
|
notifyOnAbuseMessage (abuse: MAbuseFull, message: MAbuseMessage): void {
|
||||||
const models = this.notificationModels.newAbuseMessage
|
const models = this.notificationModels.newAbuseMessage
|
||||||
|
|
||||||
|
logger.debug('Notify on abuse message', { abuse: abuse.id, message, ...lTags() })
|
||||||
|
|
||||||
this.sendNotifications(models, { abuse, message })
|
this.sendNotifications(models, { abuse, message })
|
||||||
.catch(err => logger.error('Cannot notify on new abuse %d message.', abuse.id, { err }))
|
.catch(err => logger.error('Cannot notify on new abuse %d message.', abuse.id, { err }))
|
||||||
}
|
}
|
||||||
|
@ -207,13 +241,17 @@ class Notifier {
|
||||||
notifyOfNewPeerTubeVersion (application: MApplication, latestVersion: string) {
|
notifyOfNewPeerTubeVersion (application: MApplication, latestVersion: string) {
|
||||||
const models = this.notificationModels.newPeertubeVersion
|
const models = this.notificationModels.newPeertubeVersion
|
||||||
|
|
||||||
|
logger.debug('Notify on new peertube version', { currentVersion: application.version, latestVersion, ...lTags() })
|
||||||
|
|
||||||
this.sendNotifications(models, { application, latestVersion })
|
this.sendNotifications(models, { application, latestVersion })
|
||||||
.catch(err => logger.error('Cannot notify on new PeerTubeb version %s.', latestVersion, { err }))
|
.catch(err => logger.error('Cannot notify on new PeerTube version %s.', latestVersion, { err }))
|
||||||
}
|
}
|
||||||
|
|
||||||
notifyOfNewPluginVersion (plugin: MPlugin) {
|
notifyOfNewPluginVersion (plugin: MPlugin) {
|
||||||
const models = this.notificationModels.newPluginVersion
|
const models = this.notificationModels.newPluginVersion
|
||||||
|
|
||||||
|
logger.debug('Notify on new plugin version', { plugin: plugin.name, ...lTags() })
|
||||||
|
|
||||||
this.sendNotifications(models, plugin)
|
this.sendNotifications(models, plugin)
|
||||||
.catch(err => logger.error('Cannot notify on new plugin version %s.', plugin.name, { err }))
|
.catch(err => logger.error('Cannot notify on new plugin version %s.', plugin.name, { err }))
|
||||||
}
|
}
|
||||||
|
@ -221,6 +259,8 @@ class Notifier {
|
||||||
notifyOfFinishedVideoStudioEdition (video: MVideoFullLight) {
|
notifyOfFinishedVideoStudioEdition (video: MVideoFullLight) {
|
||||||
const models = this.notificationModels.videoStudioEditionFinished
|
const models = this.notificationModels.videoStudioEditionFinished
|
||||||
|
|
||||||
|
logger.debug('Notify on finished video studio edition', { video: video.url, ...lTags() })
|
||||||
|
|
||||||
this.sendNotifications(models, video)
|
this.sendNotifications(models, video)
|
||||||
.catch(err => logger.error('Cannot notify on finished studio edition %s.', video.url, { err }))
|
.catch(err => logger.error('Cannot notify on finished studio edition %s.', video.url, { err }))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
export * from './new-video-for-subscribers.js'
|
export * from './new-video-or-live-for-subscribers.js'
|
||||||
export * from './import-finished-for-owner.js'
|
export * from './import-finished-for-owner.js'
|
||||||
export * from './owned-publication-after-auto-unblacklist.js'
|
export * from './owned-publication-after-auto-unblacklist.js'
|
||||||
export * from './owned-publication-after-schedule-update.js'
|
export * from './owned-publication-after-schedule-update.js'
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { MUserWithNotificationSetting, MVideoAccountLight, UserNotificationModel
|
||||||
import { UserNotificationType, VideoPrivacy, VideoState } from '@peertube/peertube-models'
|
import { UserNotificationType, VideoPrivacy, VideoState } from '@peertube/peertube-models'
|
||||||
import { AbstractNotification } from '../common/abstract-notification.js'
|
import { AbstractNotification } from '../common/abstract-notification.js'
|
||||||
|
|
||||||
export class NewVideoForSubscribers extends AbstractNotification <MVideoAccountLight> {
|
export class NewVideoOrLiveForSubscribers extends AbstractNotification <MVideoAccountLight> {
|
||||||
private users: MUserWithNotificationSetting[]
|
private users: MUserWithNotificationSetting[]
|
||||||
|
|
||||||
async prepare () {
|
async prepare () {
|
||||||
|
@ -32,7 +32,10 @@ export class NewVideoForSubscribers extends AbstractNotification <MVideoAccountL
|
||||||
|
|
||||||
createNotification (user: MUserWithNotificationSetting) {
|
createNotification (user: MUserWithNotificationSetting) {
|
||||||
const notification = UserNotificationModel.build<UserNotificationModelForApi>({
|
const notification = UserNotificationModel.build<UserNotificationModelForApi>({
|
||||||
type: UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION,
|
type: this.payload.isLive
|
||||||
|
? UserNotificationType.NEW_LIVE_FROM_SUBSCRIPTION
|
||||||
|
: UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION,
|
||||||
|
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
videoId: this.payload.id
|
videoId: this.payload.id
|
||||||
})
|
})
|
||||||
|
@ -41,10 +44,18 @@ export class NewVideoForSubscribers extends AbstractNotification <MVideoAccountL
|
||||||
return notification
|
return notification
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
createEmail (to: string) {
|
createEmail (to: string) {
|
||||||
const channelName = this.payload.VideoChannel.getDisplayName()
|
const channelName = this.payload.VideoChannel.getDisplayName()
|
||||||
const videoUrl = WEBSERVER.URL + this.payload.getWatchStaticPath()
|
const videoUrl = WEBSERVER.URL + this.payload.getWatchStaticPath()
|
||||||
|
|
||||||
|
if (this.payload.isLive) return this.createLiveEmail(to, channelName, videoUrl)
|
||||||
|
|
||||||
|
return this.createVideoEmail(to, channelName, videoUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
private createVideoEmail (to: string, channelName: string, videoUrl: string) {
|
||||||
return {
|
return {
|
||||||
to,
|
to,
|
||||||
subject: channelName + ' just published a new video',
|
subject: channelName + ' just published a new video',
|
||||||
|
@ -58,4 +69,19 @@ export class NewVideoForSubscribers extends AbstractNotification <MVideoAccountL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private createLiveEmail (to: string, channelName: string, videoUrl: string) {
|
||||||
|
return {
|
||||||
|
to,
|
||||||
|
subject: channelName + ' is live streaming',
|
||||||
|
text: `Your subscription ${channelName} is live streaming in "${this.payload.name}".`,
|
||||||
|
locals: {
|
||||||
|
title: 'New content ',
|
||||||
|
action: {
|
||||||
|
text: 'View video',
|
||||||
|
url: videoUrl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -110,7 +110,7 @@ async function unblacklistVideo (videoBlacklist: MVideoBlacklist, video: MVideoF
|
||||||
|
|
||||||
// Delete on object so new video notifications will send
|
// Delete on object so new video notifications will send
|
||||||
delete video.VideoBlacklist
|
delete video.VideoBlacklist
|
||||||
Notifier.Instance.notifyOnNewVideoIfNeeded(video)
|
Notifier.Instance.notifyOnNewVideoOrLiveIfNeeded(video)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -185,7 +185,7 @@ async function moveToPublishedState (options: {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isNewVideo) {
|
if (isNewVideo) {
|
||||||
Notifier.Instance.notifyOnNewVideoIfNeeded(video)
|
Notifier.Instance.notifyOnNewVideoOrLiveIfNeeded(video)
|
||||||
|
|
||||||
if (previousState === VideoState.TO_TRANSCODE) {
|
if (previousState === VideoState.TO_TRANSCODE) {
|
||||||
Notifier.Instance.notifyOnVideoPublishedAfterTranscoding(video)
|
Notifier.Instance.notifyOnVideoPublishedAfterTranscoding(video)
|
||||||
|
|
Loading…
Reference in New Issue