Add shares forward and collection on videos/video channels
[oweals/peertube.git] / server / helpers / custom-validators / videos.ts
1 import * as Bluebird from 'bluebird'
2 import { Response } from 'express'
3 import 'express-validator'
4 import { values } from 'lodash'
5 import 'multer'
6 import * as validator from 'validator'
7 import { VideoRateType } from '../../../shared'
8 import { CONSTRAINTS_FIELDS, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_RATE_TYPES } from '../../initializers'
9 import { database as db } from '../../initializers/database'
10 import { VideoInstance } from '../../models/video/video-interface'
11 import { logger } from '../logger'
12 import { isActivityPubUrlValid } from './activitypub/misc'
13 import { exists, isArray } from './misc'
14 import { VIDEO_PRIVACIES } from '../../initializers/constants'
15
16 const VIDEOS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEOS
17 const VIDEO_ABUSES_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_ABUSES
18
19 function isVideoCategoryValid (value: number) {
20   return VIDEO_CATEGORIES[value] !== undefined
21 }
22
23 function isVideoUrlValid (value: string) {
24   return isActivityPubUrlValid(value)
25 }
26
27 function isVideoLicenceValid (value: number) {
28   return VIDEO_LICENCES[value] !== undefined
29 }
30
31 function isVideoLanguageValid (value: number) {
32   return value === null || VIDEO_LANGUAGES[value] !== undefined
33 }
34
35 function isVideoNSFWValid (value: any) {
36   return typeof value === 'boolean' || (typeof value === 'string' && validator.isBoolean(value))
37 }
38
39 function isVideoDurationValid (value: string) {
40   return exists(value) && validator.isInt(value + '', VIDEOS_CONSTRAINTS_FIELDS.DURATION)
41 }
42
43 function isVideoTruncatedDescriptionValid (value: string) {
44   return exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.TRUNCATED_DESCRIPTION)
45 }
46
47 function isVideoDescriptionValid (value: string) {
48   return exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.DESCRIPTION)
49 }
50
51 function isVideoNameValid (value: string) {
52   return exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.NAME)
53 }
54
55 function isVideoTagValid (tag: string) {
56   return exists(tag) && validator.isLength(tag, VIDEOS_CONSTRAINTS_FIELDS.TAG)
57 }
58
59 function isVideoTagsValid (tags: string[]) {
60   return isArray(tags) &&
61          validator.isInt(tags.length.toString(), VIDEOS_CONSTRAINTS_FIELDS.TAGS) &&
62          tags.every(tag => isVideoTagValid(tag))
63 }
64
65 function isVideoAbuseReasonValid (value: string) {
66   return exists(value) && validator.isLength(value, VIDEO_ABUSES_CONSTRAINTS_FIELDS.REASON)
67 }
68
69 function isVideoViewsValid (value: string) {
70   return exists(value) && validator.isInt(value + '', VIDEOS_CONSTRAINTS_FIELDS.VIEWS)
71 }
72
73 function isVideoRatingTypeValid (value: string) {
74   return values(VIDEO_RATE_TYPES).indexOf(value as VideoRateType) !== -1
75 }
76
77 function isVideoFile (files: { [ fieldname: string ]: Express.Multer.File[] } | Express.Multer.File[]) {
78   // Should have files
79   if (!files) return false
80   if (isArray(files)) return false
81
82   // Should have videofile file
83   const videofile = files['videofile']
84   if (!videofile || videofile.length === 0) return false
85
86   // The file should exist
87   const file = videofile[0]
88   if (!file || !file.originalname) return false
89
90   return new RegExp('^video/(webm|mp4|ogg)$', 'i').test(file.mimetype)
91 }
92
93 function isVideoPrivacyValid (value: string) {
94   return VIDEO_PRIVACIES[value] !== undefined
95 }
96
97 function isVideoFileInfoHashValid (value: string) {
98   return exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.INFO_HASH)
99 }
100
101 function isVideoFileResolutionValid (value: string) {
102   return exists(value) && validator.isInt(value + '')
103 }
104
105 function isVideoFileSizeValid (value: string) {
106   return exists(value) && validator.isInt(value + '', VIDEOS_CONSTRAINTS_FIELDS.FILE_SIZE)
107 }
108
109 function checkVideoExists (id: string, res: Response, callback: () => void) {
110   let promise: Bluebird<VideoInstance>
111   if (validator.isInt(id)) {
112     promise = db.Video.loadAndPopulateAccountAndServerAndTags(+id)
113   } else { // UUID
114     promise = db.Video.loadByUUIDAndPopulateAccountAndServerAndTags(id)
115   }
116
117   promise.then(video => {
118     if (!video) {
119       return res.status(404)
120         .json({ error: 'Video not found' })
121         .end()
122     }
123
124     res.locals.video = video
125     callback()
126   })
127     .catch(err => {
128       logger.error('Error in video request validator.', err)
129       return res.sendStatus(500)
130     })
131 }
132
133 async function isVideoExistsPromise (id: string, res: Response) {
134   let video: VideoInstance
135
136   if (validator.isInt(id)) {
137     video = await db.Video.loadAndPopulateAccountAndServerAndTags(+id)
138   } else { // UUID
139     video = await db.Video.loadByUUIDAndPopulateAccountAndServerAndTags(id)
140   }
141
142   if (!video) {
143     res.status(404)
144       .json({ error: 'Video not found' })
145       .end()
146
147     return false
148   }
149
150   res.locals.video = video
151   return true
152 }
153
154 // ---------------------------------------------------------------------------
155
156 export {
157   isVideoCategoryValid,
158   isVideoLicenceValid,
159   isVideoLanguageValid,
160   isVideoNSFWValid,
161   isVideoTruncatedDescriptionValid,
162   isVideoDescriptionValid,
163   isVideoFileInfoHashValid,
164   isVideoNameValid,
165   isVideoTagsValid,
166   isVideoAbuseReasonValid,
167   isVideoFile,
168   isVideoViewsValid,
169   isVideoRatingTypeValid,
170   isVideoDurationValid,
171   isVideoTagValid,
172   isVideoUrlValid,
173   isVideoPrivacyValid,
174   isVideoFileResolutionValid,
175   isVideoFileSizeValid,
176   checkVideoExists,
177   isVideoExistsPromise
178 }