]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - 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
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_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'
17 import { VideoModel } from '../../models/video/video'
18 import { exists, isArray, isFileValid } from './misc'
19 import { VideoChannelModel } from '../../models/video/video-channel'
20 import { UserModel } from '../../models/account/user'
21 import * as magnetUtil from 'magnet-uri'
22 import { VideoAbuseModel } from '../../models/video/video-abuse'
23
24 const VIDEOS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEOS
25 const VIDEO_ABUSES_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_ABUSES
26
27 function isVideoCategoryValid (value: any) {
28 return value === null || VIDEO_CATEGORIES[ value ] !== undefined
29 }
30
31 function isVideoStateValid (value: any) {
32 return exists(value) && VIDEO_STATES[ value ] !== undefined
33 }
34
35 function isVideoLicenceValid (value: any) {
36 return value === null || VIDEO_LICENCES[ value ] !== undefined
37 }
38
39 function isVideoLanguageValid (value: any) {
40 return value === null ||
41 (typeof value === 'string' && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.LANGUAGE))
42 }
43
44 function isVideoDurationValid (value: string) {
45 return exists(value) && validator.isInt(value + '', VIDEOS_CONSTRAINTS_FIELDS.DURATION)
46 }
47
48 function isVideoTruncatedDescriptionValid (value: string) {
49 return exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.TRUNCATED_DESCRIPTION)
50 }
51
52 function isVideoDescriptionValid (value: string) {
53 return value === null || (exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.DESCRIPTION))
54 }
55
56 function isVideoSupportValid (value: string) {
57 return value === null || (exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.SUPPORT))
58 }
59
60 function isVideoNameValid (value: string) {
61 return exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.NAME)
62 }
63
64 function isVideoTagValid (tag: string) {
65 return exists(tag) && validator.isLength(tag, VIDEOS_CONSTRAINTS_FIELDS.TAG)
66 }
67
68 function 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
76 function isVideoViewsValid (value: string) {
77 return exists(value) && validator.isInt(value + '', VIDEOS_CONSTRAINTS_FIELDS.VIEWS)
78 }
79
80 function isVideoRatingTypeValid (value: string) {
81 return value === 'none' || values(VIDEO_RATE_TYPES).indexOf(value as VideoRateType) !== -1
82 }
83
84 const videoFileTypes = Object.keys(VIDEO_MIMETYPE_EXT).map(m => `(${m})`)
85 const videoFileTypesRegex = videoFileTypes.join('|')
86
87 function isVideoFile (files: { [ fieldname: string ]: Express.Multer.File[] } | Express.Multer.File[]) {
88 return isFileValid(files, videoFileTypesRegex, 'videofile', null)
89 }
90
91 const videoImageTypes = CONSTRAINTS_FIELDS.VIDEOS.IMAGE.EXTNAME
92 .map(v => v.replace('.', ''))
93 .join('|')
94 const videoImageTypesRegex = `image/(${videoImageTypes})`
95
96 function 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
100 function isVideoPrivacyValid (value: number) {
101 return validator.isInt(value + '') && VIDEO_PRIVACIES[ value ] !== undefined
102 }
103
104 function isScheduleVideoUpdatePrivacyValid (value: number) {
105 return validator.isInt(value + '') &&
106 (
107 value === VideoPrivacy.UNLISTED ||
108 value === VideoPrivacy.PUBLIC
109 )
110 }
111
112 function isVideoFileInfoHashValid (value: string | null | undefined) {
113 return exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.INFO_HASH)
114 }
115
116 function isVideoFileResolutionValid (value: string) {
117 return exists(value) && validator.isInt(value + '')
118 }
119
120 function isVideoFPSResolutionValid (value: string) {
121 return value === null || validator.isInt(value + '')
122 }
123
124 function isVideoFileSizeValid (value: string) {
125 return exists(value) && validator.isInt(value + '', VIDEOS_CONSTRAINTS_FIELDS.FILE_SIZE)
126 }
127
128 function isVideoMagnetUriValid (value: string) {
129 if (!exists(value)) return false
130
131 const parsed = magnetUtil.decode(value)
132 return parsed && isVideoFileInfoHashValid(parsed.infoHash)
133 }
134
135 function 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
158 async 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
179 async 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
209 export {
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 }