]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blobdiff - server/middlewares/validators/videos/videos.ts
Add ability to list comments on local videos
[github/Chocobozzz/PeerTube.git] / server / middlewares / validators / videos / videos.ts
index 3a1a905f3a5fb2603771e0f2498b126efbd7f70a..c6d31f8f053ff2aa845c1d5519e8bf3fc90517a1 100644 (file)
@@ -3,19 +3,17 @@ import { body, header, param, query, ValidationChain } from 'express-validator'
 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-handler'
 import { MUserAccountId, MVideoFullLight } from '@server/types/models'
 import { getAllPrivacies } from '@shared/core-utils'
-import { HttpStatusCode, ServerErrorCode, UserRight, VideoInclude, VideoPrivacy } from '@shared/models'
+import { HttpStatusCode, ServerErrorCode, UserRight, VideoInclude } from '@shared/models'
 import {
   exists,
   isBooleanValid,
   isDateValid,
-  isFileFieldValid,
+  isFileValid,
   isIdValid,
-  isUUIDValid,
   toArray,
   toBooleanOrNull,
   toIntOrNull,
@@ -23,24 +21,24 @@ import {
 } 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'
@@ -51,8 +49,9 @@ import { Hooks } from '../../../lib/plugins/hooks'
 import { VideoModel } from '../../../models/video/video'
 import {
   areValidationErrors,
-  checkCanSeePrivateVideo,
+  checkCanSeeVideo,
   checkUserCanManageVideo,
+  checkUserQuota,
   doesVideoChannelOfAccountExist,
   doesVideoExist,
   doesVideoFileOfVideoExist,
@@ -61,7 +60,7 @@ import {
 
 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()
@@ -102,22 +101,6 @@ const videosAddLegacyValidator = getCommonVideoEditAttributes().concat([
   }
 ])
 
-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
  */
@@ -168,7 +151,7 @@ const videosAddResumableValidator = [
 
     if (!await isVideoAccepted(req, res, file)) return cleanup()
 
-    res.locals.videoFileResumable = file
+    res.locals.videoFileResumable = { ...file, originalname: file.filename }
 
     return next()
   }
@@ -313,29 +296,9 @@ const videosCustomGetValidator = (
 
       const video = getVideoWithAttributes(res) as MVideoFullLight
 
-      // Video private or blacklisted
-      if (video.requiresAuth()) {
-        if (await checkCanSeePrivateVideo(req, res, video, authenticateInQuery)) return next()
-
-        return res.fail({
-          status: HttpStatusCode.FORBIDDEN_403,
-          message: 'Cannot get this private/internal or blocklisted video'
-        })
-      }
-
-      // Video is public, anyone can access it
-      if (video.privacy === VideoPrivacy.PUBLIC) return next()
-
-      // Video is unlisted, check we used the uuid to fetch it
-      if (video.privacy === VideoPrivacy.UNLISTED) {
-        if (isUUIDValid(req.params.id)) return next()
+      if (!await checkCanSeeVideo({ req, res, video, paramId: req.params.id, authenticateInQuery })) return
 
-        // Don't leak this unlisted video
-        return res.fail({
-          status: HttpStatusCode.NOT_FOUND_404,
-          message: 'Video not found'
-        })
-      }
+      return next()
     }
   ]
 }
@@ -391,12 +354,12 @@ const videosOverviewValidator = [
 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(', ')
       ),
@@ -436,7 +399,7 @@ function getCommonVideoEditAttributes () {
     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`
@@ -566,7 +529,6 @@ export {
   videosAddLegacyValidator,
   videosAddResumableValidator,
   videosAddResumableInitValidator,
-  videosResumableUploadIdValidator,
 
   videosUpdateValidator,
   videosGetValidator,
@@ -629,14 +591,7 @@ async function commonVideoChecksPass (parameters: {
     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
 }
@@ -671,9 +626,10 @@ export async function isVideoAccepted (
 }
 
 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
 }