aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/middlewares/validators/videos
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2023-07-19 16:02:49 +0200
committerChocobozzz <me@florianbigard.com>2023-07-21 17:38:13 +0200
commit12dc3a942a13c7f1489822dae052da197ef15905 (patch)
tree7b87b6be692af0b62ebac17e720c80244fd8a7ec /server/middlewares/validators/videos
parentc6867725fb8e3dfbc2018a37ed5a963103587cb6 (diff)
downloadPeerTube-12dc3a942a13c7f1489822dae052da197ef15905.tar.gz
PeerTube-12dc3a942a13c7f1489822dae052da197ef15905.tar.zst
PeerTube-12dc3a942a13c7f1489822dae052da197ef15905.zip
Implement replace file in server side
Diffstat (limited to 'server/middlewares/validators/videos')
-rw-r--r--server/middlewares/validators/videos/index.ts6
-rw-r--r--server/middlewares/validators/videos/shared/index.ts2
-rw-r--r--server/middlewares/validators/videos/shared/upload.ts39
-rw-r--r--server/middlewares/validators/videos/shared/video-validators.ts104
-rw-r--r--server/middlewares/validators/videos/video-source.ts108
-rw-r--r--server/middlewares/validators/videos/video-studio.ts12
-rw-r--r--server/middlewares/validators/videos/videos.ts112
7 files changed, 267 insertions, 116 deletions
diff --git a/server/middlewares/validators/videos/index.ts b/server/middlewares/validators/videos/index.ts
index 0c824c314..8c6fc49b1 100644
--- a/server/middlewares/validators/videos/index.ts
+++ b/server/middlewares/validators/videos/index.ts
@@ -1,12 +1,13 @@
1export * from './video-blacklist' 1export * from './video-blacklist'
2export * from './video-captions' 2export * from './video-captions'
3export * from './video-channel-sync'
3export * from './video-channels' 4export * from './video-channels'
4export * from './video-comments' 5export * from './video-comments'
5export * from './video-files' 6export * from './video-files'
6export * from './video-imports' 7export * from './video-imports'
7export * from './video-live' 8export * from './video-live'
8export * from './video-ownership-changes' 9export * from './video-ownership-changes'
9export * from './video-view' 10export * from './video-passwords'
10export * from './video-rates' 11export * from './video-rates'
11export * from './video-shares' 12export * from './video-shares'
12export * from './video-source' 13export * from './video-source'
@@ -14,6 +15,5 @@ export * from './video-stats'
14export * from './video-studio' 15export * from './video-studio'
15export * from './video-token' 16export * from './video-token'
16export * from './video-transcoding' 17export * from './video-transcoding'
18export * from './video-view'
17export * from './videos' 19export * from './videos'
18export * from './video-channel-sync'
19export * from './video-passwords'
diff --git a/server/middlewares/validators/videos/shared/index.ts b/server/middlewares/validators/videos/shared/index.ts
new file mode 100644
index 000000000..eb11dcc6a
--- /dev/null
+++ b/server/middlewares/validators/videos/shared/index.ts
@@ -0,0 +1,2 @@
1export * from './upload'
2export * from './video-validators'
diff --git a/server/middlewares/validators/videos/shared/upload.ts b/server/middlewares/validators/videos/shared/upload.ts
new file mode 100644
index 000000000..ea0dddc3c
--- /dev/null
+++ b/server/middlewares/validators/videos/shared/upload.ts
@@ -0,0 +1,39 @@
1import express from 'express'
2import { logger } from '@server/helpers/logger'
3import { getVideoStreamDuration } from '@shared/ffmpeg'
4import { HttpStatusCode } from '@shared/models'
5
6export async function addDurationToVideoFileIfNeeded (options: {
7 res: express.Response
8 videoFile: { path: string, duration?: number }
9 middlewareName: string
10}) {
11 const { res, middlewareName, videoFile } = options
12
13 try {
14 if (!videoFile.duration) await addDurationToVideo(videoFile)
15 } catch (err) {
16 logger.error('Invalid input file in ' + middlewareName, { err })
17
18 res.fail({
19 status: HttpStatusCode.UNPROCESSABLE_ENTITY_422,
20 message: 'Video file unreadable.'
21 })
22 return false
23 }
24
25 return true
26}
27
28// ---------------------------------------------------------------------------
29// Private
30// ---------------------------------------------------------------------------
31
32async function addDurationToVideo (videoFile: { path: string, duration?: number }) {
33 const duration = await getVideoStreamDuration(videoFile.path)
34
35 // FFmpeg may not be able to guess video duration
36 // For example with m2v files: https://trac.ffmpeg.org/ticket/9726#comment:2
37 if (isNaN(duration)) videoFile.duration = 0
38 else videoFile.duration = duration
39}
diff --git a/server/middlewares/validators/videos/shared/video-validators.ts b/server/middlewares/validators/videos/shared/video-validators.ts
new file mode 100644
index 000000000..72536011d
--- /dev/null
+++ b/server/middlewares/validators/videos/shared/video-validators.ts
@@ -0,0 +1,104 @@
1import express from 'express'
2import { isVideoFileMimeTypeValid, isVideoFileSizeValid } from '@server/helpers/custom-validators/videos'
3import { logger } from '@server/helpers/logger'
4import { CONSTRAINTS_FIELDS } from '@server/initializers/constants'
5import { isLocalVideoFileAccepted } from '@server/lib/moderation'
6import { Hooks } from '@server/lib/plugins/hooks'
7import { MUserAccountId, MVideo } from '@server/types/models'
8import { HttpStatusCode, ServerErrorCode, ServerFilterHookName, VideoState } from '@shared/models'
9import { checkUserQuota } from '../../shared'
10
11export async function commonVideoFileChecks (options: {
12 res: express.Response
13 user: MUserAccountId
14 videoFileSize: number
15 files: express.UploadFilesForCheck
16}): Promise<boolean> {
17 const { res, user, videoFileSize, files } = options
18
19 if (!isVideoFileMimeTypeValid(files)) {
20 res.fail({
21 status: HttpStatusCode.UNSUPPORTED_MEDIA_TYPE_415,
22 message: 'This file is not supported. Please, make sure it is of the following type: ' +
23 CONSTRAINTS_FIELDS.VIDEOS.EXTNAME.join(', ')
24 })
25 return false
26 }
27
28 if (!isVideoFileSizeValid(videoFileSize.toString())) {
29 res.fail({
30 status: HttpStatusCode.PAYLOAD_TOO_LARGE_413,
31 message: 'This file is too large. It exceeds the maximum file size authorized.',
32 type: ServerErrorCode.MAX_FILE_SIZE_REACHED
33 })
34 return false
35 }
36
37 if (await checkUserQuota(user, videoFileSize, res) === false) return false
38
39 return true
40}
41
42export async function isVideoFileAccepted (options: {
43 req: express.Request
44 res: express.Response
45 videoFile: express.VideoUploadFile
46 hook: Extract<ServerFilterHookName, 'filter:api.video.upload.accept.result' | 'filter:api.video.update-file.accept.result'>
47}) {
48 const { req, res, videoFile } = options
49
50 // Check we accept this video
51 const acceptParameters = {
52 videoBody: req.body,
53 videoFile,
54 user: res.locals.oauth.token.User
55 }
56 const acceptedResult = await Hooks.wrapFun(
57 isLocalVideoFileAccepted,
58 acceptParameters,
59 'filter:api.video.upload.accept.result'
60 )
61
62 if (!acceptedResult || acceptedResult.accepted !== true) {
63 logger.info('Refused local video file.', { acceptedResult, acceptParameters })
64 res.fail({
65 status: HttpStatusCode.FORBIDDEN_403,
66 message: acceptedResult.errorMessage || 'Refused local video file'
67 })
68 return false
69 }
70
71 return true
72}
73
74export function checkVideoFileCanBeEdited (video: MVideo, res: express.Response) {
75 if (video.isLive) {
76 res.fail({
77 status: HttpStatusCode.BAD_REQUEST_400,
78 message: 'Cannot edit a live video'
79 })
80
81 return false
82 }
83
84 if (video.state === VideoState.TO_TRANSCODE || video.state === VideoState.TO_EDIT) {
85 res.fail({
86 status: HttpStatusCode.CONFLICT_409,
87 message: 'Cannot edit video that is already waiting for transcoding/edition'
88 })
89
90 return false
91 }
92
93 const validStates = new Set([ VideoState.PUBLISHED, VideoState.TO_MOVE_TO_EXTERNAL_STORAGE_FAILED, VideoState.TRANSCODING_FAILED ])
94 if (!validStates.has(video.state)) {
95 res.fail({
96 status: HttpStatusCode.BAD_REQUEST_400,
97 message: 'Video state is not compatible with edition'
98 })
99
100 return false
101 }
102
103 return true
104}
diff --git a/server/middlewares/validators/videos/video-source.ts b/server/middlewares/validators/videos/video-source.ts
index c6d8f1a81..bbccb58b0 100644
--- a/server/middlewares/validators/videos/video-source.ts
+++ b/server/middlewares/validators/videos/video-source.ts
@@ -1,20 +1,31 @@
1import express from 'express' 1import express from 'express'
2import { body, header } from 'express-validator'
3import { getResumableUploadPath } from '@server/helpers/upload'
2import { getVideoWithAttributes } from '@server/helpers/video' 4import { getVideoWithAttributes } from '@server/helpers/video'
5import { CONFIG } from '@server/initializers/config'
6import { uploadx } from '@server/lib/uploadx'
3import { VideoSourceModel } from '@server/models/video/video-source' 7import { VideoSourceModel } from '@server/models/video/video-source'
4import { MVideoFullLight } from '@server/types/models' 8import { MVideoFullLight } from '@server/types/models'
5import { HttpStatusCode, UserRight } from '@shared/models' 9import { HttpStatusCode, UserRight } from '@shared/models'
10import { Metadata as UploadXMetadata } from '@uploadx/core'
11import { logger } from '../../../helpers/logger'
6import { areValidationErrors, checkUserCanManageVideo, doesVideoExist, isValidVideoIdParam } from '../shared' 12import { areValidationErrors, checkUserCanManageVideo, doesVideoExist, isValidVideoIdParam } from '../shared'
13import { addDurationToVideoFileIfNeeded, checkVideoFileCanBeEdited, commonVideoFileChecks, isVideoFileAccepted } from './shared'
7 14
8const videoSourceGetValidator = [ 15export const videoSourceGetLatestValidator = [
9 isValidVideoIdParam('id'), 16 isValidVideoIdParam('id'),
10 17
11 async (req: express.Request, res: express.Response, next: express.NextFunction) => { 18 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
12 if (areValidationErrors(req, res)) return 19 if (areValidationErrors(req, res)) return
13 if (!await doesVideoExist(req.params.id, res, 'for-api')) return 20 if (!await doesVideoExist(req.params.id, res, 'all')) return
14 21
15 const video = getVideoWithAttributes(res) as MVideoFullLight 22 const video = getVideoWithAttributes(res) as MVideoFullLight
16 23
17 res.locals.videoSource = await VideoSourceModel.loadByVideoId(video.id) 24 const user = res.locals.oauth.token.User
25 if (!checkUserCanManageVideo(user, video, UserRight.UPDATE_ANY_VIDEO, res)) return
26
27 res.locals.videoSource = await VideoSourceModel.loadLatest(video.id)
28
18 if (!res.locals.videoSource) { 29 if (!res.locals.videoSource) {
19 return res.fail({ 30 return res.fail({
20 status: HttpStatusCode.NOT_FOUND_404, 31 status: HttpStatusCode.NOT_FOUND_404,
@@ -22,13 +33,98 @@ const videoSourceGetValidator = [
22 }) 33 })
23 } 34 }
24 35
36 return next()
37 }
38]
39
40export const replaceVideoSourceResumableValidator = [
41 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
42 const body: express.CustomUploadXFile<UploadXMetadata> = req.body
43 const file = { ...body, duration: undefined, path: getResumableUploadPath(body.name), filename: body.metadata.filename }
44 const cleanup = () => uploadx.storage.delete(file).catch(err => logger.error('Cannot delete the file %s', file.name, { err }))
45
46 if (!await checkCanUpdateVideoFile({ req, res })) {
47 return cleanup()
48 }
49
50 if (!await addDurationToVideoFileIfNeeded({ videoFile: file, res, middlewareName: 'updateVideoFileResumableValidator' })) {
51 return cleanup()
52 }
53
54 if (!await isVideoFileAccepted({ req, res, videoFile: file, hook: 'filter:api.video.update-file.accept.result' })) {
55 return cleanup()
56 }
57
58 res.locals.updateVideoFileResumable = { ...file, originalname: file.filename }
59
60 return next()
61 }
62]
63
64export const replaceVideoSourceResumableInitValidator = [
65 body('filename')
66 .exists(),
67
68 header('x-upload-content-length')
69 .isNumeric()
70 .exists()
71 .withMessage('Should specify the file length'),
72 header('x-upload-content-type')
73 .isString()
74 .exists()
75 .withMessage('Should specify the file mimetype'),
76
77 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
25 const user = res.locals.oauth.token.User 78 const user = res.locals.oauth.token.User
26 if (!checkUserCanManageVideo(user, video, UserRight.UPDATE_ANY_VIDEO, res)) return 79
80 logger.debug('Checking updateVideoFileResumableInitValidator parameters and headers', {
81 parameters: req.body,
82 headers: req.headers
83 })
84
85 if (areValidationErrors(req, res, { omitLog: true })) return
86
87 if (!await checkCanUpdateVideoFile({ req, res })) return
88
89 const videoFileMetadata = {
90 mimetype: req.headers['x-upload-content-type'] as string,
91 size: +req.headers['x-upload-content-length'],
92 originalname: req.body.filename
93 }
94
95 const files = { videofile: [ videoFileMetadata ] }
96 if (await commonVideoFileChecks({ res, user, videoFileSize: videoFileMetadata.size, files }) === false) return
27 97
28 return next() 98 return next()
29 } 99 }
30] 100]
31 101
32export { 102// ---------------------------------------------------------------------------
33 videoSourceGetValidator 103// Private
104// ---------------------------------------------------------------------------
105
106async function checkCanUpdateVideoFile (options: {
107 req: express.Request
108 res: express.Response
109}) {
110 const { req, res } = options
111
112 if (!CONFIG.VIDEO_FILE.UPDATE.ENABLED) {
113 res.fail({
114 status: HttpStatusCode.FORBIDDEN_403,
115 message: 'Updating the file of an existing video is not allowed on this instance'
116 })
117 return false
118 }
119
120 if (!await doesVideoExist(req.params.id, res)) return false
121
122 const user = res.locals.oauth.token.User
123 const video = res.locals.videoAll
124
125 if (!checkUserCanManageVideo(user, video, UserRight.UPDATE_ANY_VIDEO, res)) return false
126
127 if (!checkVideoFileCanBeEdited(video, res)) return false
128
129 return true
34} 130}
diff --git a/server/middlewares/validators/videos/video-studio.ts b/server/middlewares/validators/videos/video-studio.ts
index 7a68f88e5..a375af60a 100644
--- a/server/middlewares/validators/videos/video-studio.ts
+++ b/server/middlewares/validators/videos/video-studio.ts
@@ -11,8 +11,9 @@ import { cleanUpReqFiles } from '@server/helpers/express-utils'
11import { CONFIG } from '@server/initializers/config' 11import { CONFIG } from '@server/initializers/config'
12import { approximateIntroOutroAdditionalSize, getTaskFileFromReq } from '@server/lib/video-studio' 12import { approximateIntroOutroAdditionalSize, getTaskFileFromReq } from '@server/lib/video-studio'
13import { isAudioFile } from '@shared/ffmpeg' 13import { isAudioFile } from '@shared/ffmpeg'
14import { HttpStatusCode, UserRight, VideoState, VideoStudioCreateEdition, VideoStudioTask } from '@shared/models' 14import { HttpStatusCode, UserRight, VideoStudioCreateEdition, VideoStudioTask } from '@shared/models'
15import { areValidationErrors, checkUserCanManageVideo, checkUserQuota, doesVideoExist } from '../shared' 15import { areValidationErrors, checkUserCanManageVideo, checkUserQuota, doesVideoExist } from '../shared'
16import { checkVideoFileCanBeEdited } from './shared'
16 17
17const videoStudioAddEditionValidator = [ 18const videoStudioAddEditionValidator = [
18 param('videoId') 19 param('videoId')
@@ -66,14 +67,7 @@ const videoStudioAddEditionValidator = [
66 if (!await doesVideoExist(req.params.videoId, res)) return cleanUpReqFiles(req) 67 if (!await doesVideoExist(req.params.videoId, res)) return cleanUpReqFiles(req)
67 68
68 const video = res.locals.videoAll 69 const video = res.locals.videoAll
69 if (video.state === VideoState.TO_TRANSCODE || video.state === VideoState.TO_EDIT) { 70 if (!checkVideoFileCanBeEdited(video, res)) return cleanUpReqFiles(req)
70 res.fail({
71 status: HttpStatusCode.CONFLICT_409,
72 message: 'Cannot edit video that is already waiting for transcoding/edition'
73 })
74
75 return cleanUpReqFiles(req)
76 }
77 71
78 const user = res.locals.oauth.token.User 72 const user = res.locals.oauth.token.User
79 if (!checkUserCanManageVideo(user, video, UserRight.UPDATE_ANY_VIDEO, res)) return cleanUpReqFiles(req) 73 if (!checkUserCanManageVideo(user, video, UserRight.UPDATE_ANY_VIDEO, res)) return cleanUpReqFiles(req)
diff --git a/server/middlewares/validators/videos/videos.ts b/server/middlewares/validators/videos/videos.ts
index b39d13a23..aea3453b5 100644
--- a/server/middlewares/validators/videos/videos.ts
+++ b/server/middlewares/validators/videos/videos.ts
@@ -2,13 +2,12 @@ import express from 'express'
2import { body, header, param, query, ValidationChain } from 'express-validator' 2import { 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 { uploadx } from '@server/lib/uploadx'
6import { Redis } from '@server/lib/redis' 5import { Redis } from '@server/lib/redis'
6import { uploadx } from '@server/lib/uploadx'
7import { getServerActor } from '@server/models/application/application' 7import { getServerActor } from '@server/models/application/application'
8import { ExpressPromiseHandler } from '@server/types/express-handler' 8import { ExpressPromiseHandler } from '@server/types/express-handler'
9import { MUserAccountId, MVideoFullLight } from '@server/types/models' 9import { MUserAccountId, MVideoFullLight } from '@server/types/models'
10import { arrayify, getAllPrivacies } from '@shared/core-utils' 10import { arrayify, getAllPrivacies } from '@shared/core-utils'
11import { getVideoStreamDuration } from '@shared/ffmpeg'
12import { HttpStatusCode, ServerErrorCode, UserRight, VideoInclude, VideoState } from '@shared/models' 11import { HttpStatusCode, ServerErrorCode, UserRight, VideoInclude, VideoState } from '@shared/models'
13import { 12import {
14 exists, 13 exists,
@@ -27,8 +26,6 @@ import {
27 isValidPasswordProtectedPrivacy, 26 isValidPasswordProtectedPrivacy,
28 isVideoCategoryValid, 27 isVideoCategoryValid,
29 isVideoDescriptionValid, 28 isVideoDescriptionValid,
30 isVideoFileMimeTypeValid,
31 isVideoFileSizeValid,
32 isVideoFilterValid, 29 isVideoFilterValid,
33 isVideoImageValid, 30 isVideoImageValid,
34 isVideoIncludeValid, 31 isVideoIncludeValid,
@@ -44,21 +41,19 @@ import { logger } from '../../../helpers/logger'
44import { getVideoWithAttributes } from '../../../helpers/video' 41import { getVideoWithAttributes } from '../../../helpers/video'
45import { CONFIG } from '../../../initializers/config' 42import { CONFIG } from '../../../initializers/config'
46import { CONSTRAINTS_FIELDS, OVERVIEWS } from '../../../initializers/constants' 43import { CONSTRAINTS_FIELDS, OVERVIEWS } from '../../../initializers/constants'
47import { isLocalVideoAccepted } from '../../../lib/moderation'
48import { Hooks } from '../../../lib/plugins/hooks'
49import { VideoModel } from '../../../models/video/video' 44import { VideoModel } from '../../../models/video/video'
50import { 45import {
51 areValidationErrors, 46 areValidationErrors,
52 checkCanAccessVideoStaticFiles, 47 checkCanAccessVideoStaticFiles,
53 checkCanSeeVideo, 48 checkCanSeeVideo,
54 checkUserCanManageVideo, 49 checkUserCanManageVideo,
55 checkUserQuota,
56 doesVideoChannelOfAccountExist, 50 doesVideoChannelOfAccountExist,
57 doesVideoExist, 51 doesVideoExist,
58 doesVideoFileOfVideoExist, 52 doesVideoFileOfVideoExist,
59 isValidVideoIdParam, 53 isValidVideoIdParam,
60 isValidVideoPasswordHeader 54 isValidVideoPasswordHeader
61} from '../shared' 55} from '../shared'
56import { addDurationToVideoFileIfNeeded, commonVideoFileChecks, isVideoFileAccepted } from './shared'
62 57
63const videosAddLegacyValidator = getCommonVideoEditAttributes().concat([ 58const videosAddLegacyValidator = getCommonVideoEditAttributes().concat([
64 body('videofile') 59 body('videofile')
@@ -83,26 +78,15 @@ const videosAddLegacyValidator = getCommonVideoEditAttributes().concat([
83 const videoFile: express.VideoUploadFile = req.files['videofile'][0] 78 const videoFile: express.VideoUploadFile = req.files['videofile'][0]
84 const user = res.locals.oauth.token.User 79 const user = res.locals.oauth.token.User
85 80
86 if (!await commonVideoChecksPass({ req, res, user, videoFileSize: videoFile.size, files: req.files })) { 81 if (
82 !await commonVideoChecksPass({ req, res, user, videoFileSize: videoFile.size, files: req.files }) ||
83 !isValidPasswordProtectedPrivacy(req, res) ||
84 !await addDurationToVideoFileIfNeeded({ videoFile, res, middlewareName: 'videosAddvideosAddLegacyValidatorResumableValidator' }) ||
85 !await isVideoFileAccepted({ req, res, videoFile, hook: 'filter:api.video.upload.accept.result' })
86 ) {
87 return cleanUpReqFiles(req) 87 return cleanUpReqFiles(req)
88 } 88 }
89 89
90 if (!isValidPasswordProtectedPrivacy(req, res)) return cleanUpReqFiles(req)
91
92 try {
93 if (!videoFile.duration) await addDurationToVideo(videoFile)
94 } catch (err) {
95 logger.error('Invalid input file in videosAddLegacyValidator.', { err })
96
97 res.fail({
98 status: HttpStatusCode.UNPROCESSABLE_ENTITY_422,
99 message: 'Video file unreadable.'
100 })
101 return cleanUpReqFiles(req)
102 }
103
104 if (!await isVideoAccepted(req, res, videoFile)) return cleanUpReqFiles(req)
105
106 return next() 90 return next()
107 } 91 }
108]) 92])
@@ -146,22 +130,10 @@ const videosAddResumableValidator = [
146 await Redis.Instance.setUploadSession(uploadId) 130 await Redis.Instance.setUploadSession(uploadId)
147 131
148 if (!await doesVideoChannelOfAccountExist(file.metadata.channelId, user, res)) return cleanup() 132 if (!await doesVideoChannelOfAccountExist(file.metadata.channelId, user, res)) return cleanup()
133 if (!await addDurationToVideoFileIfNeeded({ videoFile: file, res, middlewareName: 'videosAddResumableValidator' })) return cleanup()
134 if (!await isVideoFileAccepted({ req, res, videoFile: file, hook: 'filter:api.video.upload.accept.result' })) return cleanup()
149 135
150 try { 136 res.locals.uploadVideoFileResumable = { ...file, originalname: file.filename }
151 if (!file.duration) await addDurationToVideo(file)
152 } catch (err) {
153 logger.error('Invalid input file in videosAddResumableValidator.', { err })
154
155 res.fail({
156 status: HttpStatusCode.UNPROCESSABLE_ENTITY_422,
157 message: 'Video file unreadable.'
158 })
159 return cleanup()
160 }
161
162 if (!await isVideoAccepted(req, res, file)) return cleanup()
163
164 res.locals.videoFileResumable = { ...file, originalname: file.filename }
165 137
166 return next() 138 return next()
167 } 139 }
@@ -604,76 +576,20 @@ function areErrorsInScheduleUpdate (req: express.Request, res: express.Response)
604 return false 576 return false
605} 577}
606 578
607async function commonVideoChecksPass (parameters: { 579async function commonVideoChecksPass (options: {
608 req: express.Request 580 req: express.Request
609 res: express.Response 581 res: express.Response
610 user: MUserAccountId 582 user: MUserAccountId
611 videoFileSize: number 583 videoFileSize: number
612 files: express.UploadFilesForCheck 584 files: express.UploadFilesForCheck
613}): Promise<boolean> { 585}): Promise<boolean> {
614 const { req, res, user, videoFileSize, files } = parameters 586 const { req, res, user } = options
615 587
616 if (areErrorsInScheduleUpdate(req, res)) return false 588 if (areErrorsInScheduleUpdate(req, res)) return false
617 589
618 if (!await doesVideoChannelOfAccountExist(req.body.channelId, user, res)) return false 590 if (!await doesVideoChannelOfAccountExist(req.body.channelId, user, res)) return false
619 591
620 if (!isVideoFileMimeTypeValid(files)) { 592 if (!await commonVideoFileChecks(options)) return false
621 res.fail({
622 status: HttpStatusCode.UNSUPPORTED_MEDIA_TYPE_415,
623 message: 'This file is not supported. Please, make sure it is of the following type: ' +
624 CONSTRAINTS_FIELDS.VIDEOS.EXTNAME.join(', ')
625 })
626 return false
627 }
628
629 if (!isVideoFileSizeValid(videoFileSize.toString())) {
630 res.fail({
631 status: HttpStatusCode.PAYLOAD_TOO_LARGE_413,
632 message: 'This file is too large. It exceeds the maximum file size authorized.',
633 type: ServerErrorCode.MAX_FILE_SIZE_REACHED
634 })
635 return false
636 }
637
638 if (await checkUserQuota(user, videoFileSize, res) === false) return false
639
640 return true
641}
642
643export async function isVideoAccepted (
644 req: express.Request,
645 res: express.Response,
646 videoFile: express.VideoUploadFile
647) {
648 // Check we accept this video
649 const acceptParameters = {
650 videoBody: req.body,
651 videoFile,
652 user: res.locals.oauth.token.User
653 }
654 const acceptedResult = await Hooks.wrapFun(
655 isLocalVideoAccepted,
656 acceptParameters,
657 'filter:api.video.upload.accept.result'
658 )
659
660 if (!acceptedResult || acceptedResult.accepted !== true) {
661 logger.info('Refused local video.', { acceptedResult, acceptParameters })
662 res.fail({
663 status: HttpStatusCode.FORBIDDEN_403,
664 message: acceptedResult.errorMessage || 'Refused local video'
665 })
666 return false
667 }
668 593
669 return true 594 return true
670} 595}
671
672async function addDurationToVideo (videoFile: { path: string, duration?: number }) {
673 const duration = await getVideoStreamDuration(videoFile.path)
674
675 // FFmpeg may not be able to guess video duration
676 // For example with m2v files: https://trac.ffmpeg.org/ticket/9726#comment:2
677 if (isNaN(duration)) videoFile.duration = 0
678 else videoFile.duration = duration
679}