]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame_incremental - server/helpers/custom-validators/videos.ts
Merge branch 'release/v1.0.0' into develop
[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, VideoFilter, VideoPrivacy, VideoRateType } from '../../../shared'
7import {
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'
16import { VideoModel } from '../../models/video/video'
17import { exists, isArray, isFileValid } from './misc'
18import { VideoChannelModel } from '../../models/video/video-channel'
19import { UserModel } from '../../models/account/user'
20import * as magnetUtil from 'magnet-uri'
21import { fetchVideo, VideoFetchType } from '../video'
22
23const VIDEOS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEOS
24
25function isVideoFilterValid (filter: VideoFilter) {
26 return filter === 'local' || filter === 'all-local'
27}
28
29function isVideoCategoryValid (value: any) {
30 return value === null || VIDEO_CATEGORIES[ value ] !== undefined
31}
32
33function isVideoStateValid (value: any) {
34 return exists(value) && VIDEO_STATES[ value ] !== undefined
35}
36
37function isVideoLicenceValid (value: any) {
38 return value === null || VIDEO_LICENCES[ value ] !== undefined
39}
40
41function isVideoLanguageValid (value: any) {
42 return value === null ||
43 (typeof value === 'string' && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.LANGUAGE))
44}
45
46function isVideoDurationValid (value: string) {
47 return exists(value) && validator.isInt(value + '', VIDEOS_CONSTRAINTS_FIELDS.DURATION)
48}
49
50function isVideoTruncatedDescriptionValid (value: string) {
51 return exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.TRUNCATED_DESCRIPTION)
52}
53
54function isVideoDescriptionValid (value: string) {
55 return value === null || (exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.DESCRIPTION))
56}
57
58function isVideoSupportValid (value: string) {
59 return value === null || (exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.SUPPORT))
60}
61
62function isVideoNameValid (value: string) {
63 return exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.NAME)
64}
65
66function isVideoTagValid (tag: string) {
67 return exists(tag) && validator.isLength(tag, VIDEOS_CONSTRAINTS_FIELDS.TAG)
68}
69
70function isVideoTagsValid (tags: string[]) {
71 return tags === null || (
72 isArray(tags) &&
73 validator.isInt(tags.length.toString(), VIDEOS_CONSTRAINTS_FIELDS.TAGS) &&
74 tags.every(tag => isVideoTagValid(tag))
75 )
76}
77
78function isVideoViewsValid (value: string) {
79 return exists(value) && validator.isInt(value + '', VIDEOS_CONSTRAINTS_FIELDS.VIEWS)
80}
81
82function isVideoRatingTypeValid (value: string) {
83 return value === 'none' || values(VIDEO_RATE_TYPES).indexOf(value as VideoRateType) !== -1
84}
85
86const videoFileTypes = Object.keys(VIDEO_MIMETYPE_EXT).map(m => `(${m})`)
87const videoFileTypesRegex = videoFileTypes.join('|')
88
89function isVideoFile (files: { [ fieldname: string ]: Express.Multer.File[] } | Express.Multer.File[]) {
90 return isFileValid(files, videoFileTypesRegex, 'videofile', null)
91}
92
93const videoImageTypes = CONSTRAINTS_FIELDS.VIDEOS.IMAGE.EXTNAME
94 .map(v => v.replace('.', ''))
95 .join('|')
96const videoImageTypesRegex = `image/(${videoImageTypes})`
97
98function 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
102function isVideoPrivacyValid (value: number) {
103 return validator.isInt(value + '') && VIDEO_PRIVACIES[ value ] !== undefined
104}
105
106function isScheduleVideoUpdatePrivacyValid (value: number) {
107 return validator.isInt(value + '') &&
108 (
109 value === VideoPrivacy.UNLISTED ||
110 value === VideoPrivacy.PUBLIC
111 )
112}
113
114function isVideoFileInfoHashValid (value: string | null | undefined) {
115 return exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.INFO_HASH)
116}
117
118function isVideoFileResolutionValid (value: string) {
119 return exists(value) && validator.isInt(value + '')
120}
121
122function isVideoFPSResolutionValid (value: string) {
123 return value === null || validator.isInt(value + '')
124}
125
126function isVideoFileSizeValid (value: string) {
127 return exists(value) && validator.isInt(value + '', VIDEOS_CONSTRAINTS_FIELDS.FILE_SIZE)
128}
129
130function isVideoMagnetUriValid (value: string) {
131 if (!exists(value)) return false
132
133 const parsed = magnetUtil.decode(value)
134 return parsed && isVideoFileInfoHashValid(parsed.infoHash)
135}
136
137function 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
160async function isVideoExist (id: string, res: Response, fetchType: VideoFetchType = 'all') {
161 const userId = res.locals.oauth ? res.locals.oauth.token.User.id : undefined
162
163 const video = await fetchVideo(id, fetchType, userId)
164
165 if (video === null) {
166 res.status(404)
167 .json({ error: 'Video not found' })
168 .end()
169
170 return false
171 }
172
173 if (fetchType !== 'none') res.locals.video = video
174 return true
175}
176
177async function isVideoChannelOfAccountExist (channelId: number, user: UserModel, res: Response) {
178 if (user.hasRight(UserRight.UPDATE_ANY_VIDEO) === true) {
179 const videoChannel = await VideoChannelModel.loadAndPopulateAccount(channelId)
180 if (videoChannel === null) {
181 res.status(400)
182 .json({ error: 'Unknown video `video channel` on this instance.' })
183 .end()
184
185 return false
186 }
187
188 res.locals.videoChannel = videoChannel
189 return true
190 }
191
192 const videoChannel = await VideoChannelModel.loadByIdAndAccount(channelId, user.Account.id)
193 if (videoChannel === null) {
194 res.status(400)
195 .json({ error: 'Unknown video `video channel` for this account.' })
196 .end()
197
198 return false
199 }
200
201 res.locals.videoChannel = videoChannel
202 return true
203}
204
205// ---------------------------------------------------------------------------
206
207export {
208 isVideoCategoryValid,
209 checkUserCanManageVideo,
210 isVideoLicenceValid,
211 isVideoLanguageValid,
212 isVideoTruncatedDescriptionValid,
213 isVideoDescriptionValid,
214 isVideoFileInfoHashValid,
215 isVideoNameValid,
216 isVideoTagsValid,
217 isVideoFPSResolutionValid,
218 isScheduleVideoUpdatePrivacyValid,
219 isVideoFile,
220 isVideoMagnetUriValid,
221 isVideoStateValid,
222 isVideoViewsValid,
223 isVideoRatingTypeValid,
224 isVideoDurationValid,
225 isVideoTagValid,
226 isVideoPrivacyValid,
227 isVideoFileResolutionValid,
228 isVideoFileSizeValid,
229 isVideoExist,
230 isVideoImage,
231 isVideoChannelOfAccountExist,
232 isVideoSupportValid,
233 isVideoFilterValid
234}