]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/helpers/custom-validators/videos.ts
Add ability to list all local videos
[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 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 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 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, 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
177 async 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
207 export {
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 }