]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/helpers/custom-validators/videos.ts
Support additional video extensions
[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, VideoFilter, VideoPrivacy, VideoRateType } from '../../../shared'
7 import {
8 CONSTRAINTS_FIELDS, MIMETYPES,
9 VIDEO_CATEGORIES,
10 VIDEO_LICENCES,
11 VIDEO_PRIVACIES,
12 VIDEO_RATE_TYPES,
13 VIDEO_STATES
14 } from '../../initializers'
15 import { VideoModel } from '../../models/video/video'
16 import { exists, isArray, isFileValid } from './misc'
17 import { VideoChannelModel } from '../../models/video/video-channel'
18 import { UserModel } from '../../models/account/user'
19 import * as magnetUtil from 'magnet-uri'
20 import { fetchVideo, VideoFetchType } from '../video'
21
22 const VIDEOS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEOS
23
24 function isVideoFilterValid (filter: VideoFilter) {
25 return filter === 'local' || filter === 'all-local'
26 }
27
28 function isVideoCategoryValid (value: any) {
29 return value === null || VIDEO_CATEGORIES[ value ] !== undefined
30 }
31
32 function isVideoStateValid (value: any) {
33 return exists(value) && VIDEO_STATES[ value ] !== undefined
34 }
35
36 function isVideoLicenceValid (value: any) {
37 return value === null || VIDEO_LICENCES[ value ] !== undefined
38 }
39
40 function isVideoLanguageValid (value: any) {
41 return value === null ||
42 (typeof value === 'string' && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.LANGUAGE))
43 }
44
45 function isVideoDurationValid (value: string) {
46 return exists(value) && validator.isInt(value + '', VIDEOS_CONSTRAINTS_FIELDS.DURATION)
47 }
48
49 function isVideoTruncatedDescriptionValid (value: string) {
50 return exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.TRUNCATED_DESCRIPTION)
51 }
52
53 function isVideoDescriptionValid (value: string) {
54 return value === null || (exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.DESCRIPTION))
55 }
56
57 function isVideoSupportValid (value: string) {
58 return value === null || (exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.SUPPORT))
59 }
60
61 function isVideoNameValid (value: string) {
62 return exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.NAME)
63 }
64
65 function isVideoTagValid (tag: string) {
66 return exists(tag) && validator.isLength(tag, VIDEOS_CONSTRAINTS_FIELDS.TAG)
67 }
68
69 function isVideoTagsValid (tags: string[]) {
70 return tags === null || (
71 isArray(tags) &&
72 validator.isInt(tags.length.toString(), VIDEOS_CONSTRAINTS_FIELDS.TAGS) &&
73 tags.every(tag => isVideoTagValid(tag))
74 )
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 function isVideoFileExtnameValid (value: string) {
86 return exists(value) && MIMETYPES.VIDEO.EXT_MIMETYPE[value] !== undefined
87 }
88
89 function isVideoFile (files: { [ fieldname: string ]: Express.Multer.File[] } | Express.Multer.File[]) {
90 const videoFileTypesRegex = Object.keys(MIMETYPES.VIDEO.MIMETYPE_EXT)
91 .map(m => `(${m})`)
92 .join('|')
93
94 return isFileValid(files, videoFileTypesRegex, 'videofile', null)
95 }
96
97 const videoImageTypes = CONSTRAINTS_FIELDS.VIDEOS.IMAGE.EXTNAME
98 .map(v => v.replace('.', ''))
99 .join('|')
100 const videoImageTypesRegex = `image/(${videoImageTypes})`
101
102 function isVideoImage (files: { [ fieldname: string ]: Express.Multer.File[] } | Express.Multer.File[], field: string) {
103 return isFileValid(files, videoImageTypesRegex, field, CONSTRAINTS_FIELDS.VIDEOS.IMAGE.FILE_SIZE.max, true)
104 }
105
106 function isVideoPrivacyValid (value: number) {
107 return validator.isInt(value + '') && VIDEO_PRIVACIES[ value ] !== undefined
108 }
109
110 function isScheduleVideoUpdatePrivacyValid (value: number) {
111 return validator.isInt(value + '') &&
112 (
113 value === VideoPrivacy.UNLISTED ||
114 value === VideoPrivacy.PUBLIC
115 )
116 }
117
118 function isVideoFileInfoHashValid (value: string | null | undefined) {
119 return exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.INFO_HASH)
120 }
121
122 function isVideoFileResolutionValid (value: string) {
123 return exists(value) && validator.isInt(value + '')
124 }
125
126 function isVideoFPSResolutionValid (value: string) {
127 return value === null || validator.isInt(value + '')
128 }
129
130 function isVideoFileSizeValid (value: string) {
131 return exists(value) && validator.isInt(value + '', VIDEOS_CONSTRAINTS_FIELDS.FILE_SIZE)
132 }
133
134 function isVideoMagnetUriValid (value: string) {
135 if (!exists(value)) return false
136
137 const parsed = magnetUtil.decode(value)
138 return parsed && isVideoFileInfoHashValid(parsed.infoHash)
139 }
140
141 function checkUserCanManageVideo (user: UserModel, video: VideoModel, right: UserRight, res: Response) {
142 // Retrieve the user who did the request
143 if (video.isOwned() === false) {
144 res.status(403)
145 .json({ error: 'Cannot manage a video of another server.' })
146 .end()
147 return false
148 }
149
150 // Check if the user can delete the video
151 // The user can delete it if he has the right
152 // Or if s/he is the video's account
153 const account = video.VideoChannel.Account
154 if (user.hasRight(right) === false && account.userId !== user.id) {
155 res.status(403)
156 .json({ error: 'Cannot manage a video of another user.' })
157 .end()
158 return false
159 }
160
161 return true
162 }
163
164 async function isVideoExist (id: string, res: Response, fetchType: VideoFetchType = 'all') {
165 const userId = res.locals.oauth ? res.locals.oauth.token.User.id : undefined
166
167 const video = await fetchVideo(id, fetchType, userId)
168
169 if (video === null) {
170 res.status(404)
171 .json({ error: 'Video not found' })
172 .end()
173
174 return false
175 }
176
177 if (fetchType !== 'none') res.locals.video = video
178 return true
179 }
180
181 async function isVideoChannelOfAccountExist (channelId: number, user: UserModel, res: Response) {
182 if (user.hasRight(UserRight.UPDATE_ANY_VIDEO) === true) {
183 const videoChannel = await VideoChannelModel.loadAndPopulateAccount(channelId)
184 if (videoChannel === null) {
185 res.status(400)
186 .json({ error: 'Unknown video `video channel` on this instance.' })
187 .end()
188
189 return false
190 }
191
192 res.locals.videoChannel = videoChannel
193 return true
194 }
195
196 const videoChannel = await VideoChannelModel.loadByIdAndAccount(channelId, user.Account.id)
197 if (videoChannel === null) {
198 res.status(400)
199 .json({ error: 'Unknown video `video channel` for this account.' })
200 .end()
201
202 return false
203 }
204
205 res.locals.videoChannel = videoChannel
206 return true
207 }
208
209 // ---------------------------------------------------------------------------
210
211 export {
212 isVideoCategoryValid,
213 checkUserCanManageVideo,
214 isVideoLicenceValid,
215 isVideoLanguageValid,
216 isVideoTruncatedDescriptionValid,
217 isVideoDescriptionValid,
218 isVideoFileInfoHashValid,
219 isVideoNameValid,
220 isVideoTagsValid,
221 isVideoFPSResolutionValid,
222 isScheduleVideoUpdatePrivacyValid,
223 isVideoFile,
224 isVideoMagnetUriValid,
225 isVideoStateValid,
226 isVideoViewsValid,
227 isVideoRatingTypeValid,
228 isVideoFileExtnameValid,
229 isVideoDurationValid,
230 isVideoTagValid,
231 isVideoPrivacyValid,
232 isVideoFileResolutionValid,
233 isVideoFileSizeValid,
234 isVideoExist,
235 isVideoImage,
236 isVideoChannelOfAccountExist,
237 isVideoSupportValid,
238 isVideoFilterValid
239 }