import { isTestInstance } from '@server/helpers/core-utils'
import { getResumableUploadPath } from '@server/helpers/upload'
import { Redis } from '@server/lib/redis'
-import { isAbleToUploadVideo } from '@server/lib/user'
import { getServerActor } from '@server/models/application/application'
-import { ExpressPromiseHandler } from '@server/types/express'
+import { ExpressPromiseHandler } from '@server/types/express-handler'
import { MUserAccountId, MVideoFullLight } from '@server/types/models'
import { getAllPrivacies } from '@shared/core-utils'
-import { VideoInclude } from '@shared/models'
-import { ServerErrorCode, UserRight, VideoPrivacy } from '../../../../shared'
-import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
+import { HttpStatusCode, ServerErrorCode, UserRight, VideoInclude, VideoPrivacy } from '@shared/models'
import {
exists,
isBooleanValid,
isDateValid,
- isFileFieldValid,
+ isFileValid,
isIdValid,
isUUIDValid,
toArray,
} from '../../../helpers/custom-validators/misc'
import { isBooleanBothQueryValid, isNumberArray, isStringArray } from '../../../helpers/custom-validators/search'
import {
+ areVideoTagsValid,
isScheduleVideoUpdatePrivacyValid,
isVideoCategoryValid,
isVideoDescriptionValid,
isVideoFileMimeTypeValid,
isVideoFileSizeValid,
isVideoFilterValid,
- isVideoImage,
+ isVideoImageValid,
isVideoIncludeValid,
isVideoLanguageValid,
isVideoLicenceValid,
isVideoNameValid,
isVideoOriginallyPublishedAtValid,
isVideoPrivacyValid,
- isVideoSupportValid,
- isVideoTagsValid
+ isVideoSupportValid
} from '../../../helpers/custom-validators/videos'
import { cleanUpReqFiles } from '../../../helpers/express-utils'
-import { getDurationFromVideoFile } from '../../../helpers/ffprobe-utils'
+import { getVideoStreamDuration } from '../../../helpers/ffmpeg'
import { logger } from '../../../helpers/logger'
import { deleteFileAndCatch } from '../../../helpers/utils'
import { getVideoWithAttributes } from '../../../helpers/video'
import { isLocalVideoAccepted } from '../../../lib/moderation'
import { Hooks } from '../../../lib/plugins/hooks'
import { VideoModel } from '../../../models/video/video'
-import { authenticatePromiseIfNeeded } from '../../auth'
import {
areValidationErrors,
+ checkCanSeePrivateVideo,
checkUserCanManageVideo,
+ checkUserQuota,
doesVideoChannelOfAccountExist,
doesVideoExist,
doesVideoFileOfVideoExist,
const videosAddLegacyValidator = getCommonVideoEditAttributes().concat([
body('videofile')
- .custom((value, { req }) => isFileFieldValid(req.files, 'videofile'))
+ .custom((_, { req }) => isFileValid({ files: req.files, field: 'videofile', mimeTypeRegex: null, maxSize: null }))
.withMessage('Should have a file'),
body('name')
.trim()
}
])
-const videosResumableUploadIdValidator = [
- (req: express.Request, res: express.Response, next: express.NextFunction) => {
- const user = res.locals.oauth.token.User
- const uploadId = req.query.upload_id
-
- if (uploadId.startsWith(user.id + '-') !== true) {
- return res.fail({
- status: HttpStatusCode.FORBIDDEN_403,
- message: 'You cannot send chunks in another user upload'
- })
- }
-
- return next()
- }
-]
-
/**
* Gets called after the last PUT request
*/
const videoFileMetadata = {
mimetype: req.headers['x-upload-content-type'] as string,
size: +req.headers['x-upload-content-length'],
- originalname: req.body.name
+ originalname: req.body.filename
}
const user = res.locals.oauth.token.User
// Video private or blacklisted
if (video.requiresAuth()) {
- await authenticatePromiseIfNeeded(req, res, authenticateInQuery)
-
- const user = res.locals.oauth ? res.locals.oauth.token.User : null
-
- // Only the owner or a user that have blocklist rights can see the video
- if (!user || !user.canGetVideo(video)) {
- return res.fail({
- status: HttpStatusCode.FORBIDDEN_403,
- message: 'Cannot get this private/internal or blocklisted video'
- })
+ if (await checkCanSeePrivateVideo(req, res, video, authenticateInQuery)) {
+ return next()
}
- return next()
+ return
}
// Video is public, anyone can access it
function getCommonVideoEditAttributes () {
return [
body('thumbnailfile')
- .custom((value, { req }) => isVideoImage(req.files, 'thumbnailfile')).withMessage(
+ .custom((value, { req }) => isVideoImageValid(req.files, 'thumbnailfile')).withMessage(
'This thumbnail file is not supported or too large. Please, make sure it is of the following type: ' +
CONSTRAINTS_FIELDS.VIDEOS.IMAGE.EXTNAME.join(', ')
),
body('previewfile')
- .custom((value, { req }) => isVideoImage(req.files, 'previewfile')).withMessage(
+ .custom((value, { req }) => isVideoImageValid(req.files, 'previewfile')).withMessage(
'This preview file is not supported or too large. Please, make sure it is of the following type: ' +
CONSTRAINTS_FIELDS.VIDEOS.IMAGE.EXTNAME.join(', ')
),
body('tags')
.optional()
.customSanitizer(toValueOrNull)
- .custom(isVideoTagsValid)
+ .custom(areVideoTagsValid)
.withMessage(
`Should have an array of up to ${CONSTRAINTS_FIELDS.VIDEOS.TAGS.max} tags between ` +
`${CONSTRAINTS_FIELDS.VIDEOS.TAG.min} and ${CONSTRAINTS_FIELDS.VIDEOS.TAG.max} characters each`
videosAddLegacyValidator,
videosAddResumableValidator,
videosAddResumableInitValidator,
- videosResumableUploadIdValidator,
videosUpdateValidator,
videosGetValidator,
return false
}
- if (await isAbleToUploadVideo(user.id, videoFileSize) === false) {
- res.fail({
- status: HttpStatusCode.PAYLOAD_TOO_LARGE_413,
- message: 'The user video quota is exceeded with this video.',
- type: ServerErrorCode.QUOTA_REACHED
- })
- return false
- }
+ if (await checkUserQuota(user, videoFileSize, res) === false) return false
return true
}
}
async function addDurationToVideo (videoFile: { path: string, duration?: number }) {
- const duration: number = await getDurationFromVideoFile(videoFile.path)
-
- if (isNaN(duration)) throw new Error(`Couldn't get video duration`)
+ const duration = await getVideoStreamDuration(videoFile.path)
- videoFile.duration = duration
+ // FFmpeg may not be able to guess video duration
+ // For example with m2v files: https://trac.ffmpeg.org/ticket/9726#comment:2
+ if (isNaN(duration)) videoFile.duration = 0
+ else videoFile.duration = duration
}