Remove references to author
[oweals/peertube.git] / server / helpers / custom-validators / videos.ts
1 import { values } from 'lodash'
2 import * as validator from 'validator'
3 import * as Promise from 'bluebird'
4 import * as express from 'express'
5 import 'express-validator'
6 import 'multer'
7
8 import {
9   CONSTRAINTS_FIELDS,
10   VIDEO_CATEGORIES,
11   VIDEO_LICENCES,
12   VIDEO_LANGUAGES,
13   VIDEO_RATE_TYPES,
14   VIDEO_PRIVACIES,
15   database as db
16 } from '../../initializers'
17 import { isUserUsernameValid } from './users'
18 import { isArray, exists } from './misc'
19 import { VideoInstance } from '../../models'
20 import { logger } from '../../helpers'
21 import { VideoRateType } from '../../../shared'
22
23 const VIDEOS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEOS
24 const VIDEO_ABUSES_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_ABUSES
25 const VIDEO_EVENTS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_EVENTS
26
27 function isVideoCategoryValid (value: number) {
28   return VIDEO_CATEGORIES[value] !== undefined
29 }
30
31 // Maybe we don't know the remote category, but that doesn't matter
32 function isRemoteVideoCategoryValid (value: string) {
33   return validator.isInt('' + value)
34 }
35
36 function isVideoLicenceValid (value: number) {
37   return VIDEO_LICENCES[value] !== undefined
38 }
39
40 function isVideoPrivacyValid (value: string) {
41   return VIDEO_PRIVACIES[value] !== undefined
42 }
43
44 // Maybe we don't know the remote privacy setting, but that doesn't matter
45 function isRemoteVideoPrivacyValid (value: string) {
46   return validator.isInt('' + value)
47 }
48
49 // Maybe we don't know the remote licence, but that doesn't matter
50 function isRemoteVideoLicenceValid (value: string) {
51   return validator.isInt('' + value)
52 }
53
54 function isVideoLanguageValid (value: number) {
55   return value === null || VIDEO_LANGUAGES[value] !== undefined
56 }
57
58 // Maybe we don't know the remote language, but that doesn't matter
59 function isRemoteVideoLanguageValid (value: string) {
60   return validator.isInt('' + value)
61 }
62
63 function isVideoNSFWValid (value: any) {
64   return typeof value === 'boolean' || (typeof value === 'string' && validator.isBoolean(value))
65 }
66
67 function isVideoTruncatedDescriptionValid (value: string) {
68   return exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.TRUNCATED_DESCRIPTION)
69 }
70
71 function isVideoDescriptionValid (value: string) {
72   return exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.DESCRIPTION)
73 }
74
75 function isVideoDurationValid (value: string) {
76   // https://www.w3.org/TR/activitystreams-vocabulary/#dfn-duration
77   return exists(value) &&
78     typeof value === 'string' &&
79     value.startsWith('PT') &&
80     value.endsWith('S') &&
81     validator.isInt(value.replace(/[^0-9]+/, ''), VIDEOS_CONSTRAINTS_FIELDS.DURATION)
82 }
83
84 function isVideoNameValid (value: string) {
85   return exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.NAME)
86 }
87
88 function isVideoTagValid (tag: string) {
89   return exists(tag) && validator.isLength(tag, VIDEOS_CONSTRAINTS_FIELDS.TAG)
90 }
91
92 function isVideoTagsValid (tags: string[]) {
93   return isArray(tags) &&
94          validator.isInt(tags.length.toString(), VIDEOS_CONSTRAINTS_FIELDS.TAGS) &&
95          tags.every(tag => isVideoTagValid(tag))
96 }
97
98 function isVideoThumbnailValid (value: string) {
99   return exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.THUMBNAIL)
100 }
101
102 function isVideoThumbnailDataValid (value: string) {
103   return exists(value) && validator.isByteLength(value, VIDEOS_CONSTRAINTS_FIELDS.THUMBNAIL_DATA)
104 }
105
106 function isVideoAbuseReasonValid (value: string) {
107   return exists(value) && validator.isLength(value, VIDEO_ABUSES_CONSTRAINTS_FIELDS.REASON)
108 }
109
110 function isVideoAbuseReporterUsernameValid (value: string) {
111   return isUserUsernameValid(value)
112 }
113
114 function isVideoViewsValid (value: string) {
115   return exists(value) && validator.isInt(value + '', VIDEOS_CONSTRAINTS_FIELDS.VIEWS)
116 }
117
118 function isVideoLikesValid (value: string) {
119   return exists(value) && validator.isInt(value + '', VIDEOS_CONSTRAINTS_FIELDS.LIKES)
120 }
121
122 function isVideoDislikesValid (value: string) {
123   return exists(value) && validator.isInt(value + '', VIDEOS_CONSTRAINTS_FIELDS.DISLIKES)
124 }
125
126 function isVideoEventCountValid (value: string) {
127   return exists(value) && validator.isInt(value + '', VIDEO_EVENTS_CONSTRAINTS_FIELDS.COUNT)
128 }
129
130 function isVideoRatingTypeValid (value: string) {
131   return values(VIDEO_RATE_TYPES).indexOf(value as VideoRateType) !== -1
132 }
133
134 function isVideoFile (files: { [ fieldname: string ]: Express.Multer.File[] } | Express.Multer.File[]) {
135   // Should have files
136   if (!files) return false
137   if (isArray(files)) return false
138
139   // Should have videofile file
140   const videofile = files['videofile']
141   if (!videofile || videofile.length === 0) return false
142
143   // The file should exist
144   const file = videofile[0]
145   if (!file || !file.originalname) return false
146
147   return new RegExp('^video/(webm|mp4|ogg)$', 'i').test(file.mimetype)
148 }
149
150 function isVideoFileSizeValid (value: string) {
151   return exists(value) && validator.isInt(value + '', VIDEOS_CONSTRAINTS_FIELDS.FILE_SIZE)
152 }
153
154 function isVideoFileResolutionValid (value: string) {
155   return exists(value) && validator.isInt(value + '')
156 }
157
158 function isVideoFileExtnameValid (value: string) {
159   return VIDEOS_CONSTRAINTS_FIELDS.EXTNAME.indexOf(value) !== -1
160 }
161
162 function isVideoFileInfoHashValid (value: string) {
163   return exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.INFO_HASH)
164 }
165
166 function checkVideoExists (id: string, res: express.Response, callback: () => void) {
167   let promise: Promise<VideoInstance>
168   if (validator.isInt(id)) {
169     promise = db.Video.loadAndPopulateAccountAndPodAndTags(+id)
170   } else { // UUID
171     promise = db.Video.loadByUUIDAndPopulateAccountAndPodAndTags(id)
172   }
173
174   promise.then(video => {
175     if (!video) {
176       return res.status(404)
177                 .json({ error: 'Video not found' })
178                 .end()
179     }
180
181     res.locals.video = video
182     callback()
183   })
184   .catch(err => {
185     logger.error('Error in video request validator.', err)
186     return res.sendStatus(500)
187   })
188 }
189
190 // ---------------------------------------------------------------------------
191
192 export {
193   isVideoCategoryValid,
194   isVideoLicenceValid,
195   isVideoLanguageValid,
196   isVideoNSFWValid,
197   isVideoTruncatedDescriptionValid,
198   isVideoDescriptionValid,
199   isVideoDurationValid,
200   isVideoFileInfoHashValid,
201   isVideoNameValid,
202   isVideoTagsValid,
203   isVideoThumbnailValid,
204   isVideoThumbnailDataValid,
205   isVideoFileExtnameValid,
206   isVideoAbuseReasonValid,
207   isVideoAbuseReporterUsernameValid,
208   isVideoFile,
209   isVideoViewsValid,
210   isVideoLikesValid,
211   isVideoRatingTypeValid,
212   isVideoDislikesValid,
213   isVideoEventCountValid,
214   isVideoFileSizeValid,
215   isVideoPrivacyValid,
216   isRemoteVideoPrivacyValid,
217   isVideoFileResolutionValid,
218   checkVideoExists,
219   isVideoTagValid,
220   isRemoteVideoCategoryValid,
221   isRemoteVideoLicenceValid,
222   isRemoteVideoLanguageValid
223 }