Add concept of video state, and add ability to wait transcoding before
[oweals/peertube.git] / server / helpers / custom-validators / videos.ts
1 import { Response } from 'express'
2 import 'express-validator'
3 import { values } from 'lodash'
4 import 'multer'
5 import * as validator from 'validator'
6 import { UserRight, VideoRateType } from '../../../shared'
7 import {
8   CONSTRAINTS_FIELDS,
9   VIDEO_CATEGORIES,
10   VIDEO_LICENCES,
11   VIDEO_MIMETYPE_EXT,
12   VIDEO_PRIVACIES,
13   VIDEO_RATE_TYPES,
14   VIDEO_STATES
15 } from '../../initializers'
16 import { VideoModel } from '../../models/video/video'
17 import { exists, isArray, isFileValid } from './misc'
18 import { VideoChannelModel } from '../../models/video/video-channel'
19 import { UserModel } from '../../models/account/user'
20
21 const VIDEOS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEOS
22 const VIDEO_ABUSES_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_ABUSES
23
24 function isVideoCategoryValid (value: any) {
25   return value === null || VIDEO_CATEGORIES[ value ] !== undefined
26 }
27
28 function isVideoStateValid (value: any) {
29   return exists(value) && VIDEO_STATES[ value ] !== undefined
30 }
31
32 function isVideoLicenceValid (value: any) {
33   return value === null || VIDEO_LICENCES[ value ] !== undefined
34 }
35
36 function isVideoLanguageValid (value: any) {
37   return value === null ||
38     (typeof value === 'string' && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.LANGUAGE))
39 }
40
41 function isVideoDurationValid (value: string) {
42   return exists(value) && validator.isInt(value + '', VIDEOS_CONSTRAINTS_FIELDS.DURATION)
43 }
44
45 function isVideoTruncatedDescriptionValid (value: string) {
46   return exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.TRUNCATED_DESCRIPTION)
47 }
48
49 function isVideoDescriptionValid (value: string) {
50   return value === null || (exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.DESCRIPTION))
51 }
52
53 function isVideoSupportValid (value: string) {
54   return value === null || (exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.SUPPORT))
55 }
56
57 function isVideoNameValid (value: string) {
58   return exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.NAME)
59 }
60
61 function isVideoTagValid (tag: string) {
62   return exists(tag) && validator.isLength(tag, VIDEOS_CONSTRAINTS_FIELDS.TAG)
63 }
64
65 function isVideoTagsValid (tags: string[]) {
66   return tags === null || (
67     isArray(tags) &&
68     validator.isInt(tags.length.toString(), VIDEOS_CONSTRAINTS_FIELDS.TAGS) &&
69     tags.every(tag => isVideoTagValid(tag))
70   )
71 }
72
73 function isVideoAbuseReasonValid (value: string) {
74   return exists(value) && validator.isLength(value, VIDEO_ABUSES_CONSTRAINTS_FIELDS.REASON)
75 }
76
77 function isVideoViewsValid (value: string) {
78   return exists(value) && validator.isInt(value + '', VIDEOS_CONSTRAINTS_FIELDS.VIEWS)
79 }
80
81 function isVideoRatingTypeValid (value: string) {
82   return value === 'none' || values(VIDEO_RATE_TYPES).indexOf(value as VideoRateType) !== -1
83 }
84
85 const videoFileTypes = Object.keys(VIDEO_MIMETYPE_EXT).map(m => `(${m})`)
86 const videoFileTypesRegex = videoFileTypes.join('|')
87
88 function isVideoFile (files: { [ fieldname: string ]: Express.Multer.File[] } | Express.Multer.File[]) {
89   return isFileValid(files, videoFileTypesRegex, 'videofile')
90 }
91
92 const videoImageTypes = CONSTRAINTS_FIELDS.VIDEOS.IMAGE.EXTNAME
93                                           .map(v => v.replace('.', ''))
94                                           .join('|')
95 const videoImageTypesRegex = `image/(${videoImageTypes})`
96
97 function isVideoImage (files: { [ fieldname: string ]: Express.Multer.File[] } | Express.Multer.File[], field: string) {
98   return isFileValid(files, videoImageTypesRegex, field, true)
99 }
100
101 function isVideoPrivacyValid (value: string) {
102   return validator.isInt(value + '') && VIDEO_PRIVACIES[ value ] !== undefined
103 }
104
105 function isVideoFileInfoHashValid (value: string) {
106   return exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.INFO_HASH)
107 }
108
109 function isVideoFileResolutionValid (value: string) {
110   return exists(value) && validator.isInt(value + '')
111 }
112
113 function isVideoFileSizeValid (value: string) {
114   return exists(value) && validator.isInt(value + '', VIDEOS_CONSTRAINTS_FIELDS.FILE_SIZE)
115 }
116
117 async function isVideoExist (id: string, res: Response) {
118   let video: VideoModel
119
120   if (validator.isInt(id)) {
121     video = await VideoModel.loadAndPopulateAccountAndServerAndTags(+id)
122   } else { // UUID
123     video = await VideoModel.loadByUUIDAndPopulateAccountAndServerAndTags(id)
124   }
125
126   if (!video) {
127     res.status(404)
128        .json({ error: 'Video not found' })
129        .end()
130
131     return false
132   }
133
134   res.locals.video = video
135   return true
136 }
137
138 async function isVideoChannelOfAccountExist (channelId: number, user: UserModel, res: Response) {
139   if (user.hasRight(UserRight.UPDATE_ANY_VIDEO) === true) {
140     const videoChannel = await VideoChannelModel.loadAndPopulateAccount(channelId)
141     if (!videoChannel) {
142       res.status(400)
143          .json({ error: 'Unknown video video channel on this instance.' })
144          .end()
145
146       return false
147     }
148
149     res.locals.videoChannel = videoChannel
150     return true
151   }
152
153   const videoChannel = await VideoChannelModel.loadByIdAndAccount(channelId, user.Account.id)
154   if (!videoChannel) {
155     res.status(400)
156        .json({ error: 'Unknown video video channel for this account.' })
157        .end()
158
159     return false
160   }
161
162   res.locals.videoChannel = videoChannel
163   return true
164 }
165
166 // ---------------------------------------------------------------------------
167
168 export {
169   isVideoCategoryValid,
170   isVideoLicenceValid,
171   isVideoLanguageValid,
172   isVideoTruncatedDescriptionValid,
173   isVideoDescriptionValid,
174   isVideoFileInfoHashValid,
175   isVideoNameValid,
176   isVideoTagsValid,
177   isVideoAbuseReasonValid,
178   isVideoFile,
179   isVideoStateValid,
180   isVideoViewsValid,
181   isVideoRatingTypeValid,
182   isVideoDurationValid,
183   isVideoTagValid,
184   isVideoPrivacyValid,
185   isVideoFileResolutionValid,
186   isVideoFileSizeValid,
187   isVideoExist,
188   isVideoImage,
189   isVideoChannelOfAccountExist,
190   isVideoSupportValid
191 }