]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/helpers/custom-validators/videos.ts
Implement captions/subtitles
[github/Chocobozzz/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, VideoPrivacy, 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', null)
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, CONSTRAINTS_FIELDS.VIDEOS.IMAGE.FILE_SIZE.max, true)
99 }
100
101 function isVideoPrivacyValid (value: number) {
102 return validator.isInt(value + '') && VIDEO_PRIVACIES[ value ] !== undefined
103 }
104
105 function isScheduleVideoUpdatePrivacyValid (value: number) {
106 return validator.isInt(value + '') &&
107 (
108 value === VideoPrivacy.UNLISTED ||
109 value === VideoPrivacy.PUBLIC
110 )
111 }
112
113 function isVideoFileInfoHashValid (value: string) {
114 return exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.INFO_HASH)
115 }
116
117 function isVideoFileResolutionValid (value: string) {
118 return exists(value) && validator.isInt(value + '')
119 }
120
121 function isVideoFPSResolutionValid (value: string) {
122 return value === null || validator.isInt(value + '')
123 }
124
125 function isVideoFileSizeValid (value: string) {
126 return exists(value) && validator.isInt(value + '', VIDEOS_CONSTRAINTS_FIELDS.FILE_SIZE)
127 }
128
129 function checkUserCanManageVideo (user: UserModel, video: VideoModel, right: UserRight, res: Response) {
130 // Retrieve the user who did the request
131 if (video.isOwned() === false) {
132 res.status(403)
133 .json({ error: 'Cannot manage a video of another server.' })
134 .end()
135 return false
136 }
137
138 // Check if the user can delete the video
139 // The user can delete it if he has the right
140 // Or if s/he is the video's account
141 const account = video.VideoChannel.Account
142 if (user.hasRight(right) === false && account.userId !== user.id) {
143 res.status(403)
144 .json({ error: 'Cannot manage a video of another user.' })
145 .end()
146 return false
147 }
148
149 return true
150 }
151
152 async function isVideoExist (id: string, res: Response) {
153 let video: VideoModel
154
155 if (validator.isInt(id)) {
156 video = await VideoModel.loadAndPopulateAccountAndServerAndTags(+id)
157 } else { // UUID
158 video = await VideoModel.loadByUUIDAndPopulateAccountAndServerAndTags(id)
159 }
160
161 if (!video) {
162 res.status(404)
163 .json({ error: 'Video not found' })
164 .end()
165
166 return false
167 }
168
169 res.locals.video = video
170 return true
171 }
172
173 async function isVideoChannelOfAccountExist (channelId: number, user: UserModel, res: Response) {
174 if (user.hasRight(UserRight.UPDATE_ANY_VIDEO) === true) {
175 const videoChannel = await VideoChannelModel.loadAndPopulateAccount(channelId)
176 if (!videoChannel) {
177 res.status(400)
178 .json({ error: 'Unknown video video channel on this instance.' })
179 .end()
180
181 return false
182 }
183
184 res.locals.videoChannel = videoChannel
185 return true
186 }
187
188 const videoChannel = await VideoChannelModel.loadByIdAndAccount(channelId, user.Account.id)
189 if (!videoChannel) {
190 res.status(400)
191 .json({ error: 'Unknown video video channel for this account.' })
192 .end()
193
194 return false
195 }
196
197 res.locals.videoChannel = videoChannel
198 return true
199 }
200
201 // ---------------------------------------------------------------------------
202
203 export {
204 isVideoCategoryValid,
205 checkUserCanManageVideo,
206 isVideoLicenceValid,
207 isVideoLanguageValid,
208 isVideoTruncatedDescriptionValid,
209 isVideoDescriptionValid,
210 isVideoFileInfoHashValid,
211 isVideoNameValid,
212 isVideoTagsValid,
213 isVideoFPSResolutionValid,
214 isScheduleVideoUpdatePrivacyValid,
215 isVideoAbuseReasonValid,
216 isVideoFile,
217 isVideoStateValid,
218 isVideoViewsValid,
219 isVideoRatingTypeValid,
220 isVideoDurationValid,
221 isVideoTagValid,
222 isVideoPrivacyValid,
223 isVideoFileResolutionValid,
224 isVideoFileSizeValid,
225 isVideoExist,
226 isVideoImage,
227 isVideoChannelOfAccountExist,
228 isVideoSupportValid
229 }