]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/helpers/custom-validators/activitypub/videos.ts
Don't clean mastodon rates
[github/Chocobozzz/PeerTube.git] / server / helpers / custom-validators / activitypub / videos.ts
CommitLineData
7cde3b9c 1import validator from 'validator'
d9a2a031
C
2import { logger } from '@server/helpers/logger'
3import { ActivityTrackerUrlObject, ActivityVideoFileMetadataUrlObject } from '@shared/models'
f443a746 4import { LiveVideoLatencyMode, VideoState } from '../../../../shared/models/videos'
74dc3bca 5import { ACTIVITY_PUB, CONSTRAINTS_FIELDS } from '../../../initializers/constants'
c73e83da 6import { peertubeTruncate } from '../../core-utils'
09209296 7import { exists, isArray, isBooleanValid, isDateValid, isUUIDValid } from '../misc'
f443a746 8import { isLiveLatencyModeValid } from '../video-lives'
65fcc311 9import {
65fcc311 10 isVideoDurationValid,
65fcc311 11 isVideoNameValid,
2186386c 12 isVideoStateValid,
e34c85e5 13 isVideoTagValid,
8e10cf1a 14 isVideoTruncatedDescriptionValid,
8e10cf1a 15 isVideoViewsValid
65fcc311 16} from '../videos'
50d6de9c 17import { isActivityPubUrlValid, isBaseActivityValid, setValidAttributedTo } from './misc'
0d0e8dd0 18
1d6e5dfc 19function sanitizeAndCheckVideoTorrentUpdateActivity (activity: any) {
0d0e8dd0 20 return isBaseActivityValid(activity, 'Update') &&
1d6e5dfc 21 sanitizeAndCheckVideoTorrentObject(activity.object)
65fcc311
C
22}
23
8e10cf1a
C
24function isActivityPubVideoDurationValid (value: string) {
25 // https://www.w3.org/TR/activitystreams-vocabulary/#dfn-duration
26 return exists(value) &&
27 typeof value === 'string' &&
28 value.startsWith('PT') &&
29 value.endsWith('S') &&
efc32059 30 isVideoDurationValid(value.replace(/[^0-9]+/g, ''))
8e10cf1a
C
31}
32
1d6e5dfc 33function sanitizeAndCheckVideoTorrentObject (video: any) {
fbad87b0 34 if (!video || video.type !== 'Video') return false
5cf13500 35
d7a25329
C
36 if (!setValidRemoteTags(video)) {
37 logger.debug('Video has invalid tags', { video })
38 return false
39 }
40 if (!setValidRemoteVideoUrls(video)) {
41 logger.debug('Video has invalid urls', { video })
42 return false
43 }
44 if (!setRemoteVideoTruncatedContent(video)) {
45 logger.debug('Video has invalid content', { video })
46 return false
47 }
48 if (!setValidAttributedTo(video)) {
49 logger.debug('Video has invalid attributedTo', { video })
50 return false
51 }
52 if (!setValidRemoteCaptions(video)) {
53 logger.debug('Video has invalid captions', { video })
54 return false
55 }
ca6d3622
C
56 if (!setValidRemoteIcon(video)) {
57 logger.debug('Video has invalid icons', { video })
58 return false
59 }
1d6e5dfc 60
2186386c
C
61 // Default attributes
62 if (!isVideoStateValid(video.state)) video.state = VideoState.PUBLISHED
63 if (!isBooleanValid(video.waitTranscoding)) video.waitTranscoding = false
7f2cfe3a 64 if (!isBooleanValid(video.downloadEnabled)) video.downloadEnabled = true
0bc1b31d 65 if (!isBooleanValid(video.commentsEnabled)) video.commentsEnabled = false
de6310b2 66 if (!isBooleanValid(video.isLiveBroadcast)) video.isLiveBroadcast = false
af4ae64f 67 if (!isBooleanValid(video.liveSaveReplay)) video.liveSaveReplay = false
bb4ba6d9 68 if (!isBooleanValid(video.permanentLive)) video.permanentLive = false
f443a746 69 if (!isLiveLatencyModeValid(video.latencyMode)) video.latencyMode = LiveVideoLatencyMode.DEFAULT
2186386c 70
5cf13500 71 return isActivityPubUrlValid(video.id) &&
0d0e8dd0 72 isVideoNameValid(video.name) &&
8e10cf1a 73 isActivityPubVideoDurationValid(video.duration) &&
0d0e8dd0 74 isUUIDValid(video.uuid) &&
9d3ef9fe
C
75 (!video.category || isRemoteNumberIdentifierValid(video.category)) &&
76 (!video.licence || isRemoteNumberIdentifierValid(video.licence)) &&
77 (!video.language || isRemoteStringIdentifierValid(video.language)) &&
efc32059 78 isVideoViewsValid(video.views) &&
0a67e28b 79 isBooleanValid(video.sensitive) &&
0d0e8dd0
C
80 isDateValid(video.published) &&
81 isDateValid(video.updated) &&
7519127b 82 (!video.originallyPublishedAt || isDateValid(video.originallyPublishedAt)) &&
f595d394 83 (!video.content || isRemoteVideoContentValid(video.mediaType, video.content)) &&
50d6de9c 84 video.attributedTo.length !== 0
65fcc311
C
85}
86
c48e82b5 87function isRemoteVideoUrlValid (url: any) {
c48e82b5 88 return url.type === 'Link' &&
d9a2a031 89 // Video file link
c48e82b5 90 (
bdd428a6 91 ACTIVITY_PUB.URL_MIME_TYPES.VIDEO.includes(url.mediaType) &&
c48e82b5
C
92 isActivityPubUrlValid(url.href) &&
93 validator.isInt(url.height + '', { min: 0 }) &&
94 validator.isInt(url.size + '', { min: 0 }) &&
a3737cbf 95 (!url.fps || validator.isInt(url.fps + '', { min: -1 }))
c48e82b5 96 ) ||
d9a2a031 97 // Torrent link
c48e82b5 98 (
bdd428a6 99 ACTIVITY_PUB.URL_MIME_TYPES.TORRENT.includes(url.mediaType) &&
c48e82b5
C
100 isActivityPubUrlValid(url.href) &&
101 validator.isInt(url.height + '', { min: 0 })
102 ) ||
d9a2a031 103 // Magnet link
c48e82b5 104 (
bdd428a6 105 ACTIVITY_PUB.URL_MIME_TYPES.MAGNET.includes(url.mediaType) &&
c48e82b5
C
106 validator.isLength(url.href, { min: 5 }) &&
107 validator.isInt(url.height + '', { min: 0 })
09209296 108 ) ||
d9a2a031 109 // HLS playlist link
09209296
C
110 (
111 (url.mediaType || url.mimeType) === 'application/x-mpegURL' &&
112 isActivityPubUrlValid(url.href) &&
113 isArray(url.tag)
7b81edc8 114 ) ||
d9a2a031
C
115 isAPVideoTrackerUrlObject(url) ||
116 isAPVideoFileUrlMetadataObject(url)
7b81edc8
C
117}
118
d9a2a031 119function isAPVideoFileUrlMetadataObject (url: any): url is ActivityVideoFileMetadataUrlObject {
7b81edc8
C
120 return url &&
121 url.type === 'Link' &&
122 url.mediaType === 'application/json' &&
123 isArray(url.rel) && url.rel.includes('metadata')
c48e82b5
C
124}
125
d9a2a031
C
126function isAPVideoTrackerUrlObject (url: any): url is ActivityTrackerUrlObject {
127 return isArray(url.rel) &&
128 url.rel.includes('tracker') &&
129 isActivityPubUrlValid(url.href)
130}
131
65fcc311
C
132// ---------------------------------------------------------------------------
133
134export {
1d6e5dfc 135 sanitizeAndCheckVideoTorrentUpdateActivity,
9d3ef9fe 136 isRemoteStringIdentifierValid,
c48e82b5 137 sanitizeAndCheckVideoTorrentObject,
7b81edc8 138 isRemoteVideoUrlValid,
d9a2a031
C
139 isAPVideoFileUrlMetadataObject,
140 isAPVideoTrackerUrlObject
65fcc311
C
141}
142
143// ---------------------------------------------------------------------------
144
0d0e8dd0
C
145function setValidRemoteTags (video: any) {
146 if (Array.isArray(video.tag) === false) return false
65fcc311 147
a2431b7d 148 video.tag = video.tag.filter(t => {
0d0e8dd0
C
149 return t.type === 'Hashtag' &&
150 isVideoTagValid(t.name)
151 })
72c7248b 152
0d0e8dd0 153 return true
72c7248b
C
154}
155
40e87e9e
C
156function setValidRemoteCaptions (video: any) {
157 if (!video.subtitleLanguage) video.subtitleLanguage = []
158
159 if (Array.isArray(video.subtitleLanguage) === false) return false
160
161 video.subtitleLanguage = video.subtitleLanguage.filter(caption => {
ca6d3622
C
162 if (!isActivityPubUrlValid(caption.url)) caption.url = null
163
40e87e9e
C
164 return isRemoteStringIdentifierValid(caption)
165 })
166
167 return true
168}
169
9d3ef9fe 170function isRemoteNumberIdentifierValid (data: any) {
0d0e8dd0 171 return validator.isInt(data.identifier, { min: 0 })
72c7248b
C
172}
173
9d3ef9fe
C
174function isRemoteStringIdentifierValid (data: any) {
175 return typeof data.identifier === 'string'
176}
177
0d0e8dd0
C
178function isRemoteVideoContentValid (mediaType: string, content: string) {
179 return mediaType === 'text/markdown' && isVideoTruncatedDescriptionValid(content)
72c7248b
C
180}
181
ca6d3622
C
182function setValidRemoteIcon (video: any) {
183 if (video.icon && !isArray(video.icon)) video.icon = [ video.icon ]
184 if (!video.icon) video.icon = []
185
186 video.icon = video.icon.filter(icon => {
187 return icon.type === 'Image' &&
188 isActivityPubUrlValid(icon.url) &&
189 icon.mediaType === 'image/jpeg' &&
190 validator.isInt(icon.width + '', { min: 0 }) &&
191 validator.isInt(icon.height + '', { min: 0 })
192 })
193
194 return video.icon.length !== 0
72c7248b
C
195}
196
0d0e8dd0
C
197function setValidRemoteVideoUrls (video: any) {
198 if (Array.isArray(video.url) === false) return false
65fcc311 199
a2431b7d 200 video.url = video.url.filter(u => isRemoteVideoUrlValid(u))
65fcc311 201
0d0e8dd0 202 return true
65fcc311
C
203}
204
45cd28b6 205function setRemoteVideoTruncatedContent (video: any) {
c73e83da 206 if (video.content) {
687c6180 207 video.content = peertubeTruncate(video.content, { length: CONSTRAINTS_FIELDS.VIDEOS.TRUNCATED_DESCRIPTION.max })
c73e83da
C
208 }
209
210 return true
211}