aboutsummaryrefslogtreecommitdiffhomepage
path: root/server
diff options
context:
space:
mode:
authorJosh Morel <morel.josh@hotmail.com>2019-04-02 05:26:47 -0400
committerChocobozzz <chocobozzz@cpy.re>2019-04-02 11:26:47 +0200
commit7ccddd7b5250bd25a917a6e77e58b87b9484a2a4 (patch)
treee75dc991369c1768804fefa114eb2a832881087f /server
parent12fed49ebab0c414713d57ea316b6488ae6bef99 (diff)
downloadPeerTube-7ccddd7b5250bd25a917a6e77e58b87b9484a2a4.tar.gz
PeerTube-7ccddd7b5250bd25a917a6e77e58b87b9484a2a4.tar.zst
PeerTube-7ccddd7b5250bd25a917a6e77e58b87b9484a2a4.zip
add quarantine videos feature (#1637)
* add quarantine videos feature * increase Notification settings test timeout to 20000ms. was completing 7000 locally but timing out after 10000 on travis * fix quarantine video test issues -propagate misspelling -remove skip from server/tests/client.ts * WIP use blacklist for moderator video approval instead of video.quarantine boolean * finish auto-blacklist feature
Diffstat (limited to 'server')
-rw-r--r--server/controllers/api/config.ts14
-rw-r--r--server/controllers/api/users/my-notifications.ts1
-rw-r--r--server/controllers/api/videos/blacklist.ts25
-rw-r--r--server/controllers/api/videos/import.ts10
-rw-r--r--server/controllers/api/videos/index.ts18
-rw-r--r--server/helpers/custom-validators/video-blacklist.ts7
-rw-r--r--server/helpers/video.ts3
-rw-r--r--server/initializers/checker-before-init.ts2
-rw-r--r--server/initializers/constants.ts9
-rw-r--r--server/initializers/migrations/0350-video-blacklist-type.ts64
-rw-r--r--server/lib/activitypub/videos.ts2
-rw-r--r--server/lib/emailer.ts23
-rw-r--r--server/lib/job-queue/handlers/video-import.ts7
-rw-r--r--server/lib/job-queue/handlers/video-transcoding.ts12
-rw-r--r--server/lib/notifier.ts63
-rw-r--r--server/lib/schedulers/update-videos-scheduler.ts2
-rw-r--r--server/lib/user.ts1
-rw-r--r--server/lib/video-blacklist.ts31
-rw-r--r--server/middlewares/validators/videos/video-blacklist.ts25
-rw-r--r--server/models/account/user-notification-setting.ts10
-rw-r--r--server/models/video/schedule-video-update.ts3
-rw-r--r--server/models/video/video-blacklist.ts56
-rw-r--r--server/tests/api/check-params/config.ts7
-rw-r--r--server/tests/api/check-params/user-notifications.ts1
-rw-r--r--server/tests/api/check-params/video-blacklist.ts11
-rw-r--r--server/tests/api/check-params/videos.ts3
-rw-r--r--server/tests/api/server/config.ts9
-rw-r--r--server/tests/api/users/user-notifications.ts191
-rw-r--r--server/tests/api/videos/video-blacklist.ts25
29 files changed, 570 insertions, 65 deletions
diff --git a/server/controllers/api/config.ts b/server/controllers/api/config.ts
index 6497cda3c..bd0ba4f9d 100644
--- a/server/controllers/api/config.ts
+++ b/server/controllers/api/config.ts
@@ -94,6 +94,13 @@ async function getConfig (req: express.Request, res: express.Response) {
94 } 94 }
95 } 95 }
96 }, 96 },
97 autoBlacklist: {
98 videos: {
99 ofUsers: {
100 enabled: CONFIG.AUTO_BLACKLIST.VIDEOS.OF_USERS.ENABLED
101 }
102 }
103 },
97 avatar: { 104 avatar: {
98 file: { 105 file: {
99 size: { 106 size: {
@@ -265,6 +272,13 @@ function customConfig (): CustomConfig {
265 enabled: CONFIG.IMPORT.VIDEOS.TORRENT.ENABLED 272 enabled: CONFIG.IMPORT.VIDEOS.TORRENT.ENABLED
266 } 273 }
267 } 274 }
275 },
276 autoBlacklist: {
277 videos: {
278 ofUsers: {
279 enabled: CONFIG.AUTO_BLACKLIST.VIDEOS.OF_USERS.ENABLED
280 }
281 }
268 } 282 }
269 } 283 }
270} 284}
diff --git a/server/controllers/api/users/my-notifications.ts b/server/controllers/api/users/my-notifications.ts
index bbafda5a6..4edad2a74 100644
--- a/server/controllers/api/users/my-notifications.ts
+++ b/server/controllers/api/users/my-notifications.ts
@@ -69,6 +69,7 @@ async function updateNotificationSettings (req: express.Request, res: express.Re
69 newVideoFromSubscription: body.newVideoFromSubscription, 69 newVideoFromSubscription: body.newVideoFromSubscription,
70 newCommentOnMyVideo: body.newCommentOnMyVideo, 70 newCommentOnMyVideo: body.newCommentOnMyVideo,
71 videoAbuseAsModerator: body.videoAbuseAsModerator, 71 videoAbuseAsModerator: body.videoAbuseAsModerator,
72 videoAutoBlacklistAsModerator: body.videoAutoBlacklistAsModerator,
72 blacklistOnMyVideo: body.blacklistOnMyVideo, 73 blacklistOnMyVideo: body.blacklistOnMyVideo,
73 myVideoPublished: body.myVideoPublished, 74 myVideoPublished: body.myVideoPublished,
74 myVideoImportFinished: body.myVideoImportFinished, 75 myVideoImportFinished: body.myVideoImportFinished,
diff --git a/server/controllers/api/videos/blacklist.ts b/server/controllers/api/videos/blacklist.ts
index d0728eb59..27dcfb761 100644
--- a/server/controllers/api/videos/blacklist.ts
+++ b/server/controllers/api/videos/blacklist.ts
@@ -1,5 +1,5 @@
1import * as express from 'express' 1import * as express from 'express'
2import { UserRight, VideoBlacklist, VideoBlacklistCreate } from '../../../../shared' 2import { VideoBlacklist, UserRight, VideoBlacklistCreate, VideoBlacklistType } from '../../../../shared'
3import { logger } from '../../../helpers/logger' 3import { logger } from '../../../helpers/logger'
4import { getFormattedObjects } from '../../../helpers/utils' 4import { getFormattedObjects } from '../../../helpers/utils'
5import { 5import {
@@ -12,7 +12,8 @@ import {
12 setDefaultPagination, 12 setDefaultPagination,
13 videosBlacklistAddValidator, 13 videosBlacklistAddValidator,
14 videosBlacklistRemoveValidator, 14 videosBlacklistRemoveValidator,
15 videosBlacklistUpdateValidator 15 videosBlacklistUpdateValidator,
16 videosBlacklistFiltersValidator
16} from '../../../middlewares' 17} from '../../../middlewares'
17import { VideoBlacklistModel } from '../../../models/video/video-blacklist' 18import { VideoBlacklistModel } from '../../../models/video/video-blacklist'
18import { sequelizeTypescript } from '../../../initializers' 19import { sequelizeTypescript } from '../../../initializers'
@@ -36,6 +37,7 @@ blacklistRouter.get('/blacklist',
36 blacklistSortValidator, 37 blacklistSortValidator,
37 setBlacklistSort, 38 setBlacklistSort,
38 setDefaultPagination, 39 setDefaultPagination,
40 videosBlacklistFiltersValidator,
39 asyncMiddleware(listBlacklist) 41 asyncMiddleware(listBlacklist)
40) 42)
41 43
@@ -68,7 +70,8 @@ async function addVideoToBlacklist (req: express.Request, res: express.Response)
68 const toCreate = { 70 const toCreate = {
69 videoId: videoInstance.id, 71 videoId: videoInstance.id,
70 unfederated: body.unfederate === true, 72 unfederated: body.unfederate === true,
71 reason: body.reason 73 reason: body.reason,
74 type: VideoBlacklistType.MANUAL
72 } 75 }
73 76
74 const blacklist = await VideoBlacklistModel.create(toCreate) 77 const blacklist = await VideoBlacklistModel.create(toCreate)
@@ -98,7 +101,7 @@ async function updateVideoBlacklistController (req: express.Request, res: expres
98} 101}
99 102
100async function listBlacklist (req: express.Request, res: express.Response, next: express.NextFunction) { 103async function listBlacklist (req: express.Request, res: express.Response, next: express.NextFunction) {
101 const resultList = await VideoBlacklistModel.listForApi(req.query.start, req.query.count, req.query.sort) 104 const resultList = await VideoBlacklistModel.listForApi(req.query.start, req.query.count, req.query.sort, req.query.type)
102 105
103 return res.json(getFormattedObjects<VideoBlacklist, VideoBlacklistModel>(resultList.data, resultList.total)) 106 return res.json(getFormattedObjects<VideoBlacklist, VideoBlacklistModel>(resultList.data, resultList.total))
104} 107}
@@ -107,18 +110,30 @@ async function removeVideoFromBlacklistController (req: express.Request, res: ex
107 const videoBlacklist = res.locals.videoBlacklist 110 const videoBlacklist = res.locals.videoBlacklist
108 const video = res.locals.video 111 const video = res.locals.video
109 112
110 await sequelizeTypescript.transaction(async t => { 113 const videoBlacklistType = await sequelizeTypescript.transaction(async t => {
111 const unfederated = videoBlacklist.unfederated 114 const unfederated = videoBlacklist.unfederated
115 const videoBlacklistType = videoBlacklist.type
116
112 await videoBlacklist.destroy({ transaction: t }) 117 await videoBlacklist.destroy({ transaction: t })
113 118
114 // Re federate the video 119 // Re federate the video
115 if (unfederated === true) { 120 if (unfederated === true) {
116 await federateVideoIfNeeded(video, true, t) 121 await federateVideoIfNeeded(video, true, t)
117 } 122 }
123
124 return videoBlacklistType
118 }) 125 })
119 126
120 Notifier.Instance.notifyOnVideoUnblacklist(video) 127 Notifier.Instance.notifyOnVideoUnblacklist(video)
121 128
129 if (videoBlacklistType === VideoBlacklistType.AUTO_BEFORE_PUBLISHED) {
130 Notifier.Instance.notifyOnVideoPublishedAfterRemovedFromAutoBlacklist(video)
131
132 // Delete on object so new video notifications will send
133 delete video.VideoBlacklist
134 Notifier.Instance.notifyOnNewVideo(video)
135 }
136
122 logger.info('Video %s removed from blacklist.', res.locals.video.uuid) 137 logger.info('Video %s removed from blacklist.', res.locals.video.uuid)
123 138
124 return res.type('json').status(204).end() 139 return res.type('json').status(204).end()
diff --git a/server/controllers/api/videos/import.ts b/server/controllers/api/videos/import.ts
index cbd2e8514..c234a1391 100644
--- a/server/controllers/api/videos/import.ts
+++ b/server/controllers/api/videos/import.ts
@@ -18,10 +18,12 @@ import { join } from 'path'
18import { isArray } from '../../../helpers/custom-validators/misc' 18import { isArray } from '../../../helpers/custom-validators/misc'
19import { FilteredModelAttributes } from 'sequelize-typescript/lib/models/Model' 19import { FilteredModelAttributes } from 'sequelize-typescript/lib/models/Model'
20import { VideoChannelModel } from '../../../models/video/video-channel' 20import { VideoChannelModel } from '../../../models/video/video-channel'
21import { UserModel } from '../../../models/account/user'
21import * as Bluebird from 'bluebird' 22import * as Bluebird from 'bluebird'
22import * as parseTorrent from 'parse-torrent' 23import * as parseTorrent from 'parse-torrent'
23import { getSecureTorrentName } from '../../../helpers/utils' 24import { getSecureTorrentName } from '../../../helpers/utils'
24import { readFile, move } from 'fs-extra' 25import { readFile, move } from 'fs-extra'
26import { autoBlacklistVideoIfNeeded } from '../../../lib/video-blacklist'
25 27
26const auditLogger = auditLoggerFactory('video-imports') 28const auditLogger = auditLoggerFactory('video-imports')
27const videoImportsRouter = express.Router() 29const videoImportsRouter = express.Router()
@@ -85,7 +87,7 @@ async function addTorrentImport (req: express.Request, res: express.Response, to
85 videoName = isArray(parsed.name) ? parsed.name[ 0 ] : parsed.name as string 87 videoName = isArray(parsed.name) ? parsed.name[ 0 ] : parsed.name as string
86 } 88 }
87 89
88 const video = buildVideo(res.locals.videoChannel.id, body, { name: videoName }) 90 const video = buildVideo(res.locals.videoChannel.id, body, { name: videoName }, user)
89 91
90 await processThumbnail(req, video) 92 await processThumbnail(req, video)
91 await processPreview(req, video) 93 await processPreview(req, video)
@@ -128,7 +130,7 @@ async function addYoutubeDLImport (req: express.Request, res: express.Response)
128 }).end() 130 }).end()
129 } 131 }
130 132
131 const video = buildVideo(res.locals.videoChannel.id, body, youtubeDLInfo) 133 const video = buildVideo(res.locals.videoChannel.id, body, youtubeDLInfo, user)
132 134
133 const downloadThumbnail = !await processThumbnail(req, video) 135 const downloadThumbnail = !await processThumbnail(req, video)
134 const downloadPreview = !await processPreview(req, video) 136 const downloadPreview = !await processPreview(req, video)
@@ -156,7 +158,7 @@ async function addYoutubeDLImport (req: express.Request, res: express.Response)
156 return res.json(videoImport.toFormattedJSON()).end() 158 return res.json(videoImport.toFormattedJSON()).end()
157} 159}
158 160
159function buildVideo (channelId: number, body: VideoImportCreate, importData: YoutubeDLInfo) { 161function buildVideo (channelId: number, body: VideoImportCreate, importData: YoutubeDLInfo, user: UserModel) {
160 const videoData = { 162 const videoData = {
161 name: body.name || importData.name || 'Unknown name', 163 name: body.name || importData.name || 'Unknown name',
162 remote: false, 164 remote: false,
@@ -218,6 +220,8 @@ function insertIntoDB (
218 const videoCreated = await video.save(sequelizeOptions) 220 const videoCreated = await video.save(sequelizeOptions)
219 videoCreated.VideoChannel = videoChannel 221 videoCreated.VideoChannel = videoChannel
220 222
223 await autoBlacklistVideoIfNeeded(video, videoChannel.Account.User, t)
224
221 // Set tags to the video 225 // Set tags to the video
222 if (tags) { 226 if (tags) {
223 const tagInstances = await TagModel.findOrCreateTags(tags, t) 227 const tagInstances = await TagModel.findOrCreateTags(tags, t)
diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts
index 08bee97d3..393324819 100644
--- a/server/controllers/api/videos/index.ts
+++ b/server/controllers/api/videos/index.ts
@@ -6,6 +6,7 @@ import { processImage } from '../../../helpers/image-utils'
6import { logger } from '../../../helpers/logger' 6import { logger } from '../../../helpers/logger'
7import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger' 7import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger'
8import { getFormattedObjects, getServerActor } from '../../../helpers/utils' 8import { getFormattedObjects, getServerActor } from '../../../helpers/utils'
9import { autoBlacklistVideoIfNeeded } from '../../../lib/video-blacklist'
9import { 10import {
10 CONFIG, 11 CONFIG,
11 MIMETYPES, 12 MIMETYPES,
@@ -193,6 +194,7 @@ async function addVideo (req: express.Request, res: express.Response) {
193 channelId: res.locals.videoChannel.id, 194 channelId: res.locals.videoChannel.id,
194 originallyPublishedAt: videoInfo.originallyPublishedAt 195 originallyPublishedAt: videoInfo.originallyPublishedAt
195 } 196 }
197
196 const video = new VideoModel(videoData) 198 const video = new VideoModel(videoData)
197 video.url = getVideoActivityPubUrl(video) // We use the UUID, so set the URL after building the object 199 video.url = getVideoActivityPubUrl(video) // We use the UUID, so set the URL after building the object
198 200
@@ -237,7 +239,7 @@ async function addVideo (req: express.Request, res: express.Response) {
237 // Create the torrent file 239 // Create the torrent file
238 await video.createTorrentAndSetInfoHash(videoFile) 240 await video.createTorrentAndSetInfoHash(videoFile)
239 241
240 const videoCreated = await sequelizeTypescript.transaction(async t => { 242 const { videoCreated, videoWasAutoBlacklisted } = await sequelizeTypescript.transaction(async t => {
241 const sequelizeOptions = { transaction: t } 243 const sequelizeOptions = { transaction: t }
242 244
243 const videoCreated = await video.save(sequelizeOptions) 245 const videoCreated = await video.save(sequelizeOptions)
@@ -266,15 +268,23 @@ async function addVideo (req: express.Request, res: express.Response) {
266 }, { transaction: t }) 268 }, { transaction: t })
267 } 269 }
268 270
269 await federateVideoIfNeeded(video, true, t) 271 const videoWasAutoBlacklisted = await autoBlacklistVideoIfNeeded(video, res.locals.oauth.token.User, t)
272
273 if (!videoWasAutoBlacklisted) {
274 await federateVideoIfNeeded(video, true, t)
275 }
270 276
271 auditLogger.create(getAuditIdFromRes(res), new VideoAuditView(videoCreated.toFormattedDetailsJSON())) 277 auditLogger.create(getAuditIdFromRes(res), new VideoAuditView(videoCreated.toFormattedDetailsJSON()))
272 logger.info('Video with name %s and uuid %s created.', videoInfo.name, videoCreated.uuid) 278 logger.info('Video with name %s and uuid %s created.', videoInfo.name, videoCreated.uuid)
273 279
274 return videoCreated 280 return { videoCreated, videoWasAutoBlacklisted }
275 }) 281 })
276 282
277 Notifier.Instance.notifyOnNewVideo(videoCreated) 283 if (videoWasAutoBlacklisted) {
284 Notifier.Instance.notifyOnVideoAutoBlacklist(videoCreated)
285 } else {
286 Notifier.Instance.notifyOnNewVideo(videoCreated)
287 }
278 288
279 if (video.state === VideoState.TO_TRANSCODE) { 289 if (video.state === VideoState.TO_TRANSCODE) {
280 // Put uuid because we don't have id auto incremented for now 290 // Put uuid because we don't have id auto incremented for now
diff --git a/server/helpers/custom-validators/video-blacklist.ts b/server/helpers/custom-validators/video-blacklist.ts
index 25f908228..465f58a9c 100644
--- a/server/helpers/custom-validators/video-blacklist.ts
+++ b/server/helpers/custom-validators/video-blacklist.ts
@@ -1,7 +1,9 @@
1import { Response } from 'express' 1import { Response } from 'express'
2import * as validator from 'validator' 2import * as validator from 'validator'
3import { exists } from './misc'
3import { CONSTRAINTS_FIELDS } from '../../initializers' 4import { CONSTRAINTS_FIELDS } from '../../initializers'
4import { VideoBlacklistModel } from '../../models/video/video-blacklist' 5import { VideoBlacklistModel } from '../../models/video/video-blacklist'
6import { VideoBlacklistType } from '../../../shared/models/videos'
5 7
6const VIDEO_BLACKLIST_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_BLACKLIST 8const VIDEO_BLACKLIST_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_BLACKLIST
7 9
@@ -24,9 +26,14 @@ async function doesVideoBlacklistExist (videoId: number, res: Response) {
24 return true 26 return true
25} 27}
26 28
29function isVideoBlacklistTypeValid (value: any) {
30 return exists(value) && validator.isInt('' + value) && VideoBlacklistType[value] !== undefined
31}
32
27// --------------------------------------------------------------------------- 33// ---------------------------------------------------------------------------
28 34
29export { 35export {
30 isVideoBlacklistReasonValid, 36 isVideoBlacklistReasonValid,
37 isVideoBlacklistTypeValid,
31 doesVideoBlacklistExist 38 doesVideoBlacklistExist
32} 39}
diff --git a/server/helpers/video.ts b/server/helpers/video.ts
index c90fe06c7..f6f51a297 100644
--- a/server/helpers/video.ts
+++ b/server/helpers/video.ts
@@ -1,4 +1,7 @@
1import { CONFIG } from '../initializers'
1import { VideoModel } from '../models/video/video' 2import { VideoModel } from '../models/video/video'
3import { UserRight } from '../../shared'
4import { UserModel } from '../models/account/user'
2 5
3type VideoFetchType = 'all' | 'only-video' | 'only-video-with-rights' | 'id' | 'none' 6type VideoFetchType = 'all' | 'only-video' | 'only-video-with-rights' | 'id' | 'none'
4 7
diff --git a/server/initializers/checker-before-init.ts b/server/initializers/checker-before-init.ts
index ef12b3eea..e26f38564 100644
--- a/server/initializers/checker-before-init.ts
+++ b/server/initializers/checker-before-init.ts
@@ -20,7 +20,7 @@ function checkMissedConfig () {
20 'signup.filters.cidr.whitelist', 'signup.filters.cidr.blacklist', 20 'signup.filters.cidr.whitelist', 'signup.filters.cidr.blacklist',
21 'redundancy.videos.strategies', 'redundancy.videos.check_interval', 21 'redundancy.videos.strategies', 'redundancy.videos.check_interval',
22 'transcoding.enabled', 'transcoding.threads', 'transcoding.allow_additional_extensions', 22 'transcoding.enabled', 'transcoding.threads', 'transcoding.allow_additional_extensions',
23 'import.videos.http.enabled', 'import.videos.torrent.enabled', 23 'import.videos.http.enabled', 'import.videos.torrent.enabled', 'auto_blacklist.videos.of_users.enabled',
24 'trending.videos.interval_days', 24 'trending.videos.interval_days',
25 'instance.name', 'instance.short_description', 'instance.description', 'instance.terms', 'instance.default_client_route', 25 'instance.name', 'instance.short_description', 'instance.description', 'instance.terms', 'instance.default_client_route',
26 'instance.is_nsfw', 'instance.default_nsfw_policy', 'instance.robots', 'instance.securitytxt', 26 'instance.is_nsfw', 'instance.default_nsfw_policy', 'instance.robots', 'instance.securitytxt',
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts
index ff0ade17a..f59d3ef7a 100644
--- a/server/initializers/constants.ts
+++ b/server/initializers/constants.ts
@@ -18,7 +18,7 @@ let config: IConfig = require('config')
18 18
19// --------------------------------------------------------------------------- 19// ---------------------------------------------------------------------------
20 20
21const LAST_MIGRATION_VERSION = 345 21const LAST_MIGRATION_VERSION = 350
22 22
23// --------------------------------------------------------------------------- 23// ---------------------------------------------------------------------------
24 24
@@ -288,6 +288,13 @@ const CONFIG = {
288 } 288 }
289 } 289 }
290 }, 290 },
291 AUTO_BLACKLIST: {
292 VIDEOS: {
293 OF_USERS: {
294 get ENABLED () { return config.get<boolean>('auto_blacklist.videos.of_users.enabled') }
295 }
296 }
297 },
291 CACHE: { 298 CACHE: {
292 PREVIEWS: { 299 PREVIEWS: {
293 get SIZE () { return config.get<number>('cache.previews.size') } 300 get SIZE () { return config.get<number>('cache.previews.size') }
diff --git a/server/initializers/migrations/0350-video-blacklist-type.ts b/server/initializers/migrations/0350-video-blacklist-type.ts
new file mode 100644
index 000000000..4849020ef
--- /dev/null
+++ b/server/initializers/migrations/0350-video-blacklist-type.ts
@@ -0,0 +1,64 @@
1import * as Sequelize from 'sequelize'
2import { VideoBlacklistType } from '../../../shared/models/videos'
3
4async function up (utils: {
5 transaction: Sequelize.Transaction,
6 queryInterface: Sequelize.QueryInterface,
7 sequelize: Sequelize.Sequelize,
8 db: any
9}): Promise<void> {
10 {
11 const data = {
12 type: Sequelize.INTEGER,
13 allowNull: true,
14 defaultValue: null
15 }
16
17 await utils.queryInterface.addColumn('videoBlacklist', 'type', data)
18 }
19
20 {
21 const query = 'UPDATE "videoBlacklist" SET "type" = ' + VideoBlacklistType.MANUAL
22 await utils.sequelize.query(query)
23 }
24
25 {
26 const data = {
27 type: Sequelize.INTEGER,
28 allowNull: false,
29 defaultValue: null
30 }
31 await utils.queryInterface.changeColumn('videoBlacklist', 'type', data)
32 }
33
34 {
35 const data = {
36 type: Sequelize.INTEGER,
37 defaultValue: null,
38 allowNull: true
39 }
40 await utils.queryInterface.addColumn('userNotificationSetting', 'videoAutoBlacklistAsModerator', data)
41 }
42
43 {
44 const query = 'UPDATE "userNotificationSetting" SET "videoAutoBlacklistAsModerator" = 3'
45 await utils.sequelize.query(query)
46 }
47
48 {
49 const data = {
50 type: Sequelize.INTEGER,
51 defaultValue: null,
52 allowNull: false
53 }
54 await utils.queryInterface.changeColumn('userNotificationSetting', 'videoAutoBlacklistAsModerator', data)
55 }
56}
57function down (options) {
58 throw new Error('Not implemented.')
59}
60
61export {
62 up,
63 down
64}
diff --git a/server/lib/activitypub/videos.ts b/server/lib/activitypub/videos.ts
index 2c932371b..d935e3f90 100644
--- a/server/lib/activitypub/videos.ts
+++ b/server/lib/activitypub/videos.ts
@@ -45,7 +45,7 @@ import { VideoShareModel } from '../../models/video/video-share'
45import { VideoCommentModel } from '../../models/video/video-comment' 45import { VideoCommentModel } from '../../models/video/video-comment'
46 46
47async function federateVideoIfNeeded (video: VideoModel, isNewVideo: boolean, transaction?: sequelize.Transaction) { 47async function federateVideoIfNeeded (video: VideoModel, isNewVideo: boolean, transaction?: sequelize.Transaction) {
48 // If the video is not private and published, we federate it 48 // If the video is not private and is published, we federate it
49 if (video.privacy !== VideoPrivacy.PRIVATE && video.state === VideoState.PUBLISHED) { 49 if (video.privacy !== VideoPrivacy.PRIVATE && video.state === VideoState.PUBLISHED) {
50 // Fetch more attributes that we will need to serialize in AP object 50 // Fetch more attributes that we will need to serialize in AP object
51 if (isArray(video.VideoCaptions) === false) { 51 if (isArray(video.VideoCaptions) === false) {
diff --git a/server/lib/emailer.ts b/server/lib/emailer.ts
index 04e4b94b6..eec97c27e 100644
--- a/server/lib/emailer.ts
+++ b/server/lib/emailer.ts
@@ -250,6 +250,29 @@ class Emailer {
250 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) 250 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
251 } 251 }
252 252
253 addVideoAutoBlacklistModeratorsNotification (to: string[], video: VideoModel) {
254 const VIDEO_AUTO_BLACKLIST_URL = CONFIG.WEBSERVER.URL + '/admin/moderation/video-auto-blacklist/list'
255 const videoUrl = CONFIG.WEBSERVER.URL + video.getWatchStaticPath()
256
257 const text = `Hi,\n\n` +
258 `A recently added video was auto-blacklisted and requires moderator review before publishing.` +
259 `\n\n` +
260 `You can view it and take appropriate action on ${videoUrl}` +
261 `\n\n` +
262 `A full list of auto-blacklisted videos can be reviewed here: ${VIDEO_AUTO_BLACKLIST_URL}` +
263 `\n\n` +
264 `Cheers,\n` +
265 `PeerTube.`
266
267 const emailPayload: EmailPayload = {
268 to,
269 subject: '[PeerTube] An auto-blacklisted video is awaiting review',
270 text
271 }
272
273 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
274 }
275
253 addNewUserRegistrationNotification (to: string[], user: UserModel) { 276 addNewUserRegistrationNotification (to: string[], user: UserModel) {
254 const text = `Hi,\n\n` + 277 const text = `Hi,\n\n` +
255 `User ${user.username} just registered on ${CONFIG.WEBSERVER.HOST} PeerTube instance.\n\n` + 278 `User ${user.username} just registered on ${CONFIG.WEBSERVER.HOST} PeerTube instance.\n\n` +
diff --git a/server/lib/job-queue/handlers/video-import.ts b/server/lib/job-queue/handlers/video-import.ts
index d96bfdf43..c5fc1061c 100644
--- a/server/lib/job-queue/handlers/video-import.ts
+++ b/server/lib/job-queue/handlers/video-import.ts
@@ -196,9 +196,14 @@ async function processFile (downloader: () => Promise<string>, videoImport: Vide
196 return videoImportUpdated 196 return videoImportUpdated
197 }) 197 })
198 198
199 Notifier.Instance.notifyOnNewVideo(videoImportUpdated.Video)
200 Notifier.Instance.notifyOnFinishedVideoImport(videoImportUpdated, true) 199 Notifier.Instance.notifyOnFinishedVideoImport(videoImportUpdated, true)
201 200
201 if (videoImportUpdated.Video.VideoBlacklist) {
202 Notifier.Instance.notifyOnVideoAutoBlacklist(videoImportUpdated.Video)
203 } else {
204 Notifier.Instance.notifyOnNewVideo(videoImportUpdated.Video)
205 }
206
202 // Create transcoding jobs? 207 // Create transcoding jobs?
203 if (videoImportUpdated.Video.state === VideoState.TO_TRANSCODE) { 208 if (videoImportUpdated.Video.state === VideoState.TO_TRANSCODE) {
204 // Put uuid because we don't have id auto incremented for now 209 // Put uuid because we don't have id auto incremented for now
diff --git a/server/lib/job-queue/handlers/video-transcoding.ts b/server/lib/job-queue/handlers/video-transcoding.ts
index d9dad795e..581ec283e 100644
--- a/server/lib/job-queue/handlers/video-transcoding.ts
+++ b/server/lib/job-queue/handlers/video-transcoding.ts
@@ -85,10 +85,9 @@ async function publishVideoIfNeeded (video: VideoModel, payload?: VideoTranscodi
85 return { videoDatabase, videoPublished } 85 return { videoDatabase, videoPublished }
86 }) 86 })
87 87
88 // don't notify prior to scheduled video update 88 if (videoPublished) {
89 if (videoPublished && !videoDatabase.ScheduleVideoUpdate) {
90 Notifier.Instance.notifyOnNewVideo(videoDatabase) 89 Notifier.Instance.notifyOnNewVideo(videoDatabase)
91 Notifier.Instance.notifyOnPendingVideoPublished(videoDatabase) 90 Notifier.Instance.notifyOnVideoPublishedAfterTranscoding(videoDatabase)
92 } 91 }
93 92
94 await createHlsJobIfEnabled(payload) 93 await createHlsJobIfEnabled(payload)
@@ -146,11 +145,8 @@ async function onVideoFileOptimizerSuccess (videoArg: VideoModel, payload: Video
146 return { videoDatabase, videoPublished } 145 return { videoDatabase, videoPublished }
147 }) 146 })
148 147
149 // don't notify prior to scheduled video update 148 if (payload.isNewVideo) Notifier.Instance.notifyOnNewVideo(videoDatabase)
150 if (!videoDatabase.ScheduleVideoUpdate) { 149 if (videoPublished) Notifier.Instance.notifyOnVideoPublishedAfterTranscoding(videoDatabase)
151 if (payload.isNewVideo) Notifier.Instance.notifyOnNewVideo(videoDatabase)
152 if (videoPublished) Notifier.Instance.notifyOnPendingVideoPublished(videoDatabase)
153 }
154 150
155 await createHlsJobIfEnabled(Object.assign({}, payload, { resolution: videoDatabase.getOriginalFile().resolution })) 151 await createHlsJobIfEnabled(Object.assign({}, payload, { resolution: videoDatabase.getOriginalFile().resolution }))
156} 152}
diff --git a/server/lib/notifier.ts b/server/lib/notifier.ts
index 501680f6b..9fe93ec0d 100644
--- a/server/lib/notifier.ts
+++ b/server/lib/notifier.ts
@@ -23,19 +23,35 @@ class Notifier {
23 private constructor () {} 23 private constructor () {}
24 24
25 notifyOnNewVideo (video: VideoModel): void { 25 notifyOnNewVideo (video: VideoModel): void {
26 // Only notify on public and published videos 26 // Only notify on public and published videos which are not blacklisted
27 if (video.privacy !== VideoPrivacy.PUBLIC || video.state !== VideoState.PUBLISHED) return 27 if (video.privacy !== VideoPrivacy.PUBLIC || video.state !== VideoState.PUBLISHED || video.VideoBlacklist) return
28 28
29 this.notifySubscribersOfNewVideo(video) 29 this.notifySubscribersOfNewVideo(video)
30 .catch(err => logger.error('Cannot notify subscribers of new video %s.', video.url, { err })) 30 .catch(err => logger.error('Cannot notify subscribers of new video %s.', video.url, { err }))
31 } 31 }
32 32
33 notifyOnPendingVideoPublished (video: VideoModel): void { 33 notifyOnVideoPublishedAfterTranscoding (video: VideoModel): void {
34 // Only notify on public videos that has been published while the user waited transcoding/scheduled update 34 // don't notify if didn't wait for transcoding or video is still blacklisted/waiting for scheduled update
35 if (video.waitTranscoding === false && !video.ScheduleVideoUpdate) return 35 if (!video.waitTranscoding || video.VideoBlacklist || video.ScheduleVideoUpdate) return
36 36
37 this.notifyOwnedVideoHasBeenPublished(video) 37 this.notifyOwnedVideoHasBeenPublished(video)
38 .catch(err => logger.error('Cannot notify owner that its video %s has been published.', video.url, { err })) 38 .catch(err => logger.error('Cannot notify owner that its video %s has been published after transcoding.', video.url, { err }))
39 }
40
41 notifyOnVideoPublishedAfterScheduledUpdate (video: VideoModel): void {
42 // don't notify if video is still blacklisted or waiting for transcoding
43 if (video.VideoBlacklist || (video.waitTranscoding && video.state !== VideoState.PUBLISHED)) return
44
45 this.notifyOwnedVideoHasBeenPublished(video)
46 .catch(err => logger.error('Cannot notify owner that its video %s has been published after scheduled update.', video.url, { err }))
47 }
48
49 notifyOnVideoPublishedAfterRemovedFromAutoBlacklist (video: VideoModel): void {
50 // don't notify if video is still waiting for transcoding or scheduled update
51 if (video.ScheduleVideoUpdate || (video.waitTranscoding && video.state !== VideoState.PUBLISHED)) return
52
53 this.notifyOwnedVideoHasBeenPublished(video)
54 .catch(err => logger.error('Cannot notify owner that its video %s has been published after removed from auto-blacklist.', video.url, { err })) // tslint:disable-line:max-line-length
39 } 55 }
40 56
41 notifyOnNewComment (comment: VideoCommentModel): void { 57 notifyOnNewComment (comment: VideoCommentModel): void {
@@ -51,6 +67,11 @@ class Notifier {
51 .catch(err => logger.error('Cannot notify of new video abuse of video %s.', videoAbuse.Video.url, { err })) 67 .catch(err => logger.error('Cannot notify of new video abuse of video %s.', videoAbuse.Video.url, { err }))
52 } 68 }
53 69
70 notifyOnVideoAutoBlacklist (video: VideoModel): void {
71 this.notifyModeratorsOfVideoAutoBlacklist(video)
72 .catch(err => logger.error('Cannot notify of auto-blacklist of video %s.', video.url, { err }))
73 }
74
54 notifyOnVideoBlacklist (videoBlacklist: VideoBlacklistModel): void { 75 notifyOnVideoBlacklist (videoBlacklist: VideoBlacklistModel): void {
55 this.notifyVideoOwnerOfBlacklist(videoBlacklist) 76 this.notifyVideoOwnerOfBlacklist(videoBlacklist)
56 .catch(err => logger.error('Cannot notify video owner of new video blacklist of %s.', videoBlacklist.Video.url, { err })) 77 .catch(err => logger.error('Cannot notify video owner of new video blacklist of %s.', videoBlacklist.Video.url, { err }))
@@ -58,7 +79,7 @@ class Notifier {
58 79
59 notifyOnVideoUnblacklist (video: VideoModel): void { 80 notifyOnVideoUnblacklist (video: VideoModel): void {
60 this.notifyVideoOwnerOfUnblacklist(video) 81 this.notifyVideoOwnerOfUnblacklist(video)
61 .catch(err => logger.error('Cannot notify video owner of new video blacklist of %s.', video.url, { err })) 82 .catch(err => logger.error('Cannot notify video owner of unblacklist of %s.', video.url, { err }))
62 } 83 }
63 84
64 notifyOnFinishedVideoImport (videoImport: VideoImportModel, success: boolean): void { 85 notifyOnFinishedVideoImport (videoImport: VideoImportModel, success: boolean): void {
@@ -268,6 +289,34 @@ class Notifier {
268 return this.notify({ users: moderators, settingGetter, notificationCreator, emailSender }) 289 return this.notify({ users: moderators, settingGetter, notificationCreator, emailSender })
269 } 290 }
270 291
292 private async notifyModeratorsOfVideoAutoBlacklist (video: VideoModel) {
293 const moderators = await UserModel.listWithRight(UserRight.MANAGE_VIDEO_BLACKLIST)
294 if (moderators.length === 0) return
295
296 logger.info('Notifying %s moderators of video auto-blacklist %s.', moderators.length, video.url)
297
298 function settingGetter (user: UserModel) {
299 return user.NotificationSetting.videoAutoBlacklistAsModerator
300 }
301 async function notificationCreator (user: UserModel) {
302
303 const notification = await UserNotificationModel.create({
304 type: UserNotificationType.VIDEO_AUTO_BLACKLIST_FOR_MODERATORS,
305 userId: user.id,
306 videoId: video.id
307 })
308 notification.Video = video
309
310 return notification
311 }
312
313 function emailSender (emails: string[]) {
314 return Emailer.Instance.addVideoAutoBlacklistModeratorsNotification(emails, video)
315 }
316
317 return this.notify({ users: moderators, settingGetter, notificationCreator, emailSender })
318 }
319
271 private async notifyVideoOwnerOfBlacklist (videoBlacklist: VideoBlacklistModel) { 320 private async notifyVideoOwnerOfBlacklist (videoBlacklist: VideoBlacklistModel) {
272 const user = await UserModel.loadByVideoId(videoBlacklist.videoId) 321 const user = await UserModel.loadByVideoId(videoBlacklist.videoId)
273 if (!user) return 322 if (!user) return
diff --git a/server/lib/schedulers/update-videos-scheduler.ts b/server/lib/schedulers/update-videos-scheduler.ts
index 2618a5857..2179a2f26 100644
--- a/server/lib/schedulers/update-videos-scheduler.ts
+++ b/server/lib/schedulers/update-videos-scheduler.ts
@@ -57,7 +57,7 @@ export class UpdateVideosScheduler extends AbstractScheduler {
57 57
58 for (const v of publishedVideos) { 58 for (const v of publishedVideos) {
59 Notifier.Instance.notifyOnNewVideo(v) 59 Notifier.Instance.notifyOnNewVideo(v)
60 Notifier.Instance.notifyOnPendingVideoPublished(v) 60 Notifier.Instance.notifyOnVideoPublishedAfterScheduledUpdate(v)
61 } 61 }
62 } 62 }
63 63
diff --git a/server/lib/user.ts b/server/lib/user.ts
index 02a84f15b..5588b0f76 100644
--- a/server/lib/user.ts
+++ b/server/lib/user.ts
@@ -106,6 +106,7 @@ function createDefaultUserNotificationSettings (user: UserModel, t: Sequelize.Tr
106 myVideoImportFinished: UserNotificationSettingValue.WEB, 106 myVideoImportFinished: UserNotificationSettingValue.WEB,
107 myVideoPublished: UserNotificationSettingValue.WEB, 107 myVideoPublished: UserNotificationSettingValue.WEB,
108 videoAbuseAsModerator: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, 108 videoAbuseAsModerator: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
109 videoAutoBlacklistAsModerator: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
109 blacklistOnMyVideo: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, 110 blacklistOnMyVideo: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
110 newUserRegistration: UserNotificationSettingValue.WEB, 111 newUserRegistration: UserNotificationSettingValue.WEB,
111 commentMention: UserNotificationSettingValue.WEB, 112 commentMention: UserNotificationSettingValue.WEB,
diff --git a/server/lib/video-blacklist.ts b/server/lib/video-blacklist.ts
new file mode 100644
index 000000000..dc4e0aed9
--- /dev/null
+++ b/server/lib/video-blacklist.ts
@@ -0,0 +1,31 @@
1import * as sequelize from 'sequelize'
2import { CONFIG } from '../initializers/constants'
3import { VideoBlacklistType, UserRight } from '../../shared/models'
4import { VideoBlacklistModel } from '../models/video/video-blacklist'
5import { UserModel } from '../models/account/user'
6import { VideoModel } from '../models/video/video'
7import { logger } from '../helpers/logger'
8
9async function autoBlacklistVideoIfNeeded (video: VideoModel, user: UserModel, transaction: sequelize.Transaction) {
10 if (!CONFIG.AUTO_BLACKLIST.VIDEOS.OF_USERS.ENABLED) return false
11
12 if (user.hasRight(UserRight.MANAGE_VIDEO_BLACKLIST)) return false
13
14 const sequelizeOptions = { transaction }
15 const videoBlacklistToCreate = {
16 videoId: video.id,
17 unfederated: true,
18 reason: 'Auto-blacklisted. Moderator review required.',
19 type: VideoBlacklistType.AUTO_BEFORE_PUBLISHED
20 }
21 await VideoBlacklistModel.create(videoBlacklistToCreate, sequelizeOptions)
22 logger.info('Video %s auto-blacklisted.', video.uuid)
23
24 return true
25}
26
27// ---------------------------------------------------------------------------
28
29export {
30 autoBlacklistVideoIfNeeded
31}
diff --git a/server/middlewares/validators/videos/video-blacklist.ts b/server/middlewares/validators/videos/video-blacklist.ts
index db318dcdb..1d7ddb2e3 100644
--- a/server/middlewares/validators/videos/video-blacklist.ts
+++ b/server/middlewares/validators/videos/video-blacklist.ts
@@ -1,10 +1,14 @@
1import * as express from 'express' 1import * as express from 'express'
2import { body, param } from 'express-validator/check' 2import { body, param, query } from 'express-validator/check'
3import { isBooleanValid, isIdOrUUIDValid } from '../../../helpers/custom-validators/misc' 3import { isBooleanValid, isIdOrUUIDValid } from '../../../helpers/custom-validators/misc'
4import { doesVideoExist } from '../../../helpers/custom-validators/videos' 4import { doesVideoExist } from '../../../helpers/custom-validators/videos'
5import { logger } from '../../../helpers/logger' 5import { logger } from '../../../helpers/logger'
6import { areValidationErrors } from '../utils' 6import { areValidationErrors } from '../utils'
7import { doesVideoBlacklistExist, isVideoBlacklistReasonValid } from '../../../helpers/custom-validators/video-blacklist' 7import {
8 doesVideoBlacklistExist,
9 isVideoBlacklistReasonValid,
10 isVideoBlacklistTypeValid
11} from '../../../helpers/custom-validators/video-blacklist'
8 12
9const videosBlacklistRemoveValidator = [ 13const videosBlacklistRemoveValidator = [
10 param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'), 14 param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'),
@@ -65,10 +69,25 @@ const videosBlacklistUpdateValidator = [
65 } 69 }
66] 70]
67 71
72const videosBlacklistFiltersValidator = [
73 query('type')
74 .optional()
75 .custom(isVideoBlacklistTypeValid).withMessage('Should have a valid video blacklist type attribute'),
76
77 (req: express.Request, res: express.Response, next: express.NextFunction) => {
78 logger.debug('Checking videos blacklist filters query', { parameters: req.query })
79
80 if (areValidationErrors(req, res)) return
81
82 return next()
83 }
84]
85
68// --------------------------------------------------------------------------- 86// ---------------------------------------------------------------------------
69 87
70export { 88export {
71 videosBlacklistAddValidator, 89 videosBlacklistAddValidator,
72 videosBlacklistRemoveValidator, 90 videosBlacklistRemoveValidator,
73 videosBlacklistUpdateValidator 91 videosBlacklistUpdateValidator,
92 videosBlacklistFiltersValidator
74} 93}
diff --git a/server/models/account/user-notification-setting.ts b/server/models/account/user-notification-setting.ts
index f1c3ac223..ba7f739b9 100644
--- a/server/models/account/user-notification-setting.ts
+++ b/server/models/account/user-notification-setting.ts
@@ -59,6 +59,15 @@ export class UserNotificationSettingModel extends Model<UserNotificationSettingM
59 @AllowNull(false) 59 @AllowNull(false)
60 @Default(null) 60 @Default(null)
61 @Is( 61 @Is(
62 'UserNotificationSettingVideoAutoBlacklistAsModerator',
63 value => throwIfNotValid(value, isUserNotificationSettingValid, 'videoAutoBlacklistAsModerator')
64 )
65 @Column
66 videoAutoBlacklistAsModerator: UserNotificationSettingValue
67
68 @AllowNull(false)
69 @Default(null)
70 @Is(
62 'UserNotificationSettingBlacklistOnMyVideo', 71 'UserNotificationSettingBlacklistOnMyVideo',
63 value => throwIfNotValid(value, isUserNotificationSettingValid, 'blacklistOnMyVideo') 72 value => throwIfNotValid(value, isUserNotificationSettingValid, 'blacklistOnMyVideo')
64 ) 73 )
@@ -139,6 +148,7 @@ export class UserNotificationSettingModel extends Model<UserNotificationSettingM
139 newCommentOnMyVideo: this.newCommentOnMyVideo, 148 newCommentOnMyVideo: this.newCommentOnMyVideo,
140 newVideoFromSubscription: this.newVideoFromSubscription, 149 newVideoFromSubscription: this.newVideoFromSubscription,
141 videoAbuseAsModerator: this.videoAbuseAsModerator, 150 videoAbuseAsModerator: this.videoAbuseAsModerator,
151 videoAutoBlacklistAsModerator: this.videoAutoBlacklistAsModerator,
142 blacklistOnMyVideo: this.blacklistOnMyVideo, 152 blacklistOnMyVideo: this.blacklistOnMyVideo,
143 myVideoPublished: this.myVideoPublished, 153 myVideoPublished: this.myVideoPublished,
144 myVideoImportFinished: this.myVideoImportFinished, 154 myVideoImportFinished: this.myVideoImportFinished,
diff --git a/server/models/video/schedule-video-update.ts b/server/models/video/schedule-video-update.ts
index 1e56562e1..abddc1111 100644
--- a/server/models/video/schedule-video-update.ts
+++ b/server/models/video/schedule-video-update.ts
@@ -72,7 +72,8 @@ export class ScheduleVideoUpdateModel extends Model<ScheduleVideoUpdateModel> {
72 model: VideoModel.scope( 72 model: VideoModel.scope(
73 [ 73 [
74 VideoScopeNames.WITH_FILES, 74 VideoScopeNames.WITH_FILES,
75 VideoScopeNames.WITH_ACCOUNT_DETAILS 75 VideoScopeNames.WITH_ACCOUNT_DETAILS,
76 VideoScopeNames.WITH_BLACKLISTED
76 ] 77 ]
77 ) 78 )
78 } 79 }
diff --git a/server/models/video/video-blacklist.ts b/server/models/video/video-blacklist.ts
index 3b567e488..86b1f6acb 100644
--- a/server/models/video/video-blacklist.ts
+++ b/server/models/video/video-blacklist.ts
@@ -1,8 +1,21 @@
1import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' 1import {
2 AllowNull,
3 BelongsTo,
4 Column,
5 CreatedAt,
6 DataType,
7 Default,
8 ForeignKey,
9 Is, Model,
10 Table,
11 UpdatedAt,
12 IFindOptions
13} from 'sequelize-typescript'
2import { getSortOnModel, SortType, throwIfNotValid } from '../utils' 14import { getSortOnModel, SortType, throwIfNotValid } from '../utils'
3import { VideoModel } from './video' 15import { VideoModel } from './video'
4import { isVideoBlacklistReasonValid } from '../../helpers/custom-validators/video-blacklist' 16import { VideoChannelModel, ScopeNames as VideoChannelScopeNames } from './video-channel'
5import { VideoBlacklist } from '../../../shared/models/videos' 17import { isVideoBlacklistReasonValid, isVideoBlacklistTypeValid } from '../../helpers/custom-validators/video-blacklist'
18import { VideoBlacklist, VideoBlacklistType } from '../../../shared/models/videos'
6import { CONSTRAINTS_FIELDS } from '../../initializers' 19import { CONSTRAINTS_FIELDS } from '../../initializers'
7 20
8@Table({ 21@Table({
@@ -25,6 +38,12 @@ export class VideoBlacklistModel extends Model<VideoBlacklistModel> {
25 @Column 38 @Column
26 unfederated: boolean 39 unfederated: boolean
27 40
41 @AllowNull(false)
42 @Default(null)
43 @Is('VideoBlacklistType', value => throwIfNotValid(value, isVideoBlacklistTypeValid, 'type'))
44 @Column
45 type: VideoBlacklistType
46
28 @CreatedAt 47 @CreatedAt
29 createdAt: Date 48 createdAt: Date
30 49
@@ -43,19 +62,29 @@ export class VideoBlacklistModel extends Model<VideoBlacklistModel> {
43 }) 62 })
44 Video: VideoModel 63 Video: VideoModel
45 64
46 static listForApi (start: number, count: number, sort: SortType) { 65 static listForApi (start: number, count: number, sort: SortType, type?: VideoBlacklistType) {
47 const query = { 66 const query: IFindOptions<VideoBlacklistModel> = {
48 offset: start, 67 offset: start,
49 limit: count, 68 limit: count,
50 order: getSortOnModel(sort.sortModel, sort.sortValue), 69 order: getSortOnModel(sort.sortModel, sort.sortValue),
51 include: [ 70 include: [
52 { 71 {
53 model: VideoModel, 72 model: VideoModel,
54 required: true 73 required: true,
74 include: [
75 {
76 model: VideoChannelModel.scope({ method: [ VideoChannelScopeNames.SUMMARY, true ] }),
77 required: true
78 }
79 ]
55 } 80 }
56 ] 81 ]
57 } 82 }
58 83
84 if (type) {
85 query.where = { type }
86 }
87
59 return VideoBlacklistModel.findAndCountAll(query) 88 return VideoBlacklistModel.findAndCountAll(query)
60 .then(({ rows, count }) => { 89 .then(({ rows, count }) => {
61 return { 90 return {
@@ -76,26 +105,15 @@ export class VideoBlacklistModel extends Model<VideoBlacklistModel> {
76 } 105 }
77 106
78 toFormattedJSON (): VideoBlacklist { 107 toFormattedJSON (): VideoBlacklist {
79 const video = this.Video
80
81 return { 108 return {
82 id: this.id, 109 id: this.id,
83 createdAt: this.createdAt, 110 createdAt: this.createdAt,
84 updatedAt: this.updatedAt, 111 updatedAt: this.updatedAt,
85 reason: this.reason, 112 reason: this.reason,
86 unfederated: this.unfederated, 113 unfederated: this.unfederated,
114 type: this.type,
87 115
88 video: { 116 video: this.Video.toFormattedJSON()
89 id: video.id,
90 name: video.name,
91 uuid: video.uuid,
92 description: video.description,
93 duration: video.duration,
94 views: video.views,
95 likes: video.likes,
96 dislikes: video.dislikes,
97 nsfw: video.nsfw
98 }
99 } 117 }
100 } 118 }
101} 119}
diff --git a/server/tests/api/check-params/config.ts b/server/tests/api/check-params/config.ts
index c6b460f23..0b333e2f4 100644
--- a/server/tests/api/check-params/config.ts
+++ b/server/tests/api/check-params/config.ts
@@ -80,6 +80,13 @@ describe('Test config API validators', function () {
80 enabled: false 80 enabled: false
81 } 81 }
82 } 82 }
83 },
84 autoBlacklist: {
85 videos: {
86 ofUsers: {
87 enabled: false
88 }
89 }
83 } 90 }
84 } 91 }
85 92
diff --git a/server/tests/api/check-params/user-notifications.ts b/server/tests/api/check-params/user-notifications.ts
index 714f481e9..36eaceac7 100644
--- a/server/tests/api/check-params/user-notifications.ts
+++ b/server/tests/api/check-params/user-notifications.ts
@@ -168,6 +168,7 @@ describe('Test user notifications API validators', function () {
168 newVideoFromSubscription: UserNotificationSettingValue.WEB, 168 newVideoFromSubscription: UserNotificationSettingValue.WEB,
169 newCommentOnMyVideo: UserNotificationSettingValue.WEB, 169 newCommentOnMyVideo: UserNotificationSettingValue.WEB,
170 videoAbuseAsModerator: UserNotificationSettingValue.WEB, 170 videoAbuseAsModerator: UserNotificationSettingValue.WEB,
171 videoAutoBlacklistAsModerator: UserNotificationSettingValue.WEB,
171 blacklistOnMyVideo: UserNotificationSettingValue.WEB, 172 blacklistOnMyVideo: UserNotificationSettingValue.WEB,
172 myVideoImportFinished: UserNotificationSettingValue.WEB, 173 myVideoImportFinished: UserNotificationSettingValue.WEB,
173 myVideoPublished: UserNotificationSettingValue.WEB, 174 myVideoPublished: UserNotificationSettingValue.WEB,
diff --git a/server/tests/api/check-params/video-blacklist.ts b/server/tests/api/check-params/video-blacklist.ts
index 6b82643f4..fc039e847 100644
--- a/server/tests/api/check-params/video-blacklist.ts
+++ b/server/tests/api/check-params/video-blacklist.ts
@@ -8,6 +8,7 @@ import {
8 flushAndRunMultipleServers, 8 flushAndRunMultipleServers,
9 flushTests, 9 flushTests,
10 getBlacklistedVideosList, 10 getBlacklistedVideosList,
11 getBlacklistedVideosListWithTypeFilter,
11 getVideo, 12 getVideo,
12 getVideoWithToken, 13 getVideoWithToken,
13 killallServers, 14 killallServers,
@@ -24,7 +25,7 @@ import {
24 checkBadSortPagination, 25 checkBadSortPagination,
25 checkBadStartPagination 26 checkBadStartPagination
26} from '../../../../shared/utils/requests/check-api-params' 27} from '../../../../shared/utils/requests/check-api-params'
27import { VideoDetails } from '../../../../shared/models/videos' 28import { VideoDetails, VideoBlacklistType } from '../../../../shared/models/videos'
28import { expect } from 'chai' 29import { expect } from 'chai'
29 30
30describe('Test video blacklist API validators', function () { 31describe('Test video blacklist API validators', function () {
@@ -238,6 +239,14 @@ describe('Test video blacklist API validators', function () {
238 it('Should fail with an incorrect sort', async function () { 239 it('Should fail with an incorrect sort', async function () {
239 await checkBadSortPagination(servers[0].url, basePath, servers[0].accessToken) 240 await checkBadSortPagination(servers[0].url, basePath, servers[0].accessToken)
240 }) 241 })
242
243 it('Should fail with an invalid type', async function () {
244 await getBlacklistedVideosListWithTypeFilter(servers[0].url, servers[0].accessToken, 0, 400)
245 })
246
247 it('Should succeed with the correct parameters', async function () {
248 await getBlacklistedVideosListWithTypeFilter(servers[0].url, servers[0].accessToken, VideoBlacklistType.MANUAL)
249 })
241 }) 250 })
242 251
243 after(async function () { 252 after(async function () {
diff --git a/server/tests/api/check-params/videos.ts b/server/tests/api/check-params/videos.ts
index 3eccaee44..5a013b890 100644
--- a/server/tests/api/check-params/videos.ts
+++ b/server/tests/api/check-params/videos.ts
@@ -7,7 +7,8 @@ import { join } from 'path'
7import { VideoPrivacy } from '../../../../shared/models/videos/video-privacy.enum' 7import { VideoPrivacy } from '../../../../shared/models/videos/video-privacy.enum'
8import { 8import {
9 createUser, flushTests, getMyUserInformation, getVideo, getVideosList, immutableAssign, killallServers, makeDeleteRequest, 9 createUser, flushTests, getMyUserInformation, getVideo, getVideosList, immutableAssign, killallServers, makeDeleteRequest,
10 makeGetRequest, makeUploadRequest, makePutBodyRequest, removeVideo, runServer, ServerInfo, setAccessTokensToServers, userLogin 10 makeGetRequest, makeUploadRequest, makePutBodyRequest, removeVideo, uploadVideo,
11 runServer, ServerInfo, setAccessTokensToServers, userLogin, updateCustomSubConfig
11} from '../../../../shared/utils' 12} from '../../../../shared/utils'
12import { 13import {
13 checkBadCountPagination, 14 checkBadCountPagination,
diff --git a/server/tests/api/server/config.ts b/server/tests/api/server/config.ts
index 42927605d..b9f05e952 100644
--- a/server/tests/api/server/config.ts
+++ b/server/tests/api/server/config.ts
@@ -62,6 +62,7 @@ function checkInitialConfig (data: CustomConfig) {
62 62
63 expect(data.import.videos.http.enabled).to.be.true 63 expect(data.import.videos.http.enabled).to.be.true
64 expect(data.import.videos.torrent.enabled).to.be.true 64 expect(data.import.videos.torrent.enabled).to.be.true
65 expect(data.autoBlacklist.videos.ofUsers.enabled).to.be.false
65} 66}
66 67
67function checkUpdatedConfig (data: CustomConfig) { 68function checkUpdatedConfig (data: CustomConfig) {
@@ -103,6 +104,7 @@ function checkUpdatedConfig (data: CustomConfig) {
103 104
104 expect(data.import.videos.http.enabled).to.be.false 105 expect(data.import.videos.http.enabled).to.be.false
105 expect(data.import.videos.torrent.enabled).to.be.false 106 expect(data.import.videos.torrent.enabled).to.be.false
107 expect(data.autoBlacklist.videos.ofUsers.enabled).to.be.true
106} 108}
107 109
108describe('Test config', function () { 110describe('Test config', function () {
@@ -225,6 +227,13 @@ describe('Test config', function () {
225 enabled: false 227 enabled: false
226 } 228 }
227 } 229 }
230 },
231 autoBlacklist: {
232 videos: {
233 ofUsers: {
234 enabled: true
235 }
236 }
228 } 237 }
229 } 238 }
230 await updateCustomConfig(server.url, server.accessToken, newCustomConfig) 239 await updateCustomConfig(server.url, server.accessToken, newCustomConfig)
diff --git a/server/tests/api/users/user-notifications.ts b/server/tests/api/users/user-notifications.ts
index d573bf024..1b66df79b 100644
--- a/server/tests/api/users/user-notifications.ts
+++ b/server/tests/api/users/user-notifications.ts
@@ -17,7 +17,9 @@ import {
17 updateVideo, 17 updateVideo,
18 updateVideoChannel, 18 updateVideoChannel,
19 userLogin, 19 userLogin,
20 wait 20 wait,
21 getCustomConfig,
22 updateCustomConfig
21} from '../../../../shared/utils' 23} from '../../../../shared/utils'
22import { killallServers, ServerInfo, uploadVideo } from '../../../../shared/utils/index' 24import { killallServers, ServerInfo, uploadVideo } from '../../../../shared/utils/index'
23import { setAccessTokensToServers } from '../../../../shared/utils/users/login' 25import { setAccessTokensToServers } from '../../../../shared/utils/users/login'
@@ -31,6 +33,7 @@ import {
31 checkNewBlacklistOnMyVideo, 33 checkNewBlacklistOnMyVideo,
32 checkNewCommentOnMyVideo, 34 checkNewCommentOnMyVideo,
33 checkNewVideoAbuseForModerators, 35 checkNewVideoAbuseForModerators,
36 checkVideoAutoBlacklistForModerators,
34 checkNewVideoFromSubscription, 37 checkNewVideoFromSubscription,
35 checkUserRegistered, 38 checkUserRegistered,
36 checkVideoIsPublished, 39 checkVideoIsPublished,
@@ -54,6 +57,7 @@ import { getBadVideoUrl, getYoutubeVideoUrl, importVideo } from '../../../../sha
54import { addVideoCommentReply, addVideoCommentThread } from '../../../../shared/utils/videos/video-comments' 57import { addVideoCommentReply, addVideoCommentThread } from '../../../../shared/utils/videos/video-comments'
55import * as uuidv4 from 'uuid/v4' 58import * as uuidv4 from 'uuid/v4'
56import { addAccountToAccountBlocklist, removeAccountFromAccountBlocklist } from '../../../../shared/utils/users/blocklist' 59import { addAccountToAccountBlocklist, removeAccountFromAccountBlocklist } from '../../../../shared/utils/users/blocklist'
60import { CustomConfig } from '../../../../shared/models/server'
57 61
58const expect = chai.expect 62const expect = chai.expect
59 63
@@ -92,6 +96,7 @@ describe('Test users notifications', function () {
92 newVideoFromSubscription: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, 96 newVideoFromSubscription: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
93 newCommentOnMyVideo: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, 97 newCommentOnMyVideo: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
94 videoAbuseAsModerator: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, 98 videoAbuseAsModerator: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
99 videoAutoBlacklistAsModerator: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
95 blacklistOnMyVideo: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, 100 blacklistOnMyVideo: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
96 myVideoImportFinished: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, 101 myVideoImportFinished: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
97 myVideoPublished: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, 102 myVideoPublished: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
@@ -305,7 +310,7 @@ describe('Test users notifications', function () {
305 }) 310 })
306 311
307 it('Should send a new video notification after a video import', async function () { 312 it('Should send a new video notification after a video import', async function () {
308 this.timeout(30000) 313 this.timeout(100000)
309 314
310 const name = 'video import ' + uuidv4() 315 const name = 'video import ' + uuidv4()
311 316
@@ -907,6 +912,180 @@ describe('Test users notifications', function () {
907 }) 912 })
908 }) 913 })
909 914
915 describe('Video-related notifications when video auto-blacklist is enabled', function () {
916 let userBaseParams: CheckerBaseParams
917 let adminBaseParamsServer1: CheckerBaseParams
918 let adminBaseParamsServer2: CheckerBaseParams
919 let videoUUID: string
920 let videoName: string
921 let currentCustomConfig: CustomConfig
922
923 before(async () => {
924
925 adminBaseParamsServer1 = {
926 server: servers[0],
927 emails,
928 socketNotifications: adminNotifications,
929 token: servers[0].accessToken
930 }
931
932 adminBaseParamsServer2 = {
933 server: servers[1],
934 emails,
935 socketNotifications: adminNotificationsServer2,
936 token: servers[1].accessToken
937 }
938
939 userBaseParams = {
940 server: servers[0],
941 emails,
942 socketNotifications: userNotifications,
943 token: userAccessToken
944 }
945
946 const resCustomConfig = await getCustomConfig(servers[0].url, servers[0].accessToken)
947 currentCustomConfig = resCustomConfig.body
948 const autoBlacklistTestsCustomConfig = immutableAssign(currentCustomConfig, {
949 autoBlacklist: {
950 videos: {
951 ofUsers: {
952 enabled: true
953 }
954 }
955 }
956 })
957 // enable transcoding otherwise own publish notification after transcoding not expected
958 autoBlacklistTestsCustomConfig.transcoding.enabled = true
959 await updateCustomConfig(servers[0].url, servers[0].accessToken, autoBlacklistTestsCustomConfig)
960
961 await addUserSubscription(servers[0].url, servers[0].accessToken, 'user_1_channel@localhost:9001')
962 await addUserSubscription(servers[1].url, servers[1].accessToken, 'user_1_channel@localhost:9001')
963
964 })
965
966 it('Should send notification to moderators on new video with auto-blacklist', async function () {
967 this.timeout(20000)
968
969 videoName = 'video with auto-blacklist ' + uuidv4()
970 const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name: videoName })
971 videoUUID = resVideo.body.video.uuid
972
973 await waitJobs(servers)
974 await checkVideoAutoBlacklistForModerators(adminBaseParamsServer1, videoUUID, videoName, 'presence')
975 })
976
977 it('Should not send video publish notification if auto-blacklisted', async function () {
978 await checkVideoIsPublished(userBaseParams, videoName, videoUUID, 'absence')
979 })
980
981 it('Should not send a local user subscription notification if auto-blacklisted', async function () {
982 await checkNewVideoFromSubscription(adminBaseParamsServer1, videoName, videoUUID, 'absence')
983 })
984
985 it('Should not send a remote user subscription notification if auto-blacklisted', async function () {
986 await checkNewVideoFromSubscription(adminBaseParamsServer2, videoName, videoUUID, 'absence')
987 })
988
989 it('Should send video published and unblacklist after video unblacklisted', async function () {
990 this.timeout(20000)
991
992 await removeVideoFromBlacklist(servers[0].url, servers[0].accessToken, videoUUID)
993
994 await waitJobs(servers)
995
996 // FIXME: Can't test as two notifications sent to same user and util only checks last one
997 // One notification might be better anyways
998 // await checkNewBlacklistOnMyVideo(userBaseParams, videoUUID, videoName, 'unblacklist')
999 // await checkVideoIsPublished(userBaseParams, videoName, videoUUID, 'presence')
1000 })
1001
1002 it('Should send a local user subscription notification after removed from blacklist', async function () {
1003 await checkNewVideoFromSubscription(adminBaseParamsServer1, videoName, videoUUID, 'presence')
1004 })
1005
1006 it('Should send a remote user subscription notification after removed from blacklist', async function () {
1007 await checkNewVideoFromSubscription(adminBaseParamsServer2, videoName, videoUUID, 'presence')
1008 })
1009
1010 it('Should send unblacklist but not published/subscription notes after unblacklisted if scheduled update pending', async function () {
1011 this.timeout(20000)
1012
1013 let updateAt = new Date(new Date().getTime() + 100000)
1014
1015 const name = 'video with auto-blacklist and future schedule ' + uuidv4()
1016
1017 const data = {
1018 name,
1019 privacy: VideoPrivacy.PRIVATE,
1020 scheduleUpdate: {
1021 updateAt: updateAt.toISOString(),
1022 privacy: VideoPrivacy.PUBLIC
1023 }
1024 }
1025
1026 const resVideo = await uploadVideo(servers[0].url, userAccessToken, data)
1027 const uuid = resVideo.body.video.uuid
1028
1029 await removeVideoFromBlacklist(servers[0].url, servers[0].accessToken, uuid)
1030
1031 await waitJobs(servers)
1032 await checkNewBlacklistOnMyVideo(userBaseParams, uuid, name, 'unblacklist')
1033
1034 // FIXME: Can't test absence as two notifications sent to same user and util only checks last one
1035 // One notification might be better anyways
1036 // await checkVideoIsPublished(userBaseParams, name, uuid, 'absence')
1037
1038 await checkNewVideoFromSubscription(adminBaseParamsServer1, name, uuid, 'absence')
1039 await checkNewVideoFromSubscription(adminBaseParamsServer2, name, uuid, 'absence')
1040 })
1041
1042 it('Should not send publish/subscription notifications after scheduled update if video still auto-blacklisted', async function () {
1043 this.timeout(20000)
1044
1045 // In 2 seconds
1046 let updateAt = new Date(new Date().getTime() + 2000)
1047
1048 const name = 'video with schedule done and still auto-blacklisted ' + uuidv4()
1049
1050 const data = {
1051 name,
1052 privacy: VideoPrivacy.PRIVATE,
1053 scheduleUpdate: {
1054 updateAt: updateAt.toISOString(),
1055 privacy: VideoPrivacy.PUBLIC
1056 }
1057 }
1058
1059 const resVideo = await uploadVideo(servers[0].url, userAccessToken, data)
1060 const uuid = resVideo.body.video.uuid
1061
1062 await wait(6000)
1063 await checkVideoIsPublished(userBaseParams, name, uuid, 'absence')
1064 await checkNewVideoFromSubscription(adminBaseParamsServer1, name, uuid, 'absence')
1065 await checkNewVideoFromSubscription(adminBaseParamsServer2, name, uuid, 'absence')
1066 })
1067
1068 it('Should not send a notification to moderators on new video without auto-blacklist', async function () {
1069 this.timeout(20000)
1070
1071 const name = 'video without auto-blacklist ' + uuidv4()
1072
1073 // admin with blacklist right will not be auto-blacklisted
1074 const resVideo = await uploadVideo(servers[0].url, servers[0].accessToken, { name })
1075 const uuid = resVideo.body.video.uuid
1076
1077 await waitJobs(servers)
1078 await checkVideoAutoBlacklistForModerators(adminBaseParamsServer1, uuid, name, 'absence')
1079 })
1080
1081 after(async () => {
1082 await updateCustomConfig(servers[0].url, servers[0].accessToken, currentCustomConfig)
1083
1084 await removeUserSubscription(servers[0].url, servers[0].accessToken, 'user_1_channel@localhost:9001')
1085 await removeUserSubscription(servers[1].url, servers[1].accessToken, 'user_1_channel@localhost:9001')
1086 })
1087 })
1088
910 describe('Mark as read', function () { 1089 describe('Mark as read', function () {
911 it('Should mark as read some notifications', async function () { 1090 it('Should mark as read some notifications', async function () {
912 const res = await getUserNotifications(servers[ 0 ].url, userAccessToken, 2, 3) 1091 const res = await getUserNotifications(servers[ 0 ].url, userAccessToken, 2, 3)
@@ -968,7 +1147,7 @@ describe('Test users notifications', function () {
968 }) 1147 })
969 1148
970 it('Should not have notifications', async function () { 1149 it('Should not have notifications', async function () {
971 this.timeout(10000) 1150 this.timeout(20000)
972 1151
973 await updateMyNotificationSettings(servers[0].url, userAccessToken, immutableAssign(allNotificationSettings, { 1152 await updateMyNotificationSettings(servers[0].url, userAccessToken, immutableAssign(allNotificationSettings, {
974 newVideoFromSubscription: UserNotificationSettingValue.NONE 1153 newVideoFromSubscription: UserNotificationSettingValue.NONE
@@ -987,7 +1166,7 @@ describe('Test users notifications', function () {
987 }) 1166 })
988 1167
989 it('Should only have web notifications', async function () { 1168 it('Should only have web notifications', async function () {
990 this.timeout(10000) 1169 this.timeout(20000)
991 1170
992 await updateMyNotificationSettings(servers[0].url, userAccessToken, immutableAssign(allNotificationSettings, { 1171 await updateMyNotificationSettings(servers[0].url, userAccessToken, immutableAssign(allNotificationSettings, {
993 newVideoFromSubscription: UserNotificationSettingValue.WEB 1172 newVideoFromSubscription: UserNotificationSettingValue.WEB
@@ -1013,7 +1192,7 @@ describe('Test users notifications', function () {
1013 }) 1192 })
1014 1193
1015 it('Should only have mail notifications', async function () { 1194 it('Should only have mail notifications', async function () {
1016 this.timeout(10000) 1195 this.timeout(20000)
1017 1196
1018 await updateMyNotificationSettings(servers[0].url, userAccessToken, immutableAssign(allNotificationSettings, { 1197 await updateMyNotificationSettings(servers[0].url, userAccessToken, immutableAssign(allNotificationSettings, {
1019 newVideoFromSubscription: UserNotificationSettingValue.EMAIL 1198 newVideoFromSubscription: UserNotificationSettingValue.EMAIL
@@ -1039,7 +1218,7 @@ describe('Test users notifications', function () {
1039 }) 1218 })
1040 1219
1041 it('Should have email and web notifications', async function () { 1220 it('Should have email and web notifications', async function () {
1042 this.timeout(10000) 1221 this.timeout(20000)
1043 1222
1044 await updateMyNotificationSettings(servers[0].url, userAccessToken, immutableAssign(allNotificationSettings, { 1223 await updateMyNotificationSettings(servers[0].url, userAccessToken, immutableAssign(allNotificationSettings, {
1045 newVideoFromSubscription: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL 1224 newVideoFromSubscription: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL
diff --git a/server/tests/api/videos/video-blacklist.ts b/server/tests/api/videos/video-blacklist.ts
index d39ad63b4..10b412a80 100644
--- a/server/tests/api/videos/video-blacklist.ts
+++ b/server/tests/api/videos/video-blacklist.ts
@@ -7,6 +7,7 @@ import {
7 addVideoToBlacklist, 7 addVideoToBlacklist,
8 flushAndRunMultipleServers, 8 flushAndRunMultipleServers,
9 getBlacklistedVideosList, 9 getBlacklistedVideosList,
10 getBlacklistedVideosListWithTypeFilter,
10 getMyVideos, 11 getMyVideos,
11 getSortedBlacklistedVideosList, 12 getSortedBlacklistedVideosList,
12 getVideosList, 13 getVideosList,
@@ -22,7 +23,7 @@ import {
22} from '../../../../shared/utils/index' 23} from '../../../../shared/utils/index'
23import { doubleFollow } from '../../../../shared/utils/server/follows' 24import { doubleFollow } from '../../../../shared/utils/server/follows'
24import { waitJobs } from '../../../../shared/utils/server/jobs' 25import { waitJobs } from '../../../../shared/utils/server/jobs'
25import { VideoBlacklist } from '../../../../shared/models/videos' 26import { VideoBlacklist, VideoBlacklistType } from '../../../../shared/models/videos'
26 27
27const expect = chai.expect 28const expect = chai.expect
28 29
@@ -101,7 +102,7 @@ describe('Test video blacklist management', function () {
101 }) 102 })
102 }) 103 })
103 104
104 describe('When listing blacklisted videos', function () { 105 describe('When listing manually blacklisted videos', function () {
105 it('Should display all the blacklisted videos', async function () { 106 it('Should display all the blacklisted videos', async function () {
106 const res = await getBlacklistedVideosList(servers[0].url, servers[0].accessToken) 107 const res = await getBlacklistedVideosList(servers[0].url, servers[0].accessToken)
107 108
@@ -117,6 +118,26 @@ describe('Test video blacklist management', function () {
117 } 118 }
118 }) 119 })
119 120
121 it('Should display all the blacklisted videos when applying manual type filter', async function () {
122 const res = await getBlacklistedVideosListWithTypeFilter(servers[0].url, servers[0].accessToken, VideoBlacklistType.MANUAL)
123
124 expect(res.body.total).to.equal(2)
125
126 const blacklistedVideos = res.body.data
127 expect(blacklistedVideos).to.be.an('array')
128 expect(blacklistedVideos.length).to.equal(2)
129 })
130
131 it('Should display nothing when applying automatic type filter', async function () {
132 const res = await getBlacklistedVideosListWithTypeFilter(servers[0].url, servers[0].accessToken, VideoBlacklistType.AUTO_BEFORE_PUBLISHED) // tslint:disable:max-line-length
133
134 expect(res.body.total).to.equal(0)
135
136 const blacklistedVideos = res.body.data
137 expect(blacklistedVideos).to.be.an('array')
138 expect(blacklistedVideos.length).to.equal(0)
139 })
140
120 it('Should get the correct sort when sorting by descending id', async function () { 141 it('Should get the correct sort when sorting by descending id', async function () {
121 const res = await getSortedBlacklistedVideosList(servers[0].url, servers[0].accessToken, '-id') 142 const res = await getSortedBlacklistedVideosList(servers[0].url, servers[0].accessToken, '-id')
122 expect(res.body.total).to.equal(2) 143 expect(res.body.total).to.equal(2)