]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame_incremental - server/helpers/custom-validators/videos.ts
Add ability to limit videos history size
[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 MIMETYPES,
10 VIDEO_CATEGORIES,
11 VIDEO_LICENCES,
12 VIDEO_PRIVACIES,
13 VIDEO_RATE_TYPES,
14 VIDEO_STATES
15} from '../../initializers/constants'
16import { VideoModel } from '../../models/video/video'
17import { exists, isArray, isDateValid, 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
86function isVideoFileExtnameValid (value: string) {
87 return exists(value) && MIMETYPES.VIDEO.EXT_MIMETYPE[value] !== undefined
88}
89
90function isVideoFile (files: { [ fieldname: string ]: Express.Multer.File[] } | Express.Multer.File[]) {
91 const videoFileTypesRegex = Object.keys(MIMETYPES.VIDEO.MIMETYPE_EXT)
92 .map(m => `(${m})`)
93 .join('|')
94
95 return isFileValid(files, videoFileTypesRegex, 'videofile', null)
96}
97
98const videoImageTypes = CONSTRAINTS_FIELDS.VIDEOS.IMAGE.EXTNAME
99 .map(v => v.replace('.', ''))
100 .join('|')
101const videoImageTypesRegex = `image/(${videoImageTypes})`
102
103function isVideoImage (files: { [ fieldname: string ]: Express.Multer.File[] } | Express.Multer.File[], field: string) {
104 return isFileValid(files, videoImageTypesRegex, field, CONSTRAINTS_FIELDS.VIDEOS.IMAGE.FILE_SIZE.max, true)
105}
106
107function isVideoPrivacyValid (value: number) {
108 return validator.isInt(value + '') && VIDEO_PRIVACIES[ value ] !== undefined
109}
110
111function isScheduleVideoUpdatePrivacyValid (value: number) {
112 return validator.isInt(value + '') &&
113 (
114 value === VideoPrivacy.UNLISTED ||
115 value === VideoPrivacy.PUBLIC
116 )
117}
118
119function isVideoOriginallyPublishedAtValid (value: string | null) {
120 return value === null || isDateValid(value)
121}
122
123function isVideoFileInfoHashValid (value: string | null | undefined) {
124 return exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.INFO_HASH)
125}
126
127function isVideoFileResolutionValid (value: string) {
128 return exists(value) && validator.isInt(value + '')
129}
130
131function isVideoFPSResolutionValid (value: string) {
132 return value === null || validator.isInt(value + '')
133}
134
135function isVideoFileSizeValid (value: string) {
136 return exists(value) && validator.isInt(value + '', VIDEOS_CONSTRAINTS_FIELDS.FILE_SIZE)
137}
138
139function isVideoMagnetUriValid (value: string) {
140 if (!exists(value)) return false
141
142 const parsed = magnetUtil.decode(value)
143 return parsed && isVideoFileInfoHashValid(parsed.infoHash)
144}
145
146function checkUserCanManageVideo (user: UserModel, video: VideoModel, right: UserRight, res: Response) {
147 // Retrieve the user who did the request
148 if (video.isOwned() === false) {
149 res.status(403)
150 .json({ error: 'Cannot manage a video of another server.' })
151 .end()
152 return false
153 }
154
155 // Check if the user can delete the video
156 // The user can delete it if he has the right
157 // Or if s/he is the video's account
158 const account = video.VideoChannel.Account
159 if (user.hasRight(right) === false && account.userId !== user.id) {
160 res.status(403)
161 .json({ error: 'Cannot manage a video of another user.' })
162 .end()
163 return false
164 }
165
166 return true
167}
168
169async function doesVideoExist (id: number | string, res: Response, fetchType: VideoFetchType = 'all') {
170 const userId = res.locals.oauth ? res.locals.oauth.token.User.id : undefined
171
172 const video = await fetchVideo(id, fetchType, userId)
173
174 if (video === null) {
175 res.status(404)
176 .json({ error: 'Video not found' })
177 .end()
178
179 return false
180 }
181
182 if (fetchType !== 'none') res.locals.video = video
183 return true
184}
185
186async function doesVideoChannelOfAccountExist (channelId: number, user: UserModel, res: Response) {
187 if (user.hasRight(UserRight.UPDATE_ANY_VIDEO) === true) {
188 const videoChannel = await VideoChannelModel.loadAndPopulateAccount(channelId)
189 if (videoChannel === null) {
190 res.status(400)
191 .json({ error: 'Unknown video `video channel` on this instance.' })
192 .end()
193
194 return false
195 }
196
197 res.locals.videoChannel = videoChannel
198 return true
199 }
200
201 const videoChannel = await VideoChannelModel.loadByIdAndAccount(channelId, user.Account.id)
202 if (videoChannel === null) {
203 res.status(400)
204 .json({ error: 'Unknown video `video channel` for this account.' })
205 .end()
206
207 return false
208 }
209
210 res.locals.videoChannel = videoChannel
211 return true
212}
213
214// ---------------------------------------------------------------------------
215
216export {
217 isVideoCategoryValid,
218 checkUserCanManageVideo,
219 isVideoLicenceValid,
220 isVideoLanguageValid,
221 isVideoTruncatedDescriptionValid,
222 isVideoDescriptionValid,
223 isVideoFileInfoHashValid,
224 isVideoNameValid,
225 isVideoTagsValid,
226 isVideoFPSResolutionValid,
227 isScheduleVideoUpdatePrivacyValid,
228 isVideoOriginallyPublishedAtValid,
229 isVideoFile,
230 isVideoMagnetUriValid,
231 isVideoStateValid,
232 isVideoViewsValid,
233 isVideoRatingTypeValid,
234 isVideoFileExtnameValid,
235 isVideoDurationValid,
236 isVideoTagValid,
237 isVideoPrivacyValid,
238 isVideoFileResolutionValid,
239 isVideoFileSizeValid,
240 doesVideoExist,
241 isVideoImage,
242 doesVideoChannelOfAccountExist,
243 isVideoSupportValid,
244 isVideoFilterValid
245}