diff options
Diffstat (limited to 'server/lib')
-rw-r--r-- | server/lib/emailer.ts | 61 | ||||
-rw-r--r-- | server/lib/job-queue/handlers/video-file.ts | 24 | ||||
-rw-r--r-- | server/lib/job-queue/handlers/video-import.ts | 3 | ||||
-rw-r--r-- | server/lib/notifier.ts | 76 | ||||
-rw-r--r-- | server/lib/schedulers/update-videos-scheduler.ts | 14 | ||||
-rw-r--r-- | server/lib/user.ts | 2 |
6 files changed, 170 insertions, 10 deletions
diff --git a/server/lib/emailer.ts b/server/lib/emailer.ts index d766e655b..6dc8f2adf 100644 --- a/server/lib/emailer.ts +++ b/server/lib/emailer.ts | |||
@@ -10,6 +10,7 @@ import { readFileSync } from 'fs-extra' | |||
10 | import { VideoCommentModel } from '../models/video/video-comment' | 10 | import { VideoCommentModel } from '../models/video/video-comment' |
11 | import { VideoAbuseModel } from '../models/video/video-abuse' | 11 | import { VideoAbuseModel } from '../models/video/video-abuse' |
12 | import { VideoBlacklistModel } from '../models/video/video-blacklist' | 12 | import { VideoBlacklistModel } from '../models/video/video-blacklist' |
13 | import { VideoImportModel } from '../models/video/video-import' | ||
13 | 14 | ||
14 | class Emailer { | 15 | class Emailer { |
15 | 16 | ||
@@ -102,6 +103,66 @@ class Emailer { | |||
102 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) | 103 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) |
103 | } | 104 | } |
104 | 105 | ||
106 | myVideoPublishedNotification (to: string[], video: VideoModel) { | ||
107 | const videoUrl = CONFIG.WEBSERVER.URL + video.getWatchStaticPath() | ||
108 | |||
109 | const text = `Hi dear user,\n\n` + | ||
110 | `Your video ${video.name} has been published.` + | ||
111 | `\n\n` + | ||
112 | `You can view it on ${videoUrl} ` + | ||
113 | `\n\n` + | ||
114 | `Cheers,\n` + | ||
115 | `PeerTube.` | ||
116 | |||
117 | const emailPayload: EmailPayload = { | ||
118 | to, | ||
119 | subject: `Your video ${video.name} is published`, | ||
120 | text | ||
121 | } | ||
122 | |||
123 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) | ||
124 | } | ||
125 | |||
126 | myVideoImportSuccessNotification (to: string[], videoImport: VideoImportModel) { | ||
127 | const videoUrl = CONFIG.WEBSERVER.URL + videoImport.Video.getWatchStaticPath() | ||
128 | |||
129 | const text = `Hi dear user,\n\n` + | ||
130 | `Your video import ${videoImport.getTargetIdentifier()} is finished.` + | ||
131 | `\n\n` + | ||
132 | `You can view the imported video on ${videoUrl} ` + | ||
133 | `\n\n` + | ||
134 | `Cheers,\n` + | ||
135 | `PeerTube.` | ||
136 | |||
137 | const emailPayload: EmailPayload = { | ||
138 | to, | ||
139 | subject: `Your video import ${videoImport.getTargetIdentifier()} is finished`, | ||
140 | text | ||
141 | } | ||
142 | |||
143 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) | ||
144 | } | ||
145 | |||
146 | myVideoImportErrorNotification (to: string[], videoImport: VideoImportModel) { | ||
147 | const importUrl = CONFIG.WEBSERVER.URL + '/my-account/video-imports' | ||
148 | |||
149 | const text = `Hi dear user,\n\n` + | ||
150 | `Your video import ${videoImport.getTargetIdentifier()} encountered an error.` + | ||
151 | `\n\n` + | ||
152 | `See your videos import dashboard for more information: ${importUrl}` + | ||
153 | `\n\n` + | ||
154 | `Cheers,\n` + | ||
155 | `PeerTube.` | ||
156 | |||
157 | const emailPayload: EmailPayload = { | ||
158 | to, | ||
159 | subject: `Your video import ${videoImport.getTargetIdentifier()} encountered an error`, | ||
160 | text | ||
161 | } | ||
162 | |||
163 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) | ||
164 | } | ||
165 | |||
105 | addNewCommentOnMyVideoNotification (to: string[], comment: VideoCommentModel) { | 166 | addNewCommentOnMyVideoNotification (to: string[], comment: VideoCommentModel) { |
106 | const accountName = comment.Account.getDisplayName() | 167 | const accountName = comment.Account.getDisplayName() |
107 | const video = comment.Video | 168 | const video = comment.Video |
diff --git a/server/lib/job-queue/handlers/video-file.ts b/server/lib/job-queue/handlers/video-file.ts index 480d324dc..593e43cc5 100644 --- a/server/lib/job-queue/handlers/video-file.ts +++ b/server/lib/job-queue/handlers/video-file.ts | |||
@@ -68,17 +68,17 @@ async function processVideoFile (job: Bull.Job) { | |||
68 | async function onVideoFileTranscoderOrImportSuccess (video: VideoModel) { | 68 | async function onVideoFileTranscoderOrImportSuccess (video: VideoModel) { |
69 | if (video === undefined) return undefined | 69 | if (video === undefined) return undefined |
70 | 70 | ||
71 | const { videoDatabase, isNewVideo } = await sequelizeTypescript.transaction(async t => { | 71 | const { videoDatabase, videoPublished } = await sequelizeTypescript.transaction(async t => { |
72 | // Maybe the video changed in database, refresh it | 72 | // Maybe the video changed in database, refresh it |
73 | let videoDatabase = await VideoModel.loadAndPopulateAccountAndServerAndTags(video.uuid, t) | 73 | let videoDatabase = await VideoModel.loadAndPopulateAccountAndServerAndTags(video.uuid, t) |
74 | // Video does not exist anymore | 74 | // Video does not exist anymore |
75 | if (!videoDatabase) return undefined | 75 | if (!videoDatabase) return undefined |
76 | 76 | ||
77 | let isNewVideo = false | 77 | let videoPublished = false |
78 | 78 | ||
79 | // We transcoded the video file in another format, now we can publish it | 79 | // We transcoded the video file in another format, now we can publish it |
80 | if (videoDatabase.state !== VideoState.PUBLISHED) { | 80 | if (videoDatabase.state !== VideoState.PUBLISHED) { |
81 | isNewVideo = true | 81 | videoPublished = true |
82 | 82 | ||
83 | videoDatabase.state = VideoState.PUBLISHED | 83 | videoDatabase.state = VideoState.PUBLISHED |
84 | videoDatabase.publishedAt = new Date() | 84 | videoDatabase.publishedAt = new Date() |
@@ -86,12 +86,15 @@ async function onVideoFileTranscoderOrImportSuccess (video: VideoModel) { | |||
86 | } | 86 | } |
87 | 87 | ||
88 | // If the video was not published, we consider it is a new one for other instances | 88 | // If the video was not published, we consider it is a new one for other instances |
89 | await federateVideoIfNeeded(videoDatabase, isNewVideo, t) | 89 | await federateVideoIfNeeded(videoDatabase, videoPublished, t) |
90 | 90 | ||
91 | return { videoDatabase, isNewVideo } | 91 | return { videoDatabase, videoPublished } |
92 | }) | 92 | }) |
93 | 93 | ||
94 | if (isNewVideo) Notifier.Instance.notifyOnNewVideo(videoDatabase) | 94 | if (videoPublished) { |
95 | Notifier.Instance.notifyOnNewVideo(videoDatabase) | ||
96 | Notifier.Instance.notifyOnPendingVideoPublished(videoDatabase) | ||
97 | } | ||
95 | } | 98 | } |
96 | 99 | ||
97 | async function onVideoFileOptimizerSuccess (videoArg: VideoModel, isNewVideo: boolean) { | 100 | async function onVideoFileOptimizerSuccess (videoArg: VideoModel, isNewVideo: boolean) { |
@@ -100,7 +103,7 @@ async function onVideoFileOptimizerSuccess (videoArg: VideoModel, isNewVideo: bo | |||
100 | // Outside the transaction (IO on disk) | 103 | // Outside the transaction (IO on disk) |
101 | const { videoFileResolution } = await videoArg.getOriginalFileResolution() | 104 | const { videoFileResolution } = await videoArg.getOriginalFileResolution() |
102 | 105 | ||
103 | const videoDatabase = await sequelizeTypescript.transaction(async t => { | 106 | const { videoDatabase, videoPublished } = await sequelizeTypescript.transaction(async t => { |
104 | // Maybe the video changed in database, refresh it | 107 | // Maybe the video changed in database, refresh it |
105 | let videoDatabase = await VideoModel.loadAndPopulateAccountAndServerAndTags(videoArg.uuid, t) | 108 | let videoDatabase = await VideoModel.loadAndPopulateAccountAndServerAndTags(videoArg.uuid, t) |
106 | // Video does not exist anymore | 109 | // Video does not exist anymore |
@@ -113,6 +116,8 @@ async function onVideoFileOptimizerSuccess (videoArg: VideoModel, isNewVideo: bo | |||
113 | { resolutions: resolutionsEnabled } | 116 | { resolutions: resolutionsEnabled } |
114 | ) | 117 | ) |
115 | 118 | ||
119 | let videoPublished = false | ||
120 | |||
116 | if (resolutionsEnabled.length !== 0) { | 121 | if (resolutionsEnabled.length !== 0) { |
117 | const tasks: Bluebird<Bull.Job<any>>[] = [] | 122 | const tasks: Bluebird<Bull.Job<any>>[] = [] |
118 | 123 | ||
@@ -130,6 +135,8 @@ async function onVideoFileOptimizerSuccess (videoArg: VideoModel, isNewVideo: bo | |||
130 | 135 | ||
131 | logger.info('Transcoding jobs created for uuid %s.', videoDatabase.uuid, { resolutionsEnabled }) | 136 | logger.info('Transcoding jobs created for uuid %s.', videoDatabase.uuid, { resolutionsEnabled }) |
132 | } else { | 137 | } else { |
138 | videoPublished = true | ||
139 | |||
133 | // No transcoding to do, it's now published | 140 | // No transcoding to do, it's now published |
134 | videoDatabase.state = VideoState.PUBLISHED | 141 | videoDatabase.state = VideoState.PUBLISHED |
135 | videoDatabase = await videoDatabase.save({ transaction: t }) | 142 | videoDatabase = await videoDatabase.save({ transaction: t }) |
@@ -139,10 +146,11 @@ async function onVideoFileOptimizerSuccess (videoArg: VideoModel, isNewVideo: bo | |||
139 | 146 | ||
140 | await federateVideoIfNeeded(videoDatabase, isNewVideo, t) | 147 | await federateVideoIfNeeded(videoDatabase, isNewVideo, t) |
141 | 148 | ||
142 | return videoDatabase | 149 | return { videoDatabase, videoPublished } |
143 | }) | 150 | }) |
144 | 151 | ||
145 | if (isNewVideo) Notifier.Instance.notifyOnNewVideo(videoDatabase) | 152 | if (isNewVideo) Notifier.Instance.notifyOnNewVideo(videoDatabase) |
153 | if (videoPublished) Notifier.Instance.notifyOnPendingVideoPublished(videoDatabase) | ||
146 | } | 154 | } |
147 | 155 | ||
148 | // --------------------------------------------------------------------------- | 156 | // --------------------------------------------------------------------------- |
diff --git a/server/lib/job-queue/handlers/video-import.ts b/server/lib/job-queue/handlers/video-import.ts index 29cd1198c..12004dcd7 100644 --- a/server/lib/job-queue/handlers/video-import.ts +++ b/server/lib/job-queue/handlers/video-import.ts | |||
@@ -197,6 +197,7 @@ async function processFile (downloader: () => Promise<string>, videoImport: Vide | |||
197 | }) | 197 | }) |
198 | 198 | ||
199 | Notifier.Instance.notifyOnNewVideo(videoImportUpdated.Video) | 199 | Notifier.Instance.notifyOnNewVideo(videoImportUpdated.Video) |
200 | Notifier.Instance.notifyOnFinishedVideoImport(videoImportUpdated, true) | ||
200 | 201 | ||
201 | // Create transcoding jobs? | 202 | // Create transcoding jobs? |
202 | if (videoImportUpdated.Video.state === VideoState.TO_TRANSCODE) { | 203 | if (videoImportUpdated.Video.state === VideoState.TO_TRANSCODE) { |
@@ -220,6 +221,8 @@ async function processFile (downloader: () => Promise<string>, videoImport: Vide | |||
220 | videoImport.state = VideoImportState.FAILED | 221 | videoImport.state = VideoImportState.FAILED |
221 | await videoImport.save() | 222 | await videoImport.save() |
222 | 223 | ||
224 | Notifier.Instance.notifyOnFinishedVideoImport(videoImport, false) | ||
225 | |||
223 | throw err | 226 | throw err |
224 | } | 227 | } |
225 | } | 228 | } |
diff --git a/server/lib/notifier.ts b/server/lib/notifier.ts index a21b50b2d..11b0937e9 100644 --- a/server/lib/notifier.ts +++ b/server/lib/notifier.ts | |||
@@ -11,6 +11,8 @@ import { VideoPrivacy, VideoState } from '../../shared/models/videos' | |||
11 | import { VideoAbuseModel } from '../models/video/video-abuse' | 11 | import { VideoAbuseModel } from '../models/video/video-abuse' |
12 | import { VideoBlacklistModel } from '../models/video/video-blacklist' | 12 | import { VideoBlacklistModel } from '../models/video/video-blacklist' |
13 | import * as Bluebird from 'bluebird' | 13 | import * as Bluebird from 'bluebird' |
14 | import { VideoImportModel } from '../models/video/video-import' | ||
15 | import { AccountBlocklistModel } from '../models/account/account-blocklist' | ||
14 | 16 | ||
15 | class Notifier { | 17 | class Notifier { |
16 | 18 | ||
@@ -26,6 +28,14 @@ class Notifier { | |||
26 | .catch(err => logger.error('Cannot notify subscribers of new video %s.', video.url, { err })) | 28 | .catch(err => logger.error('Cannot notify subscribers of new video %s.', video.url, { err })) |
27 | } | 29 | } |
28 | 30 | ||
31 | notifyOnPendingVideoPublished (video: VideoModel): void { | ||
32 | // Only notify on public videos that has been published while the user waited transcoding/scheduled update | ||
33 | if (video.waitTranscoding === false && !video.ScheduleVideoUpdate) return | ||
34 | |||
35 | this.notifyOwnedVideoHasBeenPublished(video) | ||
36 | .catch(err => logger.error('Cannot notify owner that its video %s has been published.', video.url, { err })) | ||
37 | } | ||
38 | |||
29 | notifyOnNewComment (comment: VideoCommentModel): void { | 39 | notifyOnNewComment (comment: VideoCommentModel): void { |
30 | this.notifyVideoOwnerOfNewComment(comment) | 40 | this.notifyVideoOwnerOfNewComment(comment) |
31 | .catch(err => logger.error('Cannot notify of new comment %s.', comment.url, { err })) | 41 | .catch(err => logger.error('Cannot notify of new comment %s.', comment.url, { err })) |
@@ -46,6 +56,11 @@ class Notifier { | |||
46 | .catch(err => logger.error('Cannot notify video owner of new video blacklist of %s.', video.url, { err })) | 56 | .catch(err => logger.error('Cannot notify video owner of new video blacklist of %s.', video.url, { err })) |
47 | } | 57 | } |
48 | 58 | ||
59 | notifyOnFinishedVideoImport (videoImport: VideoImportModel, success: boolean): void { | ||
60 | this.notifyOwnerVideoImportIsFinished(videoImport, success) | ||
61 | .catch(err => logger.error('Cannot notify owner that its video import %s is finished.', videoImport.getTargetIdentifier(), { err })) | ||
62 | } | ||
63 | |||
49 | private async notifySubscribersOfNewVideo (video: VideoModel) { | 64 | private async notifySubscribersOfNewVideo (video: VideoModel) { |
50 | // List all followers that are users | 65 | // List all followers that are users |
51 | const users = await UserModel.listUserSubscribersOf(video.VideoChannel.actorId) | 66 | const users = await UserModel.listUserSubscribersOf(video.VideoChannel.actorId) |
@@ -80,6 +95,9 @@ class Notifier { | |||
80 | // Not our user or user comments its own video | 95 | // Not our user or user comments its own video |
81 | if (!user || comment.Account.userId === user.id) return | 96 | if (!user || comment.Account.userId === user.id) return |
82 | 97 | ||
98 | const accountMuted = await AccountBlocklistModel.isAccountMutedBy(user.Account.id, comment.accountId) | ||
99 | if (accountMuted) return | ||
100 | |||
83 | logger.info('Notifying user %s of new comment %s.', user.username, comment.url) | 101 | logger.info('Notifying user %s of new comment %s.', user.username, comment.url) |
84 | 102 | ||
85 | function settingGetter (user: UserModel) { | 103 | function settingGetter (user: UserModel) { |
@@ -188,6 +206,64 @@ class Notifier { | |||
188 | return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) | 206 | return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) |
189 | } | 207 | } |
190 | 208 | ||
209 | private async notifyOwnedVideoHasBeenPublished (video: VideoModel) { | ||
210 | const user = await UserModel.loadByVideoId(video.id) | ||
211 | if (!user) return | ||
212 | |||
213 | logger.info('Notifying user %s of the publication of its video %s.', user.username, video.url) | ||
214 | |||
215 | function settingGetter (user: UserModel) { | ||
216 | return user.NotificationSetting.myVideoPublished | ||
217 | } | ||
218 | |||
219 | async function notificationCreator (user: UserModel) { | ||
220 | const notification = await UserNotificationModel.create({ | ||
221 | type: UserNotificationType.MY_VIDEO_PUBLISHED, | ||
222 | userId: user.id, | ||
223 | videoId: video.id | ||
224 | }) | ||
225 | notification.Video = video | ||
226 | |||
227 | return notification | ||
228 | } | ||
229 | |||
230 | function emailSender (emails: string[]) { | ||
231 | return Emailer.Instance.myVideoPublishedNotification(emails, video) | ||
232 | } | ||
233 | |||
234 | return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) | ||
235 | } | ||
236 | |||
237 | private async notifyOwnerVideoImportIsFinished (videoImport: VideoImportModel, success: boolean) { | ||
238 | const user = await UserModel.loadByVideoImportId(videoImport.id) | ||
239 | if (!user) return | ||
240 | |||
241 | logger.info('Notifying user %s its video import %s is finished.', user.username, videoImport.getTargetIdentifier()) | ||
242 | |||
243 | function settingGetter (user: UserModel) { | ||
244 | return user.NotificationSetting.myVideoImportFinished | ||
245 | } | ||
246 | |||
247 | async function notificationCreator (user: UserModel) { | ||
248 | const notification = await UserNotificationModel.create({ | ||
249 | type: success ? UserNotificationType.MY_VIDEO_IMPORT_SUCCESS : UserNotificationType.MY_VIDEO_IMPORT_ERROR, | ||
250 | userId: user.id, | ||
251 | videoImportId: videoImport.id | ||
252 | }) | ||
253 | notification.VideoImport = videoImport | ||
254 | |||
255 | return notification | ||
256 | } | ||
257 | |||
258 | function emailSender (emails: string[]) { | ||
259 | return success | ||
260 | ? Emailer.Instance.myVideoImportSuccessNotification(emails, videoImport) | ||
261 | : Emailer.Instance.myVideoImportErrorNotification(emails, videoImport) | ||
262 | } | ||
263 | |||
264 | return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) | ||
265 | } | ||
266 | |||
191 | private async notify (options: { | 267 | private async notify (options: { |
192 | users: UserModel[], | 268 | users: UserModel[], |
193 | notificationCreator: (user: UserModel) => Promise<UserNotificationModel>, | 269 | notificationCreator: (user: UserModel) => Promise<UserNotificationModel>, |
diff --git a/server/lib/schedulers/update-videos-scheduler.ts b/server/lib/schedulers/update-videos-scheduler.ts index b7fb029f1..2618a5857 100644 --- a/server/lib/schedulers/update-videos-scheduler.ts +++ b/server/lib/schedulers/update-videos-scheduler.ts | |||
@@ -6,6 +6,7 @@ import { federateVideoIfNeeded } from '../activitypub' | |||
6 | import { SCHEDULER_INTERVALS_MS, sequelizeTypescript } from '../../initializers' | 6 | import { SCHEDULER_INTERVALS_MS, sequelizeTypescript } from '../../initializers' |
7 | import { VideoPrivacy } from '../../../shared/models/videos' | 7 | import { VideoPrivacy } from '../../../shared/models/videos' |
8 | import { Notifier } from '../notifier' | 8 | import { Notifier } from '../notifier' |
9 | import { VideoModel } from '../../models/video/video' | ||
9 | 10 | ||
10 | export class UpdateVideosScheduler extends AbstractScheduler { | 11 | export class UpdateVideosScheduler extends AbstractScheduler { |
11 | 12 | ||
@@ -24,8 +25,9 @@ export class UpdateVideosScheduler extends AbstractScheduler { | |||
24 | private async updateVideos () { | 25 | private async updateVideos () { |
25 | if (!await ScheduleVideoUpdateModel.areVideosToUpdate()) return undefined | 26 | if (!await ScheduleVideoUpdateModel.areVideosToUpdate()) return undefined |
26 | 27 | ||
27 | return sequelizeTypescript.transaction(async t => { | 28 | const publishedVideos = await sequelizeTypescript.transaction(async t => { |
28 | const schedules = await ScheduleVideoUpdateModel.listVideosToUpdate(t) | 29 | const schedules = await ScheduleVideoUpdateModel.listVideosToUpdate(t) |
30 | const publishedVideos: VideoModel[] = [] | ||
29 | 31 | ||
30 | for (const schedule of schedules) { | 32 | for (const schedule of schedules) { |
31 | const video = schedule.Video | 33 | const video = schedule.Video |
@@ -42,13 +44,21 @@ export class UpdateVideosScheduler extends AbstractScheduler { | |||
42 | await federateVideoIfNeeded(video, isNewVideo, t) | 44 | await federateVideoIfNeeded(video, isNewVideo, t) |
43 | 45 | ||
44 | if (oldPrivacy === VideoPrivacy.UNLISTED || oldPrivacy === VideoPrivacy.PRIVATE) { | 46 | if (oldPrivacy === VideoPrivacy.UNLISTED || oldPrivacy === VideoPrivacy.PRIVATE) { |
45 | Notifier.Instance.notifyOnNewVideo(video) | 47 | video.ScheduleVideoUpdate = schedule |
48 | publishedVideos.push(video) | ||
46 | } | 49 | } |
47 | } | 50 | } |
48 | 51 | ||
49 | await schedule.destroy({ transaction: t }) | 52 | await schedule.destroy({ transaction: t }) |
50 | } | 53 | } |
54 | |||
55 | return publishedVideos | ||
51 | }) | 56 | }) |
57 | |||
58 | for (const v of publishedVideos) { | ||
59 | Notifier.Instance.notifyOnNewVideo(v) | ||
60 | Notifier.Instance.notifyOnPendingVideoPublished(v) | ||
61 | } | ||
52 | } | 62 | } |
53 | 63 | ||
54 | static get Instance () { | 64 | static get Instance () { |
diff --git a/server/lib/user.ts b/server/lib/user.ts index 72127819c..481571828 100644 --- a/server/lib/user.ts +++ b/server/lib/user.ts | |||
@@ -100,6 +100,8 @@ function createDefaultUserNotificationSettings (user: UserModel, t: Sequelize.Tr | |||
100 | userId: user.id, | 100 | userId: user.id, |
101 | newVideoFromSubscription: UserNotificationSettingValue.WEB_NOTIFICATION, | 101 | newVideoFromSubscription: UserNotificationSettingValue.WEB_NOTIFICATION, |
102 | newCommentOnMyVideo: UserNotificationSettingValue.WEB_NOTIFICATION, | 102 | newCommentOnMyVideo: UserNotificationSettingValue.WEB_NOTIFICATION, |
103 | myVideoImportFinished: UserNotificationSettingValue.WEB_NOTIFICATION, | ||
104 | myVideoPublished: UserNotificationSettingValue.WEB_NOTIFICATION, | ||
103 | videoAbuseAsModerator: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL, | 105 | videoAbuseAsModerator: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL, |
104 | blacklistOnMyVideo: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL | 106 | blacklistOnMyVideo: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL |
105 | }, { transaction: t }) | 107 | }, { transaction: t }) |