aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--client/src/app/shared/video/video-details.model.ts16
-rw-r--r--client/src/app/shared/video/video-edit.model.ts3
-rw-r--r--client/src/app/shared/video/video.service.ts3
-rw-r--r--client/src/app/videos/+video-edit/shared/video-edit.component.html6
-rw-r--r--client/src/app/videos/+video-edit/shared/video-edit.component.ts1
-rw-r--r--client/src/app/videos/+video-edit/video-add.component.ts2
-rw-r--r--client/src/app/videos/+video-watch/comment/video-comments.component.html58
-rw-r--r--client/src/app/videos/+video-watch/comment/video-comments.component.ts7
-rw-r--r--server/controllers/api/videos/comment.ts26
-rw-r--r--server/controllers/api/videos/index.ts5
-rw-r--r--server/helpers/custom-validators/activitypub/activity.ts5
-rw-r--r--server/helpers/custom-validators/activitypub/video-channels.ts30
-rw-r--r--server/helpers/custom-validators/activitypub/videos.ts6
-rw-r--r--server/helpers/custom-validators/misc.ts7
-rw-r--r--server/helpers/custom-validators/videos.ts5
-rw-r--r--server/helpers/utils.ts2
-rw-r--r--server/initializers/constants.ts2
-rw-r--r--server/initializers/migrations/0155-video-comments-enabled.ts26
-rw-r--r--server/lib/activitypub/process/misc.ts1
-rw-r--r--server/middlewares/validators/users.ts5
-rw-r--r--server/middlewares/validators/video-comments.ts14
-rw-r--r--server/middlewares/validators/videos.ts10
-rw-r--r--server/models/activitypub/actor.ts4
-rw-r--r--server/models/avatar/avatar.ts2
-rw-r--r--server/models/video/video.ts11
-rw-r--r--server/tests/activitypub.ts4
-rw-r--r--server/tests/api/check-params/users.ts16
-rw-r--r--server/tests/api/check-params/video-comments.ts35
-rw-r--r--server/tests/api/check-params/videos.ts22
-rw-r--r--server/tests/api/server/follows.ts1
-rw-r--r--server/tests/api/videos/multiple-servers.ts27
-rw-r--r--server/tests/api/videos/single-server.ts5
-rw-r--r--server/tests/utils/videos/videos.ts10
-rw-r--r--shared/models/activitypub/objects/video-torrent-object.ts1
-rw-r--r--shared/models/videos/video-create.model.ts1
-rw-r--r--shared/models/videos/video-update.model.ts1
-rw-r--r--shared/models/videos/video.model.ts1
37 files changed, 269 insertions, 112 deletions
diff --git a/client/src/app/shared/video/video-details.model.ts b/client/src/app/shared/video/video-details.model.ts
index 8243b9f1c..cf6b71b60 100644
--- a/client/src/app/shared/video/video-details.model.ts
+++ b/client/src/app/shared/video/video-details.model.ts
@@ -1,14 +1,10 @@
1import { Account } from '../../../../../shared/models/actors'
2import { Video } from '../../shared/video/video.model'
3import { AuthUser } from '../../core'
4import { 1import {
5 VideoDetails as VideoDetailsServerModel, 2 UserRight, VideoChannel, VideoDetails as VideoDetailsServerModel, VideoFile, VideoPrivacy,
6 VideoFile, 3 VideoResolution
7 VideoChannel,
8 VideoResolution,
9 UserRight,
10 VideoPrivacy
11} from '../../../../../shared' 4} from '../../../../../shared'
5import { Account } from '../../../../../shared/models/actors'
6import { AuthUser } from '../../core'
7import { Video } from '../../shared/video/video.model'
12 8
13export class VideoDetails extends Video implements VideoDetailsServerModel { 9export class VideoDetails extends Video implements VideoDetailsServerModel {
14 accountName: string 10 accountName: string
@@ -48,6 +44,7 @@ export class VideoDetails extends Video implements VideoDetailsServerModel {
48 account: Account 44 account: Account
49 likesPercent: number 45 likesPercent: number
50 dislikesPercent: number 46 dislikesPercent: number
47 commentsEnabled: boolean
51 48
52 constructor (hash: VideoDetailsServerModel) { 49 constructor (hash: VideoDetailsServerModel) {
53 super(hash) 50 super(hash)
@@ -59,6 +56,7 @@ export class VideoDetails extends Video implements VideoDetailsServerModel {
59 this.channel = hash.channel 56 this.channel = hash.channel
60 this.account = hash.account 57 this.account = hash.account
61 this.tags = hash.tags 58 this.tags = hash.tags
59 this.commentsEnabled = hash.commentsEnabled
62 60
63 this.likesPercent = (this.likes / (this.likes + this.dislikes)) * 100 61 this.likesPercent = (this.likes / (this.likes + this.dislikes)) * 100
64 this.dislikesPercent = (this.dislikes / (this.likes + this.dislikes)) * 100 62 this.dislikesPercent = (this.dislikes / (this.likes + this.dislikes)) * 100
diff --git a/client/src/app/shared/video/video-edit.model.ts b/client/src/app/shared/video/video-edit.model.ts
index 47c63d976..b1c772217 100644
--- a/client/src/app/shared/video/video-edit.model.ts
+++ b/client/src/app/shared/video/video-edit.model.ts
@@ -9,6 +9,7 @@ export class VideoEdit {
9 name: string 9 name: string
10 tags: string[] 10 tags: string[]
11 nsfw: boolean 11 nsfw: boolean
12 commentsEnabled: boolean
12 channel: number 13 channel: number
13 privacy: VideoPrivacy 14 privacy: VideoPrivacy
14 uuid?: string 15 uuid?: string
@@ -25,6 +26,7 @@ export class VideoEdit {
25 this.name = videoDetails.name 26 this.name = videoDetails.name
26 this.tags = videoDetails.tags 27 this.tags = videoDetails.tags
27 this.nsfw = videoDetails.nsfw 28 this.nsfw = videoDetails.nsfw
29 this.commentsEnabled = videoDetails.commentsEnabled
28 this.channel = videoDetails.channel.id 30 this.channel = videoDetails.channel.id
29 this.privacy = videoDetails.privacy 31 this.privacy = videoDetails.privacy
30 } 32 }
@@ -45,6 +47,7 @@ export class VideoEdit {
45 name: this.name, 47 name: this.name,
46 tags: this.tags, 48 tags: this.tags,
47 nsfw: this.nsfw, 49 nsfw: this.nsfw,
50 commentsEnabled: this.commentsEnabled,
48 channelId: this.channel, 51 channelId: this.channel,
49 privacy: this.privacy 52 privacy: this.privacy
50 } 53 }
diff --git a/client/src/app/shared/video/video.service.ts b/client/src/app/shared/video/video.service.ts
index fc7505a51..073acb2b6 100644
--- a/client/src/app/shared/video/video.service.ts
+++ b/client/src/app/shared/video/video.service.ts
@@ -55,7 +55,8 @@ export class VideoService {
55 description, 55 description,
56 privacy: video.privacy, 56 privacy: video.privacy,
57 tags: video.tags, 57 tags: video.tags,
58 nsfw: video.nsfw 58 nsfw: video.nsfw,
59 commentsEnabled: video.commentsEnabled
59 } 60 }
60 61
61 return this.authHttp.put(VideoService.BASE_VIDEO_URL + video.id, body) 62 return this.authHttp.put(VideoService.BASE_VIDEO_URL + video.id, body)
diff --git a/client/src/app/videos/+video-edit/shared/video-edit.component.html b/client/src/app/videos/+video-edit/shared/video-edit.component.html
index 9acbafcb6..80377933e 100644
--- a/client/src/app/videos/+video-edit/shared/video-edit.component.html
+++ b/client/src/app/videos/+video-edit/shared/video-edit.component.html
@@ -99,5 +99,11 @@
99 <label for="nsfw">This video contains mature or explicit content</label> 99 <label for="nsfw">This video contains mature or explicit content</label>
100 </div> 100 </div>
101 101
102 <div class="form-group form-group-checkbox">
103 <input type="checkbox" id="commentsEnabled" formControlName="commentsEnabled" />
104 <label for="commentsEnabled"></label>
105 <label for="commentsEnabled">Enable video comments</label>
106 </div>
107
102 </div> 108 </div>
103</div> 109</div>
diff --git a/client/src/app/videos/+video-edit/shared/video-edit.component.ts b/client/src/app/videos/+video-edit/shared/video-edit.component.ts
index 7fe265284..2b307d5fa 100644
--- a/client/src/app/videos/+video-edit/shared/video-edit.component.ts
+++ b/client/src/app/videos/+video-edit/shared/video-edit.component.ts
@@ -70,6 +70,7 @@ export class VideoEditComponent implements OnInit {
70 this.form.addControl('privacy', new FormControl('', VIDEO_PRIVACY.VALIDATORS)) 70 this.form.addControl('privacy', new FormControl('', VIDEO_PRIVACY.VALIDATORS))
71 this.form.addControl('channelId', new FormControl({ value: '', disabled: true })) 71 this.form.addControl('channelId', new FormControl({ value: '', disabled: true }))
72 this.form.addControl('nsfw', new FormControl(false)) 72 this.form.addControl('nsfw', new FormControl(false))
73 this.form.addControl('commentsEnabled', new FormControl(true))
73 this.form.addControl('category', new FormControl('', VIDEO_CATEGORY.VALIDATORS)) 74 this.form.addControl('category', new FormControl('', VIDEO_CATEGORY.VALIDATORS))
74 this.form.addControl('licence', new FormControl('', VIDEO_LICENCE.VALIDATORS)) 75 this.form.addControl('licence', new FormControl('', VIDEO_LICENCE.VALIDATORS))
75 this.form.addControl('language', new FormControl('', VIDEO_LANGUAGE.VALIDATORS)) 76 this.form.addControl('language', new FormControl('', VIDEO_LANGUAGE.VALIDATORS))
diff --git a/client/src/app/videos/+video-edit/video-add.component.ts b/client/src/app/videos/+video-edit/video-add.component.ts
index 9bbee58d8..843475647 100644
--- a/client/src/app/videos/+video-edit/video-add.component.ts
+++ b/client/src/app/videos/+video-edit/video-add.component.ts
@@ -88,6 +88,7 @@ export class VideoAddComponent extends FormReactive implements OnInit {
88 const name = videofile.name.replace(/\.[^/.]+$/, '') 88 const name = videofile.name.replace(/\.[^/.]+$/, '')
89 const privacy = this.firstStepPrivacyId.toString() 89 const privacy = this.firstStepPrivacyId.toString()
90 const nsfw = false 90 const nsfw = false
91 const commentsEnabled = true
91 const channelId = this.firstStepChannelId.toString() 92 const channelId = this.firstStepChannelId.toString()
92 93
93 const formData = new FormData() 94 const formData = new FormData()
@@ -95,6 +96,7 @@ export class VideoAddComponent extends FormReactive implements OnInit {
95 // Put the video "private" -> we wait he validates the second step 96 // Put the video "private" -> we wait he validates the second step
96 formData.append('privacy', VideoPrivacy.PRIVATE.toString()) 97 formData.append('privacy', VideoPrivacy.PRIVATE.toString())
97 formData.append('nsfw', '' + nsfw) 98 formData.append('nsfw', '' + nsfw)
99 formData.append('commentsEnabled', '' + commentsEnabled)
98 formData.append('channelId', '' + channelId) 100 formData.append('channelId', '' + channelId)
99 formData.append('videofile', videofile) 101 formData.append('videofile', videofile)
100 102
diff --git a/client/src/app/videos/+video-watch/comment/video-comments.component.html b/client/src/app/videos/+video-watch/comment/video-comments.component.html
index 5c6908150..078900e06 100644
--- a/client/src/app/videos/+video-watch/comment/video-comments.component.html
+++ b/client/src/app/videos/+video-watch/comment/video-comments.component.html
@@ -3,35 +3,43 @@
3 Comments 3 Comments
4 </div> 4 </div>
5 5
6 <my-video-comment-add 6 <ng-template [ngIf]="video.commentsEnabled === true">
7 *ngIf="isUserLoggedIn()" 7 <my-video-comment-add
8 [video]="video" 8 *ngIf="isUserLoggedIn()"
9 (commentCreated)="onCommentThreadCreated($event)" 9 [video]="video"
10 ></my-video-comment-add> 10 (commentCreated)="onCommentThreadCreated($event)"
11 ></my-video-comment-add>
11 12
12 <div 13 <div *ngIf="componentPagination.totalItems === 0 && comments.length === 0">No comments.</div>
13 class="comment-threads"
14 infiniteScroll
15 [infiniteScrollUpDistance]="1.5"
16 [infiniteScrollDistance]="0.5"
17 (scrolled)="onNearOfBottom()"
18 >
19 <div *ngFor="let comment of comments">
20 <my-video-comment
21 [comment]="comment"
22 [video]="video"
23 [inReplyToCommentId]="inReplyToCommentId"
24 [commentTree]="threadComments[comment.id]"
25 (wantedToReply)="onWantedToReply($event)"
26 (resetReply)="onResetReply()"
27 ></my-video-comment>
28 14
29 <div *ngIf="comment.totalReplies !== 0 && !threadComments[comment.id]" (click)="viewReplies(comment)" class="view-replies"> 15 <div
30 View all {{ comment.totalReplies }} replies 16 class="comment-threads"
17 infiniteScroll
18 [infiniteScrollUpDistance]="1.5"
19 [infiniteScrollDistance]="0.5"
20 (scrolled)="onNearOfBottom()"
21 >
22 <div *ngFor="let comment of comments">
23 <my-video-comment
24 [comment]="comment"
25 [video]="video"
26 [inReplyToCommentId]="inReplyToCommentId"
27 [commentTree]="threadComments[comment.id]"
28 (wantedToReply)="onWantedToReply($event)"
29 (resetReply)="onResetReply()"
30 ></my-video-comment>
31 31
32 <span *ngIf="!threadLoading[comment.id]" class="glyphicon glyphicon-menu-down"></span> 32 <div *ngIf="comment.totalReplies !== 0 && !threadComments[comment.id]" (click)="viewReplies(comment)" class="view-replies">
33 <my-loader class="comment-thread-loading" [loading]="threadLoading[comment.id]"></my-loader> 33 View all {{ comment.totalReplies }} replies
34
35 <span *ngIf="!threadLoading[comment.id]" class="glyphicon glyphicon-menu-down"></span>
36 <my-loader class="comment-thread-loading" [loading]="threadLoading[comment.id]"></my-loader>
37 </div>
34 </div> 38 </div>
35 </div> 39 </div>
40 </ng-template>
41
42 <div *ngIf="video.commentsEnabled === false">
43 Comments are disabled.
36 </div> 44 </div>
37</div> 45</div>
diff --git a/client/src/app/videos/+video-watch/comment/video-comments.component.ts b/client/src/app/videos/+video-watch/comment/video-comments.component.ts
index f4dda9089..4d801c970 100644
--- a/client/src/app/videos/+video-watch/comment/video-comments.component.ts
+++ b/client/src/app/videos/+video-watch/comment/video-comments.component.ts
@@ -5,6 +5,7 @@ import { AuthService } from '../../../core/auth'
5import { ComponentPagination } from '../../../shared/rest/component-pagination.model' 5import { ComponentPagination } from '../../../shared/rest/component-pagination.model'
6import { User } from '../../../shared/users' 6import { User } from '../../../shared/users'
7import { SortField } from '../../../shared/video/sort-field.type' 7import { SortField } from '../../../shared/video/sort-field.type'
8import { VideoDetails } from '../../../shared/video/video-details.model'
8import { Video } from '../../../shared/video/video.model' 9import { Video } from '../../../shared/video/video.model'
9import { VideoComment } from './video-comment.model' 10import { VideoComment } from './video-comment.model'
10import { VideoCommentService } from './video-comment.service' 11import { VideoCommentService } from './video-comment.service'
@@ -15,7 +16,7 @@ import { VideoCommentService } from './video-comment.service'
15 styleUrls: ['./video-comments.component.scss'] 16 styleUrls: ['./video-comments.component.scss']
16}) 17})
17export class VideoCommentsComponent implements OnInit { 18export class VideoCommentsComponent implements OnInit {
18 @Input() video: Video 19 @Input() video: VideoDetails
19 @Input() user: User 20 @Input() user: User
20 21
21 comments: VideoComment[] = [] 22 comments: VideoComment[] = []
@@ -36,7 +37,9 @@ export class VideoCommentsComponent implements OnInit {
36 ) {} 37 ) {}
37 38
38 ngOnInit () { 39 ngOnInit () {
39 this.loadMoreComments() 40 if (this.video.commentsEnabled === true) {
41 this.loadMoreComments()
42 }
40 } 43 }
41 44
42 viewReplies (comment: VideoComment) { 45 viewReplies (comment: VideoComment) {
diff --git a/server/controllers/api/videos/comment.ts b/server/controllers/api/videos/comment.ts
index b11da2ef7..e09b242ed 100644
--- a/server/controllers/api/videos/comment.ts
+++ b/server/controllers/api/videos/comment.ts
@@ -1,4 +1,5 @@
1import * as express from 'express' 1import * as express from 'express'
2import { ResultList } from '../../../../shared/models'
2import { VideoCommentCreate } from '../../../../shared/models/videos/video-comment.model' 3import { VideoCommentCreate } from '../../../../shared/models/videos/video-comment.model'
3import { retryTransactionWrapper } from '../../../helpers/database-utils' 4import { retryTransactionWrapper } from '../../../helpers/database-utils'
4import { getFormattedObjects } from '../../../helpers/utils' 5import { getFormattedObjects } from '../../../helpers/utils'
@@ -10,6 +11,7 @@ import {
10 addVideoCommentReplyValidator, addVideoCommentThreadValidator, listVideoCommentThreadsValidator, 11 addVideoCommentReplyValidator, addVideoCommentThreadValidator, listVideoCommentThreadsValidator,
11 listVideoThreadCommentsValidator 12 listVideoThreadCommentsValidator
12} from '../../../middlewares/validators/video-comments' 13} from '../../../middlewares/validators/video-comments'
14import { VideoModel } from '../../../models/video/video'
13import { VideoCommentModel } from '../../../models/video/video-comment' 15import { VideoCommentModel } from '../../../models/video/video-comment'
14 16
15const videoCommentRouter = express.Router() 17const videoCommentRouter = express.Router()
@@ -47,13 +49,33 @@ export {
47// --------------------------------------------------------------------------- 49// ---------------------------------------------------------------------------
48 50
49async function listVideoThreads (req: express.Request, res: express.Response, next: express.NextFunction) { 51async function listVideoThreads (req: express.Request, res: express.Response, next: express.NextFunction) {
50 const resultList = await VideoCommentModel.listThreadsForApi(res.locals.video.id, req.query.start, req.query.count, req.query.sort) 52 const video = res.locals.video as VideoModel
53 let resultList: ResultList<VideoCommentModel>
54
55 if (video.commentsEnabled === true) {
56 resultList = await VideoCommentModel.listThreadsForApi(video.id, req.query.start, req.query.count, req.query.sort)
57 } else {
58 resultList = {
59 total: 0,
60 data: []
61 }
62 }
51 63
52 return res.json(getFormattedObjects(resultList.data, resultList.total)) 64 return res.json(getFormattedObjects(resultList.data, resultList.total))
53} 65}
54 66
55async function listVideoThreadComments (req: express.Request, res: express.Response, next: express.NextFunction) { 67async function listVideoThreadComments (req: express.Request, res: express.Response, next: express.NextFunction) {
56 const resultList = await VideoCommentModel.listThreadCommentsForApi(res.locals.video.id, res.locals.videoCommentThread.id) 68 const video = res.locals.video as VideoModel
69 let resultList: ResultList<VideoCommentModel>
70
71 if (video.commentsEnabled === true) {
72 resultList = await VideoCommentModel.listThreadCommentsForApi(res.locals.video.id, res.locals.videoCommentThread.id)
73 } else {
74 resultList = {
75 total: 0,
76 data: []
77 }
78 }
57 79
58 return res.json(buildFormattedCommentTree(resultList)) 80 return res.json(buildFormattedCommentTree(resultList))
59} 81}
diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts
index ff0d967e1..368327914 100644
--- a/server/controllers/api/videos/index.ts
+++ b/server/controllers/api/videos/index.ts
@@ -1,12 +1,11 @@
1import * as express from 'express' 1import * as express from 'express'
2import * as multer from 'multer'
3import { extname, join } from 'path' 2import { extname, join } from 'path'
4import { VideoCreate, VideoPrivacy, VideoUpdate } from '../../../../shared' 3import { VideoCreate, VideoPrivacy, VideoUpdate } from '../../../../shared'
5import { renamePromise } from '../../../helpers/core-utils' 4import { renamePromise } from '../../../helpers/core-utils'
6import { retryTransactionWrapper } from '../../../helpers/database-utils' 5import { retryTransactionWrapper } from '../../../helpers/database-utils'
7import { getVideoFileHeight } from '../../../helpers/ffmpeg-utils' 6import { getVideoFileHeight } from '../../../helpers/ffmpeg-utils'
8import { logger } from '../../../helpers/logger' 7import { logger } from '../../../helpers/logger'
9import { createReqFiles, generateRandomString, getFormattedObjects, getServerActor, resetSequelizeInstance } from '../../../helpers/utils' 8import { createReqFiles, getFormattedObjects, getServerActor, resetSequelizeInstance } from '../../../helpers/utils'
10import { 9import {
11 CONFIG, sequelizeTypescript, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_MIMETYPE_EXT, 10 CONFIG, sequelizeTypescript, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_MIMETYPE_EXT,
12 VIDEO_PRIVACIES 11 VIDEO_PRIVACIES
@@ -141,6 +140,7 @@ async function addVideo (req: express.Request, res: express.Response, videoPhysi
141 category: videoInfo.category, 140 category: videoInfo.category,
142 licence: videoInfo.licence, 141 licence: videoInfo.licence,
143 language: videoInfo.language, 142 language: videoInfo.language,
143 commentsEnabled: videoInfo.commentsEnabled,
144 nsfw: videoInfo.nsfw, 144 nsfw: videoInfo.nsfw,
145 description: videoInfo.description, 145 description: videoInfo.description,
146 privacy: videoInfo.privacy, 146 privacy: videoInfo.privacy,
@@ -248,6 +248,7 @@ async function updateVideo (req: express.Request, res: express.Response) {
248 if (videoInfoToUpdate.nsfw !== undefined) videoInstance.set('nsfw', videoInfoToUpdate.nsfw) 248 if (videoInfoToUpdate.nsfw !== undefined) videoInstance.set('nsfw', videoInfoToUpdate.nsfw)
249 if (videoInfoToUpdate.privacy !== undefined) videoInstance.set('privacy', parseInt(videoInfoToUpdate.privacy.toString(), 10)) 249 if (videoInfoToUpdate.privacy !== undefined) videoInstance.set('privacy', parseInt(videoInfoToUpdate.privacy.toString(), 10))
250 if (videoInfoToUpdate.description !== undefined) videoInstance.set('description', videoInfoToUpdate.description) 250 if (videoInfoToUpdate.description !== undefined) videoInstance.set('description', videoInfoToUpdate.description)
251 if (videoInfoToUpdate.commentsEnabled !== undefined) videoInstance.set('commentsEnabled', videoInfoToUpdate.commentsEnabled)
251 252
252 const videoInstanceUpdated = await videoInstance.save(sequelizeOptions) 253 const videoInstanceUpdated = await videoInstance.save(sequelizeOptions)
253 254
diff --git a/server/helpers/custom-validators/activitypub/activity.ts b/server/helpers/custom-validators/activitypub/activity.ts
index f2e137061..fbdde10ad 100644
--- a/server/helpers/custom-validators/activitypub/activity.ts
+++ b/server/helpers/custom-validators/activitypub/activity.ts
@@ -5,7 +5,6 @@ import { isAnnounceActivityValid } from './announce'
5import { isActivityPubUrlValid } from './misc' 5import { isActivityPubUrlValid } from './misc'
6import { isDislikeActivityValid, isLikeActivityValid } from './rate' 6import { isDislikeActivityValid, isLikeActivityValid } from './rate'
7import { isUndoActivityValid } from './undo' 7import { isUndoActivityValid } from './undo'
8import { isVideoChannelDeleteActivityValid, isVideoChannelUpdateActivityValid } from './video-channels'
9import { isVideoCommentCreateActivityValid } from './video-comments' 8import { isVideoCommentCreateActivityValid } from './video-comments'
10import { 9import {
11 isVideoFlagValid, 10 isVideoFlagValid,
@@ -65,13 +64,11 @@ function checkCreateActivity (activity: any) {
65} 64}
66 65
67function checkUpdateActivity (activity: any) { 66function checkUpdateActivity (activity: any) {
68 return isVideoTorrentUpdateActivityValid(activity) || 67 return isVideoTorrentUpdateActivityValid(activity)
69 isVideoChannelUpdateActivityValid(activity)
70} 68}
71 69
72function checkDeleteActivity (activity: any) { 70function checkDeleteActivity (activity: any) {
73 return isVideoTorrentDeleteActivityValid(activity) || 71 return isVideoTorrentDeleteActivityValid(activity) ||
74 isVideoChannelDeleteActivityValid(activity) ||
75 isActorDeleteActivityValid(activity) 72 isActorDeleteActivityValid(activity)
76} 73}
77 74
diff --git a/server/helpers/custom-validators/activitypub/video-channels.ts b/server/helpers/custom-validators/activitypub/video-channels.ts
deleted file mode 100644
index eb45c6372..000000000
--- a/server/helpers/custom-validators/activitypub/video-channels.ts
+++ /dev/null
@@ -1,30 +0,0 @@
1import { isDateValid, isUUIDValid } from '../misc'
2import { isVideoChannelDescriptionValid, isVideoChannelNameValid } from '../video-channels'
3import { isActivityPubUrlValid, isBaseActivityValid } from './misc'
4
5function isVideoChannelUpdateActivityValid (activity: any) {
6 return isBaseActivityValid(activity, 'Update') &&
7 isVideoChannelObjectValid(activity.object)
8}
9
10function isVideoChannelDeleteActivityValid (activity: any) {
11 return isBaseActivityValid(activity, 'Delete')
12}
13
14function isVideoChannelObjectValid (videoChannel: any) {
15 return videoChannel.type === 'VideoChannel' &&
16 isActivityPubUrlValid(videoChannel.id) &&
17 isVideoChannelNameValid(videoChannel.name) &&
18 isVideoChannelDescriptionValid(videoChannel.content) &&
19 isDateValid(videoChannel.published) &&
20 isDateValid(videoChannel.updated) &&
21 isUUIDValid(videoChannel.uuid)
22}
23
24// ---------------------------------------------------------------------------
25
26export {
27 isVideoChannelUpdateActivityValid,
28 isVideoChannelDeleteActivityValid,
29 isVideoChannelObjectValid
30}
diff --git a/server/helpers/custom-validators/activitypub/videos.ts b/server/helpers/custom-validators/activitypub/videos.ts
index ae1339611..37cd6965a 100644
--- a/server/helpers/custom-validators/activitypub/videos.ts
+++ b/server/helpers/custom-validators/activitypub/videos.ts
@@ -1,11 +1,10 @@
1import * as validator from 'validator' 1import * as validator from 'validator'
2import { ACTIVITY_PUB } from '../../../initializers' 2import { ACTIVITY_PUB } from '../../../initializers'
3import { exists, isDateValid, isUUIDValid } from '../misc' 3import { exists, isBooleanValid, isDateValid, isUUIDValid } from '../misc'
4import { 4import {
5 isVideoAbuseReasonValid, 5 isVideoAbuseReasonValid,
6 isVideoDurationValid, 6 isVideoDurationValid,
7 isVideoNameValid, 7 isVideoNameValid,
8 isVideoNSFWValid,
9 isVideoTagValid, 8 isVideoTagValid,
10 isVideoTruncatedDescriptionValid, 9 isVideoTruncatedDescriptionValid,
11 isVideoViewsValid 10 isVideoViewsValid
@@ -53,7 +52,8 @@ function isVideoTorrentObjectValid (video: any) {
53 (!video.licence || isRemoteIdentifierValid(video.licence)) && 52 (!video.licence || isRemoteIdentifierValid(video.licence)) &&
54 (!video.language || isRemoteIdentifierValid(video.language)) && 53 (!video.language || isRemoteIdentifierValid(video.language)) &&
55 isVideoViewsValid(video.views) && 54 isVideoViewsValid(video.views) &&
56 isVideoNSFWValid(video.nsfw) && 55 isBooleanValid(video.nsfw) &&
56 isBooleanValid(video.commentsEnabled) &&
57 isDateValid(video.published) && 57 isDateValid(video.published) &&
58 isDateValid(video.updated) && 58 isDateValid(video.updated) &&
59 (!video.content || isRemoteVideoContentValid(video.mediaType, video.content)) && 59 (!video.content || isRemoteVideoContentValid(video.mediaType, video.content)) &&
diff --git a/server/helpers/custom-validators/misc.ts b/server/helpers/custom-validators/misc.ts
index 160ec91f3..3903884ea 100644
--- a/server/helpers/custom-validators/misc.ts
+++ b/server/helpers/custom-validators/misc.ts
@@ -24,6 +24,10 @@ function isIdOrUUIDValid (value: string) {
24 return isIdValid(value) || isUUIDValid(value) 24 return isIdValid(value) || isUUIDValid(value)
25} 25}
26 26
27function isBooleanValid (value: string) {
28 return typeof value === 'boolean' || (typeof value === 'string' && validator.isBoolean(value))
29}
30
27// --------------------------------------------------------------------------- 31// ---------------------------------------------------------------------------
28 32
29export { 33export {
@@ -32,5 +36,6 @@ export {
32 isIdValid, 36 isIdValid,
33 isUUIDValid, 37 isUUIDValid,
34 isIdOrUUIDValid, 38 isIdOrUUIDValid,
35 isDateValid 39 isDateValid,
40 isBooleanValid
36} 41}
diff --git a/server/helpers/custom-validators/videos.ts b/server/helpers/custom-validators/videos.ts
index ee9d0ed19..1a5fdb887 100644
--- a/server/helpers/custom-validators/videos.ts
+++ b/server/helpers/custom-validators/videos.ts
@@ -30,10 +30,6 @@ function isVideoLanguageValid (value: number) {
30 return value === null || VIDEO_LANGUAGES[value] !== undefined 30 return value === null || VIDEO_LANGUAGES[value] !== undefined
31} 31}
32 32
33function isVideoNSFWValid (value: any) {
34 return typeof value === 'boolean' || (typeof value === 'string' && validator.isBoolean(value))
35}
36
37function isVideoDurationValid (value: string) { 33function isVideoDurationValid (value: string) {
38 return exists(value) && validator.isInt(value + '', VIDEOS_CONSTRAINTS_FIELDS.DURATION) 34 return exists(value) && validator.isInt(value + '', VIDEOS_CONSTRAINTS_FIELDS.DURATION)
39} 35}
@@ -131,7 +127,6 @@ export {
131 isVideoCategoryValid, 127 isVideoCategoryValid,
132 isVideoLicenceValid, 128 isVideoLicenceValid,
133 isVideoLanguageValid, 129 isVideoLanguageValid,
134 isVideoNSFWValid,
135 isVideoTruncatedDescriptionValid, 130 isVideoTruncatedDescriptionValid,
136 isVideoDescriptionValid, 131 isVideoDescriptionValid,
137 isVideoFileInfoHashValid, 132 isVideoFileInfoHashValid,
diff --git a/server/helpers/utils.ts b/server/helpers/utils.ts
index 7a32e286c..b61d6e3fa 100644
--- a/server/helpers/utils.ts
+++ b/server/helpers/utils.ts
@@ -3,7 +3,7 @@ import * as multer from 'multer'
3import { Model } from 'sequelize-typescript' 3import { Model } from 'sequelize-typescript'
4import { ResultList } from '../../shared' 4import { ResultList } from '../../shared'
5import { VideoResolution } from '../../shared/models/videos' 5import { VideoResolution } from '../../shared/models/videos'
6import { CONFIG, REMOTE_SCHEME, VIDEO_MIMETYPE_EXT } from '../initializers' 6import { CONFIG, REMOTE_SCHEME } from '../initializers'
7import { UserModel } from '../models/account/user' 7import { UserModel } from '../models/account/user'
8import { ActorModel } from '../models/activitypub/actor' 8import { ActorModel } from '../models/activitypub/actor'
9import { ApplicationModel } from '../models/application/application' 9import { ApplicationModel } from '../models/application/application'
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts
index 50a29dc43..31bb6c981 100644
--- a/server/initializers/constants.ts
+++ b/server/initializers/constants.ts
@@ -9,7 +9,7 @@ import { isTestInstance, root, sanitizeHost, sanitizeUrl } from '../helpers/core
9 9
10// --------------------------------------------------------------------------- 10// ---------------------------------------------------------------------------
11 11
12const LAST_MIGRATION_VERSION = 150 12const LAST_MIGRATION_VERSION = 155
13 13
14// --------------------------------------------------------------------------- 14// ---------------------------------------------------------------------------
15 15
diff --git a/server/initializers/migrations/0155-video-comments-enabled.ts b/server/initializers/migrations/0155-video-comments-enabled.ts
new file mode 100644
index 000000000..59f4110de
--- /dev/null
+++ b/server/initializers/migrations/0155-video-comments-enabled.ts
@@ -0,0 +1,26 @@
1import * as Sequelize from 'sequelize'
2
3async function up (utils: {
4 transaction: Sequelize.Transaction,
5 queryInterface: Sequelize.QueryInterface,
6 sequelize: Sequelize.Sequelize
7}): Promise<void> {
8 const data = {
9 type: Sequelize.BOOLEAN,
10 allowNull: false,
11 defaultValue: true
12 }
13 await utils.queryInterface.addColumn('video', 'commentsEnabled', data)
14
15 data.defaultValue = null
16 return utils.queryInterface.changeColumn('video', 'commentsEnabled', data)
17}
18
19function down (options) {
20 throw new Error('Not implemented.')
21}
22
23export {
24 up,
25 down
26}
diff --git a/server/lib/activitypub/process/misc.ts b/server/lib/activitypub/process/misc.ts
index f65395c99..461619ea7 100644
--- a/server/lib/activitypub/process/misc.ts
+++ b/server/lib/activitypub/process/misc.ts
@@ -53,6 +53,7 @@ async function videoActivityObjectToDBAttributes (
53 language, 53 language,
54 description, 54 description,
55 nsfw: videoObject.nsfw, 55 nsfw: videoObject.nsfw,
56 commentsEnabled: videoObject.commentsEnabled,
56 channelId: videoChannel.id, 57 channelId: videoChannel.id,
57 duration: parseInt(duration, 10), 58 duration: parseInt(duration, 10),
58 createdAt: new Date(videoObject.published), 59 createdAt: new Date(videoObject.published),
diff --git a/server/middlewares/validators/users.ts b/server/middlewares/validators/users.ts
index 42ebddd56..7c77e9a39 100644
--- a/server/middlewares/validators/users.ts
+++ b/server/middlewares/validators/users.ts
@@ -3,11 +3,10 @@ import 'express-validator'
3import { body, param } from 'express-validator/check' 3import { body, param } from 'express-validator/check'
4import { isIdOrUUIDValid } from '../../helpers/custom-validators/misc' 4import { isIdOrUUIDValid } from '../../helpers/custom-validators/misc'
5import { 5import {
6 isAvatarFile, 6 isAvatarFile, isUserAutoPlayVideoValid, isUserDisplayNSFWValid, isUserPasswordValid, isUserRoleValid, isUserUsernameValid,
7 isUserAutoPlayVideoValid, isUserDisplayNSFWValid, isUserPasswordValid, isUserRoleValid, isUserUsernameValid,
8 isUserVideoQuotaValid 7 isUserVideoQuotaValid
9} from '../../helpers/custom-validators/users' 8} from '../../helpers/custom-validators/users'
10import { isVideoExist, isVideoFile } from '../../helpers/custom-validators/videos' 9import { isVideoExist } from '../../helpers/custom-validators/videos'
11import { logger } from '../../helpers/logger' 10import { logger } from '../../helpers/logger'
12import { isSignupAllowed } from '../../helpers/utils' 11import { isSignupAllowed } from '../../helpers/utils'
13import { CONSTRAINTS_FIELDS } from '../../initializers' 12import { CONSTRAINTS_FIELDS } from '../../initializers'
diff --git a/server/middlewares/validators/video-comments.ts b/server/middlewares/validators/video-comments.ts
index fdd092571..ade0b7b9f 100644
--- a/server/middlewares/validators/video-comments.ts
+++ b/server/middlewares/validators/video-comments.ts
@@ -45,6 +45,7 @@ const addVideoCommentThreadValidator = [
45 45
46 if (areValidationErrors(req, res)) return 46 if (areValidationErrors(req, res)) return
47 if (!await isVideoExist(req.params.videoId, res)) return 47 if (!await isVideoExist(req.params.videoId, res)) return
48 if (!isVideoCommentsEnabled(res.locals.video, res)) return
48 49
49 return next() 50 return next()
50 } 51 }
@@ -60,6 +61,7 @@ const addVideoCommentReplyValidator = [
60 61
61 if (areValidationErrors(req, res)) return 62 if (areValidationErrors(req, res)) return
62 if (!await isVideoExist(req.params.videoId, res)) return 63 if (!await isVideoExist(req.params.videoId, res)) return
64 if (!isVideoCommentsEnabled(res.locals.video, res)) return
63 if (!await isVideoCommentExist(req.params.commentId, res.locals.video, res)) return 65 if (!await isVideoCommentExist(req.params.commentId, res.locals.video, res)) return
64 66
65 return next() 67 return next()
@@ -146,3 +148,15 @@ async function isVideoCommentExist (id: number, video: VideoModel, res: express.
146 res.locals.videoComment = videoComment 148 res.locals.videoComment = videoComment
147 return true 149 return true
148} 150}
151
152function isVideoCommentsEnabled (video: VideoModel, res: express.Response) {
153 if (video.commentsEnabled !== true) {
154 res.status(409)
155 .json({ error: 'Video comments are disabled for this video.' })
156 .end()
157
158 return false
159 }
160
161 return true
162}
diff --git a/server/middlewares/validators/videos.ts b/server/middlewares/validators/videos.ts
index bffc50322..e8cb2ae03 100644
--- a/server/middlewares/validators/videos.ts
+++ b/server/middlewares/validators/videos.ts
@@ -2,10 +2,10 @@ import * as express from 'express'
2import 'express-validator' 2import 'express-validator'
3import { body, param, query } from 'express-validator/check' 3import { body, param, query } from 'express-validator/check'
4import { UserRight, VideoPrivacy } from '../../../shared' 4import { UserRight, VideoPrivacy } from '../../../shared'
5import { isIdOrUUIDValid, isIdValid } from '../../helpers/custom-validators/misc' 5import { isBooleanValid, isIdOrUUIDValid, isIdValid } from '../../helpers/custom-validators/misc'
6import { 6import {
7 isVideoAbuseReasonValid, isVideoCategoryValid, isVideoDescriptionValid, isVideoExist, isVideoFile, isVideoLanguageValid, 7 isVideoAbuseReasonValid, isVideoCategoryValid, isVideoDescriptionValid, isVideoExist, isVideoFile, isVideoLanguageValid,
8 isVideoLicenceValid, isVideoNameValid, isVideoNSFWValid, isVideoPrivacyValid, isVideoRatingTypeValid, isVideoTagsValid 8 isVideoLicenceValid, isVideoNameValid, isVideoPrivacyValid, isVideoRatingTypeValid, isVideoTagsValid
9} from '../../helpers/custom-validators/videos' 9} from '../../helpers/custom-validators/videos'
10import { getDurationFromVideoFile } from '../../helpers/ffmpeg-utils' 10import { getDurationFromVideoFile } from '../../helpers/ffmpeg-utils'
11import { logger } from '../../helpers/logger' 11import { logger } from '../../helpers/logger'
@@ -26,11 +26,12 @@ const videosAddValidator = [
26 body('category').optional().custom(isVideoCategoryValid).withMessage('Should have a valid category'), 26 body('category').optional().custom(isVideoCategoryValid).withMessage('Should have a valid category'),
27 body('licence').optional().custom(isVideoLicenceValid).withMessage('Should have a valid licence'), 27 body('licence').optional().custom(isVideoLicenceValid).withMessage('Should have a valid licence'),
28 body('language').optional().custom(isVideoLanguageValid).withMessage('Should have a valid language'), 28 body('language').optional().custom(isVideoLanguageValid).withMessage('Should have a valid language'),
29 body('nsfw').custom(isVideoNSFWValid).withMessage('Should have a valid NSFW attribute'), 29 body('nsfw').custom(isBooleanValid).withMessage('Should have a valid NSFW attribute'),
30 body('description').optional().custom(isVideoDescriptionValid).withMessage('Should have a valid description'), 30 body('description').optional().custom(isVideoDescriptionValid).withMessage('Should have a valid description'),
31 body('channelId').custom(isIdValid).withMessage('Should have correct video channel id'), 31 body('channelId').custom(isIdValid).withMessage('Should have correct video channel id'),
32 body('privacy').custom(isVideoPrivacyValid).withMessage('Should have correct video privacy'), 32 body('privacy').custom(isVideoPrivacyValid).withMessage('Should have correct video privacy'),
33 body('tags').optional().custom(isVideoTagsValid).withMessage('Should have correct tags'), 33 body('tags').optional().custom(isVideoTagsValid).withMessage('Should have correct tags'),
34 body('commentsEnabled').custom(isBooleanValid).withMessage('Should have comments enabled boolean'),
34 35
35 async (req: express.Request, res: express.Response, next: express.NextFunction) => { 36 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
36 logger.debug('Checking videosAdd parameters', { parameters: req.body, files: req.files }) 37 logger.debug('Checking videosAdd parameters', { parameters: req.body, files: req.files })
@@ -85,10 +86,11 @@ const videosUpdateValidator = [
85 body('category').optional().custom(isVideoCategoryValid).withMessage('Should have a valid category'), 86 body('category').optional().custom(isVideoCategoryValid).withMessage('Should have a valid category'),
86 body('licence').optional().custom(isVideoLicenceValid).withMessage('Should have a valid licence'), 87 body('licence').optional().custom(isVideoLicenceValid).withMessage('Should have a valid licence'),
87 body('language').optional().custom(isVideoLanguageValid).withMessage('Should have a valid language'), 88 body('language').optional().custom(isVideoLanguageValid).withMessage('Should have a valid language'),
88 body('nsfw').optional().custom(isVideoNSFWValid).withMessage('Should have a valid NSFW attribute'), 89 body('nsfw').optional().custom(isBooleanValid).withMessage('Should have a valid NSFW attribute'),
89 body('privacy').optional().custom(isVideoPrivacyValid).withMessage('Should have correct video privacy'), 90 body('privacy').optional().custom(isVideoPrivacyValid).withMessage('Should have correct video privacy'),
90 body('description').optional().custom(isVideoDescriptionValid).withMessage('Should have a valid description'), 91 body('description').optional().custom(isVideoDescriptionValid).withMessage('Should have a valid description'),
91 body('tags').optional().custom(isVideoTagsValid).withMessage('Should have correct tags'), 92 body('tags').optional().custom(isVideoTagsValid).withMessage('Should have correct tags'),
93 body('commentsEnabled').optional().custom(isBooleanValid).withMessage('Should have comments enabled boolean'),
92 94
93 async (req: express.Request, res: express.Response, next: express.NextFunction) => { 95 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
94 logger.debug('Checking videosUpdate parameters', { parameters: req.body }) 96 logger.debug('Checking videosUpdate parameters', { parameters: req.body })
diff --git a/server/models/activitypub/actor.ts b/server/models/activitypub/actor.ts
index 8422653df..a12f3ec9e 100644
--- a/server/models/activitypub/actor.ts
+++ b/server/models/activitypub/actor.ts
@@ -1,5 +1,5 @@
1import { values } from 'lodash' 1import { values } from 'lodash'
2import { extname, join } from 'path' 2import { extname } from 'path'
3import * as Sequelize from 'sequelize' 3import * as Sequelize from 'sequelize'
4import { 4import {
5 AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, DefaultScope, ForeignKey, HasMany, HasOne, Is, IsUUID, Model, Scopes, 5 AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, DefaultScope, ForeignKey, HasMany, HasOne, Is, IsUUID, Model, Scopes,
@@ -13,7 +13,7 @@ import {
13 isActorPublicKeyValid 13 isActorPublicKeyValid
14} from '../../helpers/custom-validators/activitypub/actor' 14} from '../../helpers/custom-validators/activitypub/actor'
15import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' 15import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
16import { ACTIVITY_PUB_ACTOR_TYPES, AVATARS_DIR, CONFIG, CONSTRAINTS_FIELDS } from '../../initializers' 16import { ACTIVITY_PUB_ACTOR_TYPES, CONFIG, CONSTRAINTS_FIELDS } from '../../initializers'
17import { AccountModel } from '../account/account' 17import { AccountModel } from '../account/account'
18import { AvatarModel } from '../avatar/avatar' 18import { AvatarModel } from '../avatar/avatar'
19import { ServerModel } from '../server/server' 19import { ServerModel } from '../server/server'
diff --git a/server/models/avatar/avatar.ts b/server/models/avatar/avatar.ts
index 7493c3d75..e1d4c20bc 100644
--- a/server/models/avatar/avatar.ts
+++ b/server/models/avatar/avatar.ts
@@ -2,9 +2,7 @@ import { join } from 'path'
2import { AfterDestroy, AllowNull, Column, CreatedAt, Model, Table, UpdatedAt } from 'sequelize-typescript' 2import { AfterDestroy, AllowNull, Column, CreatedAt, Model, Table, UpdatedAt } from 'sequelize-typescript'
3import { Avatar } from '../../../shared/models/avatars/avatar.model' 3import { Avatar } from '../../../shared/models/avatars/avatar.model'
4import { unlinkPromise } from '../../helpers/core-utils' 4import { unlinkPromise } from '../../helpers/core-utils'
5import { logger } from '../../helpers/logger'
6import { CONFIG, STATIC_PATHS } from '../../initializers' 5import { CONFIG, STATIC_PATHS } from '../../initializers'
7import { sendDeleteVideo } from '../../lib/activitypub/send'
8 6
9@Table({ 7@Table({
10 tableName: 'avatar' 8 tableName: 'avatar'
diff --git a/server/models/video/video.ts b/server/models/video/video.ts
index 2504ae58a..c4b716cd2 100644
--- a/server/models/video/video.ts
+++ b/server/models/video/video.ts
@@ -15,9 +15,10 @@ import { Video, VideoDetails } from '../../../shared/models/videos'
15import { activityPubCollection } from '../../helpers/activitypub' 15import { activityPubCollection } from '../../helpers/activitypub'
16import { createTorrentPromise, renamePromise, statPromise, unlinkPromise, writeFilePromise } from '../../helpers/core-utils' 16import { createTorrentPromise, renamePromise, statPromise, unlinkPromise, writeFilePromise } from '../../helpers/core-utils'
17import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' 17import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
18import { isBooleanValid } from '../../helpers/custom-validators/misc'
18import { 19import {
19 isVideoCategoryValid, isVideoDescriptionValid, isVideoDurationValid, isVideoLanguageValid, isVideoLicenceValid, isVideoNameValid, 20 isVideoCategoryValid, isVideoDescriptionValid, isVideoDurationValid, isVideoLanguageValid, isVideoLicenceValid, isVideoNameValid,
20 isVideoNSFWValid, isVideoPrivacyValid 21 isVideoPrivacyValid
21} from '../../helpers/custom-validators/videos' 22} from '../../helpers/custom-validators/videos'
22import { generateImageFromVideoFile, getVideoFileHeight, transcode } from '../../helpers/ffmpeg-utils' 23import { generateImageFromVideoFile, getVideoFileHeight, transcode } from '../../helpers/ffmpeg-utils'
23import { logger } from '../../helpers/logger' 24import { logger } from '../../helpers/logger'
@@ -185,7 +186,7 @@ export class VideoModel extends Model<VideoModel> {
185 privacy: number 186 privacy: number
186 187
187 @AllowNull(false) 188 @AllowNull(false)
188 @Is('VideoNSFW', value => throwIfNotValid(value, isVideoNSFWValid, 'NSFW boolean')) 189 @Is('VideoNSFW', value => throwIfNotValid(value, isBooleanValid, 'NSFW boolean'))
189 @Column 190 @Column
190 nsfw: boolean 191 nsfw: boolean
191 192
@@ -230,6 +231,10 @@ export class VideoModel extends Model<VideoModel> {
230 @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEOS.URL.max)) 231 @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEOS.URL.max))
231 url: string 232 url: string
232 233
234 @AllowNull(false)
235 @Column
236 commentsEnabled: boolean
237
233 @CreatedAt 238 @CreatedAt
234 createdAt: Date 239 createdAt: Date
235 240
@@ -773,6 +778,7 @@ export class VideoModel extends Model<VideoModel> {
773 channel: this.VideoChannel.toFormattedJSON(), 778 channel: this.VideoChannel.toFormattedJSON(),
774 account: this.VideoChannel.Account.toFormattedJSON(), 779 account: this.VideoChannel.Account.toFormattedJSON(),
775 tags: map<TagModel, string>(this.Tags, 'name'), 780 tags: map<TagModel, string>(this.Tags, 'name'),
781 commentsEnabled: this.commentsEnabled,
776 files: [] 782 files: []
777 } 783 }
778 784
@@ -920,6 +926,7 @@ export class VideoModel extends Model<VideoModel> {
920 language, 926 language,
921 views: this.views, 927 views: this.views,
922 nsfw: this.nsfw, 928 nsfw: this.nsfw,
929 commentsEnabled: this.commentsEnabled,
923 published: this.createdAt.toISOString(), 930 published: this.createdAt.toISOString(),
924 updated: this.updatedAt.toISOString(), 931 updated: this.updatedAt.toISOString(),
925 mediaType: 'text/markdown', 932 mediaType: 'text/markdown',
diff --git a/server/tests/activitypub.ts b/server/tests/activitypub.ts
index 94615c63f..c8884719d 100644
--- a/server/tests/activitypub.ts
+++ b/server/tests/activitypub.ts
@@ -20,11 +20,11 @@ describe('Test activitypub', function () {
20 }) 20 })
21 21
22 it('Should return the account object', async function () { 22 it('Should return the account object', async function () {
23 const res = await makeActivityPubGetRequest(server.url, '/account/root') 23 const res = await makeActivityPubGetRequest(server.url, '/accounts/root')
24 const object = res.body 24 const object = res.body
25 25
26 expect(object.type).to.equal('Person') 26 expect(object.type).to.equal('Person')
27 expect(object.id).to.equal('http://localhost:9001/account/root') 27 expect(object.id).to.equal('http://localhost:9001/accounts/root')
28 expect(object.name).to.equal('root') 28 expect(object.name).to.equal('root')
29 expect(object.preferredUsername).to.equal('root') 29 expect(object.preferredUsername).to.equal('root')
30 }) 30 })
diff --git a/server/tests/api/check-params/users.ts b/server/tests/api/check-params/users.ts
index 44412ad82..33d92ac24 100644
--- a/server/tests/api/check-params/users.ts
+++ b/server/tests/api/check-params/users.ts
@@ -2,14 +2,13 @@
2 2
3import { omit } from 'lodash' 3import { omit } from 'lodash'
4import 'mocha' 4import 'mocha'
5import { join } from "path" 5import { join } from 'path'
6import { UserRole } from '../../../../shared' 6import { UserRole } from '../../../../shared'
7 7
8import { 8import {
9 createUser, flushTests, getMyUserInformation, getMyUserVideoRating, getUsersList, immutableAssign, killallServers, makeGetRequest, 9 createUser, flushTests, getMyUserInformation, getMyUserVideoRating, getUsersList, immutableAssign, killallServers, makeGetRequest,
10 makePostBodyRequest, makePostUploadRequest, makePutBodyRequest, registerUser, removeUser, runServer, ServerInfo, setAccessTokensToServers, 10 makePostBodyRequest, makePostUploadRequest, makePutBodyRequest, registerUser, removeUser, runServer, ServerInfo, setAccessTokensToServers,
11 updateUser, 11 updateUser, uploadVideo, userLogin
12 uploadVideo, userLogin
13} from '../../utils' 12} from '../../utils'
14import { checkBadCountPagination, checkBadSortPagination, checkBadStartPagination } from '../../utils/requests/check-api-params' 13import { checkBadCountPagination, checkBadSortPagination, checkBadStartPagination } from '../../utils/requests/check-api-params'
15 14
@@ -25,7 +24,7 @@ describe('Test users API validators', function () {
25 // --------------------------------------------------------------- 24 // ---------------------------------------------------------------
26 25
27 before(async function () { 26 before(async function () {
28 this.timeout(120000) 27 this.timeout(20000)
29 28
30 await flushTests() 29 await flushTests()
31 30
@@ -282,7 +281,14 @@ describe('Test users API validators', function () {
282 const attaches = { 281 const attaches = {
283 'avatarfile': join(__dirname, '..', 'fixtures', 'avatar.png') 282 'avatarfile': join(__dirname, '..', 'fixtures', 'avatar.png')
284 } 283 }
285 await makePostUploadRequest({ url: server.url, path: path + '/me/avatar/pick', token: server.accessToken, fields, attaches }) 284 await makePostUploadRequest({
285 url: server.url,
286 path: path + '/me/avatar/pick',
287 token: server.accessToken,
288 fields,
289 attaches,
290 statusCodeExpected: 200
291 })
286 }) 292 })
287 }) 293 })
288 294
diff --git a/server/tests/api/check-params/video-comments.ts b/server/tests/api/check-params/video-comments.ts
index cdb48a276..c11660d07 100644
--- a/server/tests/api/check-params/video-comments.ts
+++ b/server/tests/api/check-params/video-comments.ts
@@ -1,5 +1,6 @@
1/* tslint:disable:no-unused-expression */ 1/* tslint:disable:no-unused-expression */
2 2
3import * as chai from 'chai'
3import 'mocha' 4import 'mocha'
4import { 5import {
5 flushTests, killallServers, makeGetRequest, makePostBodyRequest, runServer, ServerInfo, setAccessTokensToServers, 6 flushTests, killallServers, makeGetRequest, makePostBodyRequest, runServer, ServerInfo, setAccessTokensToServers,
@@ -8,6 +9,8 @@ import {
8import { checkBadCountPagination, checkBadSortPagination, checkBadStartPagination } from '../../utils/requests/check-api-params' 9import { checkBadCountPagination, checkBadSortPagination, checkBadStartPagination } from '../../utils/requests/check-api-params'
9import { addVideoCommentThread } from '../../utils/videos/video-comments' 10import { addVideoCommentThread } from '../../utils/videos/video-comments'
10 11
12const expect = chai.expect
13
11describe('Test video comments API validator', function () { 14describe('Test video comments API validator', function () {
12 let pathThread: string 15 let pathThread: string
13 let pathComment: string 16 let pathComment: string
@@ -42,17 +45,14 @@ describe('Test video comments API validator', function () {
42 describe('When listing video comment threads', function () { 45 describe('When listing video comment threads', function () {
43 it('Should fail with a bad start pagination', async function () { 46 it('Should fail with a bad start pagination', async function () {
44 await checkBadStartPagination(server.url, pathThread, server.accessToken) 47 await checkBadStartPagination(server.url, pathThread, server.accessToken)
45
46 }) 48 })
47 49
48 it('Should fail with a bad count pagination', async function () { 50 it('Should fail with a bad count pagination', async function () {
49 await checkBadCountPagination(server.url, pathThread, server.accessToken) 51 await checkBadCountPagination(server.url, pathThread, server.accessToken)
50
51 }) 52 })
52 53
53 it('Should fail with an incorrect sort', async function () { 54 it('Should fail with an incorrect sort', async function () {
54 await checkBadSortPagination(server.url, pathThread, server.accessToken) 55 await checkBadSortPagination(server.url, pathThread, server.accessToken)
55
56 }) 56 })
57 57
58 it('Should fail with an incorrect video', async function () { 58 it('Should fail with an incorrect video', async function () {
@@ -185,6 +185,35 @@ describe('Test video comments API validator', function () {
185 }) 185 })
186 }) 186 })
187 187
188 describe('When a video has comments disabled', function () {
189 before(async function () {
190 const res = await uploadVideo(server.url, server.accessToken, { commentsEnabled: false })
191 videoUUID = res.body.video.uuid
192 pathThread = '/api/v1/videos/' + videoUUID + '/comment-threads'
193 })
194
195 it('Should return an empty thread list', async function () {
196 const res = await makeGetRequest({
197 url: server.url,
198 path: pathThread,
199 statusCodeExpected: 200
200 })
201 expect(res.body.total).to.equal(0)
202 expect(res.body.data).to.have.lengthOf(0)
203 })
204
205 it('Should return an thread comments list')
206
207 it('Should return conflict on thread add', async function () {
208 const fields = {
209 text: 'super comment'
210 }
211 await makePostBodyRequest({ url: server.url, path: pathThread, token: server.accessToken, fields, statusCodeExpected: 409 })
212 })
213
214 it('Should return conflict on comment thread add')
215 })
216
188 after(async function () { 217 after(async function () {
189 killallServers([ server ]) 218 killallServers([ server ])
190 219
diff --git a/server/tests/api/check-params/videos.ts b/server/tests/api/check-params/videos.ts
index b9484afc4..5c067dc96 100644
--- a/server/tests/api/check-params/videos.ts
+++ b/server/tests/api/check-params/videos.ts
@@ -100,6 +100,7 @@ describe('Test videos API validator', function () {
100 licence: 1, 100 licence: 1,
101 language: 6, 101 language: 6,
102 nsfw: false, 102 nsfw: false,
103 commentsEnabled: true,
103 description: 'my super description', 104 description: 'my super description',
104 tags: [ 'tag1', 'tag2' ], 105 tags: [ 'tag1', 'tag2' ],
105 privacy: VideoPrivacy.PUBLIC, 106 privacy: VideoPrivacy.PUBLIC,
@@ -162,6 +163,20 @@ describe('Test videos API validator', function () {
162 await makePostUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches }) 163 await makePostUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
163 }) 164 })
164 165
166 it('Should fail without commentsEnabled attribute', async function () {
167 const fields = omit(baseCorrectParams, 'commentsEnabled')
168 const attaches = baseCorrectAttaches
169
170 await makePostUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
171 })
172
173 it('Should fail with a bad commentsEnabled attribute', async function () {
174 const fields = immutableAssign(baseCorrectParams, { commentsEnabled: 2 })
175 const attaches = baseCorrectAttaches
176
177 await makePostUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
178 })
179
165 it('Should fail with a long description', async function () { 180 it('Should fail with a long description', async function () {
166 const fields = immutableAssign(baseCorrectParams, { description: 'super'.repeat(1500) }) 181 const fields = immutableAssign(baseCorrectParams, { description: 'super'.repeat(1500) })
167 const attaches = baseCorrectAttaches 182 const attaches = baseCorrectAttaches
@@ -291,6 +306,7 @@ describe('Test videos API validator', function () {
291 licence: 2, 306 licence: 2,
292 language: 6, 307 language: 6,
293 nsfw: false, 308 nsfw: false,
309 commentsEnabled: false,
294 description: 'my super description', 310 description: 'my super description',
295 privacy: VideoPrivacy.PUBLIC, 311 privacy: VideoPrivacy.PUBLIC,
296 tags: [ 'tag1', 'tag2' ] 312 tags: [ 'tag1', 'tag2' ]
@@ -354,6 +370,12 @@ describe('Test videos API validator', function () {
354 await makePutBodyRequest({ url: server.url, path: path + videoId, token: server.accessToken, fields }) 370 await makePutBodyRequest({ url: server.url, path: path + videoId, token: server.accessToken, fields })
355 }) 371 })
356 372
373 it('Should fail with a bad commentsEnabled attribute', async function () {
374 const fields = immutableAssign(baseCorrectParams, { commentsEnabled: 2 })
375
376 await makePutBodyRequest({ url: server.url, path: path + videoId, token: server.accessToken, fields })
377 })
378
357 it('Should fail with a long description', async function () { 379 it('Should fail with a long description', async function () {
358 const fields = immutableAssign(baseCorrectParams, { description: 'super'.repeat(1500) }) 380 const fields = immutableAssign(baseCorrectParams, { description: 'super'.repeat(1500) })
359 381
diff --git a/server/tests/api/server/follows.ts b/server/tests/api/server/follows.ts
index fc9c5c3b6..e6dfd5f62 100644
--- a/server/tests/api/server/follows.ts
+++ b/server/tests/api/server/follows.ts
@@ -246,6 +246,7 @@ describe('Test follows', function () {
246 host: 'localhost:9003', 246 host: 'localhost:9003',
247 account: 'root', 247 account: 'root',
248 isLocal, 248 isLocal,
249 commentsEnabled: true,
249 duration: 5, 250 duration: 5,
250 tags: [ 'tag1', 'tag2', 'tag3' ], 251 tags: [ 'tag1', 'tag2', 'tag3' ],
251 privacy: VideoPrivacy.PUBLIC, 252 privacy: VideoPrivacy.PUBLIC,
diff --git a/server/tests/api/videos/multiple-servers.ts b/server/tests/api/videos/multiple-servers.ts
index abd051a30..b6dfe0d1b 100644
--- a/server/tests/api/videos/multiple-servers.ts
+++ b/server/tests/api/videos/multiple-servers.ts
@@ -93,6 +93,7 @@ describe('Test multiple servers', function () {
93 duration: 10, 93 duration: 10,
94 tags: [ 'tag1p1', 'tag2p1' ], 94 tags: [ 'tag1p1', 'tag2p1' ],
95 privacy: VideoPrivacy.PUBLIC, 95 privacy: VideoPrivacy.PUBLIC,
96 commentsEnabled: true,
96 channel: { 97 channel: {
97 name: 'my channel', 98 name: 'my channel',
98 description: 'super channel', 99 description: 'super channel',
@@ -155,6 +156,7 @@ describe('Test multiple servers', function () {
155 host: 'localhost:9002', 156 host: 'localhost:9002',
156 account: 'user1', 157 account: 'user1',
157 isLocal, 158 isLocal,
159 commentsEnabled: true,
158 duration: 5, 160 duration: 5,
159 tags: [ 'tag1p2', 'tag2p2', 'tag3p2' ], 161 tags: [ 'tag1p2', 'tag2p2', 'tag3p2' ],
160 privacy: VideoPrivacy.PUBLIC, 162 privacy: VideoPrivacy.PUBLIC,
@@ -254,6 +256,7 @@ describe('Test multiple servers', function () {
254 account: 'root', 256 account: 'root',
255 isLocal, 257 isLocal,
256 duration: 5, 258 duration: 5,
259 commentsEnabled: true,
257 tags: [ 'tag1p3' ], 260 tags: [ 'tag1p3' ],
258 privacy: VideoPrivacy.PUBLIC, 261 privacy: VideoPrivacy.PUBLIC,
259 channel: { 262 channel: {
@@ -280,6 +283,7 @@ describe('Test multiple servers', function () {
280 description: 'my super description for server 3-2', 283 description: 'my super description for server 3-2',
281 host: 'localhost:9003', 284 host: 'localhost:9003',
282 account: 'root', 285 account: 'root',
286 commentsEnabled: true,
283 isLocal, 287 isLocal,
284 duration: 5, 288 duration: 5,
285 tags: [ 'tag2p3', 'tag3p3', 'tag4p3' ], 289 tags: [ 'tag2p3', 'tag3p3', 'tag4p3' ],
@@ -545,6 +549,7 @@ describe('Test multiple servers', function () {
545 account: 'root', 549 account: 'root',
546 isLocal, 550 isLocal,
547 duration: 5, 551 duration: 5,
552 commentsEnabled: true,
548 tags: [ 'tag_up_1', 'tag_up_2' ], 553 tags: [ 'tag_up_1', 'tag_up_2' ],
549 privacy: VideoPrivacy.PUBLIC, 554 privacy: VideoPrivacy.PUBLIC,
550 channel: { 555 channel: {
@@ -732,6 +737,26 @@ describe('Test multiple servers', function () {
732 expect(secondChild.children).to.have.lengthOf(0) 737 expect(secondChild.children).to.have.lengthOf(0)
733 } 738 }
734 }) 739 })
740
741 it('Should disable comments', async function () {
742 this.timeout(20000)
743
744 const attributes = {
745 commentsEnabled: false
746 }
747
748 await updateVideo(servers[0].url, servers[0].accessToken, videoUUID, attributes)
749
750 await wait(5000)
751
752 for (const server of servers) {
753 const res = await getVideo(server.url, videoUUID)
754 expect(res.body.commentsEnabled).to.be.false
755
756 const text = 'my super forbidden comment'
757 await addVideoCommentThread(server.url, server.accessToken, videoUUID, text, 409)
758 }
759 })
735 }) 760 })
736 761
737 describe('With minimum parameters', function () { 762 describe('With minimum parameters', function () {
@@ -748,6 +773,7 @@ describe('Test multiple servers', function () {
748 .field('privacy', '1') 773 .field('privacy', '1')
749 .field('nsfw', 'false') 774 .field('nsfw', 'false')
750 .field('channelId', '1') 775 .field('channelId', '1')
776 .field('commentsEnabled', 'true')
751 777
752 const filePath = join(__dirname, '..', '..', 'api', 'fixtures', 'video_short.webm') 778 const filePath = join(__dirname, '..', '..', 'api', 'fixtures', 'video_short.webm')
753 779
@@ -772,6 +798,7 @@ describe('Test multiple servers', function () {
772 account: 'root', 798 account: 'root',
773 isLocal, 799 isLocal,
774 duration: 5, 800 duration: 5,
801 commentsEnabled: true,
775 tags: [ ], 802 tags: [ ],
776 privacy: VideoPrivacy.PUBLIC, 803 privacy: VideoPrivacy.PUBLIC,
777 channel: { 804 channel: {
diff --git a/server/tests/api/videos/single-server.ts b/server/tests/api/videos/single-server.ts
index 2a3126f32..0a0c95750 100644
--- a/server/tests/api/videos/single-server.ts
+++ b/server/tests/api/videos/single-server.ts
@@ -32,6 +32,7 @@ describe('Test a single server', function () {
32 duration: 5, 32 duration: 5,
33 tags: [ 'tag1', 'tag2', 'tag3' ], 33 tags: [ 'tag1', 'tag2', 'tag3' ],
34 privacy: VideoPrivacy.PUBLIC, 34 privacy: VideoPrivacy.PUBLIC,
35 commentsEnabled: true,
35 channel: { 36 channel: {
36 name: 'Default root channel', 37 name: 'Default root channel',
37 description: '', 38 description: '',
@@ -51,7 +52,7 @@ describe('Test a single server', function () {
51 category: 4, 52 category: 4,
52 licence: 2, 53 licence: 2,
53 language: 5, 54 language: 5,
54 nsfw: true, 55 nsfw: false,
55 description: 'my super description updated', 56 description: 'my super description updated',
56 host: 'localhost:9001', 57 host: 'localhost:9001',
57 account: 'root', 58 account: 'root',
@@ -59,6 +60,7 @@ describe('Test a single server', function () {
59 tags: [ 'tagup1', 'tagup2' ], 60 tags: [ 'tagup1', 'tagup2' ],
60 privacy: VideoPrivacy.PUBLIC, 61 privacy: VideoPrivacy.PUBLIC,
61 duration: 5, 62 duration: 5,
63 commentsEnabled: false,
62 channel: { 64 channel: {
63 name: 'Default root channel', 65 name: 'Default root channel',
64 description: '', 66 description: '',
@@ -475,6 +477,7 @@ describe('Test a single server', function () {
475 language: 5, 477 language: 5,
476 nsfw: false, 478 nsfw: false,
477 description: 'my super description updated', 479 description: 'my super description updated',
480 commentsEnabled: false,
478 tags: [ 'tagup1', 'tagup2' ] 481 tags: [ 'tagup1', 'tagup2' ]
479 } 482 }
480 await updateVideo(server.url, server.accessToken, videoId, attributes) 483 await updateVideo(server.url, server.accessToken, videoId, attributes)
diff --git a/server/tests/utils/videos/videos.ts b/server/tests/utils/videos/videos.ts
index aca51ee5d..c437c21b2 100644
--- a/server/tests/utils/videos/videos.ts
+++ b/server/tests/utils/videos/videos.ts
@@ -16,6 +16,7 @@ type VideoAttributes = {
16 licence?: number 16 licence?: number
17 language?: number 17 language?: number
18 nsfw?: boolean 18 nsfw?: boolean
19 commentsEnabled?: boolean
19 description?: string 20 description?: string
20 tags?: string[] 21 tags?: string[]
21 channelId?: number 22 channelId?: number
@@ -238,6 +239,7 @@ async function uploadVideo (url: string, accessToken: string, videoAttributesArg
238 description: 'my super description', 239 description: 'my super description',
239 tags: [ 'tag' ], 240 tags: [ 'tag' ],
240 privacy: VideoPrivacy.PUBLIC, 241 privacy: VideoPrivacy.PUBLIC,
242 commentsEnabled: true,
241 fixture: 'video_short.webm' 243 fixture: 'video_short.webm'
242 } 244 }
243 attributes = Object.assign(attributes, videoAttributesArg) 245 attributes = Object.assign(attributes, videoAttributesArg)
@@ -250,6 +252,7 @@ async function uploadVideo (url: string, accessToken: string, videoAttributesArg
250 .field('category', attributes.category.toString()) 252 .field('category', attributes.category.toString())
251 .field('licence', attributes.licence.toString()) 253 .field('licence', attributes.licence.toString())
252 .field('nsfw', JSON.stringify(attributes.nsfw)) 254 .field('nsfw', JSON.stringify(attributes.nsfw))
255 .field('commentsEnabled', JSON.stringify(attributes.commentsEnabled))
253 .field('description', attributes.description) 256 .field('description', attributes.description)
254 .field('privacy', attributes.privacy.toString()) 257 .field('privacy', attributes.privacy.toString())
255 .field('channelId', attributes.channelId) 258 .field('channelId', attributes.channelId)
@@ -273,7 +276,7 @@ async function uploadVideo (url: string, accessToken: string, videoAttributesArg
273 .expect(specialStatus) 276 .expect(specialStatus)
274} 277}
275 278
276function updateVideo (url: string, accessToken: string, id: number, attributes: VideoAttributes, specialStatus = 204) { 279function updateVideo (url: string, accessToken: string, id: number | string, attributes: VideoAttributes, specialStatus = 204) {
277 const path = '/api/v1/videos/' + id 280 const path = '/api/v1/videos/' + id
278 const body = {} 281 const body = {}
279 282
@@ -281,7 +284,8 @@ function updateVideo (url: string, accessToken: string, id: number, attributes:
281 if (attributes.category) body['category'] = attributes.category 284 if (attributes.category) body['category'] = attributes.category
282 if (attributes.licence) body['licence'] = attributes.licence 285 if (attributes.licence) body['licence'] = attributes.licence
283 if (attributes.language) body['language'] = attributes.language 286 if (attributes.language) body['language'] = attributes.language
284 if (attributes.nsfw) body['nsfw'] = attributes.nsfw 287 if (attributes.nsfw !== undefined) body['nsfw'] = JSON.stringify(attributes.nsfw)
288 if (attributes.commentsEnabled !== undefined) body['commentsEnabled'] = JSON.stringify(attributes.commentsEnabled)
285 if (attributes.description) body['description'] = attributes.description 289 if (attributes.description) body['description'] = attributes.description
286 if (attributes.tags) body['tags'] = attributes.tags 290 if (attributes.tags) body['tags'] = attributes.tags
287 if (attributes.privacy) body['privacy'] = attributes.privacy 291 if (attributes.privacy) body['privacy'] = attributes.privacy
@@ -326,6 +330,7 @@ async function completeVideoCheck (
326 licence: number 330 licence: number
327 language: number 331 language: number
328 nsfw: boolean 332 nsfw: boolean
333 commentsEnabled: boolean
329 description: string 334 description: string
330 host: string 335 host: string
331 account: string 336 account: string
@@ -376,6 +381,7 @@ async function completeVideoCheck (
376 expect(videoDetails.privacy).to.deep.equal(attributes.privacy) 381 expect(videoDetails.privacy).to.deep.equal(attributes.privacy)
377 expect(videoDetails.privacyLabel).to.deep.equal(VIDEO_PRIVACIES[attributes.privacy]) 382 expect(videoDetails.privacyLabel).to.deep.equal(VIDEO_PRIVACIES[attributes.privacy])
378 expect(videoDetails.account.name).to.equal(attributes.account) 383 expect(videoDetails.account.name).to.equal(attributes.account)
384 expect(videoDetails.commentsEnabled).to.equal(attributes.commentsEnabled)
379 385
380 expect(videoDetails.channel.name).to.equal(attributes.channel.name) 386 expect(videoDetails.channel.name).to.equal(attributes.channel.name)
381 expect(videoDetails.channel.isLocal).to.equal(attributes.channel.isLocal) 387 expect(videoDetails.channel.isLocal).to.equal(attributes.channel.isLocal)
diff --git a/shared/models/activitypub/objects/video-torrent-object.ts b/shared/models/activitypub/objects/video-torrent-object.ts
index 5ccc80bcb..cf0e0ba54 100644
--- a/shared/models/activitypub/objects/video-torrent-object.ts
+++ b/shared/models/activitypub/objects/video-torrent-object.ts
@@ -18,6 +18,7 @@ export interface VideoTorrentObject {
18 language: ActivityIdentifierObject 18 language: ActivityIdentifierObject
19 views: number 19 views: number
20 nsfw: boolean 20 nsfw: boolean
21 commentsEnabled: boolean
21 published: string 22 published: string
22 updated: string 23 updated: string
23 mediaType: 'text/markdown' 24 mediaType: 'text/markdown'
diff --git a/shared/models/videos/video-create.model.ts b/shared/models/videos/video-create.model.ts
index 8bc6a6639..139c2579e 100644
--- a/shared/models/videos/video-create.model.ts
+++ b/shared/models/videos/video-create.model.ts
@@ -9,5 +9,6 @@ export interface VideoCreate {
9 nsfw: boolean 9 nsfw: boolean
10 name: string 10 name: string
11 tags?: string[] 11 tags?: string[]
12 commentsEnabled?: boolean
12 privacy: VideoPrivacy 13 privacy: VideoPrivacy
13} 14}
diff --git a/shared/models/videos/video-update.model.ts b/shared/models/videos/video-update.model.ts
index 0cf38fe6e..fc772f77b 100644
--- a/shared/models/videos/video-update.model.ts
+++ b/shared/models/videos/video-update.model.ts
@@ -8,5 +8,6 @@ export interface VideoUpdate {
8 description?: string 8 description?: string
9 privacy?: VideoPrivacy 9 privacy?: VideoPrivacy
10 tags?: string[] 10 tags?: string[]
11 commentsEnabled?: boolean
11 nsfw?: boolean 12 nsfw?: boolean
12} 13}
diff --git a/shared/models/videos/video.model.ts b/shared/models/videos/video.model.ts
index 13b9c49b3..39d1edc06 100644
--- a/shared/models/videos/video.model.ts
+++ b/shared/models/videos/video.model.ts
@@ -45,4 +45,5 @@ export interface VideoDetails extends Video {
45 tags: string[] 45 tags: string[]
46 files: VideoFile[] 46 files: VideoFile[]
47 account: Account 47 account: Account
48 commentsEnabled: boolean
48} 49}