mirror of
https://github.com/Chocobozzz/PeerTube.git
synced 2025-10-04 02:09:37 +02:00
Improve Video
AP compatibility
Compat with text/html descriptions Compat with SPDX for licences Compat with missing sensitive attribute Compat with missing tag attribute Compat with missing video file magnet URI Compat with missing streaming playlist segmentsSha256Url Compat with optional comments/likes/dislikes/shares URI in video object Add more debug logs when the object is not valid
This commit is contained in:
parent
1e3a5b25c3
commit
7c9f07e140
13 changed files with 215 additions and 124 deletions
|
@ -8,10 +8,11 @@ import {
|
|||
VideoState
|
||||
} from '@peertube/peertube-models'
|
||||
import { logger } from '@server/helpers/logger.js'
|
||||
import { spdxToPeertubeLicence } from '@server/helpers/video.js'
|
||||
import validator from 'validator'
|
||||
import { CONSTRAINTS_FIELDS, MIMETYPES } from '../../../initializers/constants.js'
|
||||
import { peertubeTruncate } from '../../core-utils.js'
|
||||
import { isArray, isBooleanValid, isDateValid, isUUIDValid } from '../misc.js'
|
||||
import { exists, isArray, isBooleanValid, isDateValid, isUUIDValid } from '../misc.js'
|
||||
import { isLiveLatencyModeValid } from '../video-lives.js'
|
||||
import {
|
||||
isVideoCommentsPolicyValid,
|
||||
|
@ -24,43 +25,28 @@ import {
|
|||
} from '../videos.js'
|
||||
import { isActivityPubUrlValid, isActivityPubVideoDurationValid, isBaseActivityValid, setValidAttributedTo } from './misc.js'
|
||||
|
||||
function sanitizeAndCheckVideoTorrentUpdateActivity (activity: any) {
|
||||
export function sanitizeAndCheckVideoTorrentUpdateActivity (activity: any) {
|
||||
return isBaseActivityValid(activity, 'Update') &&
|
||||
sanitizeAndCheckVideoTorrentObject(activity.object)
|
||||
}
|
||||
|
||||
function sanitizeAndCheckVideoTorrentObject (video: VideoObject) {
|
||||
export function sanitizeAndCheckVideoTorrentObject (video: VideoObject) {
|
||||
if (!video || video.type !== 'Video') return false
|
||||
|
||||
if (!setValidRemoteTags(video)) {
|
||||
logger.debug('Video has invalid tags', { video })
|
||||
return false
|
||||
}
|
||||
if (!setValidRemoteVideoUrls(video)) {
|
||||
logger.debug('Video has invalid urls', { video })
|
||||
return false
|
||||
}
|
||||
if (!setRemoteVideoContent(video)) {
|
||||
logger.debug('Video has invalid content', { video })
|
||||
return false
|
||||
}
|
||||
if (!setValidAttributedTo(video)) {
|
||||
logger.debug('Video has invalid attributedTo', { video })
|
||||
return false
|
||||
}
|
||||
if (!setValidRemoteCaptions(video)) {
|
||||
logger.debug('Video has invalid captions', { video })
|
||||
return false
|
||||
}
|
||||
if (!setValidRemoteIcon(video)) {
|
||||
logger.debug('Video has invalid icons', { video })
|
||||
return false
|
||||
}
|
||||
if (!setValidStoryboard(video)) {
|
||||
logger.debug('Video has invalid preview (storyboard)', { video })
|
||||
const fail = (field: string) => {
|
||||
logger.debug(`Video field is not valid to PeerTube: ${field}`, { video })
|
||||
return false
|
||||
}
|
||||
|
||||
if (!setValidRemoteTags(video)) return fail('tags')
|
||||
if (!setValidRemoteVideoUrls(video)) return fail('urls')
|
||||
if (!setRemoteVideoContent(video)) return fail('content')
|
||||
if (!setValidAttributedTo(video)) return fail('attributedTo')
|
||||
if (!setValidRemoteCaptions(video)) return fail('captions')
|
||||
if (!setValidRemoteIcon(video)) return fail('icons')
|
||||
if (!setValidStoryboard(video)) return fail('preview (storyboard)')
|
||||
if (!setValidLicence(video)) return fail('licence')
|
||||
|
||||
// TODO: compat with < 6.1, remove in 7.0
|
||||
if (!video.uuid && video['identifier']) video.uuid = video['identifier']
|
||||
|
||||
|
@ -71,6 +57,7 @@ function sanitizeAndCheckVideoTorrentObject (video: VideoObject) {
|
|||
if (!isBooleanValid(video.isLiveBroadcast)) video.isLiveBroadcast = false
|
||||
if (!isBooleanValid(video.liveSaveReplay)) video.liveSaveReplay = false
|
||||
if (!isBooleanValid(video.permanentLive)) video.permanentLive = false
|
||||
if (!isBooleanValid(video.sensitive)) video.sensitive = false
|
||||
if (!isLiveLatencyModeValid(video.latencyMode)) video.latencyMode = LiveVideoLatencyMode.DEFAULT
|
||||
|
||||
if (video.commentsPolicy) {
|
||||
|
@ -83,25 +70,31 @@ function sanitizeAndCheckVideoTorrentObject (video: VideoObject) {
|
|||
video.commentsPolicy = VideoCommentPolicy.DISABLED
|
||||
}
|
||||
|
||||
return isActivityPubUrlValid(video.id) &&
|
||||
isVideoNameValid(video.name) &&
|
||||
isActivityPubVideoDurationValid(video.duration) &&
|
||||
isVideoDurationValid(video.duration.replace(/[^0-9]+/g, '')) &&
|
||||
isUUIDValid(video.uuid) &&
|
||||
(!video.category || isRemoteNumberIdentifierValid(video.category)) &&
|
||||
(!video.licence || isRemoteNumberIdentifierValid(video.licence)) &&
|
||||
(!video.language || isRemoteStringIdentifierValid(video.language)) &&
|
||||
isVideoViewsValid(video.views) &&
|
||||
isBooleanValid(video.sensitive) &&
|
||||
isDateValid(video.published) &&
|
||||
isDateValid(video.updated) &&
|
||||
(!video.originallyPublishedAt || isDateValid(video.originallyPublishedAt)) &&
|
||||
(!video.uploadDate || isDateValid(video.uploadDate)) &&
|
||||
(!video.content || isRemoteVideoContentValid(video.mediaType, video.content)) &&
|
||||
video.attributedTo.length !== 0
|
||||
if (!isActivityPubUrlValid(video.id)) return fail('id')
|
||||
if (!isVideoNameValid(video.name)) return fail('name')
|
||||
|
||||
if (!isActivityPubVideoDurationValid(video.duration)) return fail('duration format')
|
||||
if (!isVideoDurationValid(video.duration.replace(/[^0-9]+/g, ''))) return fail('duration')
|
||||
|
||||
if (!isUUIDValid(video.uuid)) return fail('uuid')
|
||||
|
||||
if (exists(video.category) && !isRemoteNumberIdentifierValid(video.category)) return fail('category')
|
||||
if (exists(video.language) && !isRemoteStringIdentifierValid(video.language)) return fail('language')
|
||||
|
||||
if (!isVideoViewsValid(video.views)) return fail('views')
|
||||
if (!isDateValid(video.published)) return fail('published')
|
||||
if (!isDateValid(video.updated)) return fail('updated')
|
||||
|
||||
if (exists(video.originallyPublishedAt) && !isDateValid(video.originallyPublishedAt)) return fail('originallyPublishedAt')
|
||||
if (exists(video.uploadDate) && !isDateValid(video.uploadDate)) return fail('uploadDate')
|
||||
if (exists(video.content) && !isRemoteVideoContentValid(video.mediaType, video.content)) return fail('mediaType/content')
|
||||
|
||||
if (video.attributedTo.length === 0) return fail('attributedTo')
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
function isRemoteVideoUrlValid (url: any) {
|
||||
export function isRemoteVideoUrlValid (url: any) {
|
||||
return url.type === 'Link' &&
|
||||
// Video file link
|
||||
(
|
||||
|
@ -133,44 +126,32 @@ function isRemoteVideoUrlValid (url: any) {
|
|||
isAPVideoFileUrlMetadataObject(url)
|
||||
}
|
||||
|
||||
function isAPVideoFileUrlMetadataObject (url: any): url is ActivityVideoFileMetadataUrlObject {
|
||||
export function isAPVideoFileUrlMetadataObject (url: any): url is ActivityVideoFileMetadataUrlObject {
|
||||
return url &&
|
||||
url.type === 'Link' &&
|
||||
url.mediaType === 'application/json' &&
|
||||
isArray(url.rel) && url.rel.includes('metadata')
|
||||
}
|
||||
|
||||
function isAPVideoTrackerUrlObject (url: any): url is ActivityTrackerUrlObject {
|
||||
export function isAPVideoTrackerUrlObject (url: any): url is ActivityTrackerUrlObject {
|
||||
return isArray(url.rel) &&
|
||||
url.rel.includes('tracker') &&
|
||||
isActivityPubUrlValid(url.href)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export {
|
||||
isAPVideoFileUrlMetadataObject,
|
||||
isAPVideoTrackerUrlObject,
|
||||
isRemoteStringIdentifierValid,
|
||||
isRemoteVideoUrlValid,
|
||||
sanitizeAndCheckVideoTorrentObject,
|
||||
sanitizeAndCheckVideoTorrentUpdateActivity
|
||||
}
|
||||
|
||||
// Private
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function setValidRemoteTags (video: any) {
|
||||
if (Array.isArray(video.tag) === false) return false
|
||||
function setValidRemoteTags (video: VideoObject) {
|
||||
if (Array.isArray(video.tag) === false) video.tag = []
|
||||
|
||||
video.tag = video.tag.filter(t => {
|
||||
return t.type === 'Hashtag' &&
|
||||
isVideoTagValid(t.name)
|
||||
})
|
||||
video.tag = video.tag.filter(t => t.type === 'Hashtag' && isVideoTagValid(t.name))
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
function setValidRemoteCaptions (video: any) {
|
||||
function setValidRemoteCaptions (video: VideoObject) {
|
||||
if (!video.subtitleLanguage) video.subtitleLanguage = []
|
||||
|
||||
if (Array.isArray(video.subtitleLanguage) === false) return false
|
||||
|
@ -193,7 +174,7 @@ function isRemoteStringIdentifierValid (data: any) {
|
|||
}
|
||||
|
||||
function isRemoteVideoContentValid (mediaType: string, content: string) {
|
||||
return mediaType === 'text/markdown' && isVideoDescriptionValid(content)
|
||||
return (mediaType === 'text/markdown' || mediaType === 'text/html') && isVideoDescriptionValid(content)
|
||||
}
|
||||
|
||||
function setValidRemoteIcon (video: any) {
|
||||
|
@ -219,7 +200,7 @@ function setValidRemoteVideoUrls (video: any) {
|
|||
return true
|
||||
}
|
||||
|
||||
function setRemoteVideoContent (video: any) {
|
||||
function setRemoteVideoContent (video: VideoObject) {
|
||||
if (video.content) {
|
||||
video.content = peertubeTruncate(video.content, { length: CONSTRAINTS_FIELDS.VIDEOS.DESCRIPTION.max })
|
||||
}
|
||||
|
@ -227,6 +208,19 @@ function setRemoteVideoContent (video: any) {
|
|||
return true
|
||||
}
|
||||
|
||||
function setValidLicence (video: VideoObject) {
|
||||
if (!exists(video.licence)) return true
|
||||
|
||||
if (validator.default.isInt(video.licence.identifier)) return isRemoteNumberIdentifierValid(video.licence)
|
||||
|
||||
const spdx = spdxToPeertubeLicence(video.licence.identifier)
|
||||
video.licence.identifier = spdx
|
||||
? spdx + ''
|
||||
: undefined
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
function setValidStoryboard (video: VideoObject) {
|
||||
if (!video.preview) return true
|
||||
if (!Array.isArray(video.preview)) return false
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue