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