]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame_incremental - server/helpers/custom-validators/videos.ts
Add /accounts/:username/ratings endpoint (#1756)
[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, MIMETYPES,
9 VIDEO_CATEGORIES,
10 VIDEO_LICENCES,
11 VIDEO_PRIVACIES,
12 VIDEO_RATE_TYPES,
13 VIDEO_STATES
14} from '../../initializers'
15import { VideoModel } from '../../models/video/video'
16import { exists, isArray, isDateValid, isFileValid } from './misc'
17import { VideoChannelModel } from '../../models/video/video-channel'
18import { UserModel } from '../../models/account/user'
19import * as magnetUtil from 'magnet-uri'
20import { fetchVideo, VideoFetchType } from '../video'
21
22const VIDEOS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEOS
23
24function isVideoFilterValid (filter: VideoFilter) {
25 return filter === 'local' || filter === 'all-local'
26}
27
28function isVideoCategoryValid (value: any) {
29 return value === null || VIDEO_CATEGORIES[ value ] !== undefined
30}
31
32function isVideoStateValid (value: any) {
33 return exists(value) && VIDEO_STATES[ value ] !== undefined
34}
35
36function isVideoLicenceValid (value: any) {
37 return value === null || VIDEO_LICENCES[ value ] !== undefined
38}
39
40function isVideoLanguageValid (value: any) {
41 return value === null ||
42 (typeof value === 'string' && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.LANGUAGE))
43}
44
45function isVideoDurationValid (value: string) {
46 return exists(value) && validator.isInt(value + '', VIDEOS_CONSTRAINTS_FIELDS.DURATION)
47}
48
49function isVideoTruncatedDescriptionValid (value: string) {
50 return exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.TRUNCATED_DESCRIPTION)
51}
52
53function isVideoDescriptionValid (value: string) {
54 return value === null || (exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.DESCRIPTION))
55}
56
57function isVideoSupportValid (value: string) {
58 return value === null || (exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.SUPPORT))
59}
60
61function isVideoNameValid (value: string) {
62 return exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.NAME)
63}
64
65function isVideoTagValid (tag: string) {
66 return exists(tag) && validator.isLength(tag, VIDEOS_CONSTRAINTS_FIELDS.TAG)
67}
68
69function isVideoTagsValid (tags: string[]) {
70 return tags === null || (
71 isArray(tags) &&
72 validator.isInt(tags.length.toString(), VIDEOS_CONSTRAINTS_FIELDS.TAGS) &&
73 tags.every(tag => isVideoTagValid(tag))
74 )
75}
76
77function isVideoViewsValid (value: string) {
78 return exists(value) && validator.isInt(value + '', VIDEOS_CONSTRAINTS_FIELDS.VIEWS)
79}
80
81function isVideoRatingTypeValid (value: string) {
82 return value === 'none' || values(VIDEO_RATE_TYPES).indexOf(value as VideoRateType) !== -1
83}
84
85function isVideoFileExtnameValid (value: string) {
86 return exists(value) && MIMETYPES.VIDEO.EXT_MIMETYPE[value] !== undefined
87}
88
89function isVideoFile (files: { [ fieldname: string ]: Express.Multer.File[] } | Express.Multer.File[]) {
90 const videoFileTypesRegex = Object.keys(MIMETYPES.VIDEO.MIMETYPE_EXT)
91 .map(m => `(${m})`)
92 .join('|')
93
94 return isFileValid(files, videoFileTypesRegex, 'videofile', null)
95}
96
97const videoImageTypes = CONSTRAINTS_FIELDS.VIDEOS.IMAGE.EXTNAME
98 .map(v => v.replace('.', ''))
99 .join('|')
100const videoImageTypesRegex = `image/(${videoImageTypes})`
101
102function isVideoImage (files: { [ fieldname: string ]: Express.Multer.File[] } | Express.Multer.File[], field: string) {
103 return isFileValid(files, videoImageTypesRegex, field, CONSTRAINTS_FIELDS.VIDEOS.IMAGE.FILE_SIZE.max, true)
104}
105
106function isVideoPrivacyValid (value: number) {
107 return validator.isInt(value + '') && VIDEO_PRIVACIES[ value ] !== undefined
108}
109
110function isScheduleVideoUpdatePrivacyValid (value: number) {
111 return validator.isInt(value + '') &&
112 (
113 value === VideoPrivacy.UNLISTED ||
114 value === VideoPrivacy.PUBLIC
115 )
116}
117
118function isVideoOriginallyPublishedAtValid (value: string | null) {
119 return value === null || isDateValid(value)
120}
121
122function isVideoFileInfoHashValid (value: string | null | undefined) {
123 return exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.INFO_HASH)
124}
125
126function isVideoFileResolutionValid (value: string) {
127 return exists(value) && validator.isInt(value + '')
128}
129
130function isVideoFPSResolutionValid (value: string) {
131 return value === null || validator.isInt(value + '')
132}
133
134function isVideoFileSizeValid (value: string) {
135 return exists(value) && validator.isInt(value + '', VIDEOS_CONSTRAINTS_FIELDS.FILE_SIZE)
136}
137
138function isVideoMagnetUriValid (value: string) {
139 if (!exists(value)) return false
140
141 const parsed = magnetUtil.decode(value)
142 return parsed && isVideoFileInfoHashValid(parsed.infoHash)
143}
144
145function checkUserCanManageVideo (user: UserModel, video: VideoModel, right: UserRight, res: Response) {
146 // Retrieve the user who did the request
147 if (video.isOwned() === false) {
148 res.status(403)
149 .json({ error: 'Cannot manage a video of another server.' })
150 .end()
151 return false
152 }
153
154 // Check if the user can delete the video
155 // The user can delete it if he has the right
156 // Or if s/he is the video's account
157 const account = video.VideoChannel.Account
158 if (user.hasRight(right) === false && account.userId !== user.id) {
159 res.status(403)
160 .json({ error: 'Cannot manage a video of another user.' })
161 .end()
162 return false
163 }
164
165 return true
166}
167
168async function doesVideoExist (id: number | string, res: Response, fetchType: VideoFetchType = 'all') {
169 const userId = res.locals.oauth ? res.locals.oauth.token.User.id : undefined
170
171 const video = await fetchVideo(id, fetchType, userId)
172
173 if (video === null) {
174 res.status(404)
175 .json({ error: 'Video not found' })
176 .end()
177
178 return false
179 }
180
181 if (fetchType !== 'none') res.locals.video = video
182 return true
183}
184
185async function doesVideoChannelOfAccountExist (channelId: number, user: UserModel, res: Response) {
186 if (user.hasRight(UserRight.UPDATE_ANY_VIDEO) === true) {
187 const videoChannel = await VideoChannelModel.loadAndPopulateAccount(channelId)
188 if (videoChannel === null) {
189 res.status(400)
190 .json({ error: 'Unknown video `video channel` on this instance.' })
191 .end()
192
193 return false
194 }
195
196 res.locals.videoChannel = videoChannel
197 return true
198 }
199
200 const videoChannel = await VideoChannelModel.loadByIdAndAccount(channelId, user.Account.id)
201 if (videoChannel === null) {
202 res.status(400)
203 .json({ error: 'Unknown video `video channel` for this account.' })
204 .end()
205
206 return false
207 }
208
209 res.locals.videoChannel = videoChannel
210 return true
211}
212
213// ---------------------------------------------------------------------------
214
215export {
216 isVideoCategoryValid,
217 checkUserCanManageVideo,
218 isVideoLicenceValid,
219 isVideoLanguageValid,
220 isVideoTruncatedDescriptionValid,
221 isVideoDescriptionValid,
222 isVideoFileInfoHashValid,
223 isVideoNameValid,
224 isVideoTagsValid,
225 isVideoFPSResolutionValid,
226 isScheduleVideoUpdatePrivacyValid,
227 isVideoOriginallyPublishedAtValid,
228 isVideoFile,
229 isVideoMagnetUriValid,
230 isVideoStateValid,
231 isVideoViewsValid,
232 isVideoRatingTypeValid,
233 isVideoFileExtnameValid,
234 isVideoDurationValid,
235 isVideoTagValid,
236 isVideoPrivacyValid,
237 isVideoFileResolutionValid,
238 isVideoFileSizeValid,
239 doesVideoExist,
240 isVideoImage,
241 doesVideoChannelOfAccountExist,
242 isVideoSupportValid,
243 isVideoFilterValid
244}