Fix updating video tags to empty field
[oweals/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 { VideoRateType } from '../../../shared'
7 import {
8   CONSTRAINTS_FIELDS,
9   VIDEO_CATEGORIES,
10   VIDEO_LANGUAGES,
11   VIDEO_LICENCES, VIDEO_MIMETYPE_EXT,
12   VIDEO_PRIVACIES,
13   VIDEO_RATE_TYPES
14 } from '../../initializers'
15 import { VideoModel } from '../../models/video/video'
16 import { exists, isArray, isFileValid } from './misc'
17 import { VideoChannelModel } from '../../models/video/video-channel'
18
19 const VIDEOS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEOS
20 const VIDEO_ABUSES_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_ABUSES
21
22 function isVideoCategoryValid (value: any) {
23   return value === null || VIDEO_CATEGORIES[value] !== undefined
24 }
25
26 function isVideoLicenceValid (value: any) {
27   return value === null || VIDEO_LICENCES[value] !== undefined
28 }
29
30 function isVideoLanguageValid (value: any) {
31   return value === null ||
32     (typeof value === 'string' && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.LANGUAGE))
33 }
34
35 function isVideoDurationValid (value: string) {
36   return exists(value) && validator.isInt(value + '', VIDEOS_CONSTRAINTS_FIELDS.DURATION)
37 }
38
39 function isVideoTruncatedDescriptionValid (value: string) {
40   return exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.TRUNCATED_DESCRIPTION)
41 }
42
43 function isVideoDescriptionValid (value: string) {
44   return value === null || (exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.DESCRIPTION))
45 }
46
47 function isVideoSupportValid (value: string) {
48   return value === null || (exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.SUPPORT))
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 tags === null || (
61     isArray(tags) &&
62     validator.isInt(tags.length.toString(), VIDEOS_CONSTRAINTS_FIELDS.TAGS) &&
63     tags.every(tag => isVideoTagValid(tag))
64   )
65 }
66
67 function isVideoAbuseReasonValid (value: string) {
68   return exists(value) && validator.isLength(value, VIDEO_ABUSES_CONSTRAINTS_FIELDS.REASON)
69 }
70
71 function isVideoViewsValid (value: string) {
72   return exists(value) && validator.isInt(value + '', VIDEOS_CONSTRAINTS_FIELDS.VIEWS)
73 }
74
75 function isVideoRatingTypeValid (value: string) {
76   return value === 'none' || values(VIDEO_RATE_TYPES).indexOf(value as VideoRateType) !== -1
77 }
78
79 const videoFileTypes = Object.keys(VIDEO_MIMETYPE_EXT).map(m => `(${m})`)
80 const videoFileTypesRegex = videoFileTypes.join('|')
81 function isVideoFile (files: { [ fieldname: string ]: Express.Multer.File[] } | Express.Multer.File[]) {
82   return isFileValid(files, videoFileTypesRegex, 'videofile')
83 }
84
85 const videoImageTypes = CONSTRAINTS_FIELDS.VIDEOS.IMAGE.EXTNAME
86   .map(v => v.replace('.', ''))
87   .join('|')
88 const videoImageTypesRegex = `image/(${videoImageTypes})`
89 function isVideoImage (files: { [ fieldname: string ]: Express.Multer.File[] } | Express.Multer.File[], field: string) {
90   return isFileValid(files, videoImageTypesRegex, field, true)
91 }
92
93 function isVideoPrivacyValid (value: string) {
94   return validator.isInt(value + '') && 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 async function isVideoExist (id: string, res: Response) {
110   let video: VideoModel
111
112   if (validator.isInt(id)) {
113     video = await VideoModel.loadAndPopulateAccountAndServerAndTags(+id)
114   } else { // UUID
115     video = await VideoModel.loadByUUIDAndPopulateAccountAndServerAndTags(id)
116   }
117
118   if (!video) {
119     res.status(404)
120       .json({ error: 'Video not found' })
121       .end()
122
123     return false
124   }
125
126   res.locals.video = video
127   return true
128 }
129
130 async function isVideoChannelOfAccountExist (channelId: number, accountId: number, res: Response) {
131   const videoChannel = await VideoChannelModel.loadByIdAndAccount(channelId, accountId)
132   if (!videoChannel) {
133     res.status(400)
134        .json({ error: 'Unknown video video channel for this account.' })
135        .end()
136
137     return false
138   }
139
140   res.locals.videoChannel = videoChannel
141   return true
142 }
143
144 // ---------------------------------------------------------------------------
145
146 export {
147   isVideoCategoryValid,
148   isVideoLicenceValid,
149   isVideoLanguageValid,
150   isVideoTruncatedDescriptionValid,
151   isVideoDescriptionValid,
152   isVideoFileInfoHashValid,
153   isVideoNameValid,
154   isVideoTagsValid,
155   isVideoAbuseReasonValid,
156   isVideoFile,
157   isVideoViewsValid,
158   isVideoRatingTypeValid,
159   isVideoDurationValid,
160   isVideoTagValid,
161   isVideoPrivacyValid,
162   isVideoFileResolutionValid,
163   isVideoFileSizeValid,
164   isVideoExist,
165   isVideoImage,
166   isVideoChannelOfAccountExist,
167   isVideoSupportValid
168 }