aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/middlewares
diff options
context:
space:
mode:
Diffstat (limited to 'server/middlewares')
-rw-r--r--server/middlewares/validators/config.ts14
-rw-r--r--server/middlewares/validators/shared/utils.ts1
-rw-r--r--server/middlewares/validators/shared/videos.ts26
-rw-r--r--server/middlewares/validators/videos/index.ts1
-rw-r--r--server/middlewares/validators/videos/video-captions.ts10
-rw-r--r--server/middlewares/validators/videos/video-comments.ts28
-rw-r--r--server/middlewares/validators/videos/video-editor.ts112
-rw-r--r--server/middlewares/validators/videos/video-ownership-changes.ts21
-rw-r--r--server/middlewares/validators/videos/video-playlists.ts4
-rw-r--r--server/middlewares/validators/videos/video-rates.ts7
-rw-r--r--server/middlewares/validators/videos/videos.ts40
11 files changed, 180 insertions, 84 deletions
diff --git a/server/middlewares/validators/config.ts b/server/middlewares/validators/config.ts
index 8b14feb3c..e87b2e39d 100644
--- a/server/middlewares/validators/config.ts
+++ b/server/middlewares/validators/config.ts
@@ -57,6 +57,8 @@ const customConfigUpdateValidator = [
57 body('transcoding.webtorrent.enabled').isBoolean().withMessage('Should have a valid webtorrent transcoding enabled boolean'), 57 body('transcoding.webtorrent.enabled').isBoolean().withMessage('Should have a valid webtorrent transcoding enabled boolean'),
58 body('transcoding.hls.enabled').isBoolean().withMessage('Should have a valid hls transcoding enabled boolean'), 58 body('transcoding.hls.enabled').isBoolean().withMessage('Should have a valid hls transcoding enabled boolean'),
59 59
60 body('videoEditor.enabled').isBoolean().withMessage('Should have a valid video editor enabled boolean'),
61
60 body('import.videos.concurrency').isInt({ min: 0 }).withMessage('Should have a valid import concurrency number'), 62 body('import.videos.concurrency').isInt({ min: 0 }).withMessage('Should have a valid import concurrency number'),
61 body('import.videos.http.enabled').isBoolean().withMessage('Should have a valid import video http enabled boolean'), 63 body('import.videos.http.enabled').isBoolean().withMessage('Should have a valid import video http enabled boolean'),
62 body('import.videos.torrent.enabled').isBoolean().withMessage('Should have a valid import video torrent enabled boolean'), 64 body('import.videos.torrent.enabled').isBoolean().withMessage('Should have a valid import video torrent enabled boolean'),
@@ -104,6 +106,7 @@ const customConfigUpdateValidator = [
104 if (!checkInvalidConfigIfEmailDisabled(req.body, res)) return 106 if (!checkInvalidConfigIfEmailDisabled(req.body, res)) return
105 if (!checkInvalidTranscodingConfig(req.body, res)) return 107 if (!checkInvalidTranscodingConfig(req.body, res)) return
106 if (!checkInvalidLiveConfig(req.body, res)) return 108 if (!checkInvalidLiveConfig(req.body, res)) return
109 if (!checkInvalidVideoEditorConfig(req.body, res)) return
107 110
108 return next() 111 return next()
109 } 112 }
@@ -159,3 +162,14 @@ function checkInvalidLiveConfig (customConfig: CustomConfig, res: express.Respon
159 162
160 return true 163 return true
161} 164}
165
166function checkInvalidVideoEditorConfig (customConfig: CustomConfig, res: express.Response) {
167 if (customConfig.videoEditor.enabled === false) return true
168
169 if (customConfig.videoEditor.enabled === true && customConfig.transcoding.enabled === false) {
170 res.fail({ message: 'You cannot enable video editor if transcoding is not enabled' })
171 return false
172 }
173
174 return true
175}
diff --git a/server/middlewares/validators/shared/utils.ts b/server/middlewares/validators/shared/utils.ts
index 104eace91..410de4d80 100644
--- a/server/middlewares/validators/shared/utils.ts
+++ b/server/middlewares/validators/shared/utils.ts
@@ -8,6 +8,7 @@ function areValidationErrors (req: express.Request, res: express.Response) {
8 8
9 if (!errors.isEmpty()) { 9 if (!errors.isEmpty()) {
10 logger.warn('Incorrect request parameters', { path: req.originalUrl, err: errors.mapped() }) 10 logger.warn('Incorrect request parameters', { path: req.originalUrl, err: errors.mapped() })
11
11 res.fail({ 12 res.fail({
12 message: 'Incorrect request parameters: ' + Object.keys(errors.mapped()).join(', '), 13 message: 'Incorrect request parameters: ' + Object.keys(errors.mapped()).join(', '),
13 instance: req.originalUrl, 14 instance: req.originalUrl,
diff --git a/server/middlewares/validators/shared/videos.ts b/server/middlewares/validators/shared/videos.ts
index fc978b63a..8807435f6 100644
--- a/server/middlewares/validators/shared/videos.ts
+++ b/server/middlewares/validators/shared/videos.ts
@@ -1,5 +1,6 @@
1import { Request, Response } from 'express' 1import { Request, Response } from 'express'
2import { loadVideo, VideoLoadType } from '@server/lib/model-loaders' 2import { loadVideo, VideoLoadType } from '@server/lib/model-loaders'
3import { isAbleToUploadVideo } from '@server/lib/user'
3import { authenticatePromiseIfNeeded } from '@server/middlewares/auth' 4import { authenticatePromiseIfNeeded } from '@server/middlewares/auth'
4import { VideoModel } from '@server/models/video/video' 5import { VideoModel } from '@server/models/video/video'
5import { VideoChannelModel } from '@server/models/video/video-channel' 6import { VideoChannelModel } from '@server/models/video/video-channel'
@@ -7,6 +8,7 @@ import { VideoFileModel } from '@server/models/video/video-file'
7import { 8import {
8 MUser, 9 MUser,
9 MUserAccountId, 10 MUserAccountId,
11 MUserId,
10 MVideo, 12 MVideo,
11 MVideoAccountLight, 13 MVideoAccountLight,
12 MVideoFormattableDetails, 14 MVideoFormattableDetails,
@@ -16,7 +18,7 @@ import {
16 MVideoThumbnail, 18 MVideoThumbnail,
17 MVideoWithRights 19 MVideoWithRights
18} from '@server/types/models' 20} from '@server/types/models'
19import { HttpStatusCode, UserRight } from '@shared/models' 21import { HttpStatusCode, ServerErrorCode, UserRight } from '@shared/models'
20 22
21async function doesVideoExist (id: number | string, res: Response, fetchType: VideoLoadType = 'all') { 23async function doesVideoExist (id: number | string, res: Response, fetchType: VideoLoadType = 'all') {
22 const userId = res.locals.oauth ? res.locals.oauth.token.User.id : undefined 24 const userId = res.locals.oauth ? res.locals.oauth.token.User.id : undefined
@@ -108,6 +110,11 @@ async function checkCanSeePrivateVideo (req: Request, res: Response, video: MVid
108 110
109 // Only the owner or a user that have blocklist rights can see the video 111 // Only the owner or a user that have blocklist rights can see the video
110 if (!user || !user.canGetVideo(video)) { 112 if (!user || !user.canGetVideo(video)) {
113 res.fail({
114 status: HttpStatusCode.FORBIDDEN_403,
115 message: 'Cannot fetch information of private/internal/blocklisted video'
116 })
117
111 return false 118 return false
112 } 119 }
113 120
@@ -139,13 +146,28 @@ function checkUserCanManageVideo (user: MUser, video: MVideoAccountLight, right:
139 return true 146 return true
140} 147}
141 148
149async function checkUserQuota (user: MUserId, videoFileSize: number, res: Response) {
150 if (await isAbleToUploadVideo(user.id, videoFileSize) === false) {
151 res.fail({
152 status: HttpStatusCode.PAYLOAD_TOO_LARGE_413,
153 message: 'The user video quota is exceeded with this video.',
154 type: ServerErrorCode.QUOTA_REACHED
155 })
156 return false
157 }
158
159 return true
160}
161
142// --------------------------------------------------------------------------- 162// ---------------------------------------------------------------------------
143 163
144export { 164export {
145 doesVideoChannelOfAccountExist, 165 doesVideoChannelOfAccountExist,
146 doesVideoExist, 166 doesVideoExist,
147 doesVideoFileOfVideoExist, 167 doesVideoFileOfVideoExist,
168
148 checkUserCanManageVideo, 169 checkUserCanManageVideo,
149 checkCanSeeVideoIfPrivate, 170 checkCanSeeVideoIfPrivate,
150 checkCanSeePrivateVideo 171 checkCanSeePrivateVideo,
172 checkUserQuota
151} 173}
diff --git a/server/middlewares/validators/videos/index.ts b/server/middlewares/validators/videos/index.ts
index f365d8ee1..faa082510 100644
--- a/server/middlewares/validators/videos/index.ts
+++ b/server/middlewares/validators/videos/index.ts
@@ -2,6 +2,7 @@ export * from './video-blacklist'
2export * from './video-captions' 2export * from './video-captions'
3export * from './video-channels' 3export * from './video-channels'
4export * from './video-comments' 4export * from './video-comments'
5export * from './video-editor'
5export * from './video-files' 6export * from './video-files'
6export * from './video-imports' 7export * from './video-imports'
7export * from './video-live' 8export * from './video-live'
diff --git a/server/middlewares/validators/videos/video-captions.ts b/server/middlewares/validators/videos/video-captions.ts
index a399871e1..441c6b4be 100644
--- a/server/middlewares/validators/videos/video-captions.ts
+++ b/server/middlewares/validators/videos/video-captions.ts
@@ -1,6 +1,6 @@
1import express from 'express' 1import express from 'express'
2import { body, param } from 'express-validator' 2import { body, param } from 'express-validator'
3import { HttpStatusCode, UserRight } from '@shared/models' 3import { UserRight } from '@shared/models'
4import { isVideoCaptionFile, isVideoCaptionLanguageValid } from '../../../helpers/custom-validators/video-captions' 4import { isVideoCaptionFile, isVideoCaptionLanguageValid } from '../../../helpers/custom-validators/video-captions'
5import { cleanUpReqFiles } from '../../../helpers/express-utils' 5import { cleanUpReqFiles } from '../../../helpers/express-utils'
6import { logger } from '../../../helpers/logger' 6import { logger } from '../../../helpers/logger'
@@ -74,13 +74,7 @@ const listVideoCaptionsValidator = [
74 if (!await doesVideoExist(req.params.videoId, res, 'only-video')) return 74 if (!await doesVideoExist(req.params.videoId, res, 'only-video')) return
75 75
76 const video = res.locals.onlyVideo 76 const video = res.locals.onlyVideo
77 77 if (!await checkCanSeeVideoIfPrivate(req, res, video)) return
78 if (!await checkCanSeeVideoIfPrivate(req, res, video)) {
79 return res.fail({
80 status: HttpStatusCode.FORBIDDEN_403,
81 message: 'Cannot list captions of private/internal/blocklisted video'
82 })
83 }
84 78
85 return next() 79 return next()
86 } 80 }
diff --git a/server/middlewares/validators/videos/video-comments.ts b/server/middlewares/validators/videos/video-comments.ts
index 91e85711d..698afdbd1 100644
--- a/server/middlewares/validators/videos/video-comments.ts
+++ b/server/middlewares/validators/videos/video-comments.ts
@@ -54,12 +54,7 @@ const listVideoCommentThreadsValidator = [
54 if (areValidationErrors(req, res)) return 54 if (areValidationErrors(req, res)) return
55 if (!await doesVideoExist(req.params.videoId, res, 'only-video')) return 55 if (!await doesVideoExist(req.params.videoId, res, 'only-video')) return
56 56
57 if (!await checkCanSeeVideoIfPrivate(req, res, res.locals.onlyVideo)) { 57 if (!await checkCanSeeVideoIfPrivate(req, res, res.locals.onlyVideo)) return
58 return res.fail({
59 status: HttpStatusCode.FORBIDDEN_403,
60 message: 'Cannot list comments of private/internal/blocklisted video'
61 })
62 }
63 58
64 return next() 59 return next()
65 } 60 }
@@ -78,12 +73,7 @@ const listVideoThreadCommentsValidator = [
78 if (!await doesVideoExist(req.params.videoId, res, 'only-video')) return 73 if (!await doesVideoExist(req.params.videoId, res, 'only-video')) return
79 if (!await doesVideoCommentThreadExist(req.params.threadId, res.locals.onlyVideo, res)) return 74 if (!await doesVideoCommentThreadExist(req.params.threadId, res.locals.onlyVideo, res)) return
80 75
81 if (!await checkCanSeeVideoIfPrivate(req, res, res.locals.onlyVideo)) { 76 if (!await checkCanSeeVideoIfPrivate(req, res, res.locals.onlyVideo)) return
82 return res.fail({
83 status: HttpStatusCode.FORBIDDEN_403,
84 message: 'Cannot list threads of private/internal/blocklisted video'
85 })
86 }
87 77
88 return next() 78 return next()
89 } 79 }
@@ -101,12 +91,7 @@ const addVideoCommentThreadValidator = [
101 if (areValidationErrors(req, res)) return 91 if (areValidationErrors(req, res)) return
102 if (!await doesVideoExist(req.params.videoId, res)) return 92 if (!await doesVideoExist(req.params.videoId, res)) return
103 93
104 if (!await checkCanSeeVideoIfPrivate(req, res, res.locals.videoAll)) { 94 if (!await checkCanSeeVideoIfPrivate(req, res, res.locals.videoAll)) return
105 return res.fail({
106 status: HttpStatusCode.FORBIDDEN_403,
107 message: 'Cannot access to this ressource'
108 })
109 }
110 95
111 if (!isVideoCommentsEnabled(res.locals.videoAll, res)) return 96 if (!isVideoCommentsEnabled(res.locals.videoAll, res)) return
112 if (!await isVideoCommentAccepted(req, res, res.locals.videoAll, false)) return 97 if (!await isVideoCommentAccepted(req, res, res.locals.videoAll, false)) return
@@ -128,12 +113,7 @@ const addVideoCommentReplyValidator = [
128 if (areValidationErrors(req, res)) return 113 if (areValidationErrors(req, res)) return
129 if (!await doesVideoExist(req.params.videoId, res)) return 114 if (!await doesVideoExist(req.params.videoId, res)) return
130 115
131 if (!await checkCanSeeVideoIfPrivate(req, res, res.locals.videoAll)) { 116 if (!await checkCanSeeVideoIfPrivate(req, res, res.locals.videoAll)) return
132 return res.fail({
133 status: HttpStatusCode.FORBIDDEN_403,
134 message: 'Cannot access to this ressource'
135 })
136 }
137 117
138 if (!isVideoCommentsEnabled(res.locals.videoAll, res)) return 118 if (!isVideoCommentsEnabled(res.locals.videoAll, res)) return
139 if (!await doesVideoCommentExist(req.params.commentId, res.locals.videoAll, res)) return 119 if (!await doesVideoCommentExist(req.params.commentId, res.locals.videoAll, res)) return
diff --git a/server/middlewares/validators/videos/video-editor.ts b/server/middlewares/validators/videos/video-editor.ts
new file mode 100644
index 000000000..9be97be93
--- /dev/null
+++ b/server/middlewares/validators/videos/video-editor.ts
@@ -0,0 +1,112 @@
1import express from 'express'
2import { body, param } from 'express-validator'
3import { isIdOrUUIDValid } from '@server/helpers/custom-validators/misc'
4import {
5 isEditorCutTaskValid,
6 isEditorTaskAddIntroOutroValid,
7 isEditorTaskAddWatermarkValid,
8 isValidEditorTasksArray
9} from '@server/helpers/custom-validators/video-editor'
10import { cleanUpReqFiles } from '@server/helpers/express-utils'
11import { CONFIG } from '@server/initializers/config'
12import { approximateIntroOutroAdditionalSize, getTaskFile } from '@server/lib/video-editor'
13import { isAudioFile } from '@shared/extra-utils'
14import { HttpStatusCode, UserRight, VideoEditorCreateEdition, VideoEditorTask, VideoState } from '@shared/models'
15import { logger } from '../../../helpers/logger'
16import { areValidationErrors, checkUserCanManageVideo, checkUserQuota, doesVideoExist } from '../shared'
17
18const videosEditorAddEditionValidator = [
19 param('videoId').custom(isIdOrUUIDValid).withMessage('Should have a valid video id/uuid'),
20
21 body('tasks').custom(isValidEditorTasksArray).withMessage('Should have a valid array of tasks'),
22
23 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
24 logger.debug('Checking videosEditorAddEditionValidator parameters.', { parameters: req.params, body: req.body, files: req.files })
25
26 if (CONFIG.VIDEO_EDITOR.ENABLED !== true) {
27 res.fail({
28 status: HttpStatusCode.BAD_REQUEST_400,
29 message: 'Video editor is disabled on this instance'
30 })
31
32 return cleanUpReqFiles(req)
33 }
34
35 if (areValidationErrors(req, res)) return cleanUpReqFiles(req)
36
37 const body: VideoEditorCreateEdition = req.body
38 const files = req.files as Express.Multer.File[]
39
40 for (let i = 0; i < body.tasks.length; i++) {
41 const task = body.tasks[i]
42
43 if (!checkTask(req, task, i)) {
44 res.fail({
45 status: HttpStatusCode.BAD_REQUEST_400,
46 message: `Task ${task.name} is invalid`
47 })
48
49 return cleanUpReqFiles(req)
50 }
51
52 if (task.name === 'add-intro' || task.name === 'add-outro') {
53 const filePath = getTaskFile(files, i).path
54
55 // Our concat filter needs a video stream
56 if (await isAudioFile(filePath)) {
57 res.fail({
58 status: HttpStatusCode.BAD_REQUEST_400,
59 message: `Task ${task.name} is invalid: file does not contain a video stream`
60 })
61
62 return cleanUpReqFiles(req)
63 }
64 }
65 }
66
67 if (!await doesVideoExist(req.params.videoId, res)) return cleanUpReqFiles(req)
68
69 const video = res.locals.videoAll
70 if (video.state === VideoState.TO_TRANSCODE || video.state === VideoState.TO_EDIT) {
71 res.fail({
72 status: HttpStatusCode.CONFLICT_409,
73 message: 'Cannot edit video that is already waiting for transcoding/edition'
74 })
75
76 return cleanUpReqFiles(req)
77 }
78
79 const user = res.locals.oauth.token.User
80 if (!checkUserCanManageVideo(user, video, UserRight.UPDATE_ANY_VIDEO, res)) return cleanUpReqFiles(req)
81
82 // Try to make an approximation of bytes added by the intro/outro
83 const additionalBytes = await approximateIntroOutroAdditionalSize(video, body.tasks, i => getTaskFile(files, i).path)
84 if (await checkUserQuota(user, additionalBytes, res) === false) return cleanUpReqFiles(req)
85
86 return next()
87 }
88]
89
90// ---------------------------------------------------------------------------
91
92export {
93 videosEditorAddEditionValidator
94}
95
96// ---------------------------------------------------------------------------
97
98const taskCheckers: {
99 [id in VideoEditorTask['name']]: (task: VideoEditorTask, indice?: number, files?: Express.Multer.File[]) => boolean
100} = {
101 'cut': isEditorCutTaskValid,
102 'add-intro': isEditorTaskAddIntroOutroValid,
103 'add-outro': isEditorTaskAddIntroOutroValid,
104 'add-watermark': isEditorTaskAddWatermarkValid
105}
106
107function checkTask (req: express.Request, task: VideoEditorTask, indice?: number) {
108 const checker = taskCheckers[task.name]
109 if (!checker) return false
110
111 return checker(task, indice, req.files as Express.Multer.File[])
112}
diff --git a/server/middlewares/validators/videos/video-ownership-changes.ts b/server/middlewares/validators/videos/video-ownership-changes.ts
index 95e4cebce..6dcdc05f5 100644
--- a/server/middlewares/validators/videos/video-ownership-changes.ts
+++ b/server/middlewares/validators/videos/video-ownership-changes.ts
@@ -3,20 +3,13 @@ import { param } from 'express-validator'
3import { isIdValid } from '@server/helpers/custom-validators/misc' 3import { isIdValid } from '@server/helpers/custom-validators/misc'
4import { checkUserCanTerminateOwnershipChange } from '@server/helpers/custom-validators/video-ownership' 4import { checkUserCanTerminateOwnershipChange } from '@server/helpers/custom-validators/video-ownership'
5import { logger } from '@server/helpers/logger' 5import { logger } from '@server/helpers/logger'
6import { isAbleToUploadVideo } from '@server/lib/user'
7import { AccountModel } from '@server/models/account/account' 6import { AccountModel } from '@server/models/account/account'
8import { MVideoWithAllFiles } from '@server/types/models' 7import { MVideoWithAllFiles } from '@server/types/models'
9import { 8import { HttpStatusCode, UserRight, VideoChangeOwnershipAccept, VideoChangeOwnershipStatus, VideoState } from '@shared/models'
10 HttpStatusCode,
11 ServerErrorCode,
12 UserRight,
13 VideoChangeOwnershipAccept,
14 VideoChangeOwnershipStatus,
15 VideoState
16} from '@shared/models'
17import { 9import {
18 areValidationErrors, 10 areValidationErrors,
19 checkUserCanManageVideo, 11 checkUserCanManageVideo,
12 checkUserQuota,
20 doesChangeVideoOwnershipExist, 13 doesChangeVideoOwnershipExist,
21 doesVideoChannelOfAccountExist, 14 doesVideoChannelOfAccountExist,
22 doesVideoExist, 15 doesVideoExist,
@@ -113,15 +106,7 @@ async function checkCanAccept (video: MVideoWithAllFiles, res: express.Response)
113 106
114 const user = res.locals.oauth.token.User 107 const user = res.locals.oauth.token.User
115 108
116 if (!await isAbleToUploadVideo(user.id, video.getMaxQualityFile().size)) { 109 if (!await checkUserQuota(user, video.getMaxQualityFile().size, res)) return false
117 res.fail({
118 status: HttpStatusCode.PAYLOAD_TOO_LARGE_413,
119 message: 'The user video quota is exceeded with this video.',
120 type: ServerErrorCode.QUOTA_REACHED
121 })
122
123 return false
124 }
125 110
126 return true 111 return true
127} 112}
diff --git a/server/middlewares/validators/videos/video-playlists.ts b/server/middlewares/validators/videos/video-playlists.ts
index f5fee845e..241b9ed7b 100644
--- a/server/middlewares/validators/videos/video-playlists.ts
+++ b/server/middlewares/validators/videos/video-playlists.ts
@@ -27,7 +27,7 @@ import {
27 isVideoPlaylistTimestampValid, 27 isVideoPlaylistTimestampValid,
28 isVideoPlaylistTypeValid 28 isVideoPlaylistTypeValid
29} from '../../../helpers/custom-validators/video-playlists' 29} from '../../../helpers/custom-validators/video-playlists'
30import { isVideoImage } from '../../../helpers/custom-validators/videos' 30import { isVideoImageValid } from '../../../helpers/custom-validators/videos'
31import { cleanUpReqFiles } from '../../../helpers/express-utils' 31import { cleanUpReqFiles } from '../../../helpers/express-utils'
32import { logger } from '../../../helpers/logger' 32import { logger } from '../../../helpers/logger'
33import { CONSTRAINTS_FIELDS } from '../../../initializers/constants' 33import { CONSTRAINTS_FIELDS } from '../../../initializers/constants'
@@ -390,7 +390,7 @@ export {
390function getCommonPlaylistEditAttributes () { 390function getCommonPlaylistEditAttributes () {
391 return [ 391 return [
392 body('thumbnailfile') 392 body('thumbnailfile')
393 .custom((value, { req }) => isVideoImage(req.files, 'thumbnailfile')) 393 .custom((value, { req }) => isVideoImageValid(req.files, 'thumbnailfile'))
394 .withMessage( 394 .withMessage(
395 'This thumbnail file is not supported or too large. Please, make sure it is of the following type: ' + 395 'This thumbnail file is not supported or too large. Please, make sure it is of the following type: ' +
396 CONSTRAINTS_FIELDS.VIDEO_PLAYLISTS.IMAGE.EXTNAME.join(', ') 396 CONSTRAINTS_FIELDS.VIDEO_PLAYLISTS.IMAGE.EXTNAME.join(', ')
diff --git a/server/middlewares/validators/videos/video-rates.ts b/server/middlewares/validators/videos/video-rates.ts
index 923bf3eaf..1a9736034 100644
--- a/server/middlewares/validators/videos/video-rates.ts
+++ b/server/middlewares/validators/videos/video-rates.ts
@@ -21,12 +21,7 @@ const videoUpdateRateValidator = [
21 if (areValidationErrors(req, res)) return 21 if (areValidationErrors(req, res)) return
22 if (!await doesVideoExist(req.params.id, res)) return 22 if (!await doesVideoExist(req.params.id, res)) return
23 23
24 if (!await checkCanSeeVideoIfPrivate(req, res, res.locals.videoAll)) { 24 if (!await checkCanSeeVideoIfPrivate(req, res, res.locals.videoAll)) return
25 return res.fail({
26 status: HttpStatusCode.FORBIDDEN_403,
27 message: 'Cannot access to this ressource'
28 })
29 }
30 25
31 return next() 26 return next()
32 } 27 }
diff --git a/server/middlewares/validators/videos/videos.ts b/server/middlewares/validators/videos/videos.ts
index b3ffb7007..26597cf7b 100644
--- a/server/middlewares/validators/videos/videos.ts
+++ b/server/middlewares/validators/videos/videos.ts
@@ -3,7 +3,6 @@ import { body, header, param, query, ValidationChain } from 'express-validator'
3import { isTestInstance } from '@server/helpers/core-utils' 3import { isTestInstance } from '@server/helpers/core-utils'
4import { getResumableUploadPath } from '@server/helpers/upload' 4import { getResumableUploadPath } from '@server/helpers/upload'
5import { Redis } from '@server/lib/redis' 5import { Redis } from '@server/lib/redis'
6import { isAbleToUploadVideo } from '@server/lib/user'
7import { getServerActor } from '@server/models/application/application' 6import { getServerActor } from '@server/models/application/application'
8import { ExpressPromiseHandler } from '@server/types/express-handler' 7import { ExpressPromiseHandler } from '@server/types/express-handler'
9import { MUserAccountId, MVideoFullLight } from '@server/types/models' 8import { MUserAccountId, MVideoFullLight } from '@server/types/models'
@@ -13,7 +12,7 @@ import {
13 exists, 12 exists,
14 isBooleanValid, 13 isBooleanValid,
15 isDateValid, 14 isDateValid,
16 isFileFieldValid, 15 isFileValid,
17 isIdValid, 16 isIdValid,
18 isUUIDValid, 17 isUUIDValid,
19 toArray, 18 toArray,
@@ -23,24 +22,24 @@ import {
23} from '../../../helpers/custom-validators/misc' 22} from '../../../helpers/custom-validators/misc'
24import { isBooleanBothQueryValid, isNumberArray, isStringArray } from '../../../helpers/custom-validators/search' 23import { isBooleanBothQueryValid, isNumberArray, isStringArray } from '../../../helpers/custom-validators/search'
25import { 24import {
25 areVideoTagsValid,
26 isScheduleVideoUpdatePrivacyValid, 26 isScheduleVideoUpdatePrivacyValid,
27 isVideoCategoryValid, 27 isVideoCategoryValid,
28 isVideoDescriptionValid, 28 isVideoDescriptionValid,
29 isVideoFileMimeTypeValid, 29 isVideoFileMimeTypeValid,
30 isVideoFileSizeValid, 30 isVideoFileSizeValid,
31 isVideoFilterValid, 31 isVideoFilterValid,
32 isVideoImage, 32 isVideoImageValid,
33 isVideoIncludeValid, 33 isVideoIncludeValid,
34 isVideoLanguageValid, 34 isVideoLanguageValid,
35 isVideoLicenceValid, 35 isVideoLicenceValid,
36 isVideoNameValid, 36 isVideoNameValid,
37 isVideoOriginallyPublishedAtValid, 37 isVideoOriginallyPublishedAtValid,
38 isVideoPrivacyValid, 38 isVideoPrivacyValid,
39 isVideoSupportValid, 39 isVideoSupportValid
40 isVideoTagsValid
41} from '../../../helpers/custom-validators/videos' 40} from '../../../helpers/custom-validators/videos'
42import { cleanUpReqFiles } from '../../../helpers/express-utils' 41import { cleanUpReqFiles } from '../../../helpers/express-utils'
43import { getDurationFromVideoFile } from '../../../helpers/ffprobe-utils' 42import { getVideoStreamDuration } from '../../../helpers/ffmpeg'
44import { logger } from '../../../helpers/logger' 43import { logger } from '../../../helpers/logger'
45import { deleteFileAndCatch } from '../../../helpers/utils' 44import { deleteFileAndCatch } from '../../../helpers/utils'
46import { getVideoWithAttributes } from '../../../helpers/video' 45import { getVideoWithAttributes } from '../../../helpers/video'
@@ -53,6 +52,7 @@ import {
53 areValidationErrors, 52 areValidationErrors,
54 checkCanSeePrivateVideo, 53 checkCanSeePrivateVideo,
55 checkUserCanManageVideo, 54 checkUserCanManageVideo,
55 checkUserQuota,
56 doesVideoChannelOfAccountExist, 56 doesVideoChannelOfAccountExist,
57 doesVideoExist, 57 doesVideoExist,
58 doesVideoFileOfVideoExist, 58 doesVideoFileOfVideoExist,
@@ -61,7 +61,7 @@ import {
61 61
62const videosAddLegacyValidator = getCommonVideoEditAttributes().concat([ 62const videosAddLegacyValidator = getCommonVideoEditAttributes().concat([
63 body('videofile') 63 body('videofile')
64 .custom((value, { req }) => isFileFieldValid(req.files, 'videofile')) 64 .custom((_, { req }) => isFileValid({ files: req.files, field: 'videofile', mimeTypeRegex: null, maxSize: null }))
65 .withMessage('Should have a file'), 65 .withMessage('Should have a file'),
66 body('name') 66 body('name')
67 .trim() 67 .trim()
@@ -299,12 +299,11 @@ const videosCustomGetValidator = (
299 299
300 // Video private or blacklisted 300 // Video private or blacklisted
301 if (video.requiresAuth()) { 301 if (video.requiresAuth()) {
302 if (await checkCanSeePrivateVideo(req, res, video, authenticateInQuery)) return next() 302 if (await checkCanSeePrivateVideo(req, res, video, authenticateInQuery)) {
303 return next()
304 }
303 305
304 return res.fail({ 306 return
305 status: HttpStatusCode.FORBIDDEN_403,
306 message: 'Cannot get this private/internal or blocklisted video'
307 })
308 } 307 }
309 308
310 // Video is public, anyone can access it 309 // Video is public, anyone can access it
@@ -375,12 +374,12 @@ const videosOverviewValidator = [
375function getCommonVideoEditAttributes () { 374function getCommonVideoEditAttributes () {
376 return [ 375 return [
377 body('thumbnailfile') 376 body('thumbnailfile')
378 .custom((value, { req }) => isVideoImage(req.files, 'thumbnailfile')).withMessage( 377 .custom((value, { req }) => isVideoImageValid(req.files, 'thumbnailfile')).withMessage(
379 'This thumbnail file is not supported or too large. Please, make sure it is of the following type: ' + 378 'This thumbnail file is not supported or too large. Please, make sure it is of the following type: ' +
380 CONSTRAINTS_FIELDS.VIDEOS.IMAGE.EXTNAME.join(', ') 379 CONSTRAINTS_FIELDS.VIDEOS.IMAGE.EXTNAME.join(', ')
381 ), 380 ),
382 body('previewfile') 381 body('previewfile')
383 .custom((value, { req }) => isVideoImage(req.files, 'previewfile')).withMessage( 382 .custom((value, { req }) => isVideoImageValid(req.files, 'previewfile')).withMessage(
384 'This preview file is not supported or too large. Please, make sure it is of the following type: ' + 383 'This preview file is not supported or too large. Please, make sure it is of the following type: ' +
385 CONSTRAINTS_FIELDS.VIDEOS.IMAGE.EXTNAME.join(', ') 384 CONSTRAINTS_FIELDS.VIDEOS.IMAGE.EXTNAME.join(', ')
386 ), 385 ),
@@ -420,7 +419,7 @@ function getCommonVideoEditAttributes () {
420 body('tags') 419 body('tags')
421 .optional() 420 .optional()
422 .customSanitizer(toValueOrNull) 421 .customSanitizer(toValueOrNull)
423 .custom(isVideoTagsValid) 422 .custom(areVideoTagsValid)
424 .withMessage( 423 .withMessage(
425 `Should have an array of up to ${CONSTRAINTS_FIELDS.VIDEOS.TAGS.max} tags between ` + 424 `Should have an array of up to ${CONSTRAINTS_FIELDS.VIDEOS.TAGS.max} tags between ` +
426 `${CONSTRAINTS_FIELDS.VIDEOS.TAG.min} and ${CONSTRAINTS_FIELDS.VIDEOS.TAG.max} characters each` 425 `${CONSTRAINTS_FIELDS.VIDEOS.TAG.min} and ${CONSTRAINTS_FIELDS.VIDEOS.TAG.max} characters each`
@@ -612,14 +611,7 @@ async function commonVideoChecksPass (parameters: {
612 return false 611 return false
613 } 612 }
614 613
615 if (await isAbleToUploadVideo(user.id, videoFileSize) === false) { 614 if (await checkUserQuota(user, videoFileSize, res) === false) return false
616 res.fail({
617 status: HttpStatusCode.PAYLOAD_TOO_LARGE_413,
618 message: 'The user video quota is exceeded with this video.',
619 type: ServerErrorCode.QUOTA_REACHED
620 })
621 return false
622 }
623 615
624 return true 616 return true
625} 617}
@@ -654,7 +646,7 @@ export async function isVideoAccepted (
654} 646}
655 647
656async function addDurationToVideo (videoFile: { path: string, duration?: number }) { 648async function addDurationToVideo (videoFile: { path: string, duration?: number }) {
657 const duration: number = await getDurationFromVideoFile(videoFile.path) 649 const duration: number = await getVideoStreamDuration(videoFile.path)
658 650
659 if (isNaN(duration)) throw new Error(`Couldn't get video duration`) 651 if (isNaN(duration)) throw new Error(`Couldn't get video duration`)
660 652