diff options
author | Chocobozzz <me@florianbigard.com> | 2019-01-02 16:37:43 +0100 |
---|---|---|
committer | Chocobozzz <chocobozzz@cpy.re> | 2019-01-09 11:15:15 +0100 |
commit | dc13348070d808d0ba3feb56a435b835c2e7e791 (patch) | |
tree | 887202a33f1aa680fd8ece6ee465381f3931c64e | |
parent | 6e7e63b83f08ba68edc2bb9f72ff03d1802e45df (diff) | |
download | PeerTube-dc13348070d808d0ba3feb56a435b835c2e7e791.tar.gz PeerTube-dc13348070d808d0ba3feb56a435b835c2e7e791.tar.zst PeerTube-dc13348070d808d0ba3feb56a435b835c2e7e791.zip |
Add import finished and video published notifs
23 files changed, 814 insertions, 250 deletions
diff --git a/server/controllers/api/users/my-notifications.ts b/server/controllers/api/users/my-notifications.ts index cef1d237c..4b81777a4 100644 --- a/server/controllers/api/users/my-notifications.ts +++ b/server/controllers/api/users/my-notifications.ts | |||
@@ -14,10 +14,11 @@ import { getFormattedObjects } from '../../../helpers/utils' | |||
14 | import { UserNotificationModel } from '../../../models/account/user-notification' | 14 | import { UserNotificationModel } from '../../../models/account/user-notification' |
15 | import { meRouter } from './me' | 15 | import { meRouter } from './me' |
16 | import { | 16 | import { |
17 | listUserNotificationsValidator, | ||
17 | markAsReadUserNotificationsValidator, | 18 | markAsReadUserNotificationsValidator, |
18 | updateNotificationSettingsValidator | 19 | updateNotificationSettingsValidator |
19 | } from '../../../middlewares/validators/user-notifications' | 20 | } from '../../../middlewares/validators/user-notifications' |
20 | import { UserNotificationSetting } from '../../../../shared/models/users' | 21 | import { UserNotificationSetting, UserNotificationSettingValue } from '../../../../shared/models/users' |
21 | import { UserNotificationSettingModel } from '../../../models/account/user-notification-setting' | 22 | import { UserNotificationSettingModel } from '../../../models/account/user-notification-setting' |
22 | 23 | ||
23 | const myNotificationsRouter = express.Router() | 24 | const myNotificationsRouter = express.Router() |
@@ -34,6 +35,7 @@ myNotificationsRouter.get('/me/notifications', | |||
34 | userNotificationsSortValidator, | 35 | userNotificationsSortValidator, |
35 | setDefaultSort, | 36 | setDefaultSort, |
36 | setDefaultPagination, | 37 | setDefaultPagination, |
38 | listUserNotificationsValidator, | ||
37 | asyncMiddleware(listUserNotifications) | 39 | asyncMiddleware(listUserNotifications) |
38 | ) | 40 | ) |
39 | 41 | ||
@@ -61,7 +63,11 @@ async function updateNotificationSettings (req: express.Request, res: express.Re | |||
61 | 63 | ||
62 | await UserNotificationSettingModel.update({ | 64 | await UserNotificationSettingModel.update({ |
63 | newVideoFromSubscription: body.newVideoFromSubscription, | 65 | newVideoFromSubscription: body.newVideoFromSubscription, |
64 | newCommentOnMyVideo: body.newCommentOnMyVideo | 66 | newCommentOnMyVideo: body.newCommentOnMyVideo, |
67 | videoAbuseAsModerator: body.videoAbuseAsModerator, | ||
68 | blacklistOnMyVideo: body.blacklistOnMyVideo, | ||
69 | myVideoPublished: body.myVideoPublished, | ||
70 | myVideoImportFinished: body.myVideoImportFinished | ||
65 | }, query) | 71 | }, query) |
66 | 72 | ||
67 | return res.status(204).end() | 73 | return res.status(204).end() |
@@ -70,7 +76,7 @@ async function updateNotificationSettings (req: express.Request, res: express.Re | |||
70 | async function listUserNotifications (req: express.Request, res: express.Response) { | 76 | async function listUserNotifications (req: express.Request, res: express.Response) { |
71 | const user: UserModel = res.locals.oauth.token.User | 77 | const user: UserModel = res.locals.oauth.token.User |
72 | 78 | ||
73 | const resultList = await UserNotificationModel.listForApi(user.id, req.query.start, req.query.count, req.query.sort) | 79 | const resultList = await UserNotificationModel.listForApi(user.id, req.query.start, req.query.count, req.query.sort, req.query.unread) |
74 | 80 | ||
75 | return res.json(getFormattedObjects(resultList.data, resultList.total)) | 81 | return res.json(getFormattedObjects(resultList.data, resultList.total)) |
76 | } | 82 | } |
diff --git a/server/initializers/migrations/0315-user-notifications.ts b/server/initializers/migrations/0315-user-notifications.ts index 2bd9c657d..8c54c5d6c 100644 --- a/server/initializers/migrations/0315-user-notifications.ts +++ b/server/initializers/migrations/0315-user-notifications.ts | |||
@@ -13,6 +13,8 @@ CREATE TABLE IF NOT EXISTS "userNotificationSetting" ("id" SERIAL, | |||
13 | "newCommentOnMyVideo" INTEGER NOT NULL DEFAULT NULL, | 13 | "newCommentOnMyVideo" INTEGER NOT NULL DEFAULT NULL, |
14 | "videoAbuseAsModerator" INTEGER NOT NULL DEFAULT NULL, | 14 | "videoAbuseAsModerator" INTEGER NOT NULL DEFAULT NULL, |
15 | "blacklistOnMyVideo" INTEGER NOT NULL DEFAULT NULL, | 15 | "blacklistOnMyVideo" INTEGER NOT NULL DEFAULT NULL, |
16 | "myVideoPublished" INTEGER NOT NULL DEFAULT NULL, | ||
17 | "myVideoImportFinished" INTEGER NOT NULL DEFAULT NULL, | ||
16 | "userId" INTEGER REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE CASCADE, | 18 | "userId" INTEGER REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE CASCADE, |
17 | "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, | 19 | "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, |
18 | "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL, | 20 | "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL, |
@@ -24,8 +26,8 @@ PRIMARY KEY ("id")) | |||
24 | { | 26 | { |
25 | const query = 'INSERT INTO "userNotificationSetting" ' + | 27 | const query = 'INSERT INTO "userNotificationSetting" ' + |
26 | '("newVideoFromSubscription", "newCommentOnMyVideo", "videoAbuseAsModerator", "blacklistOnMyVideo", ' + | 28 | '("newVideoFromSubscription", "newCommentOnMyVideo", "videoAbuseAsModerator", "blacklistOnMyVideo", ' + |
27 | '"userId", "createdAt", "updatedAt") ' + | 29 | '"myVideoPublished", "myVideoImportFinished", "userId", "createdAt", "updatedAt") ' + |
28 | '(SELECT 2, 2, 4, 4, id, NOW(), NOW() FROM "user")' | 30 | '(SELECT 2, 2, 4, 4, 2, 2, id, NOW(), NOW() FROM "user")' |
29 | 31 | ||
30 | await utils.sequelize.query(query) | 32 | await utils.sequelize.query(query) |
31 | } | 33 | } |
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 }) |
diff --git a/server/middlewares/validators/user-notifications.ts b/server/middlewares/validators/user-notifications.ts index 8202f307e..1c31f0a73 100644 --- a/server/middlewares/validators/user-notifications.ts +++ b/server/middlewares/validators/user-notifications.ts | |||
@@ -1,11 +1,26 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import 'express-validator' | 2 | import 'express-validator' |
3 | import { body } from 'express-validator/check' | 3 | import { body, query } from 'express-validator/check' |
4 | import { logger } from '../../helpers/logger' | 4 | import { logger } from '../../helpers/logger' |
5 | import { areValidationErrors } from './utils' | 5 | import { areValidationErrors } from './utils' |
6 | import { isUserNotificationSettingValid } from '../../helpers/custom-validators/user-notifications' | 6 | import { isUserNotificationSettingValid } from '../../helpers/custom-validators/user-notifications' |
7 | import { isIntArray } from '../../helpers/custom-validators/misc' | 7 | import { isIntArray } from '../../helpers/custom-validators/misc' |
8 | 8 | ||
9 | const listUserNotificationsValidator = [ | ||
10 | query('unread') | ||
11 | .optional() | ||
12 | .toBoolean() | ||
13 | .isBoolean().withMessage('Should have a valid unread boolean'), | ||
14 | |||
15 | (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
16 | logger.debug('Checking listUserNotificationsValidator parameters', { parameters: req.query }) | ||
17 | |||
18 | if (areValidationErrors(req, res)) return | ||
19 | |||
20 | return next() | ||
21 | } | ||
22 | ] | ||
23 | |||
9 | const updateNotificationSettingsValidator = [ | 24 | const updateNotificationSettingsValidator = [ |
10 | body('newVideoFromSubscription') | 25 | body('newVideoFromSubscription') |
11 | .custom(isUserNotificationSettingValid).withMessage('Should have a valid new video from subscription notification setting'), | 26 | .custom(isUserNotificationSettingValid).withMessage('Should have a valid new video from subscription notification setting'), |
@@ -41,6 +56,7 @@ const markAsReadUserNotificationsValidator = [ | |||
41 | // --------------------------------------------------------------------------- | 56 | // --------------------------------------------------------------------------- |
42 | 57 | ||
43 | export { | 58 | export { |
59 | listUserNotificationsValidator, | ||
44 | updateNotificationSettingsValidator, | 60 | updateNotificationSettingsValidator, |
45 | markAsReadUserNotificationsValidator | 61 | markAsReadUserNotificationsValidator |
46 | } | 62 | } |
diff --git a/server/models/account/account-blocklist.ts b/server/models/account/account-blocklist.ts index fa2819235..54ac290c4 100644 --- a/server/models/account/account-blocklist.ts +++ b/server/models/account/account-blocklist.ts | |||
@@ -72,6 +72,21 @@ export class AccountBlocklistModel extends Model<AccountBlocklistModel> { | |||
72 | }) | 72 | }) |
73 | BlockedAccount: AccountModel | 73 | BlockedAccount: AccountModel |
74 | 74 | ||
75 | static isAccountMutedBy (accountId: number, targetAccountId: number) { | ||
76 | const query = { | ||
77 | attributes: [ 'id' ], | ||
78 | where: { | ||
79 | accountId, | ||
80 | targetAccountId | ||
81 | }, | ||
82 | raw: true | ||
83 | } | ||
84 | |||
85 | return AccountBlocklistModel.unscoped() | ||
86 | .findOne(query) | ||
87 | .then(a => !!a) | ||
88 | } | ||
89 | |||
75 | static loadByAccountAndTarget (accountId: number, targetAccountId: number) { | 90 | static loadByAccountAndTarget (accountId: number, targetAccountId: number) { |
76 | const query = { | 91 | const query = { |
77 | where: { | 92 | where: { |
diff --git a/server/models/account/user-notification-setting.ts b/server/models/account/user-notification-setting.ts index bc24b1e33..6470defa7 100644 --- a/server/models/account/user-notification-setting.ts +++ b/server/models/account/user-notification-setting.ts | |||
@@ -65,6 +65,24 @@ export class UserNotificationSettingModel extends Model<UserNotificationSettingM | |||
65 | @Column | 65 | @Column |
66 | blacklistOnMyVideo: UserNotificationSettingValue | 66 | blacklistOnMyVideo: UserNotificationSettingValue |
67 | 67 | ||
68 | @AllowNull(false) | ||
69 | @Default(null) | ||
70 | @Is( | ||
71 | 'UserNotificationSettingMyVideoPublished', | ||
72 | value => throwIfNotValid(value, isUserNotificationSettingValid, 'myVideoPublished') | ||
73 | ) | ||
74 | @Column | ||
75 | myVideoPublished: UserNotificationSettingValue | ||
76 | |||
77 | @AllowNull(false) | ||
78 | @Default(null) | ||
79 | @Is( | ||
80 | 'UserNotificationSettingMyVideoImportFinished', | ||
81 | value => throwIfNotValid(value, isUserNotificationSettingValid, 'myVideoImportFinished') | ||
82 | ) | ||
83 | @Column | ||
84 | myVideoImportFinished: UserNotificationSettingValue | ||
85 | |||
68 | @ForeignKey(() => UserModel) | 86 | @ForeignKey(() => UserModel) |
69 | @Column | 87 | @Column |
70 | userId: number | 88 | userId: number |
@@ -94,7 +112,9 @@ export class UserNotificationSettingModel extends Model<UserNotificationSettingM | |||
94 | newCommentOnMyVideo: this.newCommentOnMyVideo, | 112 | newCommentOnMyVideo: this.newCommentOnMyVideo, |
95 | newVideoFromSubscription: this.newVideoFromSubscription, | 113 | newVideoFromSubscription: this.newVideoFromSubscription, |
96 | videoAbuseAsModerator: this.videoAbuseAsModerator, | 114 | videoAbuseAsModerator: this.videoAbuseAsModerator, |
97 | blacklistOnMyVideo: this.blacklistOnMyVideo | 115 | blacklistOnMyVideo: this.blacklistOnMyVideo, |
116 | myVideoPublished: this.myVideoPublished, | ||
117 | myVideoImportFinished: this.myVideoImportFinished | ||
98 | } | 118 | } |
99 | } | 119 | } |
100 | } | 120 | } |
diff --git a/server/models/account/user-notification.ts b/server/models/account/user-notification.ts index e22f0d57f..251244374 100644 --- a/server/models/account/user-notification.ts +++ b/server/models/account/user-notification.ts | |||
@@ -1,4 +1,17 @@ | |||
1 | import { AllowNull, BelongsTo, Column, CreatedAt, Default, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' | 1 | import { |
2 | AllowNull, | ||
3 | BelongsTo, | ||
4 | Column, | ||
5 | CreatedAt, | ||
6 | Default, | ||
7 | ForeignKey, | ||
8 | IFindOptions, | ||
9 | Is, | ||
10 | Model, | ||
11 | Scopes, | ||
12 | Table, | ||
13 | UpdatedAt | ||
14 | } from 'sequelize-typescript' | ||
2 | import { UserNotification, UserNotificationType } from '../../../shared' | 15 | import { UserNotification, UserNotificationType } from '../../../shared' |
3 | import { getSort, throwIfNotValid } from '../utils' | 16 | import { getSort, throwIfNotValid } from '../utils' |
4 | import { isBooleanValid } from '../../helpers/custom-validators/misc' | 17 | import { isBooleanValid } from '../../helpers/custom-validators/misc' |
@@ -11,66 +24,68 @@ import { VideoChannelModel } from '../video/video-channel' | |||
11 | import { AccountModel } from './account' | 24 | import { AccountModel } from './account' |
12 | import { VideoAbuseModel } from '../video/video-abuse' | 25 | import { VideoAbuseModel } from '../video/video-abuse' |
13 | import { VideoBlacklistModel } from '../video/video-blacklist' | 26 | import { VideoBlacklistModel } from '../video/video-blacklist' |
27 | import { VideoImportModel } from '../video/video-import' | ||
14 | 28 | ||
15 | enum ScopeNames { | 29 | enum ScopeNames { |
16 | WITH_ALL = 'WITH_ALL' | 30 | WITH_ALL = 'WITH_ALL' |
17 | } | 31 | } |
18 | 32 | ||
33 | function buildVideoInclude (required: boolean) { | ||
34 | return { | ||
35 | attributes: [ 'id', 'uuid', 'name' ], | ||
36 | model: () => VideoModel.unscoped(), | ||
37 | required | ||
38 | } | ||
39 | } | ||
40 | |||
41 | function buildChannelInclude () { | ||
42 | return { | ||
43 | required: true, | ||
44 | attributes: [ 'id', 'name' ], | ||
45 | model: () => VideoChannelModel.unscoped() | ||
46 | } | ||
47 | } | ||
48 | |||
49 | function buildAccountInclude () { | ||
50 | return { | ||
51 | required: true, | ||
52 | attributes: [ 'id', 'name' ], | ||
53 | model: () => AccountModel.unscoped() | ||
54 | } | ||
55 | } | ||
56 | |||
19 | @Scopes({ | 57 | @Scopes({ |
20 | [ScopeNames.WITH_ALL]: { | 58 | [ScopeNames.WITH_ALL]: { |
21 | include: [ | 59 | include: [ |
60 | Object.assign(buildVideoInclude(false), { | ||
61 | include: [ buildChannelInclude() ] | ||
62 | }), | ||
22 | { | 63 | { |
23 | attributes: [ 'id', 'uuid', 'name' ], | 64 | attributes: [ 'id', 'originCommentId' ], |
24 | model: () => VideoModel.unscoped(), | ||
25 | required: false, | ||
26 | include: [ | ||
27 | { | ||
28 | required: true, | ||
29 | attributes: [ 'id', 'name' ], | ||
30 | model: () => VideoChannelModel.unscoped() | ||
31 | } | ||
32 | ] | ||
33 | }, | ||
34 | { | ||
35 | attributes: [ 'id' ], | ||
36 | model: () => VideoCommentModel.unscoped(), | 65 | model: () => VideoCommentModel.unscoped(), |
37 | required: false, | 66 | required: false, |
38 | include: [ | 67 | include: [ |
39 | { | 68 | buildAccountInclude(), |
40 | required: true, | 69 | buildVideoInclude(true) |
41 | attributes: [ 'id', 'name' ], | ||
42 | model: () => AccountModel.unscoped() | ||
43 | }, | ||
44 | { | ||
45 | required: true, | ||
46 | attributes: [ 'id', 'uuid', 'name' ], | ||
47 | model: () => VideoModel.unscoped() | ||
48 | } | ||
49 | ] | 70 | ] |
50 | }, | 71 | }, |
51 | { | 72 | { |
52 | attributes: [ 'id' ], | 73 | attributes: [ 'id' ], |
53 | model: () => VideoAbuseModel.unscoped(), | 74 | model: () => VideoAbuseModel.unscoped(), |
54 | required: false, | 75 | required: false, |
55 | include: [ | 76 | include: [ buildVideoInclude(true) ] |
56 | { | ||
57 | required: true, | ||
58 | attributes: [ 'id', 'uuid', 'name' ], | ||
59 | model: () => VideoModel.unscoped() | ||
60 | } | ||
61 | ] | ||
62 | }, | 77 | }, |
63 | { | 78 | { |
64 | attributes: [ 'id' ], | 79 | attributes: [ 'id' ], |
65 | model: () => VideoBlacklistModel.unscoped(), | 80 | model: () => VideoBlacklistModel.unscoped(), |
66 | required: false, | 81 | required: false, |
67 | include: [ | 82 | include: [ buildVideoInclude(true) ] |
68 | { | 83 | }, |
69 | required: true, | 84 | { |
70 | attributes: [ 'id', 'uuid', 'name' ], | 85 | attributes: [ 'id', 'magnetUri', 'targetUrl', 'torrentName' ], |
71 | model: () => VideoModel.unscoped() | 86 | model: () => VideoImportModel.unscoped(), |
72 | } | 87 | required: false, |
73 | ] | 88 | include: [ buildVideoInclude(false) ] |
74 | } | 89 | } |
75 | ] | 90 | ] |
76 | } | 91 | } |
@@ -166,8 +181,20 @@ export class UserNotificationModel extends Model<UserNotificationModel> { | |||
166 | }) | 181 | }) |
167 | VideoBlacklist: VideoBlacklistModel | 182 | VideoBlacklist: VideoBlacklistModel |
168 | 183 | ||
169 | static listForApi (userId: number, start: number, count: number, sort: string) { | 184 | @ForeignKey(() => VideoImportModel) |
170 | const query = { | 185 | @Column |
186 | videoImportId: number | ||
187 | |||
188 | @BelongsTo(() => VideoImportModel, { | ||
189 | foreignKey: { | ||
190 | allowNull: true | ||
191 | }, | ||
192 | onDelete: 'cascade' | ||
193 | }) | ||
194 | VideoImport: VideoImportModel | ||
195 | |||
196 | static listForApi (userId: number, start: number, count: number, sort: string, unread?: boolean) { | ||
197 | const query: IFindOptions<UserNotificationModel> = { | ||
171 | offset: start, | 198 | offset: start, |
172 | limit: count, | 199 | limit: count, |
173 | order: getSort(sort), | 200 | order: getSort(sort), |
@@ -176,6 +203,8 @@ export class UserNotificationModel extends Model<UserNotificationModel> { | |||
176 | } | 203 | } |
177 | } | 204 | } |
178 | 205 | ||
206 | if (unread !== undefined) query.where['read'] = !unread | ||
207 | |||
179 | return UserNotificationModel.scope(ScopeNames.WITH_ALL) | 208 | return UserNotificationModel.scope(ScopeNames.WITH_ALL) |
180 | .findAndCountAll(query) | 209 | .findAndCountAll(query) |
181 | .then(({ rows, count }) => { | 210 | .then(({ rows, count }) => { |
@@ -200,45 +229,39 @@ export class UserNotificationModel extends Model<UserNotificationModel> { | |||
200 | } | 229 | } |
201 | 230 | ||
202 | toFormattedJSON (): UserNotification { | 231 | toFormattedJSON (): UserNotification { |
203 | const video = this.Video ? { | 232 | const video = this.Video ? Object.assign(this.formatVideo(this.Video), { |
204 | id: this.Video.id, | ||
205 | uuid: this.Video.uuid, | ||
206 | name: this.Video.name, | ||
207 | channel: { | 233 | channel: { |
208 | id: this.Video.VideoChannel.id, | 234 | id: this.Video.VideoChannel.id, |
209 | displayName: this.Video.VideoChannel.getDisplayName() | 235 | displayName: this.Video.VideoChannel.getDisplayName() |
210 | } | 236 | } |
237 | }) : undefined | ||
238 | |||
239 | const videoImport = this.VideoImport ? { | ||
240 | id: this.VideoImport.id, | ||
241 | video: this.VideoImport.Video ? this.formatVideo(this.VideoImport.Video) : undefined, | ||
242 | torrentName: this.VideoImport.torrentName, | ||
243 | magnetUri: this.VideoImport.magnetUri, | ||
244 | targetUrl: this.VideoImport.targetUrl | ||
211 | } : undefined | 245 | } : undefined |
212 | 246 | ||
213 | const comment = this.Comment ? { | 247 | const comment = this.Comment ? { |
214 | id: this.Comment.id, | 248 | id: this.Comment.id, |
249 | threadId: this.Comment.getThreadId(), | ||
215 | account: { | 250 | account: { |
216 | id: this.Comment.Account.id, | 251 | id: this.Comment.Account.id, |
217 | displayName: this.Comment.Account.getDisplayName() | 252 | displayName: this.Comment.Account.getDisplayName() |
218 | }, | 253 | }, |
219 | video: { | 254 | video: this.formatVideo(this.Comment.Video) |
220 | id: this.Comment.Video.id, | ||
221 | uuid: this.Comment.Video.uuid, | ||
222 | name: this.Comment.Video.name | ||
223 | } | ||
224 | } : undefined | 255 | } : undefined |
225 | 256 | ||
226 | const videoAbuse = this.VideoAbuse ? { | 257 | const videoAbuse = this.VideoAbuse ? { |
227 | id: this.VideoAbuse.id, | 258 | id: this.VideoAbuse.id, |
228 | video: { | 259 | video: this.formatVideo(this.VideoAbuse.Video) |
229 | id: this.VideoAbuse.Video.id, | ||
230 | uuid: this.VideoAbuse.Video.uuid, | ||
231 | name: this.VideoAbuse.Video.name | ||
232 | } | ||
233 | } : undefined | 260 | } : undefined |
234 | 261 | ||
235 | const videoBlacklist = this.VideoBlacklist ? { | 262 | const videoBlacklist = this.VideoBlacklist ? { |
236 | id: this.VideoBlacklist.id, | 263 | id: this.VideoBlacklist.id, |
237 | video: { | 264 | video: this.formatVideo(this.VideoBlacklist.Video) |
238 | id: this.VideoBlacklist.Video.id, | ||
239 | uuid: this.VideoBlacklist.Video.uuid, | ||
240 | name: this.VideoBlacklist.Video.name | ||
241 | } | ||
242 | } : undefined | 265 | } : undefined |
243 | 266 | ||
244 | return { | 267 | return { |
@@ -246,6 +269,7 @@ export class UserNotificationModel extends Model<UserNotificationModel> { | |||
246 | type: this.type, | 269 | type: this.type, |
247 | read: this.read, | 270 | read: this.read, |
248 | video, | 271 | video, |
272 | videoImport, | ||
249 | comment, | 273 | comment, |
250 | videoAbuse, | 274 | videoAbuse, |
251 | videoBlacklist, | 275 | videoBlacklist, |
@@ -253,4 +277,12 @@ export class UserNotificationModel extends Model<UserNotificationModel> { | |||
253 | updatedAt: this.updatedAt.toISOString() | 277 | updatedAt: this.updatedAt.toISOString() |
254 | } | 278 | } |
255 | } | 279 | } |
280 | |||
281 | private formatVideo (video: VideoModel) { | ||
282 | return { | ||
283 | id: video.id, | ||
284 | uuid: video.uuid, | ||
285 | name: video.name | ||
286 | } | ||
287 | } | ||
256 | } | 288 | } |
diff --git a/server/models/account/user.ts b/server/models/account/user.ts index 55ec14d05..33f56f641 100644 --- a/server/models/account/user.ts +++ b/server/models/account/user.ts | |||
@@ -48,6 +48,7 @@ import { UserNotificationSettingModel } from './user-notification-setting' | |||
48 | import { VideoModel } from '../video/video' | 48 | import { VideoModel } from '../video/video' |
49 | import { ActorModel } from '../activitypub/actor' | 49 | import { ActorModel } from '../activitypub/actor' |
50 | import { ActorFollowModel } from '../activitypub/actor-follow' | 50 | import { ActorFollowModel } from '../activitypub/actor-follow' |
51 | import { VideoImportModel } from '../video/video-import' | ||
51 | 52 | ||
52 | enum ScopeNames { | 53 | enum ScopeNames { |
53 | WITH_VIDEO_CHANNEL = 'WITH_VIDEO_CHANNEL' | 54 | WITH_VIDEO_CHANNEL = 'WITH_VIDEO_CHANNEL' |
@@ -186,6 +187,12 @@ export class UserModel extends Model<UserModel> { | |||
186 | }) | 187 | }) |
187 | NotificationSetting: UserNotificationSettingModel | 188 | NotificationSetting: UserNotificationSettingModel |
188 | 189 | ||
190 | @HasMany(() => VideoImportModel, { | ||
191 | foreignKey: 'userId', | ||
192 | onDelete: 'cascade' | ||
193 | }) | ||
194 | VideoImports: VideoImportModel[] | ||
195 | |||
189 | @HasMany(() => OAuthTokenModel, { | 196 | @HasMany(() => OAuthTokenModel, { |
190 | foreignKey: 'userId', | 197 | foreignKey: 'userId', |
191 | onDelete: 'cascade' | 198 | onDelete: 'cascade' |
@@ -400,6 +407,23 @@ export class UserModel extends Model<UserModel> { | |||
400 | return UserModel.findOne(query) | 407 | return UserModel.findOne(query) |
401 | } | 408 | } |
402 | 409 | ||
410 | static loadByVideoImportId (videoImportId: number) { | ||
411 | const query = { | ||
412 | include: [ | ||
413 | { | ||
414 | required: true, | ||
415 | attributes: [ 'id' ], | ||
416 | model: VideoImportModel.unscoped(), | ||
417 | where: { | ||
418 | id: videoImportId | ||
419 | } | ||
420 | } | ||
421 | ] | ||
422 | } | ||
423 | |||
424 | return UserModel.findOne(query) | ||
425 | } | ||
426 | |||
403 | static getOriginalVideoFileTotalFromUser (user: UserModel) { | 427 | static getOriginalVideoFileTotalFromUser (user: UserModel) { |
404 | // Don't use sequelize because we need to use a sub query | 428 | // Don't use sequelize because we need to use a sub query |
405 | const query = UserModel.generateUserQuotaBaseSQL() | 429 | const query = UserModel.generateUserQuotaBaseSQL() |
diff --git a/server/models/video/video-file.ts b/server/models/video/video-file.ts index 3fd2d5a99..0fd868cd6 100644 --- a/server/models/video/video-file.ts +++ b/server/models/video/video-file.ts | |||
@@ -1,4 +1,3 @@ | |||
1 | import { values } from 'lodash' | ||
2 | import { | 1 | import { |
3 | AllowNull, | 2 | AllowNull, |
4 | BelongsTo, | 3 | BelongsTo, |
@@ -20,7 +19,6 @@ import { | |||
20 | isVideoFileSizeValid, | 19 | isVideoFileSizeValid, |
21 | isVideoFPSResolutionValid | 20 | isVideoFPSResolutionValid |
22 | } from '../../helpers/custom-validators/videos' | 21 | } from '../../helpers/custom-validators/videos' |
23 | import { CONSTRAINTS_FIELDS } from '../../initializers' | ||
24 | import { throwIfNotValid } from '../utils' | 22 | import { throwIfNotValid } from '../utils' |
25 | import { VideoModel } from './video' | 23 | import { VideoModel } from './video' |
26 | import * as Sequelize from 'sequelize' | 24 | import * as Sequelize from 'sequelize' |
diff --git a/server/models/video/video-import.ts b/server/models/video/video-import.ts index 8d442b3f8..c723e57c0 100644 --- a/server/models/video/video-import.ts +++ b/server/models/video/video-import.ts | |||
@@ -144,6 +144,10 @@ export class VideoImportModel extends Model<VideoImportModel> { | |||
144 | }) | 144 | }) |
145 | } | 145 | } |
146 | 146 | ||
147 | getTargetIdentifier () { | ||
148 | return this.targetUrl || this.magnetUri || this.torrentName | ||
149 | } | ||
150 | |||
147 | toFormattedJSON (): VideoImport { | 151 | toFormattedJSON (): VideoImport { |
148 | const videoFormatOptions = { | 152 | const videoFormatOptions = { |
149 | completeDescription: true, | 153 | completeDescription: true, |
diff --git a/server/models/video/video.ts b/server/models/video/video.ts index fc200e5d1..80a6c7832 100644 --- a/server/models/video/video.ts +++ b/server/models/video/video.ts | |||
@@ -94,6 +94,7 @@ import { | |||
94 | import * as validator from 'validator' | 94 | import * as validator from 'validator' |
95 | import { UserVideoHistoryModel } from '../account/user-video-history' | 95 | import { UserVideoHistoryModel } from '../account/user-video-history' |
96 | import { UserModel } from '../account/user' | 96 | import { UserModel } from '../account/user' |
97 | import { VideoImportModel } from './video-import' | ||
97 | 98 | ||
98 | // FIXME: Define indexes here because there is an issue with TS and Sequelize.literal when called directly in the annotation | 99 | // FIXME: Define indexes here because there is an issue with TS and Sequelize.literal when called directly in the annotation |
99 | const indexes: Sequelize.DefineIndexesOptions[] = [ | 100 | const indexes: Sequelize.DefineIndexesOptions[] = [ |
@@ -785,6 +786,15 @@ export class VideoModel extends Model<VideoModel> { | |||
785 | }) | 786 | }) |
786 | VideoBlacklist: VideoBlacklistModel | 787 | VideoBlacklist: VideoBlacklistModel |
787 | 788 | ||
789 | @HasOne(() => VideoImportModel, { | ||
790 | foreignKey: { | ||
791 | name: 'videoId', | ||
792 | allowNull: true | ||
793 | }, | ||
794 | onDelete: 'set null' | ||
795 | }) | ||
796 | VideoImport: VideoImportModel | ||
797 | |||
788 | @HasMany(() => VideoCaptionModel, { | 798 | @HasMany(() => VideoCaptionModel, { |
789 | foreignKey: { | 799 | foreignKey: { |
790 | name: 'videoId', | 800 | name: 'videoId', |
diff --git a/server/tests/api/check-params/user-notifications.ts b/server/tests/api/check-params/user-notifications.ts index 3ae36ddb3..4f21f7b95 100644 --- a/server/tests/api/check-params/user-notifications.ts +++ b/server/tests/api/check-params/user-notifications.ts | |||
@@ -52,6 +52,18 @@ describe('Test user notifications API validators', function () { | |||
52 | await checkBadSortPagination(server.url, path, server.accessToken) | 52 | await checkBadSortPagination(server.url, path, server.accessToken) |
53 | }) | 53 | }) |
54 | 54 | ||
55 | it('Should fail with an incorrect unread parameter', async function () { | ||
56 | await makeGetRequest({ | ||
57 | url: server.url, | ||
58 | path, | ||
59 | query: { | ||
60 | unread: 'toto' | ||
61 | }, | ||
62 | token: server.accessToken, | ||
63 | statusCodeExpected: 200 | ||
64 | }) | ||
65 | }) | ||
66 | |||
55 | it('Should fail with a non authenticated user', async function () { | 67 | it('Should fail with a non authenticated user', async function () { |
56 | await makeGetRequest({ | 68 | await makeGetRequest({ |
57 | url: server.url, | 69 | url: server.url, |
@@ -125,7 +137,9 @@ describe('Test user notifications API validators', function () { | |||
125 | newVideoFromSubscription: UserNotificationSettingValue.WEB_NOTIFICATION, | 137 | newVideoFromSubscription: UserNotificationSettingValue.WEB_NOTIFICATION, |
126 | newCommentOnMyVideo: UserNotificationSettingValue.WEB_NOTIFICATION, | 138 | newCommentOnMyVideo: UserNotificationSettingValue.WEB_NOTIFICATION, |
127 | videoAbuseAsModerator: UserNotificationSettingValue.WEB_NOTIFICATION, | 139 | videoAbuseAsModerator: UserNotificationSettingValue.WEB_NOTIFICATION, |
128 | blacklistOnMyVideo: UserNotificationSettingValue.WEB_NOTIFICATION | 140 | blacklistOnMyVideo: UserNotificationSettingValue.WEB_NOTIFICATION, |
141 | myVideoImportFinished: UserNotificationSettingValue.WEB_NOTIFICATION, | ||
142 | myVideoPublished: UserNotificationSettingValue.WEB_NOTIFICATION | ||
129 | } | 143 | } |
130 | 144 | ||
131 | it('Should fail with missing fields', async function () { | 145 | it('Should fail with missing fields', async function () { |
diff --git a/server/tests/api/users/user-notifications.ts b/server/tests/api/users/user-notifications.ts index 09c0479fd..e4966dbf5 100644 --- a/server/tests/api/users/user-notifications.ts +++ b/server/tests/api/users/user-notifications.ts | |||
@@ -29,33 +29,46 @@ import { | |||
29 | getLastNotification, | 29 | getLastNotification, |
30 | getUserNotifications, | 30 | getUserNotifications, |
31 | markAsReadNotifications, | 31 | markAsReadNotifications, |
32 | updateMyNotificationSettings | 32 | updateMyNotificationSettings, |
33 | checkVideoIsPublished, checkMyVideoImportIsFinished | ||
33 | } from '../../../../shared/utils/users/user-notifications' | 34 | } from '../../../../shared/utils/users/user-notifications' |
34 | import { User, UserNotification, UserNotificationSettingValue } from '../../../../shared/models/users' | 35 | import { |
36 | User, | ||
37 | UserNotification, | ||
38 | UserNotificationSetting, | ||
39 | UserNotificationSettingValue, | ||
40 | UserNotificationType | ||
41 | } from '../../../../shared/models/users' | ||
35 | import { MockSmtpServer } from '../../../../shared/utils/miscs/email' | 42 | import { MockSmtpServer } from '../../../../shared/utils/miscs/email' |
36 | import { addUserSubscription } from '../../../../shared/utils/users/user-subscriptions' | 43 | import { addUserSubscription } from '../../../../shared/utils/users/user-subscriptions' |
37 | import { VideoPrivacy } from '../../../../shared/models/videos' | 44 | import { VideoPrivacy } from '../../../../shared/models/videos' |
38 | import { getYoutubeVideoUrl, importVideo } from '../../../../shared/utils/videos/video-imports' | 45 | import { getYoutubeVideoUrl, importVideo, getBadVideoUrl } from '../../../../shared/utils/videos/video-imports' |
39 | import { addVideoCommentReply, addVideoCommentThread } from '../../../../shared/utils/videos/video-comments' | 46 | import { addVideoCommentReply, addVideoCommentThread } from '../../../../shared/utils/videos/video-comments' |
47 | import * as uuidv4 from 'uuid/v4' | ||
48 | import { addAccountToAccountBlocklist, removeAccountFromAccountBlocklist } from '../../../../shared/utils/users/blocklist' | ||
40 | 49 | ||
41 | const expect = chai.expect | 50 | const expect = chai.expect |
42 | 51 | ||
43 | async function uploadVideoByRemoteAccount (servers: ServerInfo[], videoNameId: number, additionalParams: any = {}) { | 52 | async function uploadVideoByRemoteAccount (servers: ServerInfo[], additionalParams: any = {}) { |
44 | const data = Object.assign({ name: 'remote video ' + videoNameId }, additionalParams) | 53 | const name = 'remote video ' + uuidv4() |
54 | |||
55 | const data = Object.assign({ name }, additionalParams) | ||
45 | const res = await uploadVideo(servers[ 1 ].url, servers[ 1 ].accessToken, data) | 56 | const res = await uploadVideo(servers[ 1 ].url, servers[ 1 ].accessToken, data) |
46 | 57 | ||
47 | await waitJobs(servers) | 58 | await waitJobs(servers) |
48 | 59 | ||
49 | return res.body.video.uuid | 60 | return { uuid: res.body.video.uuid, name } |
50 | } | 61 | } |
51 | 62 | ||
52 | async function uploadVideoByLocalAccount (servers: ServerInfo[], videoNameId: number, additionalParams: any = {}) { | 63 | async function uploadVideoByLocalAccount (servers: ServerInfo[], additionalParams: any = {}) { |
53 | const data = Object.assign({ name: 'local video ' + videoNameId }, additionalParams) | 64 | const name = 'local video ' + uuidv4() |
65 | |||
66 | const data = Object.assign({ name }, additionalParams) | ||
54 | const res = await uploadVideo(servers[ 0 ].url, servers[ 0 ].accessToken, data) | 67 | const res = await uploadVideo(servers[ 0 ].url, servers[ 0 ].accessToken, data) |
55 | 68 | ||
56 | await waitJobs(servers) | 69 | await waitJobs(servers) |
57 | 70 | ||
58 | return res.body.video.uuid | 71 | return { uuid: res.body.video.uuid, name } |
59 | } | 72 | } |
60 | 73 | ||
61 | describe('Test users notifications', function () { | 74 | describe('Test users notifications', function () { |
@@ -63,7 +76,18 @@ describe('Test users notifications', function () { | |||
63 | let userAccessToken: string | 76 | let userAccessToken: string |
64 | let userNotifications: UserNotification[] = [] | 77 | let userNotifications: UserNotification[] = [] |
65 | let adminNotifications: UserNotification[] = [] | 78 | let adminNotifications: UserNotification[] = [] |
79 | let adminNotificationsServer2: UserNotification[] = [] | ||
66 | const emails: object[] = [] | 80 | const emails: object[] = [] |
81 | let channelId: number | ||
82 | |||
83 | const allNotificationSettings: UserNotificationSetting = { | ||
84 | myVideoPublished: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL, | ||
85 | myVideoImportFinished: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL, | ||
86 | newCommentOnMyVideo: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL, | ||
87 | newVideoFromSubscription: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL, | ||
88 | videoAbuseAsModerator: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL, | ||
89 | blacklistOnMyVideo: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL | ||
90 | } | ||
67 | 91 | ||
68 | before(async function () { | 92 | before(async function () { |
69 | this.timeout(120000) | 93 | this.timeout(120000) |
@@ -94,12 +118,9 @@ describe('Test users notifications', function () { | |||
94 | await createUser(servers[0].url, servers[0].accessToken, user.username, user.password, 10 * 1000 * 1000) | 118 | await createUser(servers[0].url, servers[0].accessToken, user.username, user.password, 10 * 1000 * 1000) |
95 | userAccessToken = await userLogin(servers[0], user) | 119 | userAccessToken = await userLogin(servers[0], user) |
96 | 120 | ||
97 | await updateMyNotificationSettings(servers[0].url, userAccessToken, { | 121 | await updateMyNotificationSettings(servers[0].url, userAccessToken, allNotificationSettings) |
98 | newCommentOnMyVideo: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL, | 122 | await updateMyNotificationSettings(servers[0].url, servers[0].accessToken, allNotificationSettings) |
99 | newVideoFromSubscription: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL, | 123 | await updateMyNotificationSettings(servers[1].url, servers[1].accessToken, allNotificationSettings) |
100 | blacklistOnMyVideo: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL, | ||
101 | videoAbuseAsModerator: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL | ||
102 | }) | ||
103 | 124 | ||
104 | { | 125 | { |
105 | const socket = getUserNotificationSocket(servers[ 0 ].url, userAccessToken) | 126 | const socket = getUserNotificationSocket(servers[ 0 ].url, userAccessToken) |
@@ -109,6 +130,15 @@ describe('Test users notifications', function () { | |||
109 | const socket = getUserNotificationSocket(servers[ 0 ].url, servers[0].accessToken) | 130 | const socket = getUserNotificationSocket(servers[ 0 ].url, servers[0].accessToken) |
110 | socket.on('new-notification', n => adminNotifications.push(n)) | 131 | socket.on('new-notification', n => adminNotifications.push(n)) |
111 | } | 132 | } |
133 | { | ||
134 | const socket = getUserNotificationSocket(servers[ 1 ].url, servers[1].accessToken) | ||
135 | socket.on('new-notification', n => adminNotificationsServer2.push(n)) | ||
136 | } | ||
137 | |||
138 | { | ||
139 | const resChannel = await getMyUserInformation(servers[0].url, servers[0].accessToken) | ||
140 | channelId = resChannel.body.videoChannels[0].id | ||
141 | } | ||
112 | }) | 142 | }) |
113 | 143 | ||
114 | describe('New video from my subscription notification', function () { | 144 | describe('New video from my subscription notification', function () { |
@@ -124,7 +154,7 @@ describe('Test users notifications', function () { | |||
124 | }) | 154 | }) |
125 | 155 | ||
126 | it('Should not send notifications if the user does not follow the video publisher', async function () { | 156 | it('Should not send notifications if the user does not follow the video publisher', async function () { |
127 | await uploadVideoByLocalAccount(servers, 1) | 157 | await uploadVideoByLocalAccount(servers) |
128 | 158 | ||
129 | const notification = await getLastNotification(servers[ 0 ].url, userAccessToken) | 159 | const notification = await getLastNotification(servers[ 0 ].url, userAccessToken) |
130 | expect(notification).to.be.undefined | 160 | expect(notification).to.be.undefined |
@@ -136,11 +166,8 @@ describe('Test users notifications', function () { | |||
136 | it('Should send a new video notification if the user follows the local video publisher', async function () { | 166 | it('Should send a new video notification if the user follows the local video publisher', async function () { |
137 | await addUserSubscription(servers[0].url, userAccessToken, 'root_channel@localhost:9001') | 167 | await addUserSubscription(servers[0].url, userAccessToken, 'root_channel@localhost:9001') |
138 | 168 | ||
139 | const videoNameId = 10 | 169 | const { name, uuid } = await uploadVideoByLocalAccount(servers) |
140 | const videoName = 'local video ' + videoNameId | 170 | await checkNewVideoFromSubscription(baseParams, name, uuid, 'presence') |
141 | |||
142 | const uuid = await uploadVideoByLocalAccount(servers, videoNameId) | ||
143 | await checkNewVideoFromSubscription(baseParams, videoName, uuid, 'presence') | ||
144 | }) | 171 | }) |
145 | 172 | ||
146 | it('Should send a new video notification from a remote account', async function () { | 173 | it('Should send a new video notification from a remote account', async function () { |
@@ -148,21 +175,13 @@ describe('Test users notifications', function () { | |||
148 | 175 | ||
149 | await addUserSubscription(servers[0].url, userAccessToken, 'root_channel@localhost:9002') | 176 | await addUserSubscription(servers[0].url, userAccessToken, 'root_channel@localhost:9002') |
150 | 177 | ||
151 | const videoNameId = 20 | 178 | const { name, uuid } = await uploadVideoByRemoteAccount(servers) |
152 | const videoName = 'remote video ' + videoNameId | 179 | await checkNewVideoFromSubscription(baseParams, name, uuid, 'presence') |
153 | |||
154 | const uuid = await uploadVideoByRemoteAccount(servers, videoNameId) | ||
155 | await waitJobs(servers) | ||
156 | |||
157 | await checkNewVideoFromSubscription(baseParams, videoName, uuid, 'presence') | ||
158 | }) | 180 | }) |
159 | 181 | ||
160 | it('Should send a new video notification on a scheduled publication', async function () { | 182 | it('Should send a new video notification on a scheduled publication', async function () { |
161 | this.timeout(20000) | 183 | this.timeout(20000) |
162 | 184 | ||
163 | const videoNameId = 30 | ||
164 | const videoName = 'local video ' + videoNameId | ||
165 | |||
166 | // In 2 seconds | 185 | // In 2 seconds |
167 | let updateAt = new Date(new Date().getTime() + 2000) | 186 | let updateAt = new Date(new Date().getTime() + 2000) |
168 | 187 | ||
@@ -173,18 +192,15 @@ describe('Test users notifications', function () { | |||
173 | privacy: VideoPrivacy.PUBLIC | 192 | privacy: VideoPrivacy.PUBLIC |
174 | } | 193 | } |
175 | } | 194 | } |
176 | const uuid = await uploadVideoByLocalAccount(servers, videoNameId, data) | 195 | const { name, uuid } = await uploadVideoByLocalAccount(servers, data) |
177 | 196 | ||
178 | await wait(6000) | 197 | await wait(6000) |
179 | await checkNewVideoFromSubscription(baseParams, videoName, uuid, 'presence') | 198 | await checkNewVideoFromSubscription(baseParams, name, uuid, 'presence') |
180 | }) | 199 | }) |
181 | 200 | ||
182 | it('Should send a new video notification on a remote scheduled publication', async function () { | 201 | it('Should send a new video notification on a remote scheduled publication', async function () { |
183 | this.timeout(20000) | 202 | this.timeout(20000) |
184 | 203 | ||
185 | const videoNameId = 40 | ||
186 | const videoName = 'remote video ' + videoNameId | ||
187 | |||
188 | // In 2 seconds | 204 | // In 2 seconds |
189 | let updateAt = new Date(new Date().getTime() + 2000) | 205 | let updateAt = new Date(new Date().getTime() + 2000) |
190 | 206 | ||
@@ -195,19 +211,16 @@ describe('Test users notifications', function () { | |||
195 | privacy: VideoPrivacy.PUBLIC | 211 | privacy: VideoPrivacy.PUBLIC |
196 | } | 212 | } |
197 | } | 213 | } |
198 | const uuid = await uploadVideoByRemoteAccount(servers, videoNameId, data) | 214 | const { name, uuid } = await uploadVideoByRemoteAccount(servers, data) |
199 | await waitJobs(servers) | 215 | await waitJobs(servers) |
200 | 216 | ||
201 | await wait(6000) | 217 | await wait(6000) |
202 | await checkNewVideoFromSubscription(baseParams, videoName, uuid, 'presence') | 218 | await checkNewVideoFromSubscription(baseParams, name, uuid, 'presence') |
203 | }) | 219 | }) |
204 | 220 | ||
205 | it('Should not send a notification before the video is published', async function () { | 221 | it('Should not send a notification before the video is published', async function () { |
206 | this.timeout(20000) | 222 | this.timeout(20000) |
207 | 223 | ||
208 | const videoNameId = 50 | ||
209 | const videoName = 'local video ' + videoNameId | ||
210 | |||
211 | let updateAt = new Date(new Date().getTime() + 100000) | 224 | let updateAt = new Date(new Date().getTime() + 100000) |
212 | 225 | ||
213 | const data = { | 226 | const data = { |
@@ -217,86 +230,70 @@ describe('Test users notifications', function () { | |||
217 | privacy: VideoPrivacy.PUBLIC | 230 | privacy: VideoPrivacy.PUBLIC |
218 | } | 231 | } |
219 | } | 232 | } |
220 | const uuid = await uploadVideoByLocalAccount(servers, videoNameId, data) | 233 | const { name, uuid } = await uploadVideoByLocalAccount(servers, data) |
221 | 234 | ||
222 | await wait(6000) | 235 | await wait(6000) |
223 | await checkNewVideoFromSubscription(baseParams, videoName, uuid, 'absence') | 236 | await checkNewVideoFromSubscription(baseParams, name, uuid, 'absence') |
224 | }) | 237 | }) |
225 | 238 | ||
226 | it('Should send a new video notification when a video becomes public', async function () { | 239 | it('Should send a new video notification when a video becomes public', async function () { |
227 | this.timeout(10000) | 240 | this.timeout(10000) |
228 | 241 | ||
229 | const videoNameId = 60 | ||
230 | const videoName = 'local video ' + videoNameId | ||
231 | |||
232 | const data = { privacy: VideoPrivacy.PRIVATE } | 242 | const data = { privacy: VideoPrivacy.PRIVATE } |
233 | const uuid = await uploadVideoByLocalAccount(servers, videoNameId, data) | 243 | const { name, uuid } = await uploadVideoByLocalAccount(servers, data) |
234 | 244 | ||
235 | await checkNewVideoFromSubscription(baseParams, videoName, uuid, 'absence') | 245 | await checkNewVideoFromSubscription(baseParams, name, uuid, 'absence') |
236 | 246 | ||
237 | await updateVideo(servers[0].url, servers[0].accessToken, uuid, { privacy: VideoPrivacy.PUBLIC }) | 247 | await updateVideo(servers[0].url, servers[0].accessToken, uuid, { privacy: VideoPrivacy.PUBLIC }) |
238 | 248 | ||
239 | await wait(500) | 249 | await wait(500) |
240 | await checkNewVideoFromSubscription(baseParams, videoName, uuid, 'presence') | 250 | await checkNewVideoFromSubscription(baseParams, name, uuid, 'presence') |
241 | }) | 251 | }) |
242 | 252 | ||
243 | it('Should send a new video notification when a remote video becomes public', async function () { | 253 | it('Should send a new video notification when a remote video becomes public', async function () { |
244 | this.timeout(20000) | 254 | this.timeout(20000) |
245 | 255 | ||
246 | const videoNameId = 70 | ||
247 | const videoName = 'remote video ' + videoNameId | ||
248 | |||
249 | const data = { privacy: VideoPrivacy.PRIVATE } | 256 | const data = { privacy: VideoPrivacy.PRIVATE } |
250 | const uuid = await uploadVideoByRemoteAccount(servers, videoNameId, data) | 257 | const { name, uuid } = await uploadVideoByRemoteAccount(servers, data) |
251 | await waitJobs(servers) | ||
252 | 258 | ||
253 | await checkNewVideoFromSubscription(baseParams, videoName, uuid, 'absence') | 259 | await checkNewVideoFromSubscription(baseParams, name, uuid, 'absence') |
254 | 260 | ||
255 | await updateVideo(servers[1].url, servers[1].accessToken, uuid, { privacy: VideoPrivacy.PUBLIC }) | 261 | await updateVideo(servers[1].url, servers[1].accessToken, uuid, { privacy: VideoPrivacy.PUBLIC }) |
256 | 262 | ||
257 | await waitJobs(servers) | 263 | await waitJobs(servers) |
258 | await checkNewVideoFromSubscription(baseParams, videoName, uuid, 'presence') | 264 | await checkNewVideoFromSubscription(baseParams, name, uuid, 'presence') |
259 | }) | 265 | }) |
260 | 266 | ||
261 | it('Should not send a new video notification when a video becomes unlisted', async function () { | 267 | it('Should not send a new video notification when a video becomes unlisted', async function () { |
262 | this.timeout(20000) | 268 | this.timeout(20000) |
263 | 269 | ||
264 | const videoNameId = 80 | ||
265 | const videoName = 'local video ' + videoNameId | ||
266 | |||
267 | const data = { privacy: VideoPrivacy.PRIVATE } | 270 | const data = { privacy: VideoPrivacy.PRIVATE } |
268 | const uuid = await uploadVideoByLocalAccount(servers, videoNameId, data) | 271 | const { name, uuid } = await uploadVideoByLocalAccount(servers, data) |
269 | 272 | ||
270 | await updateVideo(servers[0].url, servers[0].accessToken, uuid, { privacy: VideoPrivacy.UNLISTED }) | 273 | await updateVideo(servers[0].url, servers[0].accessToken, uuid, { privacy: VideoPrivacy.UNLISTED }) |
271 | 274 | ||
272 | await checkNewVideoFromSubscription(baseParams, videoName, uuid, 'absence') | 275 | await checkNewVideoFromSubscription(baseParams, name, uuid, 'absence') |
273 | }) | 276 | }) |
274 | 277 | ||
275 | it('Should not send a new video notification when a remote video becomes unlisted', async function () { | 278 | it('Should not send a new video notification when a remote video becomes unlisted', async function () { |
276 | this.timeout(20000) | 279 | this.timeout(20000) |
277 | 280 | ||
278 | const videoNameId = 90 | ||
279 | const videoName = 'remote video ' + videoNameId | ||
280 | |||
281 | const data = { privacy: VideoPrivacy.PRIVATE } | 281 | const data = { privacy: VideoPrivacy.PRIVATE } |
282 | const uuid = await uploadVideoByRemoteAccount(servers, videoNameId, data) | 282 | const { name, uuid } = await uploadVideoByRemoteAccount(servers, data) |
283 | await waitJobs(servers) | ||
284 | 283 | ||
285 | await updateVideo(servers[1].url, servers[1].accessToken, uuid, { privacy: VideoPrivacy.UNLISTED }) | 284 | await updateVideo(servers[1].url, servers[1].accessToken, uuid, { privacy: VideoPrivacy.UNLISTED }) |
286 | 285 | ||
287 | await waitJobs(servers) | 286 | await waitJobs(servers) |
288 | await checkNewVideoFromSubscription(baseParams, videoName, uuid, 'absence') | 287 | await checkNewVideoFromSubscription(baseParams, name, uuid, 'absence') |
289 | }) | 288 | }) |
290 | 289 | ||
291 | it('Should send a new video notification after a video import', async function () { | 290 | it('Should send a new video notification after a video import', async function () { |
292 | this.timeout(30000) | 291 | this.timeout(30000) |
293 | 292 | ||
294 | const resChannel = await getMyUserInformation(servers[0].url, servers[0].accessToken) | 293 | const name = 'video import ' + uuidv4() |
295 | const channelId = resChannel.body.videoChannels[0].id | ||
296 | const videoName = 'local video 100' | ||
297 | 294 | ||
298 | const attributes = { | 295 | const attributes = { |
299 | name: videoName, | 296 | name, |
300 | channelId, | 297 | channelId, |
301 | privacy: VideoPrivacy.PUBLIC, | 298 | privacy: VideoPrivacy.PUBLIC, |
302 | targetUrl: getYoutubeVideoUrl() | 299 | targetUrl: getYoutubeVideoUrl() |
@@ -306,7 +303,7 @@ describe('Test users notifications', function () { | |||
306 | 303 | ||
307 | await waitJobs(servers) | 304 | await waitJobs(servers) |
308 | 305 | ||
309 | await checkNewVideoFromSubscription(baseParams, videoName, uuid, 'presence') | 306 | await checkNewVideoFromSubscription(baseParams, name, uuid, 'presence') |
310 | }) | 307 | }) |
311 | }) | 308 | }) |
312 | 309 | ||
@@ -348,6 +345,23 @@ describe('Test users notifications', function () { | |||
348 | await checkNewCommentOnMyVideo(baseParams, uuid, commentId, commentId, 'absence') | 345 | await checkNewCommentOnMyVideo(baseParams, uuid, commentId, commentId, 'absence') |
349 | }) | 346 | }) |
350 | 347 | ||
348 | it('Should not send a new comment notification if the account is muted', async function () { | ||
349 | this.timeout(10000) | ||
350 | |||
351 | await addAccountToAccountBlocklist(servers[ 0 ].url, userAccessToken, 'root') | ||
352 | |||
353 | const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name: 'super video' }) | ||
354 | const uuid = resVideo.body.video.uuid | ||
355 | |||
356 | const resComment = await addVideoCommentThread(servers[0].url, servers[0].accessToken, uuid, 'comment') | ||
357 | const commentId = resComment.body.comment.id | ||
358 | |||
359 | await wait(500) | ||
360 | await checkNewCommentOnMyVideo(baseParams, uuid, commentId, commentId, 'absence') | ||
361 | |||
362 | await removeAccountFromAccountBlocklist(servers[ 0 ].url, userAccessToken, 'root') | ||
363 | }) | ||
364 | |||
351 | it('Should send a new comment notification after a local comment on my video', async function () { | 365 | it('Should send a new comment notification after a local comment on my video', async function () { |
352 | this.timeout(10000) | 366 | this.timeout(10000) |
353 | 367 | ||
@@ -425,23 +439,21 @@ describe('Test users notifications', function () { | |||
425 | it('Should send a notification to moderators on local video abuse', async function () { | 439 | it('Should send a notification to moderators on local video abuse', async function () { |
426 | this.timeout(10000) | 440 | this.timeout(10000) |
427 | 441 | ||
428 | const videoName = 'local video 110' | 442 | const name = 'video for abuse ' + uuidv4() |
429 | 443 | const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name }) | |
430 | const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name: videoName }) | ||
431 | const uuid = resVideo.body.video.uuid | 444 | const uuid = resVideo.body.video.uuid |
432 | 445 | ||
433 | await reportVideoAbuse(servers[0].url, servers[0].accessToken, uuid, 'super reason') | 446 | await reportVideoAbuse(servers[0].url, servers[0].accessToken, uuid, 'super reason') |
434 | 447 | ||
435 | await waitJobs(servers) | 448 | await waitJobs(servers) |
436 | await checkNewVideoAbuseForModerators(baseParams, uuid, videoName, 'presence') | 449 | await checkNewVideoAbuseForModerators(baseParams, uuid, name, 'presence') |
437 | }) | 450 | }) |
438 | 451 | ||
439 | it('Should send a notification to moderators on remote video abuse', async function () { | 452 | it('Should send a notification to moderators on remote video abuse', async function () { |
440 | this.timeout(10000) | 453 | this.timeout(10000) |
441 | 454 | ||
442 | const videoName = 'remote video 120' | 455 | const name = 'video for abuse ' + uuidv4() |
443 | 456 | const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name }) | |
444 | const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name: videoName }) | ||
445 | const uuid = resVideo.body.video.uuid | 457 | const uuid = resVideo.body.video.uuid |
446 | 458 | ||
447 | await waitJobs(servers) | 459 | await waitJobs(servers) |
@@ -449,7 +461,7 @@ describe('Test users notifications', function () { | |||
449 | await reportVideoAbuse(servers[1].url, servers[1].accessToken, uuid, 'super reason') | 461 | await reportVideoAbuse(servers[1].url, servers[1].accessToken, uuid, 'super reason') |
450 | 462 | ||
451 | await waitJobs(servers) | 463 | await waitJobs(servers) |
452 | await checkNewVideoAbuseForModerators(baseParams, uuid, videoName, 'presence') | 464 | await checkNewVideoAbuseForModerators(baseParams, uuid, name, 'presence') |
453 | }) | 465 | }) |
454 | }) | 466 | }) |
455 | 467 | ||
@@ -468,23 +480,21 @@ describe('Test users notifications', function () { | |||
468 | it('Should send a notification to video owner on blacklist', async function () { | 480 | it('Should send a notification to video owner on blacklist', async function () { |
469 | this.timeout(10000) | 481 | this.timeout(10000) |
470 | 482 | ||
471 | const videoName = 'local video 130' | 483 | const name = 'video for abuse ' + uuidv4() |
472 | 484 | const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name }) | |
473 | const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name: videoName }) | ||
474 | const uuid = resVideo.body.video.uuid | 485 | const uuid = resVideo.body.video.uuid |
475 | 486 | ||
476 | await addVideoToBlacklist(servers[0].url, servers[0].accessToken, uuid) | 487 | await addVideoToBlacklist(servers[0].url, servers[0].accessToken, uuid) |
477 | 488 | ||
478 | await waitJobs(servers) | 489 | await waitJobs(servers) |
479 | await checkNewBlacklistOnMyVideo(baseParams, uuid, videoName, 'blacklist') | 490 | await checkNewBlacklistOnMyVideo(baseParams, uuid, name, 'blacklist') |
480 | }) | 491 | }) |
481 | 492 | ||
482 | it('Should send a notification to video owner on unblacklist', async function () { | 493 | it('Should send a notification to video owner on unblacklist', async function () { |
483 | this.timeout(10000) | 494 | this.timeout(10000) |
484 | 495 | ||
485 | const videoName = 'local video 130' | 496 | const name = 'video for abuse ' + uuidv4() |
486 | 497 | const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name }) | |
487 | const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name: videoName }) | ||
488 | const uuid = resVideo.body.video.uuid | 498 | const uuid = resVideo.body.video.uuid |
489 | 499 | ||
490 | await addVideoToBlacklist(servers[0].url, servers[0].accessToken, uuid) | 500 | await addVideoToBlacklist(servers[0].url, servers[0].accessToken, uuid) |
@@ -494,38 +504,187 @@ describe('Test users notifications', function () { | |||
494 | await waitJobs(servers) | 504 | await waitJobs(servers) |
495 | 505 | ||
496 | await wait(500) | 506 | await wait(500) |
497 | await checkNewBlacklistOnMyVideo(baseParams, uuid, videoName, 'unblacklist') | 507 | await checkNewBlacklistOnMyVideo(baseParams, uuid, name, 'unblacklist') |
508 | }) | ||
509 | }) | ||
510 | |||
511 | describe('My video is published', function () { | ||
512 | let baseParams: CheckerBaseParams | ||
513 | |||
514 | before(() => { | ||
515 | baseParams = { | ||
516 | server: servers[1], | ||
517 | emails, | ||
518 | socketNotifications: adminNotificationsServer2, | ||
519 | token: servers[1].accessToken | ||
520 | } | ||
521 | }) | ||
522 | |||
523 | it('Should not send a notification if transcoding is not enabled', async function () { | ||
524 | const { name, uuid } = await uploadVideoByLocalAccount(servers) | ||
525 | await waitJobs(servers) | ||
526 | |||
527 | await checkVideoIsPublished(baseParams, name, uuid, 'absence') | ||
528 | }) | ||
529 | |||
530 | it('Should not send a notification if the wait transcoding is false', async function () { | ||
531 | this.timeout(50000) | ||
532 | |||
533 | await uploadVideoByRemoteAccount(servers, { waitTranscoding: false }) | ||
534 | await waitJobs(servers) | ||
535 | |||
536 | const notification = await getLastNotification(servers[ 0 ].url, userAccessToken) | ||
537 | if (notification) { | ||
538 | expect(notification.type).to.not.equal(UserNotificationType.MY_VIDEO_PUBLISHED) | ||
539 | } | ||
540 | }) | ||
541 | |||
542 | it('Should send a notification even if the video is not transcoded in other resolutions', async function () { | ||
543 | this.timeout(50000) | ||
544 | |||
545 | const { name, uuid } = await uploadVideoByRemoteAccount(servers, { waitTranscoding: true, fixture: 'video_short_240p.mp4' }) | ||
546 | await waitJobs(servers) | ||
547 | |||
548 | await checkVideoIsPublished(baseParams, name, uuid, 'presence') | ||
549 | }) | ||
550 | |||
551 | it('Should send a notification with a transcoded video', async function () { | ||
552 | this.timeout(50000) | ||
553 | |||
554 | const { name, uuid } = await uploadVideoByRemoteAccount(servers, { waitTranscoding: true }) | ||
555 | await waitJobs(servers) | ||
556 | |||
557 | await checkVideoIsPublished(baseParams, name, uuid, 'presence') | ||
558 | }) | ||
559 | |||
560 | it('Should send a notification when an imported video is transcoded', async function () { | ||
561 | this.timeout(50000) | ||
562 | |||
563 | const name = 'video import ' + uuidv4() | ||
564 | |||
565 | const attributes = { | ||
566 | name, | ||
567 | channelId, | ||
568 | privacy: VideoPrivacy.PUBLIC, | ||
569 | targetUrl: getYoutubeVideoUrl(), | ||
570 | waitTranscoding: true | ||
571 | } | ||
572 | const res = await importVideo(servers[1].url, servers[1].accessToken, attributes) | ||
573 | const uuid = res.body.video.uuid | ||
574 | |||
575 | await waitJobs(servers) | ||
576 | await checkVideoIsPublished(baseParams, name, uuid, 'presence') | ||
577 | }) | ||
578 | |||
579 | it('Should send a notification when the scheduled update has been proceeded', async function () { | ||
580 | this.timeout(70000) | ||
581 | |||
582 | // In 2 seconds | ||
583 | let updateAt = new Date(new Date().getTime() + 2000) | ||
584 | |||
585 | const data = { | ||
586 | privacy: VideoPrivacy.PRIVATE, | ||
587 | scheduleUpdate: { | ||
588 | updateAt: updateAt.toISOString(), | ||
589 | privacy: VideoPrivacy.PUBLIC | ||
590 | } | ||
591 | } | ||
592 | const { name, uuid } = await uploadVideoByRemoteAccount(servers, data) | ||
593 | |||
594 | await wait(6000) | ||
595 | await checkVideoIsPublished(baseParams, name, uuid, 'presence') | ||
596 | }) | ||
597 | }) | ||
598 | |||
599 | describe('My video is imported', function () { | ||
600 | let baseParams: CheckerBaseParams | ||
601 | |||
602 | before(() => { | ||
603 | baseParams = { | ||
604 | server: servers[0], | ||
605 | emails, | ||
606 | socketNotifications: adminNotifications, | ||
607 | token: servers[0].accessToken | ||
608 | } | ||
609 | }) | ||
610 | |||
611 | it('Should send a notification when the video import failed', async function () { | ||
612 | this.timeout(70000) | ||
613 | |||
614 | const name = 'video import ' + uuidv4() | ||
615 | |||
616 | const attributes = { | ||
617 | name, | ||
618 | channelId, | ||
619 | privacy: VideoPrivacy.PRIVATE, | ||
620 | targetUrl: getBadVideoUrl() | ||
621 | } | ||
622 | const res = await importVideo(servers[0].url, servers[0].accessToken, attributes) | ||
623 | const uuid = res.body.video.uuid | ||
624 | |||
625 | await waitJobs(servers) | ||
626 | await checkMyVideoImportIsFinished(baseParams, name, uuid, getBadVideoUrl(), false, 'presence') | ||
627 | }) | ||
628 | |||
629 | it('Should send a notification when the video import succeeded', async function () { | ||
630 | this.timeout(70000) | ||
631 | |||
632 | const name = 'video import ' + uuidv4() | ||
633 | |||
634 | const attributes = { | ||
635 | name, | ||
636 | channelId, | ||
637 | privacy: VideoPrivacy.PRIVATE, | ||
638 | targetUrl: getYoutubeVideoUrl() | ||
639 | } | ||
640 | const res = await importVideo(servers[0].url, servers[0].accessToken, attributes) | ||
641 | const uuid = res.body.video.uuid | ||
642 | |||
643 | await waitJobs(servers) | ||
644 | await checkMyVideoImportIsFinished(baseParams, name, uuid, getYoutubeVideoUrl(), true, 'presence') | ||
498 | }) | 645 | }) |
499 | }) | 646 | }) |
500 | 647 | ||
501 | describe('Mark as read', function () { | 648 | describe('Mark as read', function () { |
502 | it('Should mark as read some notifications', async function () { | 649 | it('Should mark as read some notifications', async function () { |
503 | const res = await getUserNotifications(servers[0].url, userAccessToken, 2, 3) | 650 | const res = await getUserNotifications(servers[ 0 ].url, userAccessToken, 2, 3) |
504 | const ids = res.body.data.map(n => n.id) | 651 | const ids = res.body.data.map(n => n.id) |
505 | 652 | ||
506 | await markAsReadNotifications(servers[0].url, userAccessToken, ids) | 653 | await markAsReadNotifications(servers[ 0 ].url, userAccessToken, ids) |
507 | }) | 654 | }) |
508 | 655 | ||
509 | it('Should have the notifications marked as read', async function () { | 656 | it('Should have the notifications marked as read', async function () { |
510 | const res = await getUserNotifications(servers[0].url, userAccessToken, 0, 10) | 657 | const res = await getUserNotifications(servers[ 0 ].url, userAccessToken, 0, 10) |
658 | |||
659 | const notifications = res.body.data as UserNotification[] | ||
660 | expect(notifications[ 0 ].read).to.be.false | ||
661 | expect(notifications[ 1 ].read).to.be.false | ||
662 | expect(notifications[ 2 ].read).to.be.true | ||
663 | expect(notifications[ 3 ].read).to.be.true | ||
664 | expect(notifications[ 4 ].read).to.be.true | ||
665 | expect(notifications[ 5 ].read).to.be.false | ||
666 | }) | ||
667 | |||
668 | it('Should only list read notifications', async function () { | ||
669 | const res = await getUserNotifications(servers[ 0 ].url, userAccessToken, 0, 10, false) | ||
511 | 670 | ||
512 | const notifications = res.body.data as UserNotification[] | 671 | const notifications = res.body.data as UserNotification[] |
513 | expect(notifications[0].read).to.be.false | 672 | for (const notification of notifications) { |
514 | expect(notifications[1].read).to.be.false | 673 | expect(notification.read).to.be.true |
515 | expect(notifications[2].read).to.be.true | 674 | } |
516 | expect(notifications[3].read).to.be.true | 675 | }) |
517 | expect(notifications[4].read).to.be.true | 676 | |
518 | expect(notifications[5].read).to.be.false | 677 | it('Should only list unread notifications', async function () { |
678 | const res = await getUserNotifications(servers[ 0 ].url, userAccessToken, 0, 10, true) | ||
679 | |||
680 | const notifications = res.body.data as UserNotification[] | ||
681 | for (const notification of notifications) { | ||
682 | expect(notification.read).to.be.false | ||
683 | } | ||
519 | }) | 684 | }) |
520 | }) | 685 | }) |
521 | 686 | ||
522 | describe('Notification settings', function () { | 687 | describe('Notification settings', function () { |
523 | const baseUpdateNotificationParams = { | ||
524 | newCommentOnMyVideo: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL, | ||
525 | newVideoFromSubscription: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL, | ||
526 | videoAbuseAsModerator: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL, | ||
527 | blacklistOnMyVideo: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL | ||
528 | } | ||
529 | let baseParams: CheckerBaseParams | 688 | let baseParams: CheckerBaseParams |
530 | 689 | ||
531 | before(() => { | 690 | before(() => { |
@@ -538,7 +697,7 @@ describe('Test users notifications', function () { | |||
538 | }) | 697 | }) |
539 | 698 | ||
540 | it('Should not have notifications', async function () { | 699 | it('Should not have notifications', async function () { |
541 | await updateMyNotificationSettings(servers[0].url, userAccessToken, immutableAssign(baseUpdateNotificationParams, { | 700 | await updateMyNotificationSettings(servers[0].url, userAccessToken, immutableAssign(allNotificationSettings, { |
542 | newVideoFromSubscription: UserNotificationSettingValue.NONE | 701 | newVideoFromSubscription: UserNotificationSettingValue.NONE |
543 | })) | 702 | })) |
544 | 703 | ||
@@ -548,16 +707,14 @@ describe('Test users notifications', function () { | |||
548 | expect(info.notificationSettings.newVideoFromSubscription).to.equal(UserNotificationSettingValue.NONE) | 707 | expect(info.notificationSettings.newVideoFromSubscription).to.equal(UserNotificationSettingValue.NONE) |
549 | } | 708 | } |
550 | 709 | ||
551 | const videoNameId = 42 | 710 | const { name, uuid } = await uploadVideoByLocalAccount(servers) |
552 | const videoName = 'local video ' + videoNameId | ||
553 | const uuid = await uploadVideoByLocalAccount(servers, videoNameId) | ||
554 | 711 | ||
555 | const check = { web: true, mail: true } | 712 | const check = { web: true, mail: true } |
556 | await checkNewVideoFromSubscription(immutableAssign(baseParams, { check }), videoName, uuid, 'absence') | 713 | await checkNewVideoFromSubscription(immutableAssign(baseParams, { check }), name, uuid, 'absence') |
557 | }) | 714 | }) |
558 | 715 | ||
559 | it('Should only have web notifications', async function () { | 716 | it('Should only have web notifications', async function () { |
560 | await updateMyNotificationSettings(servers[0].url, userAccessToken, immutableAssign(baseUpdateNotificationParams, { | 717 | await updateMyNotificationSettings(servers[0].url, userAccessToken, immutableAssign(allNotificationSettings, { |
561 | newVideoFromSubscription: UserNotificationSettingValue.WEB_NOTIFICATION | 718 | newVideoFromSubscription: UserNotificationSettingValue.WEB_NOTIFICATION |
562 | })) | 719 | })) |
563 | 720 | ||
@@ -567,23 +724,21 @@ describe('Test users notifications', function () { | |||
567 | expect(info.notificationSettings.newVideoFromSubscription).to.equal(UserNotificationSettingValue.WEB_NOTIFICATION) | 724 | expect(info.notificationSettings.newVideoFromSubscription).to.equal(UserNotificationSettingValue.WEB_NOTIFICATION) |
568 | } | 725 | } |
569 | 726 | ||
570 | const videoNameId = 52 | 727 | const { name, uuid } = await uploadVideoByLocalAccount(servers) |
571 | const videoName = 'local video ' + videoNameId | ||
572 | const uuid = await uploadVideoByLocalAccount(servers, videoNameId) | ||
573 | 728 | ||
574 | { | 729 | { |
575 | const check = { mail: true, web: false } | 730 | const check = { mail: true, web: false } |
576 | await checkNewVideoFromSubscription(immutableAssign(baseParams, { check }), videoName, uuid, 'absence') | 731 | await checkNewVideoFromSubscription(immutableAssign(baseParams, { check }), name, uuid, 'absence') |
577 | } | 732 | } |
578 | 733 | ||
579 | { | 734 | { |
580 | const check = { mail: false, web: true } | 735 | const check = { mail: false, web: true } |
581 | await checkNewVideoFromSubscription(immutableAssign(baseParams, { check }), videoName, uuid, 'presence') | 736 | await checkNewVideoFromSubscription(immutableAssign(baseParams, { check }), name, uuid, 'presence') |
582 | } | 737 | } |
583 | }) | 738 | }) |
584 | 739 | ||
585 | it('Should only have mail notifications', async function () { | 740 | it('Should only have mail notifications', async function () { |
586 | await updateMyNotificationSettings(servers[0].url, userAccessToken, immutableAssign(baseUpdateNotificationParams, { | 741 | await updateMyNotificationSettings(servers[0].url, userAccessToken, immutableAssign(allNotificationSettings, { |
587 | newVideoFromSubscription: UserNotificationSettingValue.EMAIL | 742 | newVideoFromSubscription: UserNotificationSettingValue.EMAIL |
588 | })) | 743 | })) |
589 | 744 | ||
@@ -593,23 +748,21 @@ describe('Test users notifications', function () { | |||
593 | expect(info.notificationSettings.newVideoFromSubscription).to.equal(UserNotificationSettingValue.EMAIL) | 748 | expect(info.notificationSettings.newVideoFromSubscription).to.equal(UserNotificationSettingValue.EMAIL) |
594 | } | 749 | } |
595 | 750 | ||
596 | const videoNameId = 62 | 751 | const { name, uuid } = await uploadVideoByLocalAccount(servers) |
597 | const videoName = 'local video ' + videoNameId | ||
598 | const uuid = await uploadVideoByLocalAccount(servers, videoNameId) | ||
599 | 752 | ||
600 | { | 753 | { |
601 | const check = { mail: false, web: true } | 754 | const check = { mail: false, web: true } |
602 | await checkNewVideoFromSubscription(immutableAssign(baseParams, { check }), videoName, uuid, 'absence') | 755 | await checkNewVideoFromSubscription(immutableAssign(baseParams, { check }), name, uuid, 'absence') |
603 | } | 756 | } |
604 | 757 | ||
605 | { | 758 | { |
606 | const check = { mail: true, web: false } | 759 | const check = { mail: true, web: false } |
607 | await checkNewVideoFromSubscription(immutableAssign(baseParams, { check }), videoName, uuid, 'presence') | 760 | await checkNewVideoFromSubscription(immutableAssign(baseParams, { check }), name, uuid, 'presence') |
608 | } | 761 | } |
609 | }) | 762 | }) |
610 | 763 | ||
611 | it('Should have email and web notifications', async function () { | 764 | it('Should have email and web notifications', async function () { |
612 | await updateMyNotificationSettings(servers[0].url, userAccessToken, immutableAssign(baseUpdateNotificationParams, { | 765 | await updateMyNotificationSettings(servers[0].url, userAccessToken, immutableAssign(allNotificationSettings, { |
613 | newVideoFromSubscription: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL | 766 | newVideoFromSubscription: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL |
614 | })) | 767 | })) |
615 | 768 | ||
@@ -619,11 +772,9 @@ describe('Test users notifications', function () { | |||
619 | expect(info.notificationSettings.newVideoFromSubscription).to.equal(UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL) | 772 | expect(info.notificationSettings.newVideoFromSubscription).to.equal(UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL) |
620 | } | 773 | } |
621 | 774 | ||
622 | const videoNameId = 72 | 775 | const { name, uuid } = await uploadVideoByLocalAccount(servers) |
623 | const videoName = 'local video ' + videoNameId | ||
624 | const uuid = await uploadVideoByLocalAccount(servers, videoNameId) | ||
625 | 776 | ||
626 | await checkNewVideoFromSubscription(baseParams, videoName, uuid, 'presence') | 777 | await checkNewVideoFromSubscription(baseParams, name, uuid, 'presence') |
627 | }) | 778 | }) |
628 | }) | 779 | }) |
629 | 780 | ||
diff --git a/server/tests/fixtures/video_short_240p.mp4 b/server/tests/fixtures/video_short_240p.mp4 new file mode 100644 index 000000000..db074940b --- /dev/null +++ b/server/tests/fixtures/video_short_240p.mp4 | |||
Binary files differ | |||
diff --git a/shared/models/users/user-notification-setting.model.ts b/shared/models/users/user-notification-setting.model.ts index 7cecd70a2..55d351abf 100644 --- a/shared/models/users/user-notification-setting.model.ts +++ b/shared/models/users/user-notification-setting.model.ts | |||
@@ -10,4 +10,6 @@ export interface UserNotificationSetting { | |||
10 | newCommentOnMyVideo: UserNotificationSettingValue | 10 | newCommentOnMyVideo: UserNotificationSettingValue |
11 | videoAbuseAsModerator: UserNotificationSettingValue | 11 | videoAbuseAsModerator: UserNotificationSettingValue |
12 | blacklistOnMyVideo: UserNotificationSettingValue | 12 | blacklistOnMyVideo: UserNotificationSettingValue |
13 | myVideoPublished: UserNotificationSettingValue | ||
14 | myVideoImportFinished: UserNotificationSettingValue | ||
13 | } | 15 | } |
diff --git a/shared/models/users/user-notification.model.ts b/shared/models/users/user-notification.model.ts index 39beb2350..ee9ac275a 100644 --- a/shared/models/users/user-notification.model.ts +++ b/shared/models/users/user-notification.model.ts | |||
@@ -3,10 +3,13 @@ export enum UserNotificationType { | |||
3 | NEW_COMMENT_ON_MY_VIDEO = 2, | 3 | NEW_COMMENT_ON_MY_VIDEO = 2, |
4 | NEW_VIDEO_ABUSE_FOR_MODERATORS = 3, | 4 | NEW_VIDEO_ABUSE_FOR_MODERATORS = 3, |
5 | BLACKLIST_ON_MY_VIDEO = 4, | 5 | BLACKLIST_ON_MY_VIDEO = 4, |
6 | UNBLACKLIST_ON_MY_VIDEO = 5 | 6 | UNBLACKLIST_ON_MY_VIDEO = 5, |
7 | MY_VIDEO_PUBLISHED = 6, | ||
8 | MY_VIDEO_IMPORT_SUCCESS = 7, | ||
9 | MY_VIDEO_IMPORT_ERROR = 8 | ||
7 | } | 10 | } |
8 | 11 | ||
9 | interface VideoInfo { | 12 | export interface VideoInfo { |
10 | id: number | 13 | id: number |
11 | uuid: string | 14 | uuid: string |
12 | name: string | 15 | name: string |
@@ -24,12 +27,22 @@ export interface UserNotification { | |||
24 | } | 27 | } |
25 | } | 28 | } |
26 | 29 | ||
30 | videoImport?: { | ||
31 | id: number | ||
32 | video?: VideoInfo | ||
33 | torrentName?: string | ||
34 | magnetUri?: string | ||
35 | targetUrl?: string | ||
36 | } | ||
37 | |||
27 | comment?: { | 38 | comment?: { |
28 | id: number | 39 | id: number |
40 | threadId: number | ||
29 | account: { | 41 | account: { |
30 | id: number | 42 | id: number |
31 | displayName: string | 43 | displayName: string |
32 | } | 44 | } |
45 | video: VideoInfo | ||
33 | } | 46 | } |
34 | 47 | ||
35 | videoAbuse?: { | 48 | videoAbuse?: { |
diff --git a/shared/utils/users/user-notifications.ts b/shared/utils/users/user-notifications.ts index dbe87559e..75d52023a 100644 --- a/shared/utils/users/user-notifications.ts +++ b/shared/utils/users/user-notifications.ts | |||
@@ -4,6 +4,7 @@ import { makeGetRequest, makePostBodyRequest, makePutBodyRequest } from '../requ | |||
4 | import { UserNotification, UserNotificationSetting, UserNotificationType } from '../../models/users' | 4 | import { UserNotification, UserNotificationSetting, UserNotificationType } from '../../models/users' |
5 | import { ServerInfo } from '..' | 5 | import { ServerInfo } from '..' |
6 | import { expect } from 'chai' | 6 | import { expect } from 'chai' |
7 | import { inspect } from 'util' | ||
7 | 8 | ||
8 | function updateMyNotificationSettings (url: string, token: string, settings: UserNotificationSetting, statusCodeExpected = 204) { | 9 | function updateMyNotificationSettings (url: string, token: string, settings: UserNotificationSetting, statusCodeExpected = 204) { |
9 | const path = '/api/v1/users/me/notification-settings' | 10 | const path = '/api/v1/users/me/notification-settings' |
@@ -17,7 +18,15 @@ function updateMyNotificationSettings (url: string, token: string, settings: Use | |||
17 | }) | 18 | }) |
18 | } | 19 | } |
19 | 20 | ||
20 | function getUserNotifications (url: string, token: string, start: number, count: number, sort = '-createdAt', statusCodeExpected = 200) { | 21 | function getUserNotifications ( |
22 | url: string, | ||
23 | token: string, | ||
24 | start: number, | ||
25 | count: number, | ||
26 | unread?: boolean, | ||
27 | sort = '-createdAt', | ||
28 | statusCodeExpected = 200 | ||
29 | ) { | ||
21 | const path = '/api/v1/users/me/notifications' | 30 | const path = '/api/v1/users/me/notifications' |
22 | 31 | ||
23 | return makeGetRequest({ | 32 | return makeGetRequest({ |
@@ -27,7 +36,8 @@ function getUserNotifications (url: string, token: string, start: number, count: | |||
27 | query: { | 36 | query: { |
28 | start, | 37 | start, |
29 | count, | 38 | count, |
30 | sort | 39 | sort, |
40 | unread | ||
31 | }, | 41 | }, |
32 | statusCodeExpected | 42 | statusCodeExpected |
33 | }) | 43 | }) |
@@ -46,7 +56,7 @@ function markAsReadNotifications (url: string, token: string, ids: number[], sta | |||
46 | } | 56 | } |
47 | 57 | ||
48 | async function getLastNotification (serverUrl: string, accessToken: string) { | 58 | async function getLastNotification (serverUrl: string, accessToken: string) { |
49 | const res = await getUserNotifications(serverUrl, accessToken, 0, 1, '-createdAt') | 59 | const res = await getUserNotifications(serverUrl, accessToken, 0, 1, undefined, '-createdAt') |
50 | 60 | ||
51 | if (res.body.total === 0) return undefined | 61 | if (res.body.total === 0) return undefined |
52 | 62 | ||
@@ -65,21 +75,33 @@ type CheckerType = 'presence' | 'absence' | |||
65 | 75 | ||
66 | async function checkNotification ( | 76 | async function checkNotification ( |
67 | base: CheckerBaseParams, | 77 | base: CheckerBaseParams, |
68 | lastNotificationChecker: (notification: UserNotification) => void, | 78 | notificationChecker: (notification: UserNotification, type: CheckerType) => void, |
69 | socketNotificationFinder: (notification: UserNotification) => boolean, | ||
70 | emailNotificationFinder: (email: object) => boolean, | 79 | emailNotificationFinder: (email: object) => boolean, |
71 | checkType: 'presence' | 'absence' | 80 | checkType: CheckerType |
72 | ) { | 81 | ) { |
73 | const check = base.check || { web: true, mail: true } | 82 | const check = base.check || { web: true, mail: true } |
74 | 83 | ||
75 | if (check.web) { | 84 | if (check.web) { |
76 | const notification = await getLastNotification(base.server.url, base.token) | 85 | const notification = await getLastNotification(base.server.url, base.token) |
77 | lastNotificationChecker(notification) | ||
78 | 86 | ||
79 | const socketNotification = base.socketNotifications.find(n => socketNotificationFinder(n)) | 87 | if (notification || checkType !== 'absence') { |
88 | notificationChecker(notification, checkType) | ||
89 | } | ||
80 | 90 | ||
81 | if (checkType === 'presence') expect(socketNotification, 'The socket notification is absent.').to.not.be.undefined | 91 | const socketNotification = base.socketNotifications.find(n => { |
82 | else expect(socketNotification, 'The socket notification is present.').to.be.undefined | 92 | try { |
93 | notificationChecker(n, 'presence') | ||
94 | return true | ||
95 | } catch { | ||
96 | return false | ||
97 | } | ||
98 | }) | ||
99 | |||
100 | if (checkType === 'presence') { | ||
101 | expect(socketNotification, 'The socket notification is absent. ' + inspect(base.socketNotifications)).to.not.be.undefined | ||
102 | } else { | ||
103 | expect(socketNotification, 'The socket notification is present. ' + inspect(socketNotification)).to.be.undefined | ||
104 | } | ||
83 | } | 105 | } |
84 | 106 | ||
85 | if (check.mail) { | 107 | if (check.mail) { |
@@ -89,45 +111,127 @@ async function checkNotification ( | |||
89 | .reverse() | 111 | .reverse() |
90 | .find(e => emailNotificationFinder(e)) | 112 | .find(e => emailNotificationFinder(e)) |
91 | 113 | ||
92 | if (checkType === 'presence') expect(email, 'The email is present.').to.not.be.undefined | 114 | if (checkType === 'presence') { |
93 | else expect(email, 'The email is absent.').to.be.undefined | 115 | expect(email, 'The email is absent. ' + inspect(base.emails)).to.not.be.undefined |
116 | } else { | ||
117 | expect(email, 'The email is present. ' + inspect(email)).to.be.undefined | ||
118 | } | ||
94 | } | 119 | } |
95 | } | 120 | } |
96 | 121 | ||
122 | function checkVideo (video: any, videoName?: string, videoUUID?: string) { | ||
123 | expect(video.name).to.be.a('string') | ||
124 | expect(video.name).to.not.be.empty | ||
125 | if (videoName) expect(video.name).to.equal(videoName) | ||
126 | |||
127 | expect(video.uuid).to.be.a('string') | ||
128 | expect(video.uuid).to.not.be.empty | ||
129 | if (videoUUID) expect(video.uuid).to.equal(videoUUID) | ||
130 | |||
131 | expect(video.id).to.be.a('number') | ||
132 | } | ||
133 | |||
134 | function checkActor (channel: any) { | ||
135 | expect(channel.id).to.be.a('number') | ||
136 | expect(channel.displayName).to.be.a('string') | ||
137 | expect(channel.displayName).to.not.be.empty | ||
138 | } | ||
139 | |||
140 | function checkComment (comment: any, commentId: number, threadId: number) { | ||
141 | expect(comment.id).to.equal(commentId) | ||
142 | expect(comment.threadId).to.equal(threadId) | ||
143 | } | ||
144 | |||
97 | async function checkNewVideoFromSubscription (base: CheckerBaseParams, videoName: string, videoUUID: string, type: CheckerType) { | 145 | async function checkNewVideoFromSubscription (base: CheckerBaseParams, videoName: string, videoUUID: string, type: CheckerType) { |
98 | const notificationType = UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION | 146 | const notificationType = UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION |
99 | 147 | ||
100 | function lastNotificationChecker (notification: UserNotification) { | 148 | function notificationChecker (notification: UserNotification, type: CheckerType) { |
101 | if (type === 'presence') { | 149 | if (type === 'presence') { |
102 | expect(notification).to.not.be.undefined | 150 | expect(notification).to.not.be.undefined |
103 | expect(notification.type).to.equal(notificationType) | 151 | expect(notification.type).to.equal(notificationType) |
104 | expect(notification.video.name).to.equal(videoName) | 152 | |
153 | checkVideo(notification.video, videoName, videoUUID) | ||
154 | checkActor(notification.video.channel) | ||
105 | } else { | 155 | } else { |
106 | expect(notification.video).to.satisfy(v => v === undefined || v.name !== videoName) | 156 | expect(notification.video).to.satisfy(v => v === undefined || v.name !== videoName) |
107 | } | 157 | } |
108 | } | 158 | } |
109 | 159 | ||
110 | function socketFinder (notification: UserNotification) { | 160 | function emailFinder (email: object) { |
111 | return notification.type === notificationType && notification.video.name === videoName | 161 | return email[ 'text' ].indexOf(videoUUID) !== -1 |
162 | } | ||
163 | |||
164 | await checkNotification(base, notificationChecker, emailFinder, type) | ||
165 | } | ||
166 | |||
167 | async function checkVideoIsPublished (base: CheckerBaseParams, videoName: string, videoUUID: string, type: CheckerType) { | ||
168 | const notificationType = UserNotificationType.MY_VIDEO_PUBLISHED | ||
169 | |||
170 | function notificationChecker (notification: UserNotification, type: CheckerType) { | ||
171 | if (type === 'presence') { | ||
172 | expect(notification).to.not.be.undefined | ||
173 | expect(notification.type).to.equal(notificationType) | ||
174 | |||
175 | checkVideo(notification.video, videoName, videoUUID) | ||
176 | checkActor(notification.video.channel) | ||
177 | } else { | ||
178 | expect(notification.video).to.satisfy(v => v === undefined || v.name !== videoName) | ||
179 | } | ||
112 | } | 180 | } |
113 | 181 | ||
114 | function emailFinder (email: object) { | 182 | function emailFinder (email: object) { |
115 | return email[ 'text' ].indexOf(videoUUID) !== -1 | 183 | const text: string = email[ 'text' ] |
184 | return text.includes(videoUUID) && text.includes('Your video') | ||
116 | } | 185 | } |
117 | 186 | ||
118 | await checkNotification(base, lastNotificationChecker, socketFinder, emailFinder, type) | 187 | await checkNotification(base, notificationChecker, emailFinder, type) |
188 | } | ||
189 | |||
190 | async function checkMyVideoImportIsFinished ( | ||
191 | base: CheckerBaseParams, | ||
192 | videoName: string, | ||
193 | videoUUID: string, | ||
194 | url: string, | ||
195 | success: boolean, | ||
196 | type: CheckerType | ||
197 | ) { | ||
198 | const notificationType = success ? UserNotificationType.MY_VIDEO_IMPORT_SUCCESS : UserNotificationType.MY_VIDEO_IMPORT_ERROR | ||
199 | |||
200 | function notificationChecker (notification: UserNotification, type: CheckerType) { | ||
201 | if (type === 'presence') { | ||
202 | expect(notification).to.not.be.undefined | ||
203 | expect(notification.type).to.equal(notificationType) | ||
204 | |||
205 | expect(notification.videoImport.targetUrl).to.equal(url) | ||
206 | |||
207 | if (success) checkVideo(notification.videoImport.video, videoName, videoUUID) | ||
208 | } else { | ||
209 | expect(notification.videoImport).to.satisfy(i => i === undefined || i.targetUrl !== url) | ||
210 | } | ||
211 | } | ||
212 | |||
213 | function emailFinder (email: object) { | ||
214 | const text: string = email[ 'text' ] | ||
215 | const toFind = success ? ' finished' : ' error' | ||
216 | |||
217 | return text.includes(url) && text.includes(toFind) | ||
218 | } | ||
219 | |||
220 | await checkNotification(base, notificationChecker, emailFinder, type) | ||
119 | } | 221 | } |
120 | 222 | ||
121 | let lastEmailCount = 0 | 223 | let lastEmailCount = 0 |
122 | async function checkNewCommentOnMyVideo (base: CheckerBaseParams, uuid: string, commentId: number, threadId: number, type: CheckerType) { | 224 | async function checkNewCommentOnMyVideo (base: CheckerBaseParams, uuid: string, commentId: number, threadId: number, type: CheckerType) { |
123 | const notificationType = UserNotificationType.NEW_COMMENT_ON_MY_VIDEO | 225 | const notificationType = UserNotificationType.NEW_COMMENT_ON_MY_VIDEO |
124 | 226 | ||
125 | function lastNotificationChecker (notification: UserNotification) { | 227 | function notificationChecker (notification: UserNotification, type: CheckerType) { |
126 | if (type === 'presence') { | 228 | if (type === 'presence') { |
127 | expect(notification).to.not.be.undefined | 229 | expect(notification).to.not.be.undefined |
128 | expect(notification.type).to.equal(notificationType) | 230 | expect(notification.type).to.equal(notificationType) |
129 | expect(notification.comment.id).to.equal(commentId) | 231 | |
130 | expect(notification.comment.account.displayName).to.equal('root') | 232 | checkComment(notification.comment, commentId, threadId) |
233 | checkActor(notification.comment.account) | ||
234 | checkVideo(notification.comment.video, undefined, uuid) | ||
131 | } else { | 235 | } else { |
132 | expect(notification).to.satisfy((n: UserNotification) => { | 236 | expect(notification).to.satisfy((n: UserNotification) => { |
133 | return n === undefined || n.comment === undefined || n.comment.id !== commentId | 237 | return n === undefined || n.comment === undefined || n.comment.id !== commentId |
@@ -135,18 +239,12 @@ async function checkNewCommentOnMyVideo (base: CheckerBaseParams, uuid: string, | |||
135 | } | 239 | } |
136 | } | 240 | } |
137 | 241 | ||
138 | function socketFinder (notification: UserNotification) { | ||
139 | return notification.type === notificationType && | ||
140 | notification.comment.id === commentId && | ||
141 | notification.comment.account.displayName === 'root' | ||
142 | } | ||
143 | |||
144 | const commentUrl = `http://localhost:9001/videos/watch/${uuid};threadId=${threadId}` | 242 | const commentUrl = `http://localhost:9001/videos/watch/${uuid};threadId=${threadId}` |
145 | function emailFinder (email: object) { | 243 | function emailFinder (email: object) { |
146 | return email[ 'text' ].indexOf(commentUrl) !== -1 | 244 | return email[ 'text' ].indexOf(commentUrl) !== -1 |
147 | } | 245 | } |
148 | 246 | ||
149 | await checkNotification(base, lastNotificationChecker, socketFinder, emailFinder, type) | 247 | await checkNotification(base, notificationChecker, emailFinder, type) |
150 | 248 | ||
151 | if (type === 'presence') { | 249 | if (type === 'presence') { |
152 | // We cannot detect email duplicates, so check we received another email | 250 | // We cannot detect email duplicates, so check we received another email |
@@ -158,12 +256,13 @@ async function checkNewCommentOnMyVideo (base: CheckerBaseParams, uuid: string, | |||
158 | async function checkNewVideoAbuseForModerators (base: CheckerBaseParams, videoUUID: string, videoName: string, type: CheckerType) { | 256 | async function checkNewVideoAbuseForModerators (base: CheckerBaseParams, videoUUID: string, videoName: string, type: CheckerType) { |
159 | const notificationType = UserNotificationType.NEW_VIDEO_ABUSE_FOR_MODERATORS | 257 | const notificationType = UserNotificationType.NEW_VIDEO_ABUSE_FOR_MODERATORS |
160 | 258 | ||
161 | function lastNotificationChecker (notification: UserNotification) { | 259 | function notificationChecker (notification: UserNotification, type: CheckerType) { |
162 | if (type === 'presence') { | 260 | if (type === 'presence') { |
163 | expect(notification).to.not.be.undefined | 261 | expect(notification).to.not.be.undefined |
164 | expect(notification.type).to.equal(notificationType) | 262 | expect(notification.type).to.equal(notificationType) |
165 | expect(notification.videoAbuse.video.uuid).to.equal(videoUUID) | 263 | |
166 | expect(notification.videoAbuse.video.name).to.equal(videoName) | 264 | expect(notification.videoAbuse.id).to.be.a('number') |
265 | checkVideo(notification.videoAbuse.video, videoName, videoUUID) | ||
167 | } else { | 266 | } else { |
168 | expect(notification).to.satisfy((n: UserNotification) => { | 267 | expect(notification).to.satisfy((n: UserNotification) => { |
169 | return n === undefined || n.videoAbuse === undefined || n.videoAbuse.video.uuid !== videoUUID | 268 | return n === undefined || n.videoAbuse === undefined || n.videoAbuse.video.uuid !== videoUUID |
@@ -171,16 +270,12 @@ async function checkNewVideoAbuseForModerators (base: CheckerBaseParams, videoUU | |||
171 | } | 270 | } |
172 | } | 271 | } |
173 | 272 | ||
174 | function socketFinder (notification: UserNotification) { | ||
175 | return notification.type === notificationType && notification.videoAbuse.video.uuid === videoUUID | ||
176 | } | ||
177 | |||
178 | function emailFinder (email: object) { | 273 | function emailFinder (email: object) { |
179 | const text = email[ 'text' ] | 274 | const text = email[ 'text' ] |
180 | return text.indexOf(videoUUID) !== -1 && text.indexOf('abuse') !== -1 | 275 | return text.indexOf(videoUUID) !== -1 && text.indexOf('abuse') !== -1 |
181 | } | 276 | } |
182 | 277 | ||
183 | await checkNotification(base, lastNotificationChecker, socketFinder, emailFinder, type) | 278 | await checkNotification(base, notificationChecker, emailFinder, type) |
184 | } | 279 | } |
185 | 280 | ||
186 | async function checkNewBlacklistOnMyVideo ( | 281 | async function checkNewBlacklistOnMyVideo ( |
@@ -193,18 +288,13 @@ async function checkNewBlacklistOnMyVideo ( | |||
193 | ? UserNotificationType.BLACKLIST_ON_MY_VIDEO | 288 | ? UserNotificationType.BLACKLIST_ON_MY_VIDEO |
194 | : UserNotificationType.UNBLACKLIST_ON_MY_VIDEO | 289 | : UserNotificationType.UNBLACKLIST_ON_MY_VIDEO |
195 | 290 | ||
196 | function lastNotificationChecker (notification: UserNotification) { | 291 | function notificationChecker (notification: UserNotification) { |
197 | expect(notification).to.not.be.undefined | 292 | expect(notification).to.not.be.undefined |
198 | expect(notification.type).to.equal(notificationType) | 293 | expect(notification.type).to.equal(notificationType) |
199 | 294 | ||
200 | const video = blacklistType === 'blacklist' ? notification.videoBlacklist.video : notification.video | 295 | const video = blacklistType === 'blacklist' ? notification.videoBlacklist.video : notification.video |
201 | 296 | ||
202 | expect(video.uuid).to.equal(videoUUID) | 297 | checkVideo(video, videoName, videoUUID) |
203 | expect(video.name).to.equal(videoName) | ||
204 | } | ||
205 | |||
206 | function socketFinder (notification: UserNotification) { | ||
207 | return notification.type === notificationType && (notification.video || notification.videoBlacklist.video).uuid === videoUUID | ||
208 | } | 298 | } |
209 | 299 | ||
210 | function emailFinder (email: object) { | 300 | function emailFinder (email: object) { |
@@ -212,7 +302,7 @@ async function checkNewBlacklistOnMyVideo ( | |||
212 | return text.indexOf(videoUUID) !== -1 && text.indexOf(' ' + blacklistType) !== -1 | 302 | return text.indexOf(videoUUID) !== -1 && text.indexOf(' ' + blacklistType) !== -1 |
213 | } | 303 | } |
214 | 304 | ||
215 | await checkNotification(base, lastNotificationChecker, socketFinder, emailFinder, 'presence') | 305 | await checkNotification(base, notificationChecker, emailFinder, 'presence') |
216 | } | 306 | } |
217 | 307 | ||
218 | // --------------------------------------------------------------------------- | 308 | // --------------------------------------------------------------------------- |
@@ -221,6 +311,8 @@ export { | |||
221 | CheckerBaseParams, | 311 | CheckerBaseParams, |
222 | CheckerType, | 312 | CheckerType, |
223 | checkNotification, | 313 | checkNotification, |
314 | checkMyVideoImportIsFinished, | ||
315 | checkVideoIsPublished, | ||
224 | checkNewVideoFromSubscription, | 316 | checkNewVideoFromSubscription, |
225 | checkNewCommentOnMyVideo, | 317 | checkNewCommentOnMyVideo, |
226 | checkNewBlacklistOnMyVideo, | 318 | checkNewBlacklistOnMyVideo, |
diff --git a/shared/utils/videos/video-imports.ts b/shared/utils/videos/video-imports.ts index 3fa49b432..ec77cdcda 100644 --- a/shared/utils/videos/video-imports.ts +++ b/shared/utils/videos/video-imports.ts | |||
@@ -11,6 +11,10 @@ function getMagnetURI () { | |||
11 | return 'magnet:?xs=https%3A%2F%2Fpeertube2.cpy.re%2Fstatic%2Ftorrents%2Fb209ca00-c8bb-4b2b-b421-1ede169f3dbc-720.torrent&xt=urn:btih:0f498834733e8057ed5c6f2ee2b4efd8d84a76ee&dn=super+peertube2+video&tr=wss%3A%2F%2Fpeertube2.cpy.re%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fpeertube2.cpy.re%2Ftracker%2Fannounce&ws=https%3A%2F%2Fpeertube2.cpy.re%2Fstatic%2Fwebseed%2Fb209ca00-c8bb-4b2b-b421-1ede169f3dbc-720.mp4' | 11 | return 'magnet:?xs=https%3A%2F%2Fpeertube2.cpy.re%2Fstatic%2Ftorrents%2Fb209ca00-c8bb-4b2b-b421-1ede169f3dbc-720.torrent&xt=urn:btih:0f498834733e8057ed5c6f2ee2b4efd8d84a76ee&dn=super+peertube2+video&tr=wss%3A%2F%2Fpeertube2.cpy.re%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fpeertube2.cpy.re%2Ftracker%2Fannounce&ws=https%3A%2F%2Fpeertube2.cpy.re%2Fstatic%2Fwebseed%2Fb209ca00-c8bb-4b2b-b421-1ede169f3dbc-720.mp4' |
12 | } | 12 | } |
13 | 13 | ||
14 | function getBadVideoUrl () { | ||
15 | return 'https://download.cpy.re/peertube/bad_video.mp4' | ||
16 | } | ||
17 | |||
14 | function importVideo (url: string, token: string, attributes: VideoImportCreate) { | 18 | function importVideo (url: string, token: string, attributes: VideoImportCreate) { |
15 | const path = '/api/v1/videos/imports' | 19 | const path = '/api/v1/videos/imports' |
16 | 20 | ||
@@ -45,6 +49,7 @@ function getMyVideoImports (url: string, token: string, sort?: string) { | |||
45 | // --------------------------------------------------------------------------- | 49 | // --------------------------------------------------------------------------- |
46 | 50 | ||
47 | export { | 51 | export { |
52 | getBadVideoUrl, | ||
48 | getYoutubeVideoUrl, | 53 | getYoutubeVideoUrl, |
49 | importVideo, | 54 | importVideo, |
50 | getMagnetURI, | 55 | getMagnetURI, |