]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/commitdiff
Add import finished and video published notifs
authorChocobozzz <me@florianbigard.com>
Wed, 2 Jan 2019 15:37:43 +0000 (16:37 +0100)
committerChocobozzz <chocobozzz@cpy.re>
Wed, 9 Jan 2019 10:15:15 +0000 (11:15 +0100)
23 files changed:
server/controllers/api/users/my-notifications.ts
server/initializers/migrations/0315-user-notifications.ts
server/lib/emailer.ts
server/lib/job-queue/handlers/video-file.ts
server/lib/job-queue/handlers/video-import.ts
server/lib/notifier.ts
server/lib/schedulers/update-videos-scheduler.ts
server/lib/user.ts
server/middlewares/validators/user-notifications.ts
server/models/account/account-blocklist.ts
server/models/account/user-notification-setting.ts
server/models/account/user-notification.ts
server/models/account/user.ts
server/models/video/video-file.ts
server/models/video/video-import.ts
server/models/video/video.ts
server/tests/api/check-params/user-notifications.ts
server/tests/api/users/user-notifications.ts
server/tests/fixtures/video_short_240p.mp4 [new file with mode: 0644]
shared/models/users/user-notification-setting.model.ts
shared/models/users/user-notification.model.ts
shared/utils/users/user-notifications.ts
shared/utils/videos/video-imports.ts

index cef1d237c0f3725c859ef3ea69c6a8ac15e40e7f..4b81777a47d27e019622a7ddd137567179c6c1eb 100644 (file)
@@ -14,10 +14,11 @@ import { getFormattedObjects } from '../../../helpers/utils'
 import { UserNotificationModel } from '../../../models/account/user-notification'
 import { meRouter } from './me'
 import {
 import { UserNotificationModel } from '../../../models/account/user-notification'
 import { meRouter } from './me'
 import {
+  listUserNotificationsValidator,
   markAsReadUserNotificationsValidator,
   updateNotificationSettingsValidator
 } from '../../../middlewares/validators/user-notifications'
   markAsReadUserNotificationsValidator,
   updateNotificationSettingsValidator
 } from '../../../middlewares/validators/user-notifications'
-import { UserNotificationSetting } from '../../../../shared/models/users'
+import { UserNotificationSetting, UserNotificationSettingValue } from '../../../../shared/models/users'
 import { UserNotificationSettingModel } from '../../../models/account/user-notification-setting'
 
 const myNotificationsRouter = express.Router()
 import { UserNotificationSettingModel } from '../../../models/account/user-notification-setting'
 
 const myNotificationsRouter = express.Router()
@@ -34,6 +35,7 @@ myNotificationsRouter.get('/me/notifications',
   userNotificationsSortValidator,
   setDefaultSort,
   setDefaultPagination,
   userNotificationsSortValidator,
   setDefaultSort,
   setDefaultPagination,
+  listUserNotificationsValidator,
   asyncMiddleware(listUserNotifications)
 )
 
   asyncMiddleware(listUserNotifications)
 )
 
@@ -61,7 +63,11 @@ async function updateNotificationSettings (req: express.Request, res: express.Re
 
   await UserNotificationSettingModel.update({
     newVideoFromSubscription: body.newVideoFromSubscription,
 
   await UserNotificationSettingModel.update({
     newVideoFromSubscription: body.newVideoFromSubscription,
-    newCommentOnMyVideo: body.newCommentOnMyVideo
+    newCommentOnMyVideo: body.newCommentOnMyVideo,
+    videoAbuseAsModerator: body.videoAbuseAsModerator,
+    blacklistOnMyVideo: body.blacklistOnMyVideo,
+    myVideoPublished: body.myVideoPublished,
+    myVideoImportFinished: body.myVideoImportFinished
   }, query)
 
   return res.status(204).end()
   }, query)
 
   return res.status(204).end()
@@ -70,7 +76,7 @@ async function updateNotificationSettings (req: express.Request, res: express.Re
 async function listUserNotifications (req: express.Request, res: express.Response) {
   const user: UserModel = res.locals.oauth.token.User
 
 async function listUserNotifications (req: express.Request, res: express.Response) {
   const user: UserModel = res.locals.oauth.token.User
 
-  const resultList = await UserNotificationModel.listForApi(user.id, req.query.start, req.query.count, req.query.sort)
+  const resultList = await UserNotificationModel.listForApi(user.id, req.query.start, req.query.count, req.query.sort, req.query.unread)
 
   return res.json(getFormattedObjects(resultList.data, resultList.total))
 }
 
   return res.json(getFormattedObjects(resultList.data, resultList.total))
 }
index 2bd9c657d40d236e5e2f6754f08c164fc4db9c3b..8c54c5d6c9e5d48067b314f05188ed3f36c90d82 100644 (file)
@@ -13,6 +13,8 @@ CREATE TABLE IF NOT EXISTS "userNotificationSetting" ("id" SERIAL,
 "newCommentOnMyVideo" INTEGER NOT NULL DEFAULT NULL,
 "videoAbuseAsModerator" INTEGER NOT NULL DEFAULT NULL,
 "blacklistOnMyVideo" INTEGER NOT NULL DEFAULT NULL,
 "newCommentOnMyVideo" INTEGER NOT NULL DEFAULT NULL,
 "videoAbuseAsModerator" INTEGER NOT NULL DEFAULT NULL,
 "blacklistOnMyVideo" INTEGER NOT NULL DEFAULT NULL,
+"myVideoPublished" INTEGER NOT NULL DEFAULT NULL,
+"myVideoImportFinished" INTEGER NOT NULL DEFAULT NULL,
 "userId" INTEGER REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
 "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL,
 "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL,
 "userId" INTEGER REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
 "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL,
 "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL,
@@ -24,8 +26,8 @@ PRIMARY KEY ("id"))
   {
     const query = 'INSERT INTO "userNotificationSetting" ' +
       '("newVideoFromSubscription", "newCommentOnMyVideo", "videoAbuseAsModerator", "blacklistOnMyVideo", ' +
   {
     const query = 'INSERT INTO "userNotificationSetting" ' +
       '("newVideoFromSubscription", "newCommentOnMyVideo", "videoAbuseAsModerator", "blacklistOnMyVideo", ' +
-      '"userId", "createdAt", "updatedAt") ' +
-      '(SELECT 2, 2, 4, 4, id, NOW(), NOW() FROM "user")'
+      '"myVideoPublished", "myVideoImportFinished", "userId", "createdAt", "updatedAt") ' +
+      '(SELECT 2, 2, 4, 4, 2, 2, id, NOW(), NOW() FROM "user")'
 
     await utils.sequelize.query(query)
   }
 
     await utils.sequelize.query(query)
   }
index d766e655b2d902763320a7800a665650a5f30486..6dc8f2adf094c6799c00b585875249e136f50955 100644 (file)
@@ -10,6 +10,7 @@ import { readFileSync } from 'fs-extra'
 import { VideoCommentModel } from '../models/video/video-comment'
 import { VideoAbuseModel } from '../models/video/video-abuse'
 import { VideoBlacklistModel } from '../models/video/video-blacklist'
 import { VideoCommentModel } from '../models/video/video-comment'
 import { VideoAbuseModel } from '../models/video/video-abuse'
 import { VideoBlacklistModel } from '../models/video/video-blacklist'
+import { VideoImportModel } from '../models/video/video-import'
 
 class Emailer {
 
 
 class Emailer {
 
@@ -102,6 +103,66 @@ class Emailer {
     return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
   }
 
     return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
   }
 
+  myVideoPublishedNotification (to: string[], video: VideoModel) {
+    const videoUrl = CONFIG.WEBSERVER.URL + video.getWatchStaticPath()
+
+    const text = `Hi dear user,\n\n` +
+      `Your video ${video.name} has been published.` +
+      `\n\n` +
+      `You can view it on ${videoUrl} ` +
+      `\n\n` +
+      `Cheers,\n` +
+      `PeerTube.`
+
+    const emailPayload: EmailPayload = {
+      to,
+      subject: `Your video ${video.name} is published`,
+      text
+    }
+
+    return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
+  }
+
+  myVideoImportSuccessNotification (to: string[], videoImport: VideoImportModel) {
+    const videoUrl = CONFIG.WEBSERVER.URL + videoImport.Video.getWatchStaticPath()
+
+    const text = `Hi dear user,\n\n` +
+      `Your video import ${videoImport.getTargetIdentifier()} is finished.` +
+      `\n\n` +
+      `You can view the imported video on ${videoUrl} ` +
+      `\n\n` +
+      `Cheers,\n` +
+      `PeerTube.`
+
+    const emailPayload: EmailPayload = {
+      to,
+      subject: `Your video import ${videoImport.getTargetIdentifier()} is finished`,
+      text
+    }
+
+    return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
+  }
+
+  myVideoImportErrorNotification (to: string[], videoImport: VideoImportModel) {
+    const importUrl = CONFIG.WEBSERVER.URL + '/my-account/video-imports'
+
+    const text = `Hi dear user,\n\n` +
+      `Your video import ${videoImport.getTargetIdentifier()} encountered an error.` +
+      `\n\n` +
+      `See your videos import dashboard for more information: ${importUrl}` +
+      `\n\n` +
+      `Cheers,\n` +
+      `PeerTube.`
+
+    const emailPayload: EmailPayload = {
+      to,
+      subject: `Your video import ${videoImport.getTargetIdentifier()} encountered an error`,
+      text
+    }
+
+    return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
+  }
+
   addNewCommentOnMyVideoNotification (to: string[], comment: VideoCommentModel) {
     const accountName = comment.Account.getDisplayName()
     const video = comment.Video
   addNewCommentOnMyVideoNotification (to: string[], comment: VideoCommentModel) {
     const accountName = comment.Account.getDisplayName()
     const video = comment.Video
index 480d324dc290dae43a2fa4cb54147c096dc3fe3d..593e43cc5fe95abeb151e932a3bc1da293930584 100644 (file)
@@ -68,17 +68,17 @@ async function processVideoFile (job: Bull.Job) {
 async function onVideoFileTranscoderOrImportSuccess (video: VideoModel) {
   if (video === undefined) return undefined
 
 async function onVideoFileTranscoderOrImportSuccess (video: VideoModel) {
   if (video === undefined) return undefined
 
-  const { videoDatabase, isNewVideo } = await sequelizeTypescript.transaction(async t => {
+  const { videoDatabase, videoPublished } = await sequelizeTypescript.transaction(async t => {
     // Maybe the video changed in database, refresh it
     let videoDatabase = await VideoModel.loadAndPopulateAccountAndServerAndTags(video.uuid, t)
     // Video does not exist anymore
     if (!videoDatabase) return undefined
 
     // Maybe the video changed in database, refresh it
     let videoDatabase = await VideoModel.loadAndPopulateAccountAndServerAndTags(video.uuid, t)
     // Video does not exist anymore
     if (!videoDatabase) return undefined
 
-    let isNewVideo = false
+    let videoPublished = false
 
     // We transcoded the video file in another format, now we can publish it
     if (videoDatabase.state !== VideoState.PUBLISHED) {
 
     // We transcoded the video file in another format, now we can publish it
     if (videoDatabase.state !== VideoState.PUBLISHED) {
-      isNewVideo = true
+      videoPublished = true
 
       videoDatabase.state = VideoState.PUBLISHED
       videoDatabase.publishedAt = new Date()
 
       videoDatabase.state = VideoState.PUBLISHED
       videoDatabase.publishedAt = new Date()
@@ -86,12 +86,15 @@ async function onVideoFileTranscoderOrImportSuccess (video: VideoModel) {
     }
 
     // If the video was not published, we consider it is a new one for other instances
     }
 
     // If the video was not published, we consider it is a new one for other instances
-    await federateVideoIfNeeded(videoDatabase, isNewVideo, t)
+    await federateVideoIfNeeded(videoDatabase, videoPublished, t)
 
 
-    return { videoDatabase, isNewVideo }
+    return { videoDatabase, videoPublished }
   })
 
   })
 
-  if (isNewVideo) Notifier.Instance.notifyOnNewVideo(videoDatabase)
+  if (videoPublished) {
+    Notifier.Instance.notifyOnNewVideo(videoDatabase)
+    Notifier.Instance.notifyOnPendingVideoPublished(videoDatabase)
+  }
 }
 
 async function onVideoFileOptimizerSuccess (videoArg: VideoModel, isNewVideo: boolean) {
 }
 
 async function onVideoFileOptimizerSuccess (videoArg: VideoModel, isNewVideo: boolean) {
@@ -100,7 +103,7 @@ async function onVideoFileOptimizerSuccess (videoArg: VideoModel, isNewVideo: bo
   // Outside the transaction (IO on disk)
   const { videoFileResolution } = await videoArg.getOriginalFileResolution()
 
   // Outside the transaction (IO on disk)
   const { videoFileResolution } = await videoArg.getOriginalFileResolution()
 
-  const videoDatabase = await sequelizeTypescript.transaction(async t => {
+  const { videoDatabase, videoPublished } = await sequelizeTypescript.transaction(async t => {
     // Maybe the video changed in database, refresh it
     let videoDatabase = await VideoModel.loadAndPopulateAccountAndServerAndTags(videoArg.uuid, t)
     // Video does not exist anymore
     // Maybe the video changed in database, refresh it
     let videoDatabase = await VideoModel.loadAndPopulateAccountAndServerAndTags(videoArg.uuid, t)
     // Video does not exist anymore
@@ -113,6 +116,8 @@ async function onVideoFileOptimizerSuccess (videoArg: VideoModel, isNewVideo: bo
       { resolutions: resolutionsEnabled }
     )
 
       { resolutions: resolutionsEnabled }
     )
 
+    let videoPublished = false
+
     if (resolutionsEnabled.length !== 0) {
       const tasks: Bluebird<Bull.Job<any>>[] = []
 
     if (resolutionsEnabled.length !== 0) {
       const tasks: Bluebird<Bull.Job<any>>[] = []
 
@@ -130,6 +135,8 @@ async function onVideoFileOptimizerSuccess (videoArg: VideoModel, isNewVideo: bo
 
       logger.info('Transcoding jobs created for uuid %s.', videoDatabase.uuid, { resolutionsEnabled })
     } else {
 
       logger.info('Transcoding jobs created for uuid %s.', videoDatabase.uuid, { resolutionsEnabled })
     } else {
+      videoPublished = true
+
       // No transcoding to do, it's now published
       videoDatabase.state = VideoState.PUBLISHED
       videoDatabase = await videoDatabase.save({ transaction: t })
       // No transcoding to do, it's now published
       videoDatabase.state = VideoState.PUBLISHED
       videoDatabase = await videoDatabase.save({ transaction: t })
@@ -139,10 +146,11 @@ async function onVideoFileOptimizerSuccess (videoArg: VideoModel, isNewVideo: bo
 
     await federateVideoIfNeeded(videoDatabase, isNewVideo, t)
 
 
     await federateVideoIfNeeded(videoDatabase, isNewVideo, t)
 
-    return videoDatabase
+    return { videoDatabase, videoPublished }
   })
 
   if (isNewVideo) Notifier.Instance.notifyOnNewVideo(videoDatabase)
   })
 
   if (isNewVideo) Notifier.Instance.notifyOnNewVideo(videoDatabase)
+  if (videoPublished) Notifier.Instance.notifyOnPendingVideoPublished(videoDatabase)
 }
 
 // ---------------------------------------------------------------------------
 }
 
 // ---------------------------------------------------------------------------
index 29cd1198c7fedcddeabac4a2b0b21c1f782ca892..12004dcd7ef5fb7af316bb2c23da7163f92343df 100644 (file)
@@ -197,6 +197,7 @@ async function processFile (downloader: () => Promise<string>, videoImport: Vide
     })
 
     Notifier.Instance.notifyOnNewVideo(videoImportUpdated.Video)
     })
 
     Notifier.Instance.notifyOnNewVideo(videoImportUpdated.Video)
+    Notifier.Instance.notifyOnFinishedVideoImport(videoImportUpdated, true)
 
     // Create transcoding jobs?
     if (videoImportUpdated.Video.state === VideoState.TO_TRANSCODE) {
 
     // Create transcoding jobs?
     if (videoImportUpdated.Video.state === VideoState.TO_TRANSCODE) {
@@ -220,6 +221,8 @@ async function processFile (downloader: () => Promise<string>, videoImport: Vide
     videoImport.state = VideoImportState.FAILED
     await videoImport.save()
 
     videoImport.state = VideoImportState.FAILED
     await videoImport.save()
 
+    Notifier.Instance.notifyOnFinishedVideoImport(videoImport, false)
+
     throw err
   }
 }
     throw err
   }
 }
index a21b50b2dfa5222a3e58c42f233d03420e82bcef..11b0937e9c72fb30a26ce3e41b13240a699a2fae 100644 (file)
@@ -11,6 +11,8 @@ import { VideoPrivacy, VideoState } from '../../shared/models/videos'
 import { VideoAbuseModel } from '../models/video/video-abuse'
 import { VideoBlacklistModel } from '../models/video/video-blacklist'
 import * as Bluebird from 'bluebird'
 import { VideoAbuseModel } from '../models/video/video-abuse'
 import { VideoBlacklistModel } from '../models/video/video-blacklist'
 import * as Bluebird from 'bluebird'
+import { VideoImportModel } from '../models/video/video-import'
+import { AccountBlocklistModel } from '../models/account/account-blocklist'
 
 class Notifier {
 
 
 class Notifier {
 
@@ -26,6 +28,14 @@ class Notifier {
       .catch(err => logger.error('Cannot notify subscribers of new video %s.', video.url, { err }))
   }
 
       .catch(err => logger.error('Cannot notify subscribers of new video %s.', video.url, { err }))
   }
 
+  notifyOnPendingVideoPublished (video: VideoModel): void {
+    // Only notify on public videos that has been published while the user waited transcoding/scheduled update
+    if (video.waitTranscoding === false && !video.ScheduleVideoUpdate) return
+
+    this.notifyOwnedVideoHasBeenPublished(video)
+        .catch(err => logger.error('Cannot notify owner that its video %s has been published.', video.url, { err }))
+  }
+
   notifyOnNewComment (comment: VideoCommentModel): void {
     this.notifyVideoOwnerOfNewComment(comment)
         .catch(err => logger.error('Cannot notify of new comment %s.', comment.url, { err }))
   notifyOnNewComment (comment: VideoCommentModel): void {
     this.notifyVideoOwnerOfNewComment(comment)
         .catch(err => logger.error('Cannot notify of new comment %s.', comment.url, { err }))
@@ -46,6 +56,11 @@ class Notifier {
         .catch(err => logger.error('Cannot notify video owner of new video blacklist of %s.', video.url, { err }))
   }
 
         .catch(err => logger.error('Cannot notify video owner of new video blacklist of %s.', video.url, { err }))
   }
 
+  notifyOnFinishedVideoImport (videoImport: VideoImportModel, success: boolean): void {
+    this.notifyOwnerVideoImportIsFinished(videoImport, success)
+      .catch(err => logger.error('Cannot notify owner that its video import %s is finished.', videoImport.getTargetIdentifier(), { err }))
+  }
+
   private async notifySubscribersOfNewVideo (video: VideoModel) {
     // List all followers that are users
     const users = await UserModel.listUserSubscribersOf(video.VideoChannel.actorId)
   private async notifySubscribersOfNewVideo (video: VideoModel) {
     // List all followers that are users
     const users = await UserModel.listUserSubscribersOf(video.VideoChannel.actorId)
@@ -80,6 +95,9 @@ class Notifier {
     // Not our user or user comments its own video
     if (!user || comment.Account.userId === user.id) return
 
     // Not our user or user comments its own video
     if (!user || comment.Account.userId === user.id) return
 
+    const accountMuted = await AccountBlocklistModel.isAccountMutedBy(user.Account.id, comment.accountId)
+    if (accountMuted) return
+
     logger.info('Notifying user %s of new comment %s.', user.username, comment.url)
 
     function settingGetter (user: UserModel) {
     logger.info('Notifying user %s of new comment %s.', user.username, comment.url)
 
     function settingGetter (user: UserModel) {
@@ -188,6 +206,64 @@ class Notifier {
     return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender })
   }
 
     return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender })
   }
 
+  private async notifyOwnedVideoHasBeenPublished (video: VideoModel) {
+    const user = await UserModel.loadByVideoId(video.id)
+    if (!user) return
+
+    logger.info('Notifying user %s of the publication of its video %s.', user.username, video.url)
+
+    function settingGetter (user: UserModel) {
+      return user.NotificationSetting.myVideoPublished
+    }
+
+    async function notificationCreator (user: UserModel) {
+      const notification = await UserNotificationModel.create({
+        type: UserNotificationType.MY_VIDEO_PUBLISHED,
+        userId: user.id,
+        videoId: video.id
+      })
+      notification.Video = video
+
+      return notification
+    }
+
+    function emailSender (emails: string[]) {
+      return Emailer.Instance.myVideoPublishedNotification(emails, video)
+    }
+
+    return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender })
+  }
+
+  private async notifyOwnerVideoImportIsFinished (videoImport: VideoImportModel, success: boolean) {
+    const user = await UserModel.loadByVideoImportId(videoImport.id)
+    if (!user) return
+
+    logger.info('Notifying user %s its video import %s is finished.', user.username, videoImport.getTargetIdentifier())
+
+    function settingGetter (user: UserModel) {
+      return user.NotificationSetting.myVideoImportFinished
+    }
+
+    async function notificationCreator (user: UserModel) {
+      const notification = await UserNotificationModel.create({
+        type: success ? UserNotificationType.MY_VIDEO_IMPORT_SUCCESS : UserNotificationType.MY_VIDEO_IMPORT_ERROR,
+        userId: user.id,
+        videoImportId: videoImport.id
+      })
+      notification.VideoImport = videoImport
+
+      return notification
+    }
+
+    function emailSender (emails: string[]) {
+      return success
+        ? Emailer.Instance.myVideoImportSuccessNotification(emails, videoImport)
+        : Emailer.Instance.myVideoImportErrorNotification(emails, videoImport)
+    }
+
+    return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender })
+  }
+
   private async notify (options: {
     users: UserModel[],
     notificationCreator: (user: UserModel) => Promise<UserNotificationModel>,
   private async notify (options: {
     users: UserModel[],
     notificationCreator: (user: UserModel) => Promise<UserNotificationModel>,
index b7fb029f1bd6d6fd9c89dd154d01b298a694ee3c..2618a5857d24aa77a0c50e6c02693fb3513f4dc3 100644 (file)
@@ -6,6 +6,7 @@ import { federateVideoIfNeeded } from '../activitypub'
 import { SCHEDULER_INTERVALS_MS, sequelizeTypescript } from '../../initializers'
 import { VideoPrivacy } from '../../../shared/models/videos'
 import { Notifier } from '../notifier'
 import { SCHEDULER_INTERVALS_MS, sequelizeTypescript } from '../../initializers'
 import { VideoPrivacy } from '../../../shared/models/videos'
 import { Notifier } from '../notifier'
+import { VideoModel } from '../../models/video/video'
 
 export class UpdateVideosScheduler extends AbstractScheduler {
 
 
 export class UpdateVideosScheduler extends AbstractScheduler {
 
@@ -24,8 +25,9 @@ export class UpdateVideosScheduler extends AbstractScheduler {
   private async updateVideos () {
     if (!await ScheduleVideoUpdateModel.areVideosToUpdate()) return undefined
 
   private async updateVideos () {
     if (!await ScheduleVideoUpdateModel.areVideosToUpdate()) return undefined
 
-    return sequelizeTypescript.transaction(async t => {
+    const publishedVideos = await sequelizeTypescript.transaction(async t => {
       const schedules = await ScheduleVideoUpdateModel.listVideosToUpdate(t)
       const schedules = await ScheduleVideoUpdateModel.listVideosToUpdate(t)
+      const publishedVideos: VideoModel[] = []
 
       for (const schedule of schedules) {
         const video = schedule.Video
 
       for (const schedule of schedules) {
         const video = schedule.Video
@@ -42,13 +44,21 @@ export class UpdateVideosScheduler extends AbstractScheduler {
           await federateVideoIfNeeded(video, isNewVideo, t)
 
           if (oldPrivacy === VideoPrivacy.UNLISTED || oldPrivacy === VideoPrivacy.PRIVATE) {
           await federateVideoIfNeeded(video, isNewVideo, t)
 
           if (oldPrivacy === VideoPrivacy.UNLISTED || oldPrivacy === VideoPrivacy.PRIVATE) {
-            Notifier.Instance.notifyOnNewVideo(video)
+            video.ScheduleVideoUpdate = schedule
+            publishedVideos.push(video)
           }
         }
 
         await schedule.destroy({ transaction: t })
       }
           }
         }
 
         await schedule.destroy({ transaction: t })
       }
+
+      return publishedVideos
     })
     })
+
+    for (const v of publishedVideos) {
+      Notifier.Instance.notifyOnNewVideo(v)
+      Notifier.Instance.notifyOnPendingVideoPublished(v)
+    }
   }
 
   static get Instance () {
   }
 
   static get Instance () {
index 72127819c0d5bff9e561f772d825f28626c57b1c..481571828265c05dee3170a0115968d9839e6ce6 100644 (file)
@@ -100,6 +100,8 @@ function createDefaultUserNotificationSettings (user: UserModel, t: Sequelize.Tr
     userId: user.id,
     newVideoFromSubscription: UserNotificationSettingValue.WEB_NOTIFICATION,
     newCommentOnMyVideo: UserNotificationSettingValue.WEB_NOTIFICATION,
     userId: user.id,
     newVideoFromSubscription: UserNotificationSettingValue.WEB_NOTIFICATION,
     newCommentOnMyVideo: UserNotificationSettingValue.WEB_NOTIFICATION,
+    myVideoImportFinished: UserNotificationSettingValue.WEB_NOTIFICATION,
+    myVideoPublished: UserNotificationSettingValue.WEB_NOTIFICATION,
     videoAbuseAsModerator: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL,
     blacklistOnMyVideo: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL
   }, { transaction: t })
     videoAbuseAsModerator: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL,
     blacklistOnMyVideo: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL
   }, { transaction: t })
index 8202f307e5ebfee4170137dc052fd8e9338bf472..1c31f0a73b001b5330ae9ddbb635cc23b449d354 100644 (file)
@@ -1,11 +1,26 @@
 import * as express from 'express'
 import 'express-validator'
 import * as express from 'express'
 import 'express-validator'
-import { body } from 'express-validator/check'
+import { body, query } from 'express-validator/check'
 import { logger } from '../../helpers/logger'
 import { areValidationErrors } from './utils'
 import { isUserNotificationSettingValid } from '../../helpers/custom-validators/user-notifications'
 import { isIntArray } from '../../helpers/custom-validators/misc'
 
 import { logger } from '../../helpers/logger'
 import { areValidationErrors } from './utils'
 import { isUserNotificationSettingValid } from '../../helpers/custom-validators/user-notifications'
 import { isIntArray } from '../../helpers/custom-validators/misc'
 
+const listUserNotificationsValidator = [
+  query('unread')
+    .optional()
+    .toBoolean()
+    .isBoolean().withMessage('Should have a valid unread boolean'),
+
+  (req: express.Request, res: express.Response, next: express.NextFunction) => {
+    logger.debug('Checking listUserNotificationsValidator parameters', { parameters: req.query })
+
+    if (areValidationErrors(req, res)) return
+
+    return next()
+  }
+]
+
 const updateNotificationSettingsValidator = [
   body('newVideoFromSubscription')
     .custom(isUserNotificationSettingValid).withMessage('Should have a valid new video from subscription notification setting'),
 const updateNotificationSettingsValidator = [
   body('newVideoFromSubscription')
     .custom(isUserNotificationSettingValid).withMessage('Should have a valid new video from subscription notification setting'),
@@ -41,6 +56,7 @@ const markAsReadUserNotificationsValidator = [
 // ---------------------------------------------------------------------------
 
 export {
 // ---------------------------------------------------------------------------
 
 export {
+  listUserNotificationsValidator,
   updateNotificationSettingsValidator,
   markAsReadUserNotificationsValidator
 }
   updateNotificationSettingsValidator,
   markAsReadUserNotificationsValidator
 }
index fa281923547be9764d1395d474a25c0b13fe9666..54ac290c4b69eb13873a9bf8e86d1864afde604b 100644 (file)
@@ -72,6 +72,21 @@ export class AccountBlocklistModel extends Model<AccountBlocklistModel> {
   })
   BlockedAccount: AccountModel
 
   })
   BlockedAccount: AccountModel
 
+  static isAccountMutedBy (accountId: number, targetAccountId: number) {
+    const query = {
+      attributes: [ 'id' ],
+      where: {
+        accountId,
+        targetAccountId
+      },
+      raw: true
+    }
+
+    return AccountBlocklistModel.unscoped()
+                                .findOne(query)
+                                .then(a => !!a)
+  }
+
   static loadByAccountAndTarget (accountId: number, targetAccountId: number) {
     const query = {
       where: {
   static loadByAccountAndTarget (accountId: number, targetAccountId: number) {
     const query = {
       where: {
index bc24b1e3360df8cf4e435e2e5bd1af629df3f8d3..6470defa75c927319dc203e0b9ecb95e02e67c59 100644 (file)
@@ -65,6 +65,24 @@ export class UserNotificationSettingModel extends Model<UserNotificationSettingM
   @Column
   blacklistOnMyVideo: UserNotificationSettingValue
 
   @Column
   blacklistOnMyVideo: UserNotificationSettingValue
 
+  @AllowNull(false)
+  @Default(null)
+  @Is(
+    'UserNotificationSettingMyVideoPublished',
+    value => throwIfNotValid(value, isUserNotificationSettingValid, 'myVideoPublished')
+  )
+  @Column
+  myVideoPublished: UserNotificationSettingValue
+
+  @AllowNull(false)
+  @Default(null)
+  @Is(
+    'UserNotificationSettingMyVideoImportFinished',
+    value => throwIfNotValid(value, isUserNotificationSettingValid, 'myVideoImportFinished')
+  )
+  @Column
+  myVideoImportFinished: UserNotificationSettingValue
+
   @ForeignKey(() => UserModel)
   @Column
   userId: number
   @ForeignKey(() => UserModel)
   @Column
   userId: number
@@ -94,7 +112,9 @@ export class UserNotificationSettingModel extends Model<UserNotificationSettingM
       newCommentOnMyVideo: this.newCommentOnMyVideo,
       newVideoFromSubscription: this.newVideoFromSubscription,
       videoAbuseAsModerator: this.videoAbuseAsModerator,
       newCommentOnMyVideo: this.newCommentOnMyVideo,
       newVideoFromSubscription: this.newVideoFromSubscription,
       videoAbuseAsModerator: this.videoAbuseAsModerator,
-      blacklistOnMyVideo: this.blacklistOnMyVideo
+      blacklistOnMyVideo: this.blacklistOnMyVideo,
+      myVideoPublished: this.myVideoPublished,
+      myVideoImportFinished: this.myVideoImportFinished
     }
   }
 }
     }
   }
 }
index e22f0d57f661767296704fabe439baf4f98c664c..25124437418e4f4ddbe52bc56a199861ecee9c65 100644 (file)
@@ -1,4 +1,17 @@
-import { AllowNull, BelongsTo, Column, CreatedAt, Default, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript'
+import {
+  AllowNull,
+  BelongsTo,
+  Column,
+  CreatedAt,
+  Default,
+  ForeignKey,
+  IFindOptions,
+  Is,
+  Model,
+  Scopes,
+  Table,
+  UpdatedAt
+} from 'sequelize-typescript'
 import { UserNotification, UserNotificationType } from '../../../shared'
 import { getSort, throwIfNotValid } from '../utils'
 import { isBooleanValid } from '../../helpers/custom-validators/misc'
 import { UserNotification, UserNotificationType } from '../../../shared'
 import { getSort, throwIfNotValid } from '../utils'
 import { isBooleanValid } from '../../helpers/custom-validators/misc'
@@ -11,66 +24,68 @@ import { VideoChannelModel } from '../video/video-channel'
 import { AccountModel } from './account'
 import { VideoAbuseModel } from '../video/video-abuse'
 import { VideoBlacklistModel } from '../video/video-blacklist'
 import { AccountModel } from './account'
 import { VideoAbuseModel } from '../video/video-abuse'
 import { VideoBlacklistModel } from '../video/video-blacklist'
+import { VideoImportModel } from '../video/video-import'
 
 enum ScopeNames {
   WITH_ALL = 'WITH_ALL'
 }
 
 
 enum ScopeNames {
   WITH_ALL = 'WITH_ALL'
 }
 
+function buildVideoInclude (required: boolean) {
+  return {
+    attributes: [ 'id', 'uuid', 'name' ],
+    model: () => VideoModel.unscoped(),
+    required
+  }
+}
+
+function buildChannelInclude () {
+  return {
+    required: true,
+    attributes: [ 'id', 'name' ],
+    model: () => VideoChannelModel.unscoped()
+  }
+}
+
+function buildAccountInclude () {
+  return {
+    required: true,
+    attributes: [ 'id', 'name' ],
+    model: () => AccountModel.unscoped()
+  }
+}
+
 @Scopes({
   [ScopeNames.WITH_ALL]: {
     include: [
 @Scopes({
   [ScopeNames.WITH_ALL]: {
     include: [
+      Object.assign(buildVideoInclude(false), {
+        include: [ buildChannelInclude() ]
+      }),
       {
       {
-        attributes: [ 'id', 'uuid', 'name' ],
-        model: () => VideoModel.unscoped(),
-        required: false,
-        include: [
-          {
-            required: true,
-            attributes: [ 'id', 'name' ],
-            model: () => VideoChannelModel.unscoped()
-          }
-        ]
-      },
-      {
-        attributes: [ 'id' ],
+        attributes: [ 'id', 'originCommentId' ],
         model: () => VideoCommentModel.unscoped(),
         required: false,
         include: [
         model: () => VideoCommentModel.unscoped(),
         required: false,
         include: [
-          {
-            required: true,
-            attributes: [ 'id', 'name' ],
-            model: () => AccountModel.unscoped()
-          },
-          {
-            required: true,
-            attributes: [ 'id', 'uuid', 'name' ],
-            model: () => VideoModel.unscoped()
-          }
+          buildAccountInclude(),
+          buildVideoInclude(true)
         ]
       },
       {
         attributes: [ 'id' ],
         model: () => VideoAbuseModel.unscoped(),
         required: false,
         ]
       },
       {
         attributes: [ 'id' ],
         model: () => VideoAbuseModel.unscoped(),
         required: false,
-        include: [
-          {
-            required: true,
-            attributes: [ 'id', 'uuid', 'name' ],
-            model: () => VideoModel.unscoped()
-          }
-        ]
+        include: [ buildVideoInclude(true) ]
       },
       {
         attributes: [ 'id' ],
         model: () => VideoBlacklistModel.unscoped(),
         required: false,
       },
       {
         attributes: [ 'id' ],
         model: () => VideoBlacklistModel.unscoped(),
         required: false,
-        include: [
-          {
-            required: true,
-            attributes: [ 'id', 'uuid', 'name' ],
-            model: () => VideoModel.unscoped()
-          }
-        ]
+        include: [ buildVideoInclude(true) ]
+      },
+      {
+        attributes: [ 'id', 'magnetUri', 'targetUrl', 'torrentName' ],
+        model: () => VideoImportModel.unscoped(),
+        required: false,
+        include: [ buildVideoInclude(false) ]
       }
     ]
   }
       }
     ]
   }
@@ -166,8 +181,20 @@ export class UserNotificationModel extends Model<UserNotificationModel> {
   })
   VideoBlacklist: VideoBlacklistModel
 
   })
   VideoBlacklist: VideoBlacklistModel
 
-  static listForApi (userId: number, start: number, count: number, sort: string) {
-    const query = {
+  @ForeignKey(() => VideoImportModel)
+  @Column
+  videoImportId: number
+
+  @BelongsTo(() => VideoImportModel, {
+    foreignKey: {
+      allowNull: true
+    },
+    onDelete: 'cascade'
+  })
+  VideoImport: VideoImportModel
+
+  static listForApi (userId: number, start: number, count: number, sort: string, unread?: boolean) {
+    const query: IFindOptions<UserNotificationModel> = {
       offset: start,
       limit: count,
       order: getSort(sort),
       offset: start,
       limit: count,
       order: getSort(sort),
@@ -176,6 +203,8 @@ export class UserNotificationModel extends Model<UserNotificationModel> {
       }
     }
 
       }
     }
 
+    if (unread !== undefined) query.where['read'] = !unread
+
     return UserNotificationModel.scope(ScopeNames.WITH_ALL)
                                 .findAndCountAll(query)
                                 .then(({ rows, count }) => {
     return UserNotificationModel.scope(ScopeNames.WITH_ALL)
                                 .findAndCountAll(query)
                                 .then(({ rows, count }) => {
@@ -200,45 +229,39 @@ export class UserNotificationModel extends Model<UserNotificationModel> {
   }
 
   toFormattedJSON (): UserNotification {
   }
 
   toFormattedJSON (): UserNotification {
-    const video = this.Video ? {
-      id: this.Video.id,
-      uuid: this.Video.uuid,
-      name: this.Video.name,
+    const video = this.Video ? Object.assign(this.formatVideo(this.Video), {
       channel: {
         id: this.Video.VideoChannel.id,
         displayName: this.Video.VideoChannel.getDisplayName()
       }
       channel: {
         id: this.Video.VideoChannel.id,
         displayName: this.Video.VideoChannel.getDisplayName()
       }
+    }) : undefined
+
+    const videoImport = this.VideoImport ? {
+      id: this.VideoImport.id,
+      video: this.VideoImport.Video ? this.formatVideo(this.VideoImport.Video) : undefined,
+      torrentName: this.VideoImport.torrentName,
+      magnetUri: this.VideoImport.magnetUri,
+      targetUrl: this.VideoImport.targetUrl
     } : undefined
 
     const comment = this.Comment ? {
       id: this.Comment.id,
     } : undefined
 
     const comment = this.Comment ? {
       id: this.Comment.id,
+      threadId: this.Comment.getThreadId(),
       account: {
         id: this.Comment.Account.id,
         displayName: this.Comment.Account.getDisplayName()
       },
       account: {
         id: this.Comment.Account.id,
         displayName: this.Comment.Account.getDisplayName()
       },
-      video: {
-        id: this.Comment.Video.id,
-        uuid: this.Comment.Video.uuid,
-        name: this.Comment.Video.name
-      }
+      video: this.formatVideo(this.Comment.Video)
     } : undefined
 
     const videoAbuse = this.VideoAbuse ? {
       id: this.VideoAbuse.id,
     } : undefined
 
     const videoAbuse = this.VideoAbuse ? {
       id: this.VideoAbuse.id,
-      video: {
-        id: this.VideoAbuse.Video.id,
-        uuid: this.VideoAbuse.Video.uuid,
-        name: this.VideoAbuse.Video.name
-      }
+      video: this.formatVideo(this.VideoAbuse.Video)
     } : undefined
 
     const videoBlacklist = this.VideoBlacklist ? {
       id: this.VideoBlacklist.id,
     } : undefined
 
     const videoBlacklist = this.VideoBlacklist ? {
       id: this.VideoBlacklist.id,
-      video: {
-        id: this.VideoBlacklist.Video.id,
-        uuid: this.VideoBlacklist.Video.uuid,
-        name: this.VideoBlacklist.Video.name
-      }
+      video: this.formatVideo(this.VideoBlacklist.Video)
     } : undefined
 
     return {
     } : undefined
 
     return {
@@ -246,6 +269,7 @@ export class UserNotificationModel extends Model<UserNotificationModel> {
       type: this.type,
       read: this.read,
       video,
       type: this.type,
       read: this.read,
       video,
+      videoImport,
       comment,
       videoAbuse,
       videoBlacklist,
       comment,
       videoAbuse,
       videoBlacklist,
@@ -253,4 +277,12 @@ export class UserNotificationModel extends Model<UserNotificationModel> {
       updatedAt: this.updatedAt.toISOString()
     }
   }
       updatedAt: this.updatedAt.toISOString()
     }
   }
+
+  private formatVideo (video: VideoModel) {
+    return {
+      id: video.id,
+      uuid: video.uuid,
+      name: video.name
+    }
+  }
 }
 }
index 55ec14d0568a2b378630a142c93f16cff399d38c..33f56f64193b5eecfca16b4892edb30d2ca13f2b 100644 (file)
@@ -48,6 +48,7 @@ import { UserNotificationSettingModel } from './user-notification-setting'
 import { VideoModel } from '../video/video'
 import { ActorModel } from '../activitypub/actor'
 import { ActorFollowModel } from '../activitypub/actor-follow'
 import { VideoModel } from '../video/video'
 import { ActorModel } from '../activitypub/actor'
 import { ActorFollowModel } from '../activitypub/actor-follow'
+import { VideoImportModel } from '../video/video-import'
 
 enum ScopeNames {
   WITH_VIDEO_CHANNEL = 'WITH_VIDEO_CHANNEL'
 
 enum ScopeNames {
   WITH_VIDEO_CHANNEL = 'WITH_VIDEO_CHANNEL'
@@ -186,6 +187,12 @@ export class UserModel extends Model<UserModel> {
   })
   NotificationSetting: UserNotificationSettingModel
 
   })
   NotificationSetting: UserNotificationSettingModel
 
+  @HasMany(() => VideoImportModel, {
+    foreignKey: 'userId',
+    onDelete: 'cascade'
+  })
+  VideoImports: VideoImportModel[]
+
   @HasMany(() => OAuthTokenModel, {
     foreignKey: 'userId',
     onDelete: 'cascade'
   @HasMany(() => OAuthTokenModel, {
     foreignKey: 'userId',
     onDelete: 'cascade'
@@ -400,6 +407,23 @@ export class UserModel extends Model<UserModel> {
     return UserModel.findOne(query)
   }
 
     return UserModel.findOne(query)
   }
 
+  static loadByVideoImportId (videoImportId: number) {
+    const query = {
+      include: [
+        {
+          required: true,
+          attributes: [ 'id' ],
+          model: VideoImportModel.unscoped(),
+          where: {
+            id: videoImportId
+          }
+        }
+      ]
+    }
+
+    return UserModel.findOne(query)
+  }
+
   static getOriginalVideoFileTotalFromUser (user: UserModel) {
     // Don't use sequelize because we need to use a sub query
     const query = UserModel.generateUserQuotaBaseSQL()
   static getOriginalVideoFileTotalFromUser (user: UserModel) {
     // Don't use sequelize because we need to use a sub query
     const query = UserModel.generateUserQuotaBaseSQL()
index 3fd2d5a99d66e42bb231f73aa221b729d1b305c3..0fd868cd61a33e9a5fdf2c649bc67e65b4376245 100644 (file)
@@ -1,4 +1,3 @@
-import { values } from 'lodash'
 import {
   AllowNull,
   BelongsTo,
 import {
   AllowNull,
   BelongsTo,
@@ -20,7 +19,6 @@ import {
   isVideoFileSizeValid,
   isVideoFPSResolutionValid
 } from '../../helpers/custom-validators/videos'
   isVideoFileSizeValid,
   isVideoFPSResolutionValid
 } from '../../helpers/custom-validators/videos'
-import { CONSTRAINTS_FIELDS } from '../../initializers'
 import { throwIfNotValid } from '../utils'
 import { VideoModel } from './video'
 import * as Sequelize from 'sequelize'
 import { throwIfNotValid } from '../utils'
 import { VideoModel } from './video'
 import * as Sequelize from 'sequelize'
index 8d442b3f849940526fe5207afd157364f772dada..c723e57c0d20fe16f945b98db37811137f124418 100644 (file)
@@ -144,6 +144,10 @@ export class VideoImportModel extends Model<VideoImportModel> {
                            })
   }
 
                            })
   }
 
+  getTargetIdentifier () {
+    return this.targetUrl || this.magnetUri || this.torrentName
+  }
+
   toFormattedJSON (): VideoImport {
     const videoFormatOptions = {
       completeDescription: true,
   toFormattedJSON (): VideoImport {
     const videoFormatOptions = {
       completeDescription: true,
index fc200e5d1af159420b271d68b07e3d2873fbe995..80a6c78320e20bf5a63f7e71ac51bfc951721ee5 100644 (file)
@@ -94,6 +94,7 @@ import {
 import * as validator from 'validator'
 import { UserVideoHistoryModel } from '../account/user-video-history'
 import { UserModel } from '../account/user'
 import * as validator from 'validator'
 import { UserVideoHistoryModel } from '../account/user-video-history'
 import { UserModel } from '../account/user'
+import { VideoImportModel } from './video-import'
 
 // FIXME: Define indexes here because there is an issue with TS and Sequelize.literal when called directly in the annotation
 const indexes: Sequelize.DefineIndexesOptions[] = [
 
 // FIXME: Define indexes here because there is an issue with TS and Sequelize.literal when called directly in the annotation
 const indexes: Sequelize.DefineIndexesOptions[] = [
@@ -785,6 +786,15 @@ export class VideoModel extends Model<VideoModel> {
   })
   VideoBlacklist: VideoBlacklistModel
 
   })
   VideoBlacklist: VideoBlacklistModel
 
+  @HasOne(() => VideoImportModel, {
+    foreignKey: {
+      name: 'videoId',
+      allowNull: true
+    },
+    onDelete: 'set null'
+  })
+  VideoImport: VideoImportModel
+
   @HasMany(() => VideoCaptionModel, {
     foreignKey: {
       name: 'videoId',
   @HasMany(() => VideoCaptionModel, {
     foreignKey: {
       name: 'videoId',
index 3ae36ddb325434b87a6ad9edfe684353bca62ed1..4f21f7b9520807a5479912082fb81d85317ef59c 100644 (file)
@@ -52,6 +52,18 @@ describe('Test user notifications API validators', function () {
       await checkBadSortPagination(server.url, path, server.accessToken)
     })
 
       await checkBadSortPagination(server.url, path, server.accessToken)
     })
 
+    it('Should fail with an incorrect unread parameter', async function () {
+      await makeGetRequest({
+        url: server.url,
+        path,
+        query: {
+          unread: 'toto'
+        },
+        token: server.accessToken,
+        statusCodeExpected: 200
+      })
+    })
+
     it('Should fail with a non authenticated user', async function () {
       await makeGetRequest({
         url: server.url,
     it('Should fail with a non authenticated user', async function () {
       await makeGetRequest({
         url: server.url,
@@ -125,7 +137,9 @@ describe('Test user notifications API validators', function () {
       newVideoFromSubscription: UserNotificationSettingValue.WEB_NOTIFICATION,
       newCommentOnMyVideo: UserNotificationSettingValue.WEB_NOTIFICATION,
       videoAbuseAsModerator: UserNotificationSettingValue.WEB_NOTIFICATION,
       newVideoFromSubscription: UserNotificationSettingValue.WEB_NOTIFICATION,
       newCommentOnMyVideo: UserNotificationSettingValue.WEB_NOTIFICATION,
       videoAbuseAsModerator: UserNotificationSettingValue.WEB_NOTIFICATION,
-      blacklistOnMyVideo: UserNotificationSettingValue.WEB_NOTIFICATION
+      blacklistOnMyVideo: UserNotificationSettingValue.WEB_NOTIFICATION,
+      myVideoImportFinished: UserNotificationSettingValue.WEB_NOTIFICATION,
+      myVideoPublished: UserNotificationSettingValue.WEB_NOTIFICATION
     }
 
     it('Should fail with missing fields', async function () {
     }
 
     it('Should fail with missing fields', async function () {
index 09c0479fdf79c45fda9530490ce00926a64b5a59..e4966dbf570e4f5d7e87e32efd33393103640c67 100644 (file)
@@ -29,33 +29,46 @@ import {
   getLastNotification,
   getUserNotifications,
   markAsReadNotifications,
   getLastNotification,
   getUserNotifications,
   markAsReadNotifications,
-  updateMyNotificationSettings
+  updateMyNotificationSettings,
+  checkVideoIsPublished, checkMyVideoImportIsFinished
 } from '../../../../shared/utils/users/user-notifications'
 } from '../../../../shared/utils/users/user-notifications'
-import { User, UserNotification, UserNotificationSettingValue } from '../../../../shared/models/users'
+import {
+  User,
+  UserNotification,
+  UserNotificationSetting,
+  UserNotificationSettingValue,
+  UserNotificationType
+} from '../../../../shared/models/users'
 import { MockSmtpServer } from '../../../../shared/utils/miscs/email'
 import { addUserSubscription } from '../../../../shared/utils/users/user-subscriptions'
 import { VideoPrivacy } from '../../../../shared/models/videos'
 import { MockSmtpServer } from '../../../../shared/utils/miscs/email'
 import { addUserSubscription } from '../../../../shared/utils/users/user-subscriptions'
 import { VideoPrivacy } from '../../../../shared/models/videos'
-import { getYoutubeVideoUrl, importVideo } from '../../../../shared/utils/videos/video-imports'
+import { getYoutubeVideoUrl, importVideo, getBadVideoUrl } from '../../../../shared/utils/videos/video-imports'
 import { addVideoCommentReply, addVideoCommentThread } from '../../../../shared/utils/videos/video-comments'
 import { addVideoCommentReply, addVideoCommentThread } from '../../../../shared/utils/videos/video-comments'
+import * as uuidv4 from 'uuid/v4'
+import { addAccountToAccountBlocklist, removeAccountFromAccountBlocklist } from '../../../../shared/utils/users/blocklist'
 
 const expect = chai.expect
 
 
 const expect = chai.expect
 
-async function uploadVideoByRemoteAccount (servers: ServerInfo[], videoNameId: number, additionalParams: any = {}) {
-  const data = Object.assign({ name: 'remote video ' + videoNameId }, additionalParams)
+async function uploadVideoByRemoteAccount (servers: ServerInfo[], additionalParams: any = {}) {
+  const name = 'remote video ' + uuidv4()
+
+  const data = Object.assign({ name }, additionalParams)
   const res = await uploadVideo(servers[ 1 ].url, servers[ 1 ].accessToken, data)
 
   await waitJobs(servers)
 
   const res = await uploadVideo(servers[ 1 ].url, servers[ 1 ].accessToken, data)
 
   await waitJobs(servers)
 
-  return res.body.video.uuid
+  return { uuid: res.body.video.uuid, name }
 }
 
 }
 
-async function uploadVideoByLocalAccount (servers: ServerInfo[], videoNameId: number, additionalParams: any = {}) {
-  const data = Object.assign({ name: 'local video ' + videoNameId }, additionalParams)
+async function uploadVideoByLocalAccount (servers: ServerInfo[], additionalParams: any = {}) {
+  const name = 'local video ' + uuidv4()
+
+  const data = Object.assign({ name }, additionalParams)
   const res = await uploadVideo(servers[ 0 ].url, servers[ 0 ].accessToken, data)
 
   await waitJobs(servers)
 
   const res = await uploadVideo(servers[ 0 ].url, servers[ 0 ].accessToken, data)
 
   await waitJobs(servers)
 
-  return res.body.video.uuid
+  return { uuid: res.body.video.uuid, name }
 }
 
 describe('Test users notifications', function () {
 }
 
 describe('Test users notifications', function () {
@@ -63,7 +76,18 @@ describe('Test users notifications', function () {
   let userAccessToken: string
   let userNotifications: UserNotification[] = []
   let adminNotifications: UserNotification[] = []
   let userAccessToken: string
   let userNotifications: UserNotification[] = []
   let adminNotifications: UserNotification[] = []
+  let adminNotificationsServer2: UserNotification[] = []
   const emails: object[] = []
   const emails: object[] = []
+  let channelId: number
+
+  const allNotificationSettings: UserNotificationSetting = {
+    myVideoPublished: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL,
+    myVideoImportFinished: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL,
+    newCommentOnMyVideo: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL,
+    newVideoFromSubscription: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL,
+    videoAbuseAsModerator: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL,
+    blacklistOnMyVideo: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL
+  }
 
   before(async function () {
     this.timeout(120000)
 
   before(async function () {
     this.timeout(120000)
@@ -94,12 +118,9 @@ describe('Test users notifications', function () {
     await createUser(servers[0].url, servers[0].accessToken, user.username, user.password, 10 * 1000 * 1000)
     userAccessToken = await userLogin(servers[0], user)
 
     await createUser(servers[0].url, servers[0].accessToken, user.username, user.password, 10 * 1000 * 1000)
     userAccessToken = await userLogin(servers[0], user)
 
-    await updateMyNotificationSettings(servers[0].url, userAccessToken, {
-      newCommentOnMyVideo: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL,
-      newVideoFromSubscription: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL,
-      blacklistOnMyVideo: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL,
-      videoAbuseAsModerator: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL
-    })
+    await updateMyNotificationSettings(servers[0].url, userAccessToken, allNotificationSettings)
+    await updateMyNotificationSettings(servers[0].url, servers[0].accessToken, allNotificationSettings)
+    await updateMyNotificationSettings(servers[1].url, servers[1].accessToken, allNotificationSettings)
 
     {
       const socket = getUserNotificationSocket(servers[ 0 ].url, userAccessToken)
 
     {
       const socket = getUserNotificationSocket(servers[ 0 ].url, userAccessToken)
@@ -109,6 +130,15 @@ describe('Test users notifications', function () {
       const socket = getUserNotificationSocket(servers[ 0 ].url, servers[0].accessToken)
       socket.on('new-notification', n => adminNotifications.push(n))
     }
       const socket = getUserNotificationSocket(servers[ 0 ].url, servers[0].accessToken)
       socket.on('new-notification', n => adminNotifications.push(n))
     }
+    {
+      const socket = getUserNotificationSocket(servers[ 1 ].url, servers[1].accessToken)
+      socket.on('new-notification', n => adminNotificationsServer2.push(n))
+    }
+
+    {
+      const resChannel = await getMyUserInformation(servers[0].url, servers[0].accessToken)
+      channelId = resChannel.body.videoChannels[0].id
+    }
   })
 
   describe('New video from my subscription notification', function () {
   })
 
   describe('New video from my subscription notification', function () {
@@ -124,7 +154,7 @@ describe('Test users notifications', function () {
     })
 
     it('Should not send notifications if the user does not follow the video publisher', async function () {
     })
 
     it('Should not send notifications if the user does not follow the video publisher', async function () {
-      await uploadVideoByLocalAccount(servers, 1)
+      await uploadVideoByLocalAccount(servers)
 
       const notification = await getLastNotification(servers[ 0 ].url, userAccessToken)
       expect(notification).to.be.undefined
 
       const notification = await getLastNotification(servers[ 0 ].url, userAccessToken)
       expect(notification).to.be.undefined
@@ -136,11 +166,8 @@ describe('Test users notifications', function () {
     it('Should send a new video notification if the user follows the local video publisher', async function () {
       await addUserSubscription(servers[0].url, userAccessToken, 'root_channel@localhost:9001')
 
     it('Should send a new video notification if the user follows the local video publisher', async function () {
       await addUserSubscription(servers[0].url, userAccessToken, 'root_channel@localhost:9001')
 
-      const videoNameId = 10
-      const videoName = 'local video ' + videoNameId
-
-      const uuid = await uploadVideoByLocalAccount(servers, videoNameId)
-      await checkNewVideoFromSubscription(baseParams, videoName, uuid, 'presence')
+      const { name, uuid } = await uploadVideoByLocalAccount(servers)
+      await checkNewVideoFromSubscription(baseParams, name, uuid, 'presence')
     })
 
     it('Should send a new video notification from a remote account', async function () {
     })
 
     it('Should send a new video notification from a remote account', async function () {
@@ -148,21 +175,13 @@ describe('Test users notifications', function () {
 
       await addUserSubscription(servers[0].url, userAccessToken, 'root_channel@localhost:9002')
 
 
       await addUserSubscription(servers[0].url, userAccessToken, 'root_channel@localhost:9002')
 
-      const videoNameId = 20
-      const videoName = 'remote video ' + videoNameId
-
-      const uuid = await uploadVideoByRemoteAccount(servers, videoNameId)
-      await waitJobs(servers)
-
-      await checkNewVideoFromSubscription(baseParams, videoName, uuid, 'presence')
+      const { name, uuid } = await uploadVideoByRemoteAccount(servers)
+      await checkNewVideoFromSubscription(baseParams, name, uuid, 'presence')
     })
 
     it('Should send a new video notification on a scheduled publication', async function () {
       this.timeout(20000)
 
     })
 
     it('Should send a new video notification on a scheduled publication', async function () {
       this.timeout(20000)
 
-      const videoNameId = 30
-      const videoName = 'local video ' + videoNameId
-
       // In 2 seconds
       let updateAt = new Date(new Date().getTime() + 2000)
 
       // In 2 seconds
       let updateAt = new Date(new Date().getTime() + 2000)
 
@@ -173,18 +192,15 @@ describe('Test users notifications', function () {
           privacy: VideoPrivacy.PUBLIC
         }
       }
           privacy: VideoPrivacy.PUBLIC
         }
       }
-      const uuid = await uploadVideoByLocalAccount(servers, videoNameId, data)
+      const { name, uuid } = await uploadVideoByLocalAccount(servers, data)
 
       await wait(6000)
 
       await wait(6000)
-      await checkNewVideoFromSubscription(baseParams, videoName, uuid, 'presence')
+      await checkNewVideoFromSubscription(baseParams, name, uuid, 'presence')
     })
 
     it('Should send a new video notification on a remote scheduled publication', async function () {
       this.timeout(20000)
 
     })
 
     it('Should send a new video notification on a remote scheduled publication', async function () {
       this.timeout(20000)
 
-      const videoNameId = 40
-      const videoName = 'remote video ' + videoNameId
-
       // In 2 seconds
       let updateAt = new Date(new Date().getTime() + 2000)
 
       // In 2 seconds
       let updateAt = new Date(new Date().getTime() + 2000)
 
@@ -195,19 +211,16 @@ describe('Test users notifications', function () {
           privacy: VideoPrivacy.PUBLIC
         }
       }
           privacy: VideoPrivacy.PUBLIC
         }
       }
-      const uuid = await uploadVideoByRemoteAccount(servers, videoNameId, data)
+      const { name, uuid } = await uploadVideoByRemoteAccount(servers, data)
       await waitJobs(servers)
 
       await wait(6000)
       await waitJobs(servers)
 
       await wait(6000)
-      await checkNewVideoFromSubscription(baseParams, videoName, uuid, 'presence')
+      await checkNewVideoFromSubscription(baseParams, name, uuid, 'presence')
     })
 
     it('Should not send a notification before the video is published', async function () {
       this.timeout(20000)
 
     })
 
     it('Should not send a notification before the video is published', async function () {
       this.timeout(20000)
 
-      const videoNameId = 50
-      const videoName = 'local video ' + videoNameId
-
       let updateAt = new Date(new Date().getTime() + 100000)
 
       const data = {
       let updateAt = new Date(new Date().getTime() + 100000)
 
       const data = {
@@ -217,86 +230,70 @@ describe('Test users notifications', function () {
           privacy: VideoPrivacy.PUBLIC
         }
       }
           privacy: VideoPrivacy.PUBLIC
         }
       }
-      const uuid = await uploadVideoByLocalAccount(servers, videoNameId, data)
+      const { name, uuid } = await uploadVideoByLocalAccount(servers, data)
 
       await wait(6000)
 
       await wait(6000)
-      await checkNewVideoFromSubscription(baseParams, videoName, uuid, 'absence')
+      await checkNewVideoFromSubscription(baseParams, name, uuid, 'absence')
     })
 
     it('Should send a new video notification when a video becomes public', async function () {
       this.timeout(10000)
 
     })
 
     it('Should send a new video notification when a video becomes public', async function () {
       this.timeout(10000)
 
-      const videoNameId = 60
-      const videoName = 'local video ' + videoNameId
-
       const data = { privacy: VideoPrivacy.PRIVATE }
       const data = { privacy: VideoPrivacy.PRIVATE }
-      const uuid = await uploadVideoByLocalAccount(servers, videoNameId, data)
+      const { name, uuid } = await uploadVideoByLocalAccount(servers, data)
 
 
-      await checkNewVideoFromSubscription(baseParams, videoName, uuid, 'absence')
+      await checkNewVideoFromSubscription(baseParams, name, uuid, 'absence')
 
       await updateVideo(servers[0].url, servers[0].accessToken, uuid, { privacy: VideoPrivacy.PUBLIC })
 
       await wait(500)
 
       await updateVideo(servers[0].url, servers[0].accessToken, uuid, { privacy: VideoPrivacy.PUBLIC })
 
       await wait(500)
-      await checkNewVideoFromSubscription(baseParams, videoName, uuid, 'presence')
+      await checkNewVideoFromSubscription(baseParams, name, uuid, 'presence')
     })
 
     it('Should send a new video notification when a remote video becomes public', async function () {
       this.timeout(20000)
 
     })
 
     it('Should send a new video notification when a remote video becomes public', async function () {
       this.timeout(20000)
 
-      const videoNameId = 70
-      const videoName = 'remote video ' + videoNameId
-
       const data = { privacy: VideoPrivacy.PRIVATE }
       const data = { privacy: VideoPrivacy.PRIVATE }
-      const uuid = await uploadVideoByRemoteAccount(servers, videoNameId, data)
-      await waitJobs(servers)
+      const { name, uuid } = await uploadVideoByRemoteAccount(servers, data)
 
 
-      await checkNewVideoFromSubscription(baseParams, videoName, uuid, 'absence')
+      await checkNewVideoFromSubscription(baseParams, name, uuid, 'absence')
 
       await updateVideo(servers[1].url, servers[1].accessToken, uuid, { privacy: VideoPrivacy.PUBLIC })
 
       await waitJobs(servers)
 
       await updateVideo(servers[1].url, servers[1].accessToken, uuid, { privacy: VideoPrivacy.PUBLIC })
 
       await waitJobs(servers)
-      await checkNewVideoFromSubscription(baseParams, videoName, uuid, 'presence')
+      await checkNewVideoFromSubscription(baseParams, name, uuid, 'presence')
     })
 
     it('Should not send a new video notification when a video becomes unlisted', async function () {
       this.timeout(20000)
 
     })
 
     it('Should not send a new video notification when a video becomes unlisted', async function () {
       this.timeout(20000)
 
-      const videoNameId = 80
-      const videoName = 'local video ' + videoNameId
-
       const data = { privacy: VideoPrivacy.PRIVATE }
       const data = { privacy: VideoPrivacy.PRIVATE }
-      const uuid = await uploadVideoByLocalAccount(servers, videoNameId, data)
+      const { name, uuid } = await uploadVideoByLocalAccount(servers, data)
 
       await updateVideo(servers[0].url, servers[0].accessToken, uuid, { privacy: VideoPrivacy.UNLISTED })
 
 
       await updateVideo(servers[0].url, servers[0].accessToken, uuid, { privacy: VideoPrivacy.UNLISTED })
 
-      await checkNewVideoFromSubscription(baseParams, videoName, uuid, 'absence')
+      await checkNewVideoFromSubscription(baseParams, name, uuid, 'absence')
     })
 
     it('Should not send a new video notification when a remote video becomes unlisted', async function () {
       this.timeout(20000)
 
     })
 
     it('Should not send a new video notification when a remote video becomes unlisted', async function () {
       this.timeout(20000)
 
-      const videoNameId = 90
-      const videoName = 'remote video ' + videoNameId
-
       const data = { privacy: VideoPrivacy.PRIVATE }
       const data = { privacy: VideoPrivacy.PRIVATE }
-      const uuid = await uploadVideoByRemoteAccount(servers, videoNameId, data)
-      await waitJobs(servers)
+      const { name, uuid } = await uploadVideoByRemoteAccount(servers, data)
 
       await updateVideo(servers[1].url, servers[1].accessToken, uuid, { privacy: VideoPrivacy.UNLISTED })
 
       await waitJobs(servers)
 
       await updateVideo(servers[1].url, servers[1].accessToken, uuid, { privacy: VideoPrivacy.UNLISTED })
 
       await waitJobs(servers)
-      await checkNewVideoFromSubscription(baseParams, videoName, uuid, 'absence')
+      await checkNewVideoFromSubscription(baseParams, name, uuid, 'absence')
     })
 
     it('Should send a new video notification after a video import', async function () {
       this.timeout(30000)
 
     })
 
     it('Should send a new video notification after a video import', async function () {
       this.timeout(30000)
 
-      const resChannel = await getMyUserInformation(servers[0].url, servers[0].accessToken)
-      const channelId = resChannel.body.videoChannels[0].id
-      const videoName = 'local video 100'
+      const name = 'video import ' + uuidv4()
 
       const attributes = {
 
       const attributes = {
-        name: videoName,
+        name,
         channelId,
         privacy: VideoPrivacy.PUBLIC,
         targetUrl: getYoutubeVideoUrl()
         channelId,
         privacy: VideoPrivacy.PUBLIC,
         targetUrl: getYoutubeVideoUrl()
@@ -306,7 +303,7 @@ describe('Test users notifications', function () {
 
       await waitJobs(servers)
 
 
       await waitJobs(servers)
 
-      await checkNewVideoFromSubscription(baseParams, videoName, uuid, 'presence')
+      await checkNewVideoFromSubscription(baseParams, name, uuid, 'presence')
     })
   })
 
     })
   })
 
@@ -348,6 +345,23 @@ describe('Test users notifications', function () {
       await checkNewCommentOnMyVideo(baseParams, uuid, commentId, commentId, 'absence')
     })
 
       await checkNewCommentOnMyVideo(baseParams, uuid, commentId, commentId, 'absence')
     })
 
+    it('Should not send a new comment notification if the account is muted', async function () {
+      this.timeout(10000)
+
+      await addAccountToAccountBlocklist(servers[ 0 ].url, userAccessToken, 'root')
+
+      const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name: 'super video' })
+      const uuid = resVideo.body.video.uuid
+
+      const resComment = await addVideoCommentThread(servers[0].url, servers[0].accessToken, uuid, 'comment')
+      const commentId = resComment.body.comment.id
+
+      await wait(500)
+      await checkNewCommentOnMyVideo(baseParams, uuid, commentId, commentId, 'absence')
+
+      await removeAccountFromAccountBlocklist(servers[ 0 ].url, userAccessToken, 'root')
+    })
+
     it('Should send a new comment notification after a local comment on my video', async function () {
       this.timeout(10000)
 
     it('Should send a new comment notification after a local comment on my video', async function () {
       this.timeout(10000)
 
@@ -425,23 +439,21 @@ describe('Test users notifications', function () {
     it('Should send a notification to moderators on local video abuse', async function () {
       this.timeout(10000)
 
     it('Should send a notification to moderators on local video abuse', async function () {
       this.timeout(10000)
 
-      const videoName = 'local video 110'
-
-      const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name: videoName })
+      const name = 'video for abuse ' + uuidv4()
+      const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name })
       const uuid = resVideo.body.video.uuid
 
       await reportVideoAbuse(servers[0].url, servers[0].accessToken, uuid, 'super reason')
 
       await waitJobs(servers)
       const uuid = resVideo.body.video.uuid
 
       await reportVideoAbuse(servers[0].url, servers[0].accessToken, uuid, 'super reason')
 
       await waitJobs(servers)
-      await checkNewVideoAbuseForModerators(baseParams, uuid, videoName, 'presence')
+      await checkNewVideoAbuseForModerators(baseParams, uuid, name, 'presence')
     })
 
     it('Should send a notification to moderators on remote video abuse', async function () {
       this.timeout(10000)
 
     })
 
     it('Should send a notification to moderators on remote video abuse', async function () {
       this.timeout(10000)
 
-      const videoName = 'remote video 120'
-
-      const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name: videoName })
+      const name = 'video for abuse ' + uuidv4()
+      const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name })
       const uuid = resVideo.body.video.uuid
 
       await waitJobs(servers)
       const uuid = resVideo.body.video.uuid
 
       await waitJobs(servers)
@@ -449,7 +461,7 @@ describe('Test users notifications', function () {
       await reportVideoAbuse(servers[1].url, servers[1].accessToken, uuid, 'super reason')
 
       await waitJobs(servers)
       await reportVideoAbuse(servers[1].url, servers[1].accessToken, uuid, 'super reason')
 
       await waitJobs(servers)
-      await checkNewVideoAbuseForModerators(baseParams, uuid, videoName, 'presence')
+      await checkNewVideoAbuseForModerators(baseParams, uuid, name, 'presence')
     })
   })
 
     })
   })
 
@@ -468,23 +480,21 @@ describe('Test users notifications', function () {
     it('Should send a notification to video owner on blacklist', async function () {
       this.timeout(10000)
 
     it('Should send a notification to video owner on blacklist', async function () {
       this.timeout(10000)
 
-      const videoName = 'local video 130'
-
-      const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name: videoName })
+      const name = 'video for abuse ' + uuidv4()
+      const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name })
       const uuid = resVideo.body.video.uuid
 
       await addVideoToBlacklist(servers[0].url, servers[0].accessToken, uuid)
 
       await waitJobs(servers)
       const uuid = resVideo.body.video.uuid
 
       await addVideoToBlacklist(servers[0].url, servers[0].accessToken, uuid)
 
       await waitJobs(servers)
-      await checkNewBlacklistOnMyVideo(baseParams, uuid, videoName, 'blacklist')
+      await checkNewBlacklistOnMyVideo(baseParams, uuid, name, 'blacklist')
     })
 
     it('Should send a notification to video owner on unblacklist', async function () {
       this.timeout(10000)
 
     })
 
     it('Should send a notification to video owner on unblacklist', async function () {
       this.timeout(10000)
 
-      const videoName = 'local video 130'
-
-      const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name: videoName })
+      const name = 'video for abuse ' + uuidv4()
+      const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name })
       const uuid = resVideo.body.video.uuid
 
       await addVideoToBlacklist(servers[0].url, servers[0].accessToken, uuid)
       const uuid = resVideo.body.video.uuid
 
       await addVideoToBlacklist(servers[0].url, servers[0].accessToken, uuid)
@@ -494,38 +504,187 @@ describe('Test users notifications', function () {
       await waitJobs(servers)
 
       await wait(500)
       await waitJobs(servers)
 
       await wait(500)
-      await checkNewBlacklistOnMyVideo(baseParams, uuid, videoName, 'unblacklist')
+      await checkNewBlacklistOnMyVideo(baseParams, uuid, name, 'unblacklist')
+    })
+  })
+
+  describe('My video is published', function () {
+    let baseParams: CheckerBaseParams
+
+    before(() => {
+      baseParams = {
+        server: servers[1],
+        emails,
+        socketNotifications: adminNotificationsServer2,
+        token: servers[1].accessToken
+      }
+    })
+
+    it('Should not send a notification if transcoding is not enabled', async function () {
+      const { name, uuid } = await uploadVideoByLocalAccount(servers)
+      await waitJobs(servers)
+
+      await checkVideoIsPublished(baseParams, name, uuid, 'absence')
+    })
+
+    it('Should not send a notification if the wait transcoding is false', async function () {
+      this.timeout(50000)
+
+      await uploadVideoByRemoteAccount(servers, { waitTranscoding: false })
+      await waitJobs(servers)
+
+      const notification = await getLastNotification(servers[ 0 ].url, userAccessToken)
+      if (notification) {
+        expect(notification.type).to.not.equal(UserNotificationType.MY_VIDEO_PUBLISHED)
+      }
+    })
+
+    it('Should send a notification even if the video is not transcoded in other resolutions', async function () {
+      this.timeout(50000)
+
+      const { name, uuid } = await uploadVideoByRemoteAccount(servers, { waitTranscoding: true, fixture: 'video_short_240p.mp4' })
+      await waitJobs(servers)
+
+      await checkVideoIsPublished(baseParams, name, uuid, 'presence')
+    })
+
+    it('Should send a notification with a transcoded video', async function () {
+      this.timeout(50000)
+
+      const { name, uuid } = await uploadVideoByRemoteAccount(servers, { waitTranscoding: true })
+      await waitJobs(servers)
+
+      await checkVideoIsPublished(baseParams, name, uuid, 'presence')
+    })
+
+    it('Should send a notification when an imported video is transcoded', async function () {
+      this.timeout(50000)
+
+      const name = 'video import ' + uuidv4()
+
+      const attributes = {
+        name,
+        channelId,
+        privacy: VideoPrivacy.PUBLIC,
+        targetUrl: getYoutubeVideoUrl(),
+        waitTranscoding: true
+      }
+      const res = await importVideo(servers[1].url, servers[1].accessToken, attributes)
+      const uuid = res.body.video.uuid
+
+      await waitJobs(servers)
+      await checkVideoIsPublished(baseParams, name, uuid, 'presence')
+    })
+
+    it('Should send a notification when the scheduled update has been proceeded', async function () {
+      this.timeout(70000)
+
+      // In 2 seconds
+      let updateAt = new Date(new Date().getTime() + 2000)
+
+      const data = {
+        privacy: VideoPrivacy.PRIVATE,
+        scheduleUpdate: {
+          updateAt: updateAt.toISOString(),
+          privacy: VideoPrivacy.PUBLIC
+        }
+      }
+      const { name, uuid } = await uploadVideoByRemoteAccount(servers, data)
+
+      await wait(6000)
+      await checkVideoIsPublished(baseParams, name, uuid, 'presence')
+    })
+  })
+
+  describe('My video is imported', function () {
+    let baseParams: CheckerBaseParams
+
+    before(() => {
+      baseParams = {
+        server: servers[0],
+        emails,
+        socketNotifications: adminNotifications,
+        token: servers[0].accessToken
+      }
+    })
+
+    it('Should send a notification when the video import failed', async function () {
+      this.timeout(70000)
+
+      const name = 'video import ' + uuidv4()
+
+      const attributes = {
+        name,
+        channelId,
+        privacy: VideoPrivacy.PRIVATE,
+        targetUrl: getBadVideoUrl()
+      }
+      const res = await importVideo(servers[0].url, servers[0].accessToken, attributes)
+      const uuid = res.body.video.uuid
+
+      await waitJobs(servers)
+      await checkMyVideoImportIsFinished(baseParams, name, uuid, getBadVideoUrl(), false, 'presence')
+    })
+
+    it('Should send a notification when the video import succeeded', async function () {
+      this.timeout(70000)
+
+      const name = 'video import ' + uuidv4()
+
+      const attributes = {
+        name,
+        channelId,
+        privacy: VideoPrivacy.PRIVATE,
+        targetUrl: getYoutubeVideoUrl()
+      }
+      const res = await importVideo(servers[0].url, servers[0].accessToken, attributes)
+      const uuid = res.body.video.uuid
+
+      await waitJobs(servers)
+      await checkMyVideoImportIsFinished(baseParams, name, uuid, getYoutubeVideoUrl(), true, 'presence')
     })
   })
 
   describe('Mark as read', function () {
     it('Should mark as read some notifications', async function () {
     })
   })
 
   describe('Mark as read', function () {
     it('Should mark as read some notifications', async function () {
-      const res = await getUserNotifications(servers[0].url, userAccessToken, 2, 3)
+      const res = await getUserNotifications(servers[ 0 ].url, userAccessToken, 2, 3)
       const ids = res.body.data.map(n => n.id)
 
       const ids = res.body.data.map(n => n.id)
 
-      await markAsReadNotifications(servers[0].url, userAccessToken, ids)
+      await markAsReadNotifications(servers[ 0 ].url, userAccessToken, ids)
     })
 
     it('Should have the notifications marked as read', async function () {
     })
 
     it('Should have the notifications marked as read', async function () {
-      const res = await getUserNotifications(servers[0].url, userAccessToken, 0, 10)
+      const res = await getUserNotifications(servers[ 0 ].url, userAccessToken, 0, 10)
+
+      const notifications = res.body.data as UserNotification[]
+      expect(notifications[ 0 ].read).to.be.false
+      expect(notifications[ 1 ].read).to.be.false
+      expect(notifications[ 2 ].read).to.be.true
+      expect(notifications[ 3 ].read).to.be.true
+      expect(notifications[ 4 ].read).to.be.true
+      expect(notifications[ 5 ].read).to.be.false
+    })
+
+    it('Should only list read notifications', async function () {
+      const res = await getUserNotifications(servers[ 0 ].url, userAccessToken, 0, 10, false)
 
       const notifications = res.body.data as UserNotification[]
 
       const notifications = res.body.data as UserNotification[]
-      expect(notifications[0].read).to.be.false
-      expect(notifications[1].read).to.be.false
-      expect(notifications[2].read).to.be.true
-      expect(notifications[3].read).to.be.true
-      expect(notifications[4].read).to.be.true
-      expect(notifications[5].read).to.be.false
+      for (const notification of notifications) {
+        expect(notification.read).to.be.true
+      }
+    })
+
+    it('Should only list unread notifications', async function () {
+      const res = await getUserNotifications(servers[ 0 ].url, userAccessToken, 0, 10, true)
+
+      const notifications = res.body.data as UserNotification[]
+      for (const notification of notifications) {
+        expect(notification.read).to.be.false
+      }
     })
   })
 
   describe('Notification settings', function () {
     })
   })
 
   describe('Notification settings', function () {
-    const baseUpdateNotificationParams = {
-      newCommentOnMyVideo: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL,
-      newVideoFromSubscription: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL,
-      videoAbuseAsModerator: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL,
-      blacklistOnMyVideo: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL
-    }
     let baseParams: CheckerBaseParams
 
     before(() => {
     let baseParams: CheckerBaseParams
 
     before(() => {
@@ -538,7 +697,7 @@ describe('Test users notifications', function () {
     })
 
     it('Should not have notifications', async function () {
     })
 
     it('Should not have notifications', async function () {
-      await updateMyNotificationSettings(servers[0].url, userAccessToken, immutableAssign(baseUpdateNotificationParams, {
+      await updateMyNotificationSettings(servers[0].url, userAccessToken, immutableAssign(allNotificationSettings, {
         newVideoFromSubscription: UserNotificationSettingValue.NONE
       }))
 
         newVideoFromSubscription: UserNotificationSettingValue.NONE
       }))
 
@@ -548,16 +707,14 @@ describe('Test users notifications', function () {
         expect(info.notificationSettings.newVideoFromSubscription).to.equal(UserNotificationSettingValue.NONE)
       }
 
         expect(info.notificationSettings.newVideoFromSubscription).to.equal(UserNotificationSettingValue.NONE)
       }
 
-      const videoNameId = 42
-      const videoName = 'local video ' + videoNameId
-      const uuid = await uploadVideoByLocalAccount(servers, videoNameId)
+      const { name, uuid } = await uploadVideoByLocalAccount(servers)
 
       const check = { web: true, mail: true }
 
       const check = { web: true, mail: true }
-      await checkNewVideoFromSubscription(immutableAssign(baseParams, { check }), videoName, uuid, 'absence')
+      await checkNewVideoFromSubscription(immutableAssign(baseParams, { check }), name, uuid, 'absence')
     })
 
     it('Should only have web notifications', async function () {
     })
 
     it('Should only have web notifications', async function () {
-      await updateMyNotificationSettings(servers[0].url, userAccessToken, immutableAssign(baseUpdateNotificationParams, {
+      await updateMyNotificationSettings(servers[0].url, userAccessToken, immutableAssign(allNotificationSettings, {
         newVideoFromSubscription: UserNotificationSettingValue.WEB_NOTIFICATION
       }))
 
         newVideoFromSubscription: UserNotificationSettingValue.WEB_NOTIFICATION
       }))
 
@@ -567,23 +724,21 @@ describe('Test users notifications', function () {
         expect(info.notificationSettings.newVideoFromSubscription).to.equal(UserNotificationSettingValue.WEB_NOTIFICATION)
       }
 
         expect(info.notificationSettings.newVideoFromSubscription).to.equal(UserNotificationSettingValue.WEB_NOTIFICATION)
       }
 
-      const videoNameId = 52
-      const videoName = 'local video ' + videoNameId
-      const uuid = await uploadVideoByLocalAccount(servers, videoNameId)
+      const { name, uuid } = await uploadVideoByLocalAccount(servers)
 
       {
         const check = { mail: true, web: false }
 
       {
         const check = { mail: true, web: false }
-        await checkNewVideoFromSubscription(immutableAssign(baseParams, { check }), videoName, uuid, 'absence')
+        await checkNewVideoFromSubscription(immutableAssign(baseParams, { check }), name, uuid, 'absence')
       }
 
       {
         const check = { mail: false, web: true }
       }
 
       {
         const check = { mail: false, web: true }
-        await checkNewVideoFromSubscription(immutableAssign(baseParams, { check }), videoName, uuid, 'presence')
+        await checkNewVideoFromSubscription(immutableAssign(baseParams, { check }), name, uuid, 'presence')
       }
     })
 
     it('Should only have mail notifications', async function () {
       }
     })
 
     it('Should only have mail notifications', async function () {
-      await updateMyNotificationSettings(servers[0].url, userAccessToken, immutableAssign(baseUpdateNotificationParams, {
+      await updateMyNotificationSettings(servers[0].url, userAccessToken, immutableAssign(allNotificationSettings, {
         newVideoFromSubscription: UserNotificationSettingValue.EMAIL
       }))
 
         newVideoFromSubscription: UserNotificationSettingValue.EMAIL
       }))
 
@@ -593,23 +748,21 @@ describe('Test users notifications', function () {
         expect(info.notificationSettings.newVideoFromSubscription).to.equal(UserNotificationSettingValue.EMAIL)
       }
 
         expect(info.notificationSettings.newVideoFromSubscription).to.equal(UserNotificationSettingValue.EMAIL)
       }
 
-      const videoNameId = 62
-      const videoName = 'local video ' + videoNameId
-      const uuid = await uploadVideoByLocalAccount(servers, videoNameId)
+      const { name, uuid } = await uploadVideoByLocalAccount(servers)
 
       {
         const check = { mail: false, web: true }
 
       {
         const check = { mail: false, web: true }
-        await checkNewVideoFromSubscription(immutableAssign(baseParams, { check }), videoName, uuid, 'absence')
+        await checkNewVideoFromSubscription(immutableAssign(baseParams, { check }), name, uuid, 'absence')
       }
 
       {
         const check = { mail: true, web: false }
       }
 
       {
         const check = { mail: true, web: false }
-        await checkNewVideoFromSubscription(immutableAssign(baseParams, { check }), videoName, uuid, 'presence')
+        await checkNewVideoFromSubscription(immutableAssign(baseParams, { check }), name, uuid, 'presence')
       }
     })
 
     it('Should have email and web notifications', async function () {
       }
     })
 
     it('Should have email and web notifications', async function () {
-      await updateMyNotificationSettings(servers[0].url, userAccessToken, immutableAssign(baseUpdateNotificationParams, {
+      await updateMyNotificationSettings(servers[0].url, userAccessToken, immutableAssign(allNotificationSettings, {
         newVideoFromSubscription: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL
       }))
 
         newVideoFromSubscription: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL
       }))
 
@@ -619,11 +772,9 @@ describe('Test users notifications', function () {
         expect(info.notificationSettings.newVideoFromSubscription).to.equal(UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL)
       }
 
         expect(info.notificationSettings.newVideoFromSubscription).to.equal(UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL)
       }
 
-      const videoNameId = 72
-      const videoName = 'local video ' + videoNameId
-      const uuid = await uploadVideoByLocalAccount(servers, videoNameId)
+      const { name, uuid } = await uploadVideoByLocalAccount(servers)
 
 
-      await checkNewVideoFromSubscription(baseParams, videoName, uuid, 'presence')
+      await checkNewVideoFromSubscription(baseParams, name, uuid, 'presence')
     })
   })
 
     })
   })
 
diff --git a/server/tests/fixtures/video_short_240p.mp4 b/server/tests/fixtures/video_short_240p.mp4
new file mode 100644 (file)
index 0000000..db07494
Binary files /dev/null and b/server/tests/fixtures/video_short_240p.mp4 differ
index 7cecd70a27739389373a9d81d8e2cc5aa8631904..55d351abfe582c10303f49577182cbfd5f54e161 100644 (file)
@@ -10,4 +10,6 @@ export interface UserNotificationSetting {
   newCommentOnMyVideo: UserNotificationSettingValue
   videoAbuseAsModerator: UserNotificationSettingValue
   blacklistOnMyVideo: UserNotificationSettingValue
   newCommentOnMyVideo: UserNotificationSettingValue
   videoAbuseAsModerator: UserNotificationSettingValue
   blacklistOnMyVideo: UserNotificationSettingValue
+  myVideoPublished: UserNotificationSettingValue
+  myVideoImportFinished: UserNotificationSettingValue
 }
 }
index 39beb2350e08a3a51e44b68eb2e87d3d918172d5..ee9ac275abb9b4c617bae5f10df7f2c60ad338ae 100644 (file)
@@ -3,10 +3,13 @@ export enum UserNotificationType {
   NEW_COMMENT_ON_MY_VIDEO = 2,
   NEW_VIDEO_ABUSE_FOR_MODERATORS = 3,
   BLACKLIST_ON_MY_VIDEO = 4,
   NEW_COMMENT_ON_MY_VIDEO = 2,
   NEW_VIDEO_ABUSE_FOR_MODERATORS = 3,
   BLACKLIST_ON_MY_VIDEO = 4,
-  UNBLACKLIST_ON_MY_VIDEO = 5
+  UNBLACKLIST_ON_MY_VIDEO = 5,
+  MY_VIDEO_PUBLISHED = 6,
+  MY_VIDEO_IMPORT_SUCCESS = 7,
+  MY_VIDEO_IMPORT_ERROR = 8
 }
 
 }
 
-interface VideoInfo {
+export interface VideoInfo {
   id: number
   uuid: string
   name: string
   id: number
   uuid: string
   name: string
@@ -24,12 +27,22 @@ export interface UserNotification {
     }
   }
 
     }
   }
 
+  videoImport?: {
+    id: number
+    video?: VideoInfo
+    torrentName?: string
+    magnetUri?: string
+    targetUrl?: string
+  }
+
   comment?: {
     id: number
   comment?: {
     id: number
+    threadId: number
     account: {
       id: number
       displayName: string
     }
     account: {
       id: number
       displayName: string
     }
+    video: VideoInfo
   }
 
   videoAbuse?: {
   }
 
   videoAbuse?: {
index dbe87559ef0067105a8c175bfb8b94b57f897254..75d52023a31904b0a4a7179b358dc3a2dbdc43a7 100644 (file)
@@ -4,6 +4,7 @@ import { makeGetRequest, makePostBodyRequest, makePutBodyRequest } from '../requ
 import { UserNotification, UserNotificationSetting, UserNotificationType } from '../../models/users'
 import { ServerInfo } from '..'
 import { expect } from 'chai'
 import { UserNotification, UserNotificationSetting, UserNotificationType } from '../../models/users'
 import { ServerInfo } from '..'
 import { expect } from 'chai'
+import { inspect } from 'util'
 
 function updateMyNotificationSettings (url: string, token: string, settings: UserNotificationSetting, statusCodeExpected = 204) {
   const path = '/api/v1/users/me/notification-settings'
 
 function updateMyNotificationSettings (url: string, token: string, settings: UserNotificationSetting, statusCodeExpected = 204) {
   const path = '/api/v1/users/me/notification-settings'
@@ -17,7 +18,15 @@ function updateMyNotificationSettings (url: string, token: string, settings: Use
   })
 }
 
   })
 }
 
-function getUserNotifications (url: string, token: string, start: number, count: number, sort = '-createdAt', statusCodeExpected = 200) {
+function getUserNotifications (
+  url: string,
+  token: string,
+  start: number,
+  count: number,
+  unread?: boolean,
+  sort = '-createdAt',
+  statusCodeExpected = 200
+) {
   const path = '/api/v1/users/me/notifications'
 
   return makeGetRequest({
   const path = '/api/v1/users/me/notifications'
 
   return makeGetRequest({
@@ -27,7 +36,8 @@ function getUserNotifications (url: string, token: string, start: number, count:
     query: {
       start,
       count,
     query: {
       start,
       count,
-      sort
+      sort,
+      unread
     },
     statusCodeExpected
   })
     },
     statusCodeExpected
   })
@@ -46,7 +56,7 @@ function markAsReadNotifications (url: string, token: string, ids: number[], sta
 }
 
 async function getLastNotification (serverUrl: string, accessToken: string) {
 }
 
 async function getLastNotification (serverUrl: string, accessToken: string) {
-  const res = await getUserNotifications(serverUrl, accessToken, 0, 1, '-createdAt')
+  const res = await getUserNotifications(serverUrl, accessToken, 0, 1, undefined, '-createdAt')
 
   if (res.body.total === 0) return undefined
 
 
   if (res.body.total === 0) return undefined
 
@@ -65,21 +75,33 @@ type CheckerType = 'presence' | 'absence'
 
 async function checkNotification (
   base: CheckerBaseParams,
 
 async function checkNotification (
   base: CheckerBaseParams,
-  lastNotificationChecker: (notification: UserNotification) => void,
-  socketNotificationFinder: (notification: UserNotification) => boolean,
+  notificationChecker: (notification: UserNotification, type: CheckerType) => void,
   emailNotificationFinder: (email: object) => boolean,
   emailNotificationFinder: (email: object) => boolean,
-  checkType: 'presence' | 'absence'
+  checkType: CheckerType
 ) {
   const check = base.check || { web: true, mail: true }
 
   if (check.web) {
     const notification = await getLastNotification(base.server.url, base.token)
 ) {
   const check = base.check || { web: true, mail: true }
 
   if (check.web) {
     const notification = await getLastNotification(base.server.url, base.token)
-    lastNotificationChecker(notification)
 
 
-    const socketNotification = base.socketNotifications.find(n => socketNotificationFinder(n))
+    if (notification || checkType !== 'absence') {
+      notificationChecker(notification, checkType)
+    }
 
 
-    if (checkType === 'presence') expect(socketNotification, 'The socket notification is absent.').to.not.be.undefined
-    else expect(socketNotification, 'The socket notification is present.').to.be.undefined
+    const socketNotification = base.socketNotifications.find(n => {
+      try {
+        notificationChecker(n, 'presence')
+        return true
+      } catch {
+        return false
+      }
+    })
+
+    if (checkType === 'presence') {
+      expect(socketNotification, 'The socket notification is absent. ' + inspect(base.socketNotifications)).to.not.be.undefined
+    } else {
+      expect(socketNotification, 'The socket notification is present. ' + inspect(socketNotification)).to.be.undefined
+    }
   }
 
   if (check.mail) {
   }
 
   if (check.mail) {
@@ -89,45 +111,127 @@ async function checkNotification (
                       .reverse()
                       .find(e => emailNotificationFinder(e))
 
                       .reverse()
                       .find(e => emailNotificationFinder(e))
 
-    if (checkType === 'presence') expect(email, 'The email is present.').to.not.be.undefined
-    else expect(email, 'The email is absent.').to.be.undefined
+    if (checkType === 'presence') {
+      expect(email, 'The email is absent. ' + inspect(base.emails)).to.not.be.undefined
+    } else {
+      expect(email, 'The email is present. ' + inspect(email)).to.be.undefined
+    }
   }
 }
 
   }
 }
 
+function checkVideo (video: any, videoName?: string, videoUUID?: string) {
+  expect(video.name).to.be.a('string')
+  expect(video.name).to.not.be.empty
+  if (videoName) expect(video.name).to.equal(videoName)
+
+  expect(video.uuid).to.be.a('string')
+  expect(video.uuid).to.not.be.empty
+  if (videoUUID) expect(video.uuid).to.equal(videoUUID)
+
+  expect(video.id).to.be.a('number')
+}
+
+function checkActor (channel: any) {
+  expect(channel.id).to.be.a('number')
+  expect(channel.displayName).to.be.a('string')
+  expect(channel.displayName).to.not.be.empty
+}
+
+function checkComment (comment: any, commentId: number, threadId: number) {
+  expect(comment.id).to.equal(commentId)
+  expect(comment.threadId).to.equal(threadId)
+}
+
 async function checkNewVideoFromSubscription (base: CheckerBaseParams, videoName: string, videoUUID: string, type: CheckerType) {
   const notificationType = UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION
 
 async function checkNewVideoFromSubscription (base: CheckerBaseParams, videoName: string, videoUUID: string, type: CheckerType) {
   const notificationType = UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION
 
-  function lastNotificationChecker (notification: UserNotification) {
+  function notificationChecker (notification: UserNotification, type: CheckerType) {
     if (type === 'presence') {
       expect(notification).to.not.be.undefined
       expect(notification.type).to.equal(notificationType)
     if (type === 'presence') {
       expect(notification).to.not.be.undefined
       expect(notification.type).to.equal(notificationType)
-      expect(notification.video.name).to.equal(videoName)
+
+      checkVideo(notification.video, videoName, videoUUID)
+      checkActor(notification.video.channel)
     } else {
       expect(notification.video).to.satisfy(v => v === undefined || v.name !== videoName)
     }
   }
 
     } else {
       expect(notification.video).to.satisfy(v => v === undefined || v.name !== videoName)
     }
   }
 
-  function socketFinder (notification: UserNotification) {
-    return notification.type === notificationType && notification.video.name === videoName
+  function emailFinder (email: object) {
+    return email[ 'text' ].indexOf(videoUUID) !== -1
+  }
+
+  await checkNotification(base, notificationChecker, emailFinder, type)
+}
+
+async function checkVideoIsPublished (base: CheckerBaseParams, videoName: string, videoUUID: string, type: CheckerType) {
+  const notificationType = UserNotificationType.MY_VIDEO_PUBLISHED
+
+  function notificationChecker (notification: UserNotification, type: CheckerType) {
+    if (type === 'presence') {
+      expect(notification).to.not.be.undefined
+      expect(notification.type).to.equal(notificationType)
+
+      checkVideo(notification.video, videoName, videoUUID)
+      checkActor(notification.video.channel)
+    } else {
+      expect(notification.video).to.satisfy(v => v === undefined || v.name !== videoName)
+    }
   }
 
   function emailFinder (email: object) {
   }
 
   function emailFinder (email: object) {
-    return email[ 'text' ].indexOf(videoUUID) !== -1
+    const text: string = email[ 'text' ]
+    return text.includes(videoUUID) && text.includes('Your video')
   }
 
   }
 
-  await checkNotification(base, lastNotificationChecker, socketFinder, emailFinder, type)
+  await checkNotification(base, notificationChecker, emailFinder, type)
+}
+
+async function checkMyVideoImportIsFinished (
+  base: CheckerBaseParams,
+  videoName: string,
+  videoUUID: string,
+  url: string,
+  success: boolean,
+  type: CheckerType
+) {
+  const notificationType = success ? UserNotificationType.MY_VIDEO_IMPORT_SUCCESS : UserNotificationType.MY_VIDEO_IMPORT_ERROR
+
+  function notificationChecker (notification: UserNotification, type: CheckerType) {
+    if (type === 'presence') {
+      expect(notification).to.not.be.undefined
+      expect(notification.type).to.equal(notificationType)
+
+      expect(notification.videoImport.targetUrl).to.equal(url)
+
+      if (success) checkVideo(notification.videoImport.video, videoName, videoUUID)
+    } else {
+      expect(notification.videoImport).to.satisfy(i => i === undefined || i.targetUrl !== url)
+    }
+  }
+
+  function emailFinder (email: object) {
+    const text: string = email[ 'text' ]
+    const toFind = success ? ' finished' : ' error'
+
+    return text.includes(url) && text.includes(toFind)
+  }
+
+  await checkNotification(base, notificationChecker, emailFinder, type)
 }
 
 let lastEmailCount = 0
 async function checkNewCommentOnMyVideo (base: CheckerBaseParams, uuid: string, commentId: number, threadId: number, type: CheckerType) {
   const notificationType = UserNotificationType.NEW_COMMENT_ON_MY_VIDEO
 
 }
 
 let lastEmailCount = 0
 async function checkNewCommentOnMyVideo (base: CheckerBaseParams, uuid: string, commentId: number, threadId: number, type: CheckerType) {
   const notificationType = UserNotificationType.NEW_COMMENT_ON_MY_VIDEO
 
-  function lastNotificationChecker (notification: UserNotification) {
+  function notificationChecker (notification: UserNotification, type: CheckerType) {
     if (type === 'presence') {
       expect(notification).to.not.be.undefined
       expect(notification.type).to.equal(notificationType)
     if (type === 'presence') {
       expect(notification).to.not.be.undefined
       expect(notification.type).to.equal(notificationType)
-      expect(notification.comment.id).to.equal(commentId)
-      expect(notification.comment.account.displayName).to.equal('root')
+
+      checkComment(notification.comment, commentId, threadId)
+      checkActor(notification.comment.account)
+      checkVideo(notification.comment.video, undefined, uuid)
     } else {
       expect(notification).to.satisfy((n: UserNotification) => {
         return n === undefined || n.comment === undefined || n.comment.id !== commentId
     } else {
       expect(notification).to.satisfy((n: UserNotification) => {
         return n === undefined || n.comment === undefined || n.comment.id !== commentId
@@ -135,18 +239,12 @@ async function checkNewCommentOnMyVideo (base: CheckerBaseParams, uuid: string,
     }
   }
 
     }
   }
 
-  function socketFinder (notification: UserNotification) {
-    return notification.type === notificationType &&
-      notification.comment.id === commentId &&
-      notification.comment.account.displayName === 'root'
-  }
-
   const commentUrl = `http://localhost:9001/videos/watch/${uuid};threadId=${threadId}`
   function emailFinder (email: object) {
     return email[ 'text' ].indexOf(commentUrl) !== -1
   }
 
   const commentUrl = `http://localhost:9001/videos/watch/${uuid};threadId=${threadId}`
   function emailFinder (email: object) {
     return email[ 'text' ].indexOf(commentUrl) !== -1
   }
 
-  await checkNotification(base, lastNotificationChecker, socketFinder, emailFinder, type)
+  await checkNotification(base, notificationChecker, emailFinder, type)
 
   if (type === 'presence') {
     // We cannot detect email duplicates, so check we received another email
 
   if (type === 'presence') {
     // We cannot detect email duplicates, so check we received another email
@@ -158,12 +256,13 @@ async function checkNewCommentOnMyVideo (base: CheckerBaseParams, uuid: string,
 async function checkNewVideoAbuseForModerators (base: CheckerBaseParams, videoUUID: string, videoName: string, type: CheckerType) {
   const notificationType = UserNotificationType.NEW_VIDEO_ABUSE_FOR_MODERATORS
 
 async function checkNewVideoAbuseForModerators (base: CheckerBaseParams, videoUUID: string, videoName: string, type: CheckerType) {
   const notificationType = UserNotificationType.NEW_VIDEO_ABUSE_FOR_MODERATORS
 
-  function lastNotificationChecker (notification: UserNotification) {
+  function notificationChecker (notification: UserNotification, type: CheckerType) {
     if (type === 'presence') {
       expect(notification).to.not.be.undefined
       expect(notification.type).to.equal(notificationType)
     if (type === 'presence') {
       expect(notification).to.not.be.undefined
       expect(notification.type).to.equal(notificationType)
-      expect(notification.videoAbuse.video.uuid).to.equal(videoUUID)
-      expect(notification.videoAbuse.video.name).to.equal(videoName)
+
+      expect(notification.videoAbuse.id).to.be.a('number')
+      checkVideo(notification.videoAbuse.video, videoName, videoUUID)
     } else {
       expect(notification).to.satisfy((n: UserNotification) => {
         return n === undefined || n.videoAbuse === undefined || n.videoAbuse.video.uuid !== videoUUID
     } else {
       expect(notification).to.satisfy((n: UserNotification) => {
         return n === undefined || n.videoAbuse === undefined || n.videoAbuse.video.uuid !== videoUUID
@@ -171,16 +270,12 @@ async function checkNewVideoAbuseForModerators (base: CheckerBaseParams, videoUU
     }
   }
 
     }
   }
 
-  function socketFinder (notification: UserNotification) {
-    return notification.type === notificationType && notification.videoAbuse.video.uuid === videoUUID
-  }
-
   function emailFinder (email: object) {
     const text = email[ 'text' ]
     return text.indexOf(videoUUID) !== -1 && text.indexOf('abuse') !== -1
   }
 
   function emailFinder (email: object) {
     const text = email[ 'text' ]
     return text.indexOf(videoUUID) !== -1 && text.indexOf('abuse') !== -1
   }
 
-  await checkNotification(base, lastNotificationChecker, socketFinder, emailFinder, type)
+  await checkNotification(base, notificationChecker, emailFinder, type)
 }
 
 async function checkNewBlacklistOnMyVideo (
 }
 
 async function checkNewBlacklistOnMyVideo (
@@ -193,18 +288,13 @@ async function checkNewBlacklistOnMyVideo (
     ? UserNotificationType.BLACKLIST_ON_MY_VIDEO
     : UserNotificationType.UNBLACKLIST_ON_MY_VIDEO
 
     ? UserNotificationType.BLACKLIST_ON_MY_VIDEO
     : UserNotificationType.UNBLACKLIST_ON_MY_VIDEO
 
-  function lastNotificationChecker (notification: UserNotification) {
+  function notificationChecker (notification: UserNotification) {
     expect(notification).to.not.be.undefined
     expect(notification.type).to.equal(notificationType)
 
     const video = blacklistType === 'blacklist' ? notification.videoBlacklist.video : notification.video
 
     expect(notification).to.not.be.undefined
     expect(notification.type).to.equal(notificationType)
 
     const video = blacklistType === 'blacklist' ? notification.videoBlacklist.video : notification.video
 
-    expect(video.uuid).to.equal(videoUUID)
-    expect(video.name).to.equal(videoName)
-  }
-
-  function socketFinder (notification: UserNotification) {
-    return notification.type === notificationType && (notification.video || notification.videoBlacklist.video).uuid === videoUUID
+    checkVideo(video, videoName, videoUUID)
   }
 
   function emailFinder (email: object) {
   }
 
   function emailFinder (email: object) {
@@ -212,7 +302,7 @@ async function checkNewBlacklistOnMyVideo (
     return text.indexOf(videoUUID) !== -1 && text.indexOf(' ' + blacklistType) !== -1
   }
 
     return text.indexOf(videoUUID) !== -1 && text.indexOf(' ' + blacklistType) !== -1
   }
 
-  await checkNotification(base, lastNotificationChecker, socketFinder, emailFinder, 'presence')
+  await checkNotification(base, notificationChecker, emailFinder, 'presence')
 }
 
 // ---------------------------------------------------------------------------
 }
 
 // ---------------------------------------------------------------------------
@@ -221,6 +311,8 @@ export {
   CheckerBaseParams,
   CheckerType,
   checkNotification,
   CheckerBaseParams,
   CheckerType,
   checkNotification,
+  checkMyVideoImportIsFinished,
+  checkVideoIsPublished,
   checkNewVideoFromSubscription,
   checkNewCommentOnMyVideo,
   checkNewBlacklistOnMyVideo,
   checkNewVideoFromSubscription,
   checkNewCommentOnMyVideo,
   checkNewBlacklistOnMyVideo,
index 3fa49b4320fad5da696afd9d0544a1bc0541790d..ec77cdcda6503c770b2a3c6120f197368aa3b3e3 100644 (file)
@@ -11,6 +11,10 @@ function getMagnetURI () {
   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'
 }
 
   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'
 }
 
+function getBadVideoUrl () {
+  return 'https://download.cpy.re/peertube/bad_video.mp4'
+}
+
 function importVideo (url: string, token: string, attributes: VideoImportCreate) {
   const path = '/api/v1/videos/imports'
 
 function importVideo (url: string, token: string, attributes: VideoImportCreate) {
   const path = '/api/v1/videos/imports'
 
@@ -45,6 +49,7 @@ function getMyVideoImports (url: string, token: string, sort?: string) {
 // ---------------------------------------------------------------------------
 
 export {
 // ---------------------------------------------------------------------------
 
 export {
+  getBadVideoUrl,
   getYoutubeVideoUrl,
   importVideo,
   getMagnetURI,
   getYoutubeVideoUrl,
   importVideo,
   getMagnetURI,