]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/helpers/custom-validators/videos.ts
Add ability to limit videos history size
[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, VideoFilter, VideoPrivacy, VideoRateType } from '../../../shared'
7 import {
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'
16 import { VideoModel } from '../../models/video/video'
17 import { exists, isArray, isDateValid, 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 import { fetchVideo, VideoFetchType } from '../video'
22
23 const VIDEOS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEOS
24
25 function isVideoFilterValid (filter: VideoFilter) {
26 return filter === 'local' || filter === 'all-local'
27 }
28
29 function isVideoCategoryValid (value: any) {
30 return value === null || VIDEO_CATEGORIES[ value ] !== undefined
31 }
32
33 function isVideoStateValid (value: any) {
34 return exists(value) && VIDEO_STATES[ value ] !== undefined
35 }
36
37 function isVideoLicenceValid (value: any) {
38 return value === null || VIDEO_LICENCES[ value ] !== undefined
39 }
40
41 function isVideoLanguageValid (value: any) {
42 return value === null ||
43 (typeof value === 'string' && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.LANGUAGE))
44 }
45
46 function isVideoDurationValid (value: string) {
47 return exists(value) && validator.isInt(value + '', VIDEOS_CONSTRAINTS_FIELDS.DURATION)
48 }
49
50 function isVideoTruncatedDescriptionValid (value: string) {
51 return exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.TRUNCATED_DESCRIPTION)
52 }
53
54 function isVideoDescriptionValid (value: string) {
55 return value === null || (exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.DESCRIPTION))
56 }
57
58 function isVideoSupportValid (value: string) {
59 return value === null || (exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.SUPPORT))
60 }
61
62 function isVideoNameValid (value: string) {
63 return exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.NAME)
64 }
65
66 function isVideoTagValid (tag: string) {
67 return exists(tag) && validator.isLength(tag, VIDEOS_CONSTRAINTS_FIELDS.TAG)
68 }
69
70 function 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
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 function isVideoFileExtnameValid (value: string) {
87 return exists(value) && MIMETYPES.VIDEO.EXT_MIMETYPE[value] !== undefined
88 }
89
90 function 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
98 const videoImageTypes = CONSTRAINTS_FIELDS.VIDEOS.IMAGE.EXTNAME
99 .map(v => v.replace('.', ''))
100 .join('|')
101 const videoImageTypesRegex = `image/(${videoImageTypes})`
102
103 function 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
107 function isVideoPrivacyValid (value: number) {
108 return validator.isInt(value + '') && VIDEO_PRIVACIES[ value ] !== undefined
109 }
110
111 function isScheduleVideoUpdatePrivacyValid (value: number) {
112 return validator.isInt(value + '') &&
113 (
114 value === VideoPrivacy.UNLISTED ||
115 value === VideoPrivacy.PUBLIC
116 )
117 }
118
119 function isVideoOriginallyPublishedAtValid (value: string | null) {
120 return value === null || isDateValid(value)
121 }
122
123 function isVideoFileInfoHashValid (value: string | null | undefined) {
124 return exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.INFO_HASH)
125 }
126
127 function isVideoFileResolutionValid (value: string) {
128 return exists(value) && validator.isInt(value + '')
129 }
130
131 function isVideoFPSResolutionValid (value: string) {
132 return value === null || validator.isInt(value + '')
133 }
134
135 function isVideoFileSizeValid (value: string) {
136 return exists(value) && validator.isInt(value + '', VIDEOS_CONSTRAINTS_FIELDS.FILE_SIZE)
137 }
138
139 function isVideoMagnetUriValid (value: string) {
140 if (!exists(value)) return false
141
142 const parsed = magnetUtil.decode(value)
143 return parsed && isVideoFileInfoHashValid(parsed.infoHash)
144 }
145
146 function 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
169 async 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
186 async 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
216 export {
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 }