diff options
author | Chocobozzz <me@florianbigard.com> | 2022-02-11 10:51:33 +0100 |
---|---|---|
committer | Chocobozzz <chocobozzz@cpy.re> | 2022-02-28 10:42:19 +0100 |
commit | c729caf6cc34630877a0e5a1bda1719384cd0c8a (patch) | |
tree | 1d2e13722e518c73d2c9e6f0969615e29d51cf8c /server/middlewares | |
parent | a24bf4dc659cebb65d887862bf21d7a35e9ec791 (diff) | |
download | PeerTube-c729caf6cc34630877a0e5a1bda1719384cd0c8a.tar.gz PeerTube-c729caf6cc34630877a0e5a1bda1719384cd0c8a.tar.zst PeerTube-c729caf6cc34630877a0e5a1bda1719384cd0c8a.zip |
Add basic video editor support
Diffstat (limited to 'server/middlewares')
-rw-r--r-- | server/middlewares/validators/config.ts | 14 | ||||
-rw-r--r-- | server/middlewares/validators/shared/utils.ts | 1 | ||||
-rw-r--r-- | server/middlewares/validators/shared/videos.ts | 26 | ||||
-rw-r--r-- | server/middlewares/validators/videos/index.ts | 1 | ||||
-rw-r--r-- | server/middlewares/validators/videos/video-captions.ts | 10 | ||||
-rw-r--r-- | server/middlewares/validators/videos/video-comments.ts | 14 | ||||
-rw-r--r-- | server/middlewares/validators/videos/video-editor.ts | 112 | ||||
-rw-r--r-- | server/middlewares/validators/videos/video-ownership-changes.ts | 21 | ||||
-rw-r--r-- | server/middlewares/validators/videos/video-playlists.ts | 4 | ||||
-rw-r--r-- | server/middlewares/validators/videos/videos.ts | 40 |
10 files changed, 177 insertions, 66 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 | |||
166 | function 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 @@ | |||
1 | import { Request, Response } from 'express' | 1 | import { Request, Response } from 'express' |
2 | import { loadVideo, VideoLoadType } from '@server/lib/model-loaders' | 2 | import { loadVideo, VideoLoadType } from '@server/lib/model-loaders' |
3 | import { isAbleToUploadVideo } from '@server/lib/user' | ||
3 | import { authenticatePromiseIfNeeded } from '@server/middlewares/auth' | 4 | import { authenticatePromiseIfNeeded } from '@server/middlewares/auth' |
4 | import { VideoModel } from '@server/models/video/video' | 5 | import { VideoModel } from '@server/models/video/video' |
5 | import { VideoChannelModel } from '@server/models/video/video-channel' | 6 | import { VideoChannelModel } from '@server/models/video/video-channel' |
@@ -7,6 +8,7 @@ import { VideoFileModel } from '@server/models/video/video-file' | |||
7 | import { | 8 | import { |
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' |
19 | import { HttpStatusCode, UserRight } from '@shared/models' | 21 | import { HttpStatusCode, ServerErrorCode, UserRight } from '@shared/models' |
20 | 22 | ||
21 | async function doesVideoExist (id: number | string, res: Response, fetchType: VideoLoadType = 'all') { | 23 | async 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 | ||
149 | async 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 | ||
144 | export { | 164 | export { |
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' | |||
2 | export * from './video-captions' | 2 | export * from './video-captions' |
3 | export * from './video-channels' | 3 | export * from './video-channels' |
4 | export * from './video-comments' | 4 | export * from './video-comments' |
5 | export * from './video-editor' | ||
5 | export * from './video-files' | 6 | export * from './video-files' |
6 | export * from './video-imports' | 7 | export * from './video-imports' |
7 | export * from './video-live' | 8 | export * 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 @@ | |||
1 | import express from 'express' | 1 | import express from 'express' |
2 | import { body, param } from 'express-validator' | 2 | import { body, param } from 'express-validator' |
3 | import { HttpStatusCode, UserRight } from '@shared/models' | 3 | import { UserRight } from '@shared/models' |
4 | import { isVideoCaptionFile, isVideoCaptionLanguageValid } from '../../../helpers/custom-validators/video-captions' | 4 | import { isVideoCaptionFile, isVideoCaptionLanguageValid } from '../../../helpers/custom-validators/video-captions' |
5 | import { cleanUpReqFiles } from '../../../helpers/express-utils' | 5 | import { cleanUpReqFiles } from '../../../helpers/express-utils' |
6 | import { logger } from '../../../helpers/logger' | 6 | import { 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..96d956035 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 | } |
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 @@ | |||
1 | import express from 'express' | ||
2 | import { body, param } from 'express-validator' | ||
3 | import { isIdOrUUIDValid } from '@server/helpers/custom-validators/misc' | ||
4 | import { | ||
5 | isEditorCutTaskValid, | ||
6 | isEditorTaskAddIntroOutroValid, | ||
7 | isEditorTaskAddWatermarkValid, | ||
8 | isValidEditorTasksArray | ||
9 | } from '@server/helpers/custom-validators/video-editor' | ||
10 | import { cleanUpReqFiles } from '@server/helpers/express-utils' | ||
11 | import { CONFIG } from '@server/initializers/config' | ||
12 | import { approximateIntroOutroAdditionalSize, getTaskFile } from '@server/lib/video-editor' | ||
13 | import { isAudioFile } from '@shared/extra-utils' | ||
14 | import { HttpStatusCode, UserRight, VideoEditorCreateEdition, VideoEditorTask, VideoState } from '@shared/models' | ||
15 | import { logger } from '../../../helpers/logger' | ||
16 | import { areValidationErrors, checkUserCanManageVideo, checkUserQuota, doesVideoExist } from '../shared' | ||
17 | |||
18 | const 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 | |||
92 | export { | ||
93 | videosEditorAddEditionValidator | ||
94 | } | ||
95 | |||
96 | // --------------------------------------------------------------------------- | ||
97 | |||
98 | const 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 | |||
107 | function 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' | |||
3 | import { isIdValid } from '@server/helpers/custom-validators/misc' | 3 | import { isIdValid } from '@server/helpers/custom-validators/misc' |
4 | import { checkUserCanTerminateOwnershipChange } from '@server/helpers/custom-validators/video-ownership' | 4 | import { checkUserCanTerminateOwnershipChange } from '@server/helpers/custom-validators/video-ownership' |
5 | import { logger } from '@server/helpers/logger' | 5 | import { logger } from '@server/helpers/logger' |
6 | import { isAbleToUploadVideo } from '@server/lib/user' | ||
7 | import { AccountModel } from '@server/models/account/account' | 6 | import { AccountModel } from '@server/models/account/account' |
8 | import { MVideoWithAllFiles } from '@server/types/models' | 7 | import { MVideoWithAllFiles } from '@server/types/models' |
9 | import { | 8 | import { HttpStatusCode, UserRight, VideoChangeOwnershipAccept, VideoChangeOwnershipStatus, VideoState } from '@shared/models' |
10 | HttpStatusCode, | ||
11 | ServerErrorCode, | ||
12 | UserRight, | ||
13 | VideoChangeOwnershipAccept, | ||
14 | VideoChangeOwnershipStatus, | ||
15 | VideoState | ||
16 | } from '@shared/models' | ||
17 | import { | 9 | import { |
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' |
30 | import { isVideoImage } from '../../../helpers/custom-validators/videos' | 30 | import { isVideoImageValid } from '../../../helpers/custom-validators/videos' |
31 | import { cleanUpReqFiles } from '../../../helpers/express-utils' | 31 | import { cleanUpReqFiles } from '../../../helpers/express-utils' |
32 | import { logger } from '../../../helpers/logger' | 32 | import { logger } from '../../../helpers/logger' |
33 | import { CONSTRAINTS_FIELDS } from '../../../initializers/constants' | 33 | import { CONSTRAINTS_FIELDS } from '../../../initializers/constants' |
@@ -390,7 +390,7 @@ export { | |||
390 | function getCommonPlaylistEditAttributes () { | 390 | function 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/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' | |||
3 | import { isTestInstance } from '@server/helpers/core-utils' | 3 | import { isTestInstance } from '@server/helpers/core-utils' |
4 | import { getResumableUploadPath } from '@server/helpers/upload' | 4 | import { getResumableUploadPath } from '@server/helpers/upload' |
5 | import { Redis } from '@server/lib/redis' | 5 | import { Redis } from '@server/lib/redis' |
6 | import { isAbleToUploadVideo } from '@server/lib/user' | ||
7 | import { getServerActor } from '@server/models/application/application' | 6 | import { getServerActor } from '@server/models/application/application' |
8 | import { ExpressPromiseHandler } from '@server/types/express-handler' | 7 | import { ExpressPromiseHandler } from '@server/types/express-handler' |
9 | import { MUserAccountId, MVideoFullLight } from '@server/types/models' | 8 | import { 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' |
24 | import { isBooleanBothQueryValid, isNumberArray, isStringArray } from '../../../helpers/custom-validators/search' | 23 | import { isBooleanBothQueryValid, isNumberArray, isStringArray } from '../../../helpers/custom-validators/search' |
25 | import { | 24 | import { |
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' |
42 | import { cleanUpReqFiles } from '../../../helpers/express-utils' | 41 | import { cleanUpReqFiles } from '../../../helpers/express-utils' |
43 | import { getDurationFromVideoFile } from '../../../helpers/ffprobe-utils' | 42 | import { getVideoStreamDuration } from '../../../helpers/ffmpeg' |
44 | import { logger } from '../../../helpers/logger' | 43 | import { logger } from '../../../helpers/logger' |
45 | import { deleteFileAndCatch } from '../../../helpers/utils' | 44 | import { deleteFileAndCatch } from '../../../helpers/utils' |
46 | import { getVideoWithAttributes } from '../../../helpers/video' | 45 | import { 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 | ||
62 | const videosAddLegacyValidator = getCommonVideoEditAttributes().concat([ | 62 | const 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 = [ | |||
375 | function getCommonVideoEditAttributes () { | 374 | function 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 | ||
656 | async function addDurationToVideo (videoFile: { path: string, duration?: number }) { | 648 | async 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 | ||