aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/lib
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/lib
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/lib')
-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
8 files changed, 123 insertions, 18 deletions
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}