videos.ts 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. import * as validator from 'validator'
  2. import { ACTIVITY_PUB, CONSTRAINTS_FIELDS } from '../../../initializers/constants'
  3. import { peertubeTruncate } from '../../core-utils'
  4. import { exists, isArray, isBooleanValid, isDateValid, isUUIDValid } from '../misc'
  5. import {
  6. isVideoDurationValid,
  7. isVideoNameValid,
  8. isVideoStateValid,
  9. isVideoTagValid,
  10. isVideoTruncatedDescriptionValid,
  11. isVideoViewsValid
  12. } from '../videos'
  13. import { isActivityPubUrlValid, isBaseActivityValid, setValidAttributedTo } from './misc'
  14. import { VideoState } from '../../../../shared/models/videos'
  15. import { logger } from '@server/helpers/logger'
  16. function sanitizeAndCheckVideoTorrentUpdateActivity (activity: any) {
  17. return isBaseActivityValid(activity, 'Update') &&
  18. sanitizeAndCheckVideoTorrentObject(activity.object)
  19. }
  20. function isActivityPubVideoDurationValid (value: string) {
  21. // https://www.w3.org/TR/activitystreams-vocabulary/#dfn-duration
  22. return exists(value) &&
  23. typeof value === 'string' &&
  24. value.startsWith('PT') &&
  25. value.endsWith('S') &&
  26. isVideoDurationValid(value.replace(/[^0-9]+/g, ''))
  27. }
  28. function sanitizeAndCheckVideoTorrentObject (video: any) {
  29. if (!video || video.type !== 'Video') return false
  30. if (!setValidRemoteTags(video)) {
  31. logger.debug('Video has invalid tags', { video })
  32. return false
  33. }
  34. if (!setValidRemoteVideoUrls(video)) {
  35. logger.debug('Video has invalid urls', { video })
  36. return false
  37. }
  38. if (!setRemoteVideoTruncatedContent(video)) {
  39. logger.debug('Video has invalid content', { video })
  40. return false
  41. }
  42. if (!setValidAttributedTo(video)) {
  43. logger.debug('Video has invalid attributedTo', { video })
  44. return false
  45. }
  46. if (!setValidRemoteCaptions(video)) {
  47. logger.debug('Video has invalid captions', { video })
  48. return false
  49. }
  50. // Default attributes
  51. if (!isVideoStateValid(video.state)) video.state = VideoState.PUBLISHED
  52. if (!isBooleanValid(video.waitTranscoding)) video.waitTranscoding = false
  53. if (!isBooleanValid(video.downloadEnabled)) video.downloadEnabled = true
  54. return isActivityPubUrlValid(video.id) &&
  55. isVideoNameValid(video.name) &&
  56. isActivityPubVideoDurationValid(video.duration) &&
  57. isUUIDValid(video.uuid) &&
  58. (!video.category || isRemoteNumberIdentifierValid(video.category)) &&
  59. (!video.licence || isRemoteNumberIdentifierValid(video.licence)) &&
  60. (!video.language || isRemoteStringIdentifierValid(video.language)) &&
  61. isVideoViewsValid(video.views) &&
  62. isBooleanValid(video.sensitive) &&
  63. isBooleanValid(video.commentsEnabled) &&
  64. isBooleanValid(video.downloadEnabled) &&
  65. isDateValid(video.published) &&
  66. isDateValid(video.updated) &&
  67. (!video.originallyPublishedAt || isDateValid(video.originallyPublishedAt)) &&
  68. (!video.content || isRemoteVideoContentValid(video.mediaType, video.content)) &&
  69. isRemoteVideoIconValid(video.icon) &&
  70. video.url.length !== 0 &&
  71. video.attributedTo.length !== 0
  72. }
  73. function isRemoteVideoUrlValid (url: any) {
  74. return url.type === 'Link' &&
  75. (
  76. ACTIVITY_PUB.URL_MIME_TYPES.VIDEO.indexOf(url.mediaType) !== -1 &&
  77. isActivityPubUrlValid(url.href) &&
  78. validator.isInt(url.height + '', { min: 0 }) &&
  79. validator.isInt(url.size + '', { min: 0 }) &&
  80. (!url.fps || validator.isInt(url.fps + '', { min: -1 }))
  81. ) ||
  82. (
  83. ACTIVITY_PUB.URL_MIME_TYPES.TORRENT.indexOf(url.mediaType) !== -1 &&
  84. isActivityPubUrlValid(url.href) &&
  85. validator.isInt(url.height + '', { min: 0 })
  86. ) ||
  87. (
  88. ACTIVITY_PUB.URL_MIME_TYPES.MAGNET.indexOf(url.mediaType) !== -1 &&
  89. validator.isLength(url.href, { min: 5 }) &&
  90. validator.isInt(url.height + '', { min: 0 })
  91. ) ||
  92. (
  93. (url.mediaType || url.mimeType) === 'application/x-mpegURL' &&
  94. isActivityPubUrlValid(url.href) &&
  95. isArray(url.tag)
  96. )
  97. }
  98. // ---------------------------------------------------------------------------
  99. export {
  100. sanitizeAndCheckVideoTorrentUpdateActivity,
  101. isRemoteStringIdentifierValid,
  102. sanitizeAndCheckVideoTorrentObject,
  103. isRemoteVideoUrlValid
  104. }
  105. // ---------------------------------------------------------------------------
  106. function setValidRemoteTags (video: any) {
  107. if (Array.isArray(video.tag) === false) return false
  108. video.tag = video.tag.filter(t => {
  109. return t.type === 'Hashtag' &&
  110. isVideoTagValid(t.name)
  111. })
  112. return true
  113. }
  114. function setValidRemoteCaptions (video: any) {
  115. if (!video.subtitleLanguage) video.subtitleLanguage = []
  116. if (Array.isArray(video.subtitleLanguage) === false) return false
  117. video.subtitleLanguage = video.subtitleLanguage.filter(caption => {
  118. return isRemoteStringIdentifierValid(caption)
  119. })
  120. return true
  121. }
  122. function isRemoteNumberIdentifierValid (data: any) {
  123. return validator.isInt(data.identifier, { min: 0 })
  124. }
  125. function isRemoteStringIdentifierValid (data: any) {
  126. return typeof data.identifier === 'string'
  127. }
  128. function isRemoteVideoContentValid (mediaType: string, content: string) {
  129. return mediaType === 'text/markdown' && isVideoTruncatedDescriptionValid(content)
  130. }
  131. function isRemoteVideoIconValid (icon: any) {
  132. return icon.type === 'Image' &&
  133. isActivityPubUrlValid(icon.url) &&
  134. icon.mediaType === 'image/jpeg' &&
  135. validator.isInt(icon.width + '', { min: 0 }) &&
  136. validator.isInt(icon.height + '', { min: 0 })
  137. }
  138. function setValidRemoteVideoUrls (video: any) {
  139. if (Array.isArray(video.url) === false) return false
  140. video.url = video.url.filter(u => isRemoteVideoUrlValid(u))
  141. return true
  142. }
  143. function setRemoteVideoTruncatedContent (video: any) {
  144. if (video.content) {
  145. video.content = peertubeTruncate(video.content, { length: CONSTRAINTS_FIELDS.VIDEOS.TRUNCATED_DESCRIPTION.max })
  146. }
  147. return true
  148. }