Skip to content

Commit

Permalink
Add notification on subscription live stream
Browse files Browse the repository at this point in the history
  • Loading branch information
Chocobozzz committed Feb 15, 2024
1 parent 4300cc1 commit a012d6c
Show file tree
Hide file tree
Showing 19 changed files with 273 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export class MyAccountNotificationPreferencesComponent implements OnInit {
private notifier: Notifier
) {
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`,
abuseAsModerator: $localize`New abuse`,
videoAutoBlacklistAsModerator: $localize`An automatically blocked video is awaiting review`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ export class UserNotification implements UserNotificationServer {

switch (this.type) {
case UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION:
case UserNotificationType.NEW_LIVE_FROM_SUBSCRIPTION:
this.videoUrl = this.buildVideoUrl(this.video)
break

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,21 @@

<ng-container [ngSwitch]="notification.type">
<ng-container *ngSwitchCase="1"> <!-- UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION -->
<ng-container *ngIf="notification.video; then hasVideo; else noVideo"></ng-container>

<ng-template #hasVideo>
@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 }} published a new video: <a (click)="markAsRead(notification)" [routerLink]="notification.videoUrl">{{ notification.video.name }}</a>
</div>
</ng-template>

<ng-template #noVideo>
} @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-template>
}
</ng-container>

<ng-container *ngSwitchCase="5"> <!-- UserNotificationType.UNBLACKLIST_ON_MY_VIDEO -->
Expand Down Expand Up @@ -224,6 +220,24 @@
</div>
</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>
<my-global-icon iconName="alert" aria-hidden="true"></my-global-icon>

Expand Down
4 changes: 3 additions & 1 deletion packages/models/src/users/user-notification.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ export const UserNotificationType = {

MY_VIDEO_STUDIO_EDITION_FINISHED: 19,

NEW_USER_REGISTRATION_REQUEST: 20
NEW_USER_REGISTRATION_REQUEST: 20,

NEW_LIVE_FROM_SUBSCRIPTION: 21
} as const

export type UserNotificationType_Type = typeof UserNotificationType[keyof typeof UserNotificationType]
Expand Down
5 changes: 3 additions & 2 deletions packages/server-commands/src/videos/live-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,12 +119,13 @@ export class LiveCommand extends AbstractCommand {
}

async quickCreate (options: OverrideCommandOptions & {
name: string
saveReplay: boolean
permanentLive: boolean
privacy?: VideoPrivacyType
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
? { privacy: VideoPrivacy.PRIVATE }
Expand All @@ -134,7 +135,7 @@ export class LiveCommand extends AbstractCommand {
...options,

fields: {
name: 'live',
name,
permanentLive,
saveReplay,
replaySettings,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
checkNewInstanceFollower,
checkAutoInstanceFollowing,
checkVideoAutoBlacklistForModerators,
checkVideoIsPublished,
checkMyVideoIsPublished,
checkNewVideoFromSubscription
} from '@tests/shared/notifications.js'

Expand Down Expand Up @@ -487,7 +487,7 @@ describe('Test moderation notifications', function () {
it('Should not send video publish notification if auto-blacklisted', async function () {
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 () {
Expand Down Expand Up @@ -576,7 +576,7 @@ describe('Test moderation notifications', function () {
const { shortUUID } = await servers[0].videos.upload({ token: userToken1, attributes })

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({ ...adminBaseParamsServer2, videoName: name, shortUUID, checkType: 'absence' })
})
Expand Down
100 changes: 89 additions & 11 deletions packages/tests/src/api/notifications/user-notifications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ import {
prepareNotificationsTest,
CheckerBaseParams,
checkNewVideoFromSubscription,
checkVideoIsPublished,
checkMyVideoIsPublished,
checkVideoStudioEditionIsFinished,
checkMyVideoImportIsFinished,
checkNewActorFollow
checkNewActorFollow,
checkNewLiveFromSubscription,
waitUntilNotification
} from '@tests/shared/notifications.js'
import { FIXTURE_URLS } from '@tests/shared/tests.js'
import { uploadRandomVideoOnServers } from '@tests/shared/videos.js'
Expand Down Expand Up @@ -209,6 +211,82 @@ describe('Test user notifications', function () {

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 () {
Expand All @@ -229,7 +307,7 @@ describe('Test user notifications', function () {
const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 1)
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 () {
Expand All @@ -250,7 +328,7 @@ describe('Test user notifications', function () {
const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 2, { waitTranscoding: true, fixture: 'video_short_240p.mp4' })
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 () {
Expand All @@ -259,7 +337,7 @@ describe('Test user notifications', function () {
const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 2, { waitTranscoding: true })
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 () {
Expand All @@ -277,7 +355,7 @@ describe('Test user notifications', function () {
const { video } = await servers[1].imports.importVideo({ attributes })

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 () {
Expand All @@ -296,7 +374,7 @@ describe('Test user notifications', function () {
const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 2, data)

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 () {
Expand All @@ -314,7 +392,7 @@ describe('Test user notifications', function () {
const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 2, data)

await wait(6000)
await checkVideoIsPublished({ ...baseParams, videoName: name, shortUUID, checkType: 'absence' })
await checkMyVideoIsPublished({ ...baseParams, videoName: name, shortUUID, checkType: 'absence' })
})
})

Expand Down Expand Up @@ -354,7 +432,7 @@ describe('Test user notifications', function () {
await servers[1].live.waitUntilReplacedByReplay({ videoId: shortUUID })

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 () {
Expand Down Expand Up @@ -386,7 +464,7 @@ describe('Test user notifications', function () {
const video = await findExternalSavedVideo(servers[1], liveDetails)
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' })
})
})

Expand All @@ -408,7 +486,7 @@ describe('Test user notifications', function () {
const { name, shortUUID, id } = await uploadRandomVideoOnServers(servers, 2, { waitTranscoding: true })

await waitJobs(servers)
await checkVideoIsPublished({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
await checkMyVideoIsPublished({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })

const tasks: VideoStudioTask[] = [
{
Expand Down
Loading

0 comments on commit a012d6c

Please sign in to comment.