]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/helpers/custom-validators/videos.ts
f4c1c8b0720d5f1944ed8f59f0c3ddf16a5515de
[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 import * as magnetUtil from 'magnet-uri'
21
22 const VIDEOS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEOS
23 const VIDEO_ABUSES_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_ABUSES
24
25 function isVideoCategoryValid (value: any) {
26 return value === null || VIDEO_CATEGORIES[ value ] !== undefined
27 }
28
29 function isVideoStateValid (value: any) {
30 return exists(value) && VIDEO_STATES[ value ] !== undefined
31 }
32
33 function isVideoLicenceValid (value: any) {
34 return value === null || VIDEO_LICENCES[ value ] !== undefined
35 }
36
37 function isVideoLanguageValid (value: any) {
38 return value === null ||
39 (typeof value === 'string' && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.LANGUAGE))
40 }
41
42 function isVideoDurationValid (value: string) {
43 return exists(value) && validator.isInt(value + '', VIDEOS_CONSTRAINTS_FIELDS.DURATION)
44 }
45
46 function isVideoTruncatedDescriptionValid (value: string) {
47 return exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.TRUNCATED_DESCRIPTION)
48 }
49
50 function isVideoDescriptionValid (value: string) {
51 return value === null || (exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.DESCRIPTION))
52 }
53
54 function isVideoSupportValid (value: string) {
55 return value === null || (exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.SUPPORT))
56 }
57
58 function isVideoNameValid (value: string) {
59 return exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.NAME)
60 }
61
62 function isVideoTagValid (tag: string) {
63 return exists(tag) && validator.isLength(tag, VIDEOS_CONSTRAINTS_FIELDS.TAG)
64 }
65
66 function isVideoTagsValid (tags: string[]) {
67 return tags === null || (
68 isArray(tags) &&
69 validator.isInt(tags.length.toString(), VIDEOS_CONSTRAINTS_FIELDS.TAGS) &&
70 tags.every(tag => isVideoTagValid(tag))
71 )
72 }
73
74 function isVideoAbuseReasonValid (value: string) {
75 return exists(value) && validator.isLength(value, VIDEO_ABUSES_CONSTRAINTS_FIELDS.REASON)
76 }
77
78 function isVideoViewsValid (value: string) {
79 return exists(value) && validator.isInt(value + '', VIDEOS_CONSTRAINTS_FIELDS.VIEWS)
80 }
81
82 function isVideoRatingTypeValid (value: string) {
83 return value === 'none' || values(VIDEO_RATE_TYPES).indexOf(value as VideoRateType) !== -1
84 }
85
86 const videoFileTypes = Object.keys(VIDEO_MIMETYPE_EXT).map(m => `(${m})`)
87 const videoFileTypesRegex = videoFileTypes.join('|')
88
89 function isVideoFile (files: { [ fieldname: string ]: Express.Multer.File[] } | Express.Multer.File[]) {
90 return isFileValid(files, videoFileTypesRegex, 'videofile', null)
91 }
92
93 const videoImageTypes = CONSTRAINTS_FIELDS.VIDEOS.IMAGE.EXTNAME
94 .map(v => v.replace('.', ''))
95 .join('|')
96 const videoImageTypesRegex = `image/(${videoImageTypes})`
97
98 function isVideoImage (files: { [ fieldname: string ]: Express.Multer.File[] } | Express.Multer.File[], field: string) {
99 return isFileValid(files, videoImageTypesRegex, field, CONSTRAINTS_FIELDS.VIDEOS.IMAGE.FILE_SIZE.max, true)
100 }
101
102 function isVideoPrivacyValid (value: number) {
103 return validator.isInt(value + '') && VIDEO_PRIVACIES[ value ] !== undefined
104 }
105
106 function isScheduleVideoUpdatePrivacyValid (value: number) {
107 return validator.isInt(value + '') &&
108 (
109 value === VideoPrivacy.UNLISTED ||
110 value === VideoPrivacy.PUBLIC
111 )
112 }
113
114 function isVideoFileInfoHashValid (value: string | null | undefined) {
115 return exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.INFO_HASH)
116 }
117
118 function isVideoFileResolutionValid (value: string) {
119 return exists(value) && validator.isInt(value + '')
120 }
121
122 function isVideoFPSResolutionValid (value: string) {
123 return value === null || validator.isInt(value + '')
124 }
125
126 function isVideoFileSizeValid (value: string) {
127 return exists(value) && validator.isInt(value + '', VIDEOS_CONSTRAINTS_FIELDS.FILE_SIZE)
128 }
129
130 function isVideoMagnetUriValid (value: string) {
131 if (!exists(value)) return false
132
133 const parsed = magnetUtil.decode(value)
134 return parsed && isVideoFileInfoHashValid(parsed.infoHash)
135 }
136
137 function checkUserCanManageVideo (user: UserModel, video: VideoModel, right: UserRight, res: Response) {
138 // Retrieve the user who did the request
139 if (video.isOwned() === false) {
140 res.status(403)
141 .json({ error: 'Cannot manage a video of another server.' })
142 .end()
143 return false
144 }
145
146 // Check if the user can delete the video
147 // The user can delete it if he has the right
148 // Or if s/he is the video's account
149 const account = video.VideoChannel.Account
150 if (user.hasRight(right) === false && account.userId !== user.id) {
151 res.status(403)
152 .json({ error: 'Cannot manage a video of another user.' })
153 .end()
154 return false
155 }
156
157 return true
158 }
159
160 async function isVideoExist (id: string, res: Response) {
161 let video: VideoModel | null
162
163 if (validator.isInt(id)) {
164 video = await VideoModel.loadAndPopulateAccountAndServerAndTags(+id)
165 } else { // UUID
166 video = await VideoModel.loadByUUIDAndPopulateAccountAndServerAndTags(id)
167 }
168
169 if (video === null) {
170 res.status(404)
171 .json({ error: 'Video not found' })
172 .end()
173
174 return false
175 }
176
177 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 isVideoAbuseReasonValid,
224 isVideoFile,
225 isVideoMagnetUriValid,
226 isVideoStateValid,
227 isVideoViewsValid,
228 isVideoRatingTypeValid,
229 isVideoDurationValid,
230 isVideoTagValid,
231 isVideoPrivacyValid,
232 isVideoFileResolutionValid,
233 isVideoFileSizeValid,
234 isVideoExist,
235 isVideoImage,
236 isVideoChannelOfAccountExist,
237 isVideoSupportValid
238 }