Add ability to disable video comments
This commit is contained in:
parent
c5911fd347
commit
47564bbe2e
|
@ -1,14 +1,10 @@
|
|||
import { Account } from '../../../../../shared/models/actors'
|
||||
import { Video } from '../../shared/video/video.model'
|
||||
import { AuthUser } from '../../core'
|
||||
import {
|
||||
VideoDetails as VideoDetailsServerModel,
|
||||
VideoFile,
|
||||
VideoChannel,
|
||||
VideoResolution,
|
||||
UserRight,
|
||||
VideoPrivacy
|
||||
UserRight, VideoChannel, VideoDetails as VideoDetailsServerModel, VideoFile, VideoPrivacy,
|
||||
VideoResolution
|
||||
} from '../../../../../shared'
|
||||
import { Account } from '../../../../../shared/models/actors'
|
||||
import { AuthUser } from '../../core'
|
||||
import { Video } from '../../shared/video/video.model'
|
||||
|
||||
export class VideoDetails extends Video implements VideoDetailsServerModel {
|
||||
accountName: string
|
||||
|
@ -48,6 +44,7 @@ export class VideoDetails extends Video implements VideoDetailsServerModel {
|
|||
account: Account
|
||||
likesPercent: number
|
||||
dislikesPercent: number
|
||||
commentsEnabled: boolean
|
||||
|
||||
constructor (hash: VideoDetailsServerModel) {
|
||||
super(hash)
|
||||
|
@ -59,6 +56,7 @@ export class VideoDetails extends Video implements VideoDetailsServerModel {
|
|||
this.channel = hash.channel
|
||||
this.account = hash.account
|
||||
this.tags = hash.tags
|
||||
this.commentsEnabled = hash.commentsEnabled
|
||||
|
||||
this.likesPercent = (this.likes / (this.likes + this.dislikes)) * 100
|
||||
this.dislikesPercent = (this.dislikes / (this.likes + this.dislikes)) * 100
|
||||
|
|
|
@ -9,6 +9,7 @@ export class VideoEdit {
|
|||
name: string
|
||||
tags: string[]
|
||||
nsfw: boolean
|
||||
commentsEnabled: boolean
|
||||
channel: number
|
||||
privacy: VideoPrivacy
|
||||
uuid?: string
|
||||
|
@ -25,6 +26,7 @@ export class VideoEdit {
|
|||
this.name = videoDetails.name
|
||||
this.tags = videoDetails.tags
|
||||
this.nsfw = videoDetails.nsfw
|
||||
this.commentsEnabled = videoDetails.commentsEnabled
|
||||
this.channel = videoDetails.channel.id
|
||||
this.privacy = videoDetails.privacy
|
||||
}
|
||||
|
@ -45,6 +47,7 @@ export class VideoEdit {
|
|||
name: this.name,
|
||||
tags: this.tags,
|
||||
nsfw: this.nsfw,
|
||||
commentsEnabled: this.commentsEnabled,
|
||||
channelId: this.channel,
|
||||
privacy: this.privacy
|
||||
}
|
||||
|
|
|
@ -55,7 +55,8 @@ export class VideoService {
|
|||
description,
|
||||
privacy: video.privacy,
|
||||
tags: video.tags,
|
||||
nsfw: video.nsfw
|
||||
nsfw: video.nsfw,
|
||||
commentsEnabled: video.commentsEnabled
|
||||
}
|
||||
|
||||
return this.authHttp.put(VideoService.BASE_VIDEO_URL + video.id, body)
|
||||
|
|
|
@ -99,5 +99,11 @@
|
|||
<label for="nsfw">This video contains mature or explicit content</label>
|
||||
</div>
|
||||
|
||||
<div class="form-group form-group-checkbox">
|
||||
<input type="checkbox" id="commentsEnabled" formControlName="commentsEnabled" />
|
||||
<label for="commentsEnabled"></label>
|
||||
<label for="commentsEnabled">Enable video comments</label>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -70,6 +70,7 @@ export class VideoEditComponent implements OnInit {
|
|||
this.form.addControl('privacy', new FormControl('', VIDEO_PRIVACY.VALIDATORS))
|
||||
this.form.addControl('channelId', new FormControl({ value: '', disabled: true }))
|
||||
this.form.addControl('nsfw', new FormControl(false))
|
||||
this.form.addControl('commentsEnabled', new FormControl(true))
|
||||
this.form.addControl('category', new FormControl('', VIDEO_CATEGORY.VALIDATORS))
|
||||
this.form.addControl('licence', new FormControl('', VIDEO_LICENCE.VALIDATORS))
|
||||
this.form.addControl('language', new FormControl('', VIDEO_LANGUAGE.VALIDATORS))
|
||||
|
|
|
@ -88,6 +88,7 @@ export class VideoAddComponent extends FormReactive implements OnInit {
|
|||
const name = videofile.name.replace(/\.[^/.]+$/, '')
|
||||
const privacy = this.firstStepPrivacyId.toString()
|
||||
const nsfw = false
|
||||
const commentsEnabled = true
|
||||
const channelId = this.firstStepChannelId.toString()
|
||||
|
||||
const formData = new FormData()
|
||||
|
@ -95,6 +96,7 @@ export class VideoAddComponent extends FormReactive implements OnInit {
|
|||
// Put the video "private" -> we wait he validates the second step
|
||||
formData.append('privacy', VideoPrivacy.PRIVATE.toString())
|
||||
formData.append('nsfw', '' + nsfw)
|
||||
formData.append('commentsEnabled', '' + commentsEnabled)
|
||||
formData.append('channelId', '' + channelId)
|
||||
formData.append('videofile', videofile)
|
||||
|
||||
|
|
|
@ -3,12 +3,15 @@
|
|||
Comments
|
||||
</div>
|
||||
|
||||
<ng-template [ngIf]="video.commentsEnabled === true">
|
||||
<my-video-comment-add
|
||||
*ngIf="isUserLoggedIn()"
|
||||
[video]="video"
|
||||
(commentCreated)="onCommentThreadCreated($event)"
|
||||
></my-video-comment-add>
|
||||
|
||||
<div *ngIf="componentPagination.totalItems === 0 && comments.length === 0">No comments.</div>
|
||||
|
||||
<div
|
||||
class="comment-threads"
|
||||
infiniteScroll
|
||||
|
@ -34,4 +37,9 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<div *ngIf="video.commentsEnabled === false">
|
||||
Comments are disabled.
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -5,6 +5,7 @@ import { AuthService } from '../../../core/auth'
|
|||
import { ComponentPagination } from '../../../shared/rest/component-pagination.model'
|
||||
import { User } from '../../../shared/users'
|
||||
import { SortField } from '../../../shared/video/sort-field.type'
|
||||
import { VideoDetails } from '../../../shared/video/video-details.model'
|
||||
import { Video } from '../../../shared/video/video.model'
|
||||
import { VideoComment } from './video-comment.model'
|
||||
import { VideoCommentService } from './video-comment.service'
|
||||
|
@ -15,7 +16,7 @@ import { VideoCommentService } from './video-comment.service'
|
|||
styleUrls: ['./video-comments.component.scss']
|
||||
})
|
||||
export class VideoCommentsComponent implements OnInit {
|
||||
@Input() video: Video
|
||||
@Input() video: VideoDetails
|
||||
@Input() user: User
|
||||
|
||||
comments: VideoComment[] = []
|
||||
|
@ -36,8 +37,10 @@ export class VideoCommentsComponent implements OnInit {
|
|||
) {}
|
||||
|
||||
ngOnInit () {
|
||||
if (this.video.commentsEnabled === true) {
|
||||
this.loadMoreComments()
|
||||
}
|
||||
}
|
||||
|
||||
viewReplies (comment: VideoComment) {
|
||||
this.threadLoading[comment.id] = true
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import * as express from 'express'
|
||||
import { ResultList } from '../../../../shared/models'
|
||||
import { VideoCommentCreate } from '../../../../shared/models/videos/video-comment.model'
|
||||
import { retryTransactionWrapper } from '../../../helpers/database-utils'
|
||||
import { getFormattedObjects } from '../../../helpers/utils'
|
||||
|
@ -10,6 +11,7 @@ import {
|
|||
addVideoCommentReplyValidator, addVideoCommentThreadValidator, listVideoCommentThreadsValidator,
|
||||
listVideoThreadCommentsValidator
|
||||
} from '../../../middlewares/validators/video-comments'
|
||||
import { VideoModel } from '../../../models/video/video'
|
||||
import { VideoCommentModel } from '../../../models/video/video-comment'
|
||||
|
||||
const videoCommentRouter = express.Router()
|
||||
|
@ -47,13 +49,33 @@ export {
|
|||
// ---------------------------------------------------------------------------
|
||||
|
||||
async function listVideoThreads (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||
const resultList = await VideoCommentModel.listThreadsForApi(res.locals.video.id, req.query.start, req.query.count, req.query.sort)
|
||||
const video = res.locals.video as VideoModel
|
||||
let resultList: ResultList<VideoCommentModel>
|
||||
|
||||
if (video.commentsEnabled === true) {
|
||||
resultList = await VideoCommentModel.listThreadsForApi(video.id, req.query.start, req.query.count, req.query.sort)
|
||||
} else {
|
||||
resultList = {
|
||||
total: 0,
|
||||
data: []
|
||||
}
|
||||
}
|
||||
|
||||
return res.json(getFormattedObjects(resultList.data, resultList.total))
|
||||
}
|
||||
|
||||
async function listVideoThreadComments (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||
const resultList = await VideoCommentModel.listThreadCommentsForApi(res.locals.video.id, res.locals.videoCommentThread.id)
|
||||
const video = res.locals.video as VideoModel
|
||||
let resultList: ResultList<VideoCommentModel>
|
||||
|
||||
if (video.commentsEnabled === true) {
|
||||
resultList = await VideoCommentModel.listThreadCommentsForApi(res.locals.video.id, res.locals.videoCommentThread.id)
|
||||
} else {
|
||||
resultList = {
|
||||
total: 0,
|
||||
data: []
|
||||
}
|
||||
}
|
||||
|
||||
return res.json(buildFormattedCommentTree(resultList))
|
||||
}
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
import * as express from 'express'
|
||||
import * as multer from 'multer'
|
||||
import { extname, join } from 'path'
|
||||
import { VideoCreate, VideoPrivacy, VideoUpdate } from '../../../../shared'
|
||||
import { renamePromise } from '../../../helpers/core-utils'
|
||||
import { retryTransactionWrapper } from '../../../helpers/database-utils'
|
||||
import { getVideoFileHeight } from '../../../helpers/ffmpeg-utils'
|
||||
import { logger } from '../../../helpers/logger'
|
||||
import { createReqFiles, generateRandomString, getFormattedObjects, getServerActor, resetSequelizeInstance } from '../../../helpers/utils'
|
||||
import { createReqFiles, getFormattedObjects, getServerActor, resetSequelizeInstance } from '../../../helpers/utils'
|
||||
import {
|
||||
CONFIG, sequelizeTypescript, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_MIMETYPE_EXT,
|
||||
VIDEO_PRIVACIES
|
||||
|
@ -141,6 +140,7 @@ async function addVideo (req: express.Request, res: express.Response, videoPhysi
|
|||
category: videoInfo.category,
|
||||
licence: videoInfo.licence,
|
||||
language: videoInfo.language,
|
||||
commentsEnabled: videoInfo.commentsEnabled,
|
||||
nsfw: videoInfo.nsfw,
|
||||
description: videoInfo.description,
|
||||
privacy: videoInfo.privacy,
|
||||
|
@ -248,6 +248,7 @@ async function updateVideo (req: express.Request, res: express.Response) {
|
|||
if (videoInfoToUpdate.nsfw !== undefined) videoInstance.set('nsfw', videoInfoToUpdate.nsfw)
|
||||
if (videoInfoToUpdate.privacy !== undefined) videoInstance.set('privacy', parseInt(videoInfoToUpdate.privacy.toString(), 10))
|
||||
if (videoInfoToUpdate.description !== undefined) videoInstance.set('description', videoInfoToUpdate.description)
|
||||
if (videoInfoToUpdate.commentsEnabled !== undefined) videoInstance.set('commentsEnabled', videoInfoToUpdate.commentsEnabled)
|
||||
|
||||
const videoInstanceUpdated = await videoInstance.save(sequelizeOptions)
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@ import { isAnnounceActivityValid } from './announce'
|
|||
import { isActivityPubUrlValid } from './misc'
|
||||
import { isDislikeActivityValid, isLikeActivityValid } from './rate'
|
||||
import { isUndoActivityValid } from './undo'
|
||||
import { isVideoChannelDeleteActivityValid, isVideoChannelUpdateActivityValid } from './video-channels'
|
||||
import { isVideoCommentCreateActivityValid } from './video-comments'
|
||||
import {
|
||||
isVideoFlagValid,
|
||||
|
@ -65,13 +64,11 @@ function checkCreateActivity (activity: any) {
|
|||
}
|
||||
|
||||
function checkUpdateActivity (activity: any) {
|
||||
return isVideoTorrentUpdateActivityValid(activity) ||
|
||||
isVideoChannelUpdateActivityValid(activity)
|
||||
return isVideoTorrentUpdateActivityValid(activity)
|
||||
}
|
||||
|
||||
function checkDeleteActivity (activity: any) {
|
||||
return isVideoTorrentDeleteActivityValid(activity) ||
|
||||
isVideoChannelDeleteActivityValid(activity) ||
|
||||
isActorDeleteActivityValid(activity)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
import { isDateValid, isUUIDValid } from '../misc'
|
||||
import { isVideoChannelDescriptionValid, isVideoChannelNameValid } from '../video-channels'
|
||||
import { isActivityPubUrlValid, isBaseActivityValid } from './misc'
|
||||
|
||||
function isVideoChannelUpdateActivityValid (activity: any) {
|
||||
return isBaseActivityValid(activity, 'Update') &&
|
||||
isVideoChannelObjectValid(activity.object)
|
||||
}
|
||||
|
||||
function isVideoChannelDeleteActivityValid (activity: any) {
|
||||
return isBaseActivityValid(activity, 'Delete')
|
||||
}
|
||||
|
||||
function isVideoChannelObjectValid (videoChannel: any) {
|
||||
return videoChannel.type === 'VideoChannel' &&
|
||||
isActivityPubUrlValid(videoChannel.id) &&
|
||||
isVideoChannelNameValid(videoChannel.name) &&
|
||||
isVideoChannelDescriptionValid(videoChannel.content) &&
|
||||
isDateValid(videoChannel.published) &&
|
||||
isDateValid(videoChannel.updated) &&
|
||||
isUUIDValid(videoChannel.uuid)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export {
|
||||
isVideoChannelUpdateActivityValid,
|
||||
isVideoChannelDeleteActivityValid,
|
||||
isVideoChannelObjectValid
|
||||
}
|
|
@ -1,11 +1,10 @@
|
|||
import * as validator from 'validator'
|
||||
import { ACTIVITY_PUB } from '../../../initializers'
|
||||
import { exists, isDateValid, isUUIDValid } from '../misc'
|
||||
import { exists, isBooleanValid, isDateValid, isUUIDValid } from '../misc'
|
||||
import {
|
||||
isVideoAbuseReasonValid,
|
||||
isVideoDurationValid,
|
||||
isVideoNameValid,
|
||||
isVideoNSFWValid,
|
||||
isVideoTagValid,
|
||||
isVideoTruncatedDescriptionValid,
|
||||
isVideoViewsValid
|
||||
|
@ -53,7 +52,8 @@ function isVideoTorrentObjectValid (video: any) {
|
|||
(!video.licence || isRemoteIdentifierValid(video.licence)) &&
|
||||
(!video.language || isRemoteIdentifierValid(video.language)) &&
|
||||
isVideoViewsValid(video.views) &&
|
||||
isVideoNSFWValid(video.nsfw) &&
|
||||
isBooleanValid(video.nsfw) &&
|
||||
isBooleanValid(video.commentsEnabled) &&
|
||||
isDateValid(video.published) &&
|
||||
isDateValid(video.updated) &&
|
||||
(!video.content || isRemoteVideoContentValid(video.mediaType, video.content)) &&
|
||||
|
|
|
@ -24,6 +24,10 @@ function isIdOrUUIDValid (value: string) {
|
|||
return isIdValid(value) || isUUIDValid(value)
|
||||
}
|
||||
|
||||
function isBooleanValid (value: string) {
|
||||
return typeof value === 'boolean' || (typeof value === 'string' && validator.isBoolean(value))
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export {
|
||||
|
@ -32,5 +36,6 @@ export {
|
|||
isIdValid,
|
||||
isUUIDValid,
|
||||
isIdOrUUIDValid,
|
||||
isDateValid
|
||||
isDateValid,
|
||||
isBooleanValid
|
||||
}
|
||||
|
|
|
@ -30,10 +30,6 @@ function isVideoLanguageValid (value: number) {
|
|||
return value === null || VIDEO_LANGUAGES[value] !== undefined
|
||||
}
|
||||
|
||||
function isVideoNSFWValid (value: any) {
|
||||
return typeof value === 'boolean' || (typeof value === 'string' && validator.isBoolean(value))
|
||||
}
|
||||
|
||||
function isVideoDurationValid (value: string) {
|
||||
return exists(value) && validator.isInt(value + '', VIDEOS_CONSTRAINTS_FIELDS.DURATION)
|
||||
}
|
||||
|
@ -131,7 +127,6 @@ export {
|
|||
isVideoCategoryValid,
|
||||
isVideoLicenceValid,
|
||||
isVideoLanguageValid,
|
||||
isVideoNSFWValid,
|
||||
isVideoTruncatedDescriptionValid,
|
||||
isVideoDescriptionValid,
|
||||
isVideoFileInfoHashValid,
|
||||
|
|
|
@ -3,7 +3,7 @@ import * as multer from 'multer'
|
|||
import { Model } from 'sequelize-typescript'
|
||||
import { ResultList } from '../../shared'
|
||||
import { VideoResolution } from '../../shared/models/videos'
|
||||
import { CONFIG, REMOTE_SCHEME, VIDEO_MIMETYPE_EXT } from '../initializers'
|
||||
import { CONFIG, REMOTE_SCHEME } from '../initializers'
|
||||
import { UserModel } from '../models/account/user'
|
||||
import { ActorModel } from '../models/activitypub/actor'
|
||||
import { ApplicationModel } from '../models/application/application'
|
||||
|
|
|
@ -9,7 +9,7 @@ import { isTestInstance, root, sanitizeHost, sanitizeUrl } from '../helpers/core
|
|||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const LAST_MIGRATION_VERSION = 150
|
||||
const LAST_MIGRATION_VERSION = 155
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
import * as Sequelize from 'sequelize'
|
||||
|
||||
async function up (utils: {
|
||||
transaction: Sequelize.Transaction,
|
||||
queryInterface: Sequelize.QueryInterface,
|
||||
sequelize: Sequelize.Sequelize
|
||||
}): Promise<void> {
|
||||
const data = {
|
||||
type: Sequelize.BOOLEAN,
|
||||
allowNull: false,
|
||||
defaultValue: true
|
||||
}
|
||||
await utils.queryInterface.addColumn('video', 'commentsEnabled', data)
|
||||
|
||||
data.defaultValue = null
|
||||
return utils.queryInterface.changeColumn('video', 'commentsEnabled', data)
|
||||
}
|
||||
|
||||
function down (options) {
|
||||
throw new Error('Not implemented.')
|
||||
}
|
||||
|
||||
export {
|
||||
up,
|
||||
down
|
||||
}
|
|
@ -53,6 +53,7 @@ async function videoActivityObjectToDBAttributes (
|
|||
language,
|
||||
description,
|
||||
nsfw: videoObject.nsfw,
|
||||
commentsEnabled: videoObject.commentsEnabled,
|
||||
channelId: videoChannel.id,
|
||||
duration: parseInt(duration, 10),
|
||||
createdAt: new Date(videoObject.published),
|
||||
|
|
|
@ -3,11 +3,10 @@ import 'express-validator'
|
|||
import { body, param } from 'express-validator/check'
|
||||
import { isIdOrUUIDValid } from '../../helpers/custom-validators/misc'
|
||||
import {
|
||||
isAvatarFile,
|
||||
isUserAutoPlayVideoValid, isUserDisplayNSFWValid, isUserPasswordValid, isUserRoleValid, isUserUsernameValid,
|
||||
isAvatarFile, isUserAutoPlayVideoValid, isUserDisplayNSFWValid, isUserPasswordValid, isUserRoleValid, isUserUsernameValid,
|
||||
isUserVideoQuotaValid
|
||||
} from '../../helpers/custom-validators/users'
|
||||
import { isVideoExist, isVideoFile } from '../../helpers/custom-validators/videos'
|
||||
import { isVideoExist } from '../../helpers/custom-validators/videos'
|
||||
import { logger } from '../../helpers/logger'
|
||||
import { isSignupAllowed } from '../../helpers/utils'
|
||||
import { CONSTRAINTS_FIELDS } from '../../initializers'
|
||||
|
|
|
@ -45,6 +45,7 @@ const addVideoCommentThreadValidator = [
|
|||
|
||||
if (areValidationErrors(req, res)) return
|
||||
if (!await isVideoExist(req.params.videoId, res)) return
|
||||
if (!isVideoCommentsEnabled(res.locals.video, res)) return
|
||||
|
||||
return next()
|
||||
}
|
||||
|
@ -60,6 +61,7 @@ const addVideoCommentReplyValidator = [
|
|||
|
||||
if (areValidationErrors(req, res)) return
|
||||
if (!await isVideoExist(req.params.videoId, res)) return
|
||||
if (!isVideoCommentsEnabled(res.locals.video, res)) return
|
||||
if (!await isVideoCommentExist(req.params.commentId, res.locals.video, res)) return
|
||||
|
||||
return next()
|
||||
|
@ -146,3 +148,15 @@ async function isVideoCommentExist (id: number, video: VideoModel, res: express.
|
|||
res.locals.videoComment = videoComment
|
||||
return true
|
||||
}
|
||||
|
||||
function isVideoCommentsEnabled (video: VideoModel, res: express.Response) {
|
||||
if (video.commentsEnabled !== true) {
|
||||
res.status(409)
|
||||
.json({ error: 'Video comments are disabled for this video.' })
|
||||
.end()
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -2,10 +2,10 @@ import * as express from 'express'
|
|||
import 'express-validator'
|
||||
import { body, param, query } from 'express-validator/check'
|
||||
import { UserRight, VideoPrivacy } from '../../../shared'
|
||||
import { isIdOrUUIDValid, isIdValid } from '../../helpers/custom-validators/misc'
|
||||
import { isBooleanValid, isIdOrUUIDValid, isIdValid } from '../../helpers/custom-validators/misc'
|
||||
import {
|
||||
isVideoAbuseReasonValid, isVideoCategoryValid, isVideoDescriptionValid, isVideoExist, isVideoFile, isVideoLanguageValid,
|
||||
isVideoLicenceValid, isVideoNameValid, isVideoNSFWValid, isVideoPrivacyValid, isVideoRatingTypeValid, isVideoTagsValid
|
||||
isVideoLicenceValid, isVideoNameValid, isVideoPrivacyValid, isVideoRatingTypeValid, isVideoTagsValid
|
||||
} from '../../helpers/custom-validators/videos'
|
||||
import { getDurationFromVideoFile } from '../../helpers/ffmpeg-utils'
|
||||
import { logger } from '../../helpers/logger'
|
||||
|
@ -26,11 +26,12 @@ const videosAddValidator = [
|
|||
body('category').optional().custom(isVideoCategoryValid).withMessage('Should have a valid category'),
|
||||
body('licence').optional().custom(isVideoLicenceValid).withMessage('Should have a valid licence'),
|
||||
body('language').optional().custom(isVideoLanguageValid).withMessage('Should have a valid language'),
|
||||
body('nsfw').custom(isVideoNSFWValid).withMessage('Should have a valid NSFW attribute'),
|
||||
body('nsfw').custom(isBooleanValid).withMessage('Should have a valid NSFW attribute'),
|
||||
body('description').optional().custom(isVideoDescriptionValid).withMessage('Should have a valid description'),
|
||||
body('channelId').custom(isIdValid).withMessage('Should have correct video channel id'),
|
||||
body('privacy').custom(isVideoPrivacyValid).withMessage('Should have correct video privacy'),
|
||||
body('tags').optional().custom(isVideoTagsValid).withMessage('Should have correct tags'),
|
||||
body('commentsEnabled').custom(isBooleanValid).withMessage('Should have comments enabled boolean'),
|
||||
|
||||
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||
logger.debug('Checking videosAdd parameters', { parameters: req.body, files: req.files })
|
||||
|
@ -85,10 +86,11 @@ const videosUpdateValidator = [
|
|||
body('category').optional().custom(isVideoCategoryValid).withMessage('Should have a valid category'),
|
||||
body('licence').optional().custom(isVideoLicenceValid).withMessage('Should have a valid licence'),
|
||||
body('language').optional().custom(isVideoLanguageValid).withMessage('Should have a valid language'),
|
||||
body('nsfw').optional().custom(isVideoNSFWValid).withMessage('Should have a valid NSFW attribute'),
|
||||
body('nsfw').optional().custom(isBooleanValid).withMessage('Should have a valid NSFW attribute'),
|
||||
body('privacy').optional().custom(isVideoPrivacyValid).withMessage('Should have correct video privacy'),
|
||||
body('description').optional().custom(isVideoDescriptionValid).withMessage('Should have a valid description'),
|
||||
body('tags').optional().custom(isVideoTagsValid).withMessage('Should have correct tags'),
|
||||
body('commentsEnabled').optional().custom(isBooleanValid).withMessage('Should have comments enabled boolean'),
|
||||
|
||||
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||
logger.debug('Checking videosUpdate parameters', { parameters: req.body })
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { values } from 'lodash'
|
||||
import { extname, join } from 'path'
|
||||
import { extname } from 'path'
|
||||
import * as Sequelize from 'sequelize'
|
||||
import {
|
||||
AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, DefaultScope, ForeignKey, HasMany, HasOne, Is, IsUUID, Model, Scopes,
|
||||
|
@ -13,7 +13,7 @@ import {
|
|||
isActorPublicKeyValid
|
||||
} from '../../helpers/custom-validators/activitypub/actor'
|
||||
import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
|
||||
import { ACTIVITY_PUB_ACTOR_TYPES, AVATARS_DIR, CONFIG, CONSTRAINTS_FIELDS } from '../../initializers'
|
||||
import { ACTIVITY_PUB_ACTOR_TYPES, CONFIG, CONSTRAINTS_FIELDS } from '../../initializers'
|
||||
import { AccountModel } from '../account/account'
|
||||
import { AvatarModel } from '../avatar/avatar'
|
||||
import { ServerModel } from '../server/server'
|
||||
|
|
|
@ -2,9 +2,7 @@ import { join } from 'path'
|
|||
import { AfterDestroy, AllowNull, Column, CreatedAt, Model, Table, UpdatedAt } from 'sequelize-typescript'
|
||||
import { Avatar } from '../../../shared/models/avatars/avatar.model'
|
||||
import { unlinkPromise } from '../../helpers/core-utils'
|
||||
import { logger } from '../../helpers/logger'
|
||||
import { CONFIG, STATIC_PATHS } from '../../initializers'
|
||||
import { sendDeleteVideo } from '../../lib/activitypub/send'
|
||||
|
||||
@Table({
|
||||
tableName: 'avatar'
|
||||
|
|
|
@ -15,9 +15,10 @@ import { Video, VideoDetails } from '../../../shared/models/videos'
|
|||
import { activityPubCollection } from '../../helpers/activitypub'
|
||||
import { createTorrentPromise, renamePromise, statPromise, unlinkPromise, writeFilePromise } from '../../helpers/core-utils'
|
||||
import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
|
||||
import { isBooleanValid } from '../../helpers/custom-validators/misc'
|
||||
import {
|
||||
isVideoCategoryValid, isVideoDescriptionValid, isVideoDurationValid, isVideoLanguageValid, isVideoLicenceValid, isVideoNameValid,
|
||||
isVideoNSFWValid, isVideoPrivacyValid
|
||||
isVideoPrivacyValid
|
||||
} from '../../helpers/custom-validators/videos'
|
||||
import { generateImageFromVideoFile, getVideoFileHeight, transcode } from '../../helpers/ffmpeg-utils'
|
||||
import { logger } from '../../helpers/logger'
|
||||
|
@ -185,7 +186,7 @@ export class VideoModel extends Model<VideoModel> {
|
|||
privacy: number
|
||||
|
||||
@AllowNull(false)
|
||||
@Is('VideoNSFW', value => throwIfNotValid(value, isVideoNSFWValid, 'NSFW boolean'))
|
||||
@Is('VideoNSFW', value => throwIfNotValid(value, isBooleanValid, 'NSFW boolean'))
|
||||
@Column
|
||||
nsfw: boolean
|
||||
|
||||
|
@ -230,6 +231,10 @@ export class VideoModel extends Model<VideoModel> {
|
|||
@Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEOS.URL.max))
|
||||
url: string
|
||||
|
||||
@AllowNull(false)
|
||||
@Column
|
||||
commentsEnabled: boolean
|
||||
|
||||
@CreatedAt
|
||||
createdAt: Date
|
||||
|
||||
|
@ -773,6 +778,7 @@ export class VideoModel extends Model<VideoModel> {
|
|||
channel: this.VideoChannel.toFormattedJSON(),
|
||||
account: this.VideoChannel.Account.toFormattedJSON(),
|
||||
tags: map<TagModel, string>(this.Tags, 'name'),
|
||||
commentsEnabled: this.commentsEnabled,
|
||||
files: []
|
||||
}
|
||||
|
||||
|
@ -920,6 +926,7 @@ export class VideoModel extends Model<VideoModel> {
|
|||
language,
|
||||
views: this.views,
|
||||
nsfw: this.nsfw,
|
||||
commentsEnabled: this.commentsEnabled,
|
||||
published: this.createdAt.toISOString(),
|
||||
updated: this.updatedAt.toISOString(),
|
||||
mediaType: 'text/markdown',
|
||||
|
|
|
@ -20,11 +20,11 @@ describe('Test activitypub', function () {
|
|||
})
|
||||
|
||||
it('Should return the account object', async function () {
|
||||
const res = await makeActivityPubGetRequest(server.url, '/account/root')
|
||||
const res = await makeActivityPubGetRequest(server.url, '/accounts/root')
|
||||
const object = res.body
|
||||
|
||||
expect(object.type).to.equal('Person')
|
||||
expect(object.id).to.equal('http://localhost:9001/account/root')
|
||||
expect(object.id).to.equal('http://localhost:9001/accounts/root')
|
||||
expect(object.name).to.equal('root')
|
||||
expect(object.preferredUsername).to.equal('root')
|
||||
})
|
||||
|
|
|
@ -2,14 +2,13 @@
|
|||
|
||||
import { omit } from 'lodash'
|
||||
import 'mocha'
|
||||
import { join } from "path"
|
||||
import { join } from 'path'
|
||||
import { UserRole } from '../../../../shared'
|
||||
|
||||
import {
|
||||
createUser, flushTests, getMyUserInformation, getMyUserVideoRating, getUsersList, immutableAssign, killallServers, makeGetRequest,
|
||||
makePostBodyRequest, makePostUploadRequest, makePutBodyRequest, registerUser, removeUser, runServer, ServerInfo, setAccessTokensToServers,
|
||||
updateUser,
|
||||
uploadVideo, userLogin
|
||||
updateUser, uploadVideo, userLogin
|
||||
} from '../../utils'
|
||||
import { checkBadCountPagination, checkBadSortPagination, checkBadStartPagination } from '../../utils/requests/check-api-params'
|
||||
|
||||
|
@ -25,7 +24,7 @@ describe('Test users API validators', function () {
|
|||
// ---------------------------------------------------------------
|
||||
|
||||
before(async function () {
|
||||
this.timeout(120000)
|
||||
this.timeout(20000)
|
||||
|
||||
await flushTests()
|
||||
|
||||
|
@ -282,7 +281,14 @@ describe('Test users API validators', function () {
|
|||
const attaches = {
|
||||
'avatarfile': join(__dirname, '..', 'fixtures', 'avatar.png')
|
||||
}
|
||||
await makePostUploadRequest({ url: server.url, path: path + '/me/avatar/pick', token: server.accessToken, fields, attaches })
|
||||
await makePostUploadRequest({
|
||||
url: server.url,
|
||||
path: path + '/me/avatar/pick',
|
||||
token: server.accessToken,
|
||||
fields,
|
||||
attaches,
|
||||
statusCodeExpected: 200
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/* tslint:disable:no-unused-expression */
|
||||
|
||||
import * as chai from 'chai'
|
||||
import 'mocha'
|
||||
import {
|
||||
flushTests, killallServers, makeGetRequest, makePostBodyRequest, runServer, ServerInfo, setAccessTokensToServers,
|
||||
|
@ -8,6 +9,8 @@ import {
|
|||
import { checkBadCountPagination, checkBadSortPagination, checkBadStartPagination } from '../../utils/requests/check-api-params'
|
||||
import { addVideoCommentThread } from '../../utils/videos/video-comments'
|
||||
|
||||
const expect = chai.expect
|
||||
|
||||
describe('Test video comments API validator', function () {
|
||||
let pathThread: string
|
||||
let pathComment: string
|
||||
|
@ -42,17 +45,14 @@ describe('Test video comments API validator', function () {
|
|||
describe('When listing video comment threads', function () {
|
||||
it('Should fail with a bad start pagination', async function () {
|
||||
await checkBadStartPagination(server.url, pathThread, server.accessToken)
|
||||
|
||||
})
|
||||
|
||||
it('Should fail with a bad count pagination', async function () {
|
||||
await checkBadCountPagination(server.url, pathThread, server.accessToken)
|
||||
|
||||
})
|
||||
|
||||
it('Should fail with an incorrect sort', async function () {
|
||||
await checkBadSortPagination(server.url, pathThread, server.accessToken)
|
||||
|
||||
})
|
||||
|
||||
it('Should fail with an incorrect video', async function () {
|
||||
|
@ -185,6 +185,35 @@ describe('Test video comments API validator', function () {
|
|||
})
|
||||
})
|
||||
|
||||
describe('When a video has comments disabled', function () {
|
||||
before(async function () {
|
||||
const res = await uploadVideo(server.url, server.accessToken, { commentsEnabled: false })
|
||||
videoUUID = res.body.video.uuid
|
||||
pathThread = '/api/v1/videos/' + videoUUID + '/comment-threads'
|
||||
})
|
||||
|
||||
it('Should return an empty thread list', async function () {
|
||||
const res = await makeGetRequest({
|
||||
url: server.url,
|
||||
path: pathThread,
|
||||
statusCodeExpected: 200
|
||||
})
|
||||
expect(res.body.total).to.equal(0)
|
||||
expect(res.body.data).to.have.lengthOf(0)
|
||||
})
|
||||
|
||||
it('Should return an thread comments list')
|
||||
|
||||
it('Should return conflict on thread add', async function () {
|
||||
const fields = {
|
||||
text: 'super comment'
|
||||
}
|
||||
await makePostBodyRequest({ url: server.url, path: pathThread, token: server.accessToken, fields, statusCodeExpected: 409 })
|
||||
})
|
||||
|
||||
it('Should return conflict on comment thread add')
|
||||
})
|
||||
|
||||
after(async function () {
|
||||
killallServers([ server ])
|
||||
|
||||
|
|
|
@ -100,6 +100,7 @@ describe('Test videos API validator', function () {
|
|||
licence: 1,
|
||||
language: 6,
|
||||
nsfw: false,
|
||||
commentsEnabled: true,
|
||||
description: 'my super description',
|
||||
tags: [ 'tag1', 'tag2' ],
|
||||
privacy: VideoPrivacy.PUBLIC,
|
||||
|
@ -162,6 +163,20 @@ describe('Test videos API validator', function () {
|
|||
await makePostUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
|
||||
})
|
||||
|
||||
it('Should fail without commentsEnabled attribute', async function () {
|
||||
const fields = omit(baseCorrectParams, 'commentsEnabled')
|
||||
const attaches = baseCorrectAttaches
|
||||
|
||||
await makePostUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
|
||||
})
|
||||
|
||||
it('Should fail with a bad commentsEnabled attribute', async function () {
|
||||
const fields = immutableAssign(baseCorrectParams, { commentsEnabled: 2 })
|
||||
const attaches = baseCorrectAttaches
|
||||
|
||||
await makePostUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
|
||||
})
|
||||
|
||||
it('Should fail with a long description', async function () {
|
||||
const fields = immutableAssign(baseCorrectParams, { description: 'super'.repeat(1500) })
|
||||
const attaches = baseCorrectAttaches
|
||||
|
@ -291,6 +306,7 @@ describe('Test videos API validator', function () {
|
|||
licence: 2,
|
||||
language: 6,
|
||||
nsfw: false,
|
||||
commentsEnabled: false,
|
||||
description: 'my super description',
|
||||
privacy: VideoPrivacy.PUBLIC,
|
||||
tags: [ 'tag1', 'tag2' ]
|
||||
|
@ -354,6 +370,12 @@ describe('Test videos API validator', function () {
|
|||
await makePutBodyRequest({ url: server.url, path: path + videoId, token: server.accessToken, fields })
|
||||
})
|
||||
|
||||
it('Should fail with a bad commentsEnabled attribute', async function () {
|
||||
const fields = immutableAssign(baseCorrectParams, { commentsEnabled: 2 })
|
||||
|
||||
await makePutBodyRequest({ url: server.url, path: path + videoId, token: server.accessToken, fields })
|
||||
})
|
||||
|
||||
it('Should fail with a long description', async function () {
|
||||
const fields = immutableAssign(baseCorrectParams, { description: 'super'.repeat(1500) })
|
||||
|
||||
|
|
|
@ -246,6 +246,7 @@ describe('Test follows', function () {
|
|||
host: 'localhost:9003',
|
||||
account: 'root',
|
||||
isLocal,
|
||||
commentsEnabled: true,
|
||||
duration: 5,
|
||||
tags: [ 'tag1', 'tag2', 'tag3' ],
|
||||
privacy: VideoPrivacy.PUBLIC,
|
||||
|
|
|
@ -93,6 +93,7 @@ describe('Test multiple servers', function () {
|
|||
duration: 10,
|
||||
tags: [ 'tag1p1', 'tag2p1' ],
|
||||
privacy: VideoPrivacy.PUBLIC,
|
||||
commentsEnabled: true,
|
||||
channel: {
|
||||
name: 'my channel',
|
||||
description: 'super channel',
|
||||
|
@ -155,6 +156,7 @@ describe('Test multiple servers', function () {
|
|||
host: 'localhost:9002',
|
||||
account: 'user1',
|
||||
isLocal,
|
||||
commentsEnabled: true,
|
||||
duration: 5,
|
||||
tags: [ 'tag1p2', 'tag2p2', 'tag3p2' ],
|
||||
privacy: VideoPrivacy.PUBLIC,
|
||||
|
@ -254,6 +256,7 @@ describe('Test multiple servers', function () {
|
|||
account: 'root',
|
||||
isLocal,
|
||||
duration: 5,
|
||||
commentsEnabled: true,
|
||||
tags: [ 'tag1p3' ],
|
||||
privacy: VideoPrivacy.PUBLIC,
|
||||
channel: {
|
||||
|
@ -280,6 +283,7 @@ describe('Test multiple servers', function () {
|
|||
description: 'my super description for server 3-2',
|
||||
host: 'localhost:9003',
|
||||
account: 'root',
|
||||
commentsEnabled: true,
|
||||
isLocal,
|
||||
duration: 5,
|
||||
tags: [ 'tag2p3', 'tag3p3', 'tag4p3' ],
|
||||
|
@ -545,6 +549,7 @@ describe('Test multiple servers', function () {
|
|||
account: 'root',
|
||||
isLocal,
|
||||
duration: 5,
|
||||
commentsEnabled: true,
|
||||
tags: [ 'tag_up_1', 'tag_up_2' ],
|
||||
privacy: VideoPrivacy.PUBLIC,
|
||||
channel: {
|
||||
|
@ -732,6 +737,26 @@ describe('Test multiple servers', function () {
|
|||
expect(secondChild.children).to.have.lengthOf(0)
|
||||
}
|
||||
})
|
||||
|
||||
it('Should disable comments', async function () {
|
||||
this.timeout(20000)
|
||||
|
||||
const attributes = {
|
||||
commentsEnabled: false
|
||||
}
|
||||
|
||||
await updateVideo(servers[0].url, servers[0].accessToken, videoUUID, attributes)
|
||||
|
||||
await wait(5000)
|
||||
|
||||
for (const server of servers) {
|
||||
const res = await getVideo(server.url, videoUUID)
|
||||
expect(res.body.commentsEnabled).to.be.false
|
||||
|
||||
const text = 'my super forbidden comment'
|
||||
await addVideoCommentThread(server.url, server.accessToken, videoUUID, text, 409)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('With minimum parameters', function () {
|
||||
|
@ -748,6 +773,7 @@ describe('Test multiple servers', function () {
|
|||
.field('privacy', '1')
|
||||
.field('nsfw', 'false')
|
||||
.field('channelId', '1')
|
||||
.field('commentsEnabled', 'true')
|
||||
|
||||
const filePath = join(__dirname, '..', '..', 'api', 'fixtures', 'video_short.webm')
|
||||
|
||||
|
@ -772,6 +798,7 @@ describe('Test multiple servers', function () {
|
|||
account: 'root',
|
||||
isLocal,
|
||||
duration: 5,
|
||||
commentsEnabled: true,
|
||||
tags: [ ],
|
||||
privacy: VideoPrivacy.PUBLIC,
|
||||
channel: {
|
||||
|
|
|
@ -32,6 +32,7 @@ describe('Test a single server', function () {
|
|||
duration: 5,
|
||||
tags: [ 'tag1', 'tag2', 'tag3' ],
|
||||
privacy: VideoPrivacy.PUBLIC,
|
||||
commentsEnabled: true,
|
||||
channel: {
|
||||
name: 'Default root channel',
|
||||
description: '',
|
||||
|
@ -51,7 +52,7 @@ describe('Test a single server', function () {
|
|||
category: 4,
|
||||
licence: 2,
|
||||
language: 5,
|
||||
nsfw: true,
|
||||
nsfw: false,
|
||||
description: 'my super description updated',
|
||||
host: 'localhost:9001',
|
||||
account: 'root',
|
||||
|
@ -59,6 +60,7 @@ describe('Test a single server', function () {
|
|||
tags: [ 'tagup1', 'tagup2' ],
|
||||
privacy: VideoPrivacy.PUBLIC,
|
||||
duration: 5,
|
||||
commentsEnabled: false,
|
||||
channel: {
|
||||
name: 'Default root channel',
|
||||
description: '',
|
||||
|
@ -475,6 +477,7 @@ describe('Test a single server', function () {
|
|||
language: 5,
|
||||
nsfw: false,
|
||||
description: 'my super description updated',
|
||||
commentsEnabled: false,
|
||||
tags: [ 'tagup1', 'tagup2' ]
|
||||
}
|
||||
await updateVideo(server.url, server.accessToken, videoId, attributes)
|
||||
|
|
|
@ -16,6 +16,7 @@ type VideoAttributes = {
|
|||
licence?: number
|
||||
language?: number
|
||||
nsfw?: boolean
|
||||
commentsEnabled?: boolean
|
||||
description?: string
|
||||
tags?: string[]
|
||||
channelId?: number
|
||||
|
@ -238,6 +239,7 @@ async function uploadVideo (url: string, accessToken: string, videoAttributesArg
|
|||
description: 'my super description',
|
||||
tags: [ 'tag' ],
|
||||
privacy: VideoPrivacy.PUBLIC,
|
||||
commentsEnabled: true,
|
||||
fixture: 'video_short.webm'
|
||||
}
|
||||
attributes = Object.assign(attributes, videoAttributesArg)
|
||||
|
@ -250,6 +252,7 @@ async function uploadVideo (url: string, accessToken: string, videoAttributesArg
|
|||
.field('category', attributes.category.toString())
|
||||
.field('licence', attributes.licence.toString())
|
||||
.field('nsfw', JSON.stringify(attributes.nsfw))
|
||||
.field('commentsEnabled', JSON.stringify(attributes.commentsEnabled))
|
||||
.field('description', attributes.description)
|
||||
.field('privacy', attributes.privacy.toString())
|
||||
.field('channelId', attributes.channelId)
|
||||
|
@ -273,7 +276,7 @@ async function uploadVideo (url: string, accessToken: string, videoAttributesArg
|
|||
.expect(specialStatus)
|
||||
}
|
||||
|
||||
function updateVideo (url: string, accessToken: string, id: number, attributes: VideoAttributes, specialStatus = 204) {
|
||||
function updateVideo (url: string, accessToken: string, id: number | string, attributes: VideoAttributes, specialStatus = 204) {
|
||||
const path = '/api/v1/videos/' + id
|
||||
const body = {}
|
||||
|
||||
|
@ -281,7 +284,8 @@ function updateVideo (url: string, accessToken: string, id: number, attributes:
|
|||
if (attributes.category) body['category'] = attributes.category
|
||||
if (attributes.licence) body['licence'] = attributes.licence
|
||||
if (attributes.language) body['language'] = attributes.language
|
||||
if (attributes.nsfw) body['nsfw'] = attributes.nsfw
|
||||
if (attributes.nsfw !== undefined) body['nsfw'] = JSON.stringify(attributes.nsfw)
|
||||
if (attributes.commentsEnabled !== undefined) body['commentsEnabled'] = JSON.stringify(attributes.commentsEnabled)
|
||||
if (attributes.description) body['description'] = attributes.description
|
||||
if (attributes.tags) body['tags'] = attributes.tags
|
||||
if (attributes.privacy) body['privacy'] = attributes.privacy
|
||||
|
@ -326,6 +330,7 @@ async function completeVideoCheck (
|
|||
licence: number
|
||||
language: number
|
||||
nsfw: boolean
|
||||
commentsEnabled: boolean
|
||||
description: string
|
||||
host: string
|
||||
account: string
|
||||
|
@ -376,6 +381,7 @@ async function completeVideoCheck (
|
|||
expect(videoDetails.privacy).to.deep.equal(attributes.privacy)
|
||||
expect(videoDetails.privacyLabel).to.deep.equal(VIDEO_PRIVACIES[attributes.privacy])
|
||||
expect(videoDetails.account.name).to.equal(attributes.account)
|
||||
expect(videoDetails.commentsEnabled).to.equal(attributes.commentsEnabled)
|
||||
|
||||
expect(videoDetails.channel.name).to.equal(attributes.channel.name)
|
||||
expect(videoDetails.channel.isLocal).to.equal(attributes.channel.isLocal)
|
||||
|
|
|
@ -18,6 +18,7 @@ export interface VideoTorrentObject {
|
|||
language: ActivityIdentifierObject
|
||||
views: number
|
||||
nsfw: boolean
|
||||
commentsEnabled: boolean
|
||||
published: string
|
||||
updated: string
|
||||
mediaType: 'text/markdown'
|
||||
|
|
|
@ -9,5 +9,6 @@ export interface VideoCreate {
|
|||
nsfw: boolean
|
||||
name: string
|
||||
tags?: string[]
|
||||
commentsEnabled?: boolean
|
||||
privacy: VideoPrivacy
|
||||
}
|
||||
|
|
|
@ -8,5 +8,6 @@ export interface VideoUpdate {
|
|||
description?: string
|
||||
privacy?: VideoPrivacy
|
||||
tags?: string[]
|
||||
commentsEnabled?: boolean
|
||||
nsfw?: boolean
|
||||
}
|
||||
|
|
|
@ -45,4 +45,5 @@ export interface VideoDetails extends Video {
|
|||
tags: string[]
|
||||
files: VideoFile[]
|
||||
account: Account
|
||||
commentsEnabled: boolean
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue