PeerTube/server/lib/activitypub/process/misc.ts

194 lines
5.7 KiB
TypeScript
Raw Normal View History

2017-11-10 13:34:45 +00:00
import * as magnetUtil from 'magnet-uri'
2017-11-20 08:43:39 +00:00
import { VideoTorrentObject } from '../../../../shared'
2017-12-28 10:16:08 +00:00
import { VideoCommentObject } from '../../../../shared/models/activitypub/objects/video-comment-object'
2017-12-12 16:53:50 +00:00
import { VideoPrivacy } from '../../../../shared/models/videos'
2017-11-20 08:43:39 +00:00
import { isVideoFileInfoHashValid } from '../../../helpers/custom-validators/videos'
2017-12-28 10:16:08 +00:00
import { logger } from '../../../helpers/logger'
import { doRequest } from '../../../helpers/requests'
2017-12-12 16:53:50 +00:00
import { ACTIVITY_PUB, VIDEO_MIMETYPE_EXT } from '../../../initializers'
2017-12-28 10:16:08 +00:00
import { ActorModel } from '../../../models/activitypub/actor'
2017-12-12 16:53:50 +00:00
import { VideoModel } from '../../../models/video/video'
import { VideoChannelModel } from '../../../models/video/video-channel'
2017-12-28 10:16:08 +00:00
import { VideoCommentModel } from '../../../models/video/video-comment'
2017-12-12 16:53:50 +00:00
import { VideoShareModel } from '../../../models/video/video-share'
2017-12-14 16:38:41 +00:00
import { getOrCreateActorAndServerAndModel } from '../actor'
2017-11-10 13:34:45 +00:00
2017-11-10 16:27:49 +00:00
async function videoActivityObjectToDBAttributes (
2017-12-12 16:53:50 +00:00
videoChannel: VideoChannelModel,
2017-11-17 14:20:42 +00:00
videoObject: VideoTorrentObject,
to: string[] = [],
cc: string[] = []
2017-11-10 16:27:49 +00:00
) {
2017-11-17 14:20:42 +00:00
let privacy = VideoPrivacy.PRIVATE
if (to.indexOf(ACTIVITY_PUB.PUBLIC) !== -1) privacy = VideoPrivacy.PUBLIC
else if (cc.indexOf(ACTIVITY_PUB.PUBLIC) !== -1) privacy = VideoPrivacy.UNLISTED
2017-11-10 13:34:45 +00:00
const duration = videoObject.duration.replace(/[^\d]+/, '')
2017-11-22 15:25:03 +00:00
let language = null
if (videoObject.language) {
language = parseInt(videoObject.language.identifier, 10)
}
2017-12-08 16:31:21 +00:00
let category = null
if (videoObject.category) {
category = parseInt(videoObject.category.identifier, 10)
}
let licence = null
if (videoObject.licence) {
licence = parseInt(videoObject.licence.identifier, 10)
}
let description = null
if (videoObject.content) {
description = videoObject.content
}
2017-12-12 16:53:50 +00:00
return {
2017-11-10 13:34:45 +00:00
name: videoObject.name,
uuid: videoObject.uuid,
url: videoObject.id,
2017-12-08 16:31:21 +00:00
category,
licence,
2017-11-22 15:25:03 +00:00
language,
2017-12-08 16:31:21 +00:00
description,
2017-11-10 13:34:45 +00:00
nsfw: videoObject.nsfw,
channelId: videoChannel.id,
duration: parseInt(duration, 10),
createdAt: new Date(videoObject.published),
2017-11-10 13:34:45 +00:00
// FIXME: updatedAt does not seems to be considered by Sequelize
updatedAt: new Date(videoObject.updated),
2017-11-10 13:34:45 +00:00
views: videoObject.views,
likes: 0,
dislikes: 0,
remote: true,
2017-11-17 14:20:42 +00:00
privacy
2017-11-10 13:34:45 +00:00
}
}
2017-12-12 16:53:50 +00:00
function videoFileActivityUrlToDBAttributes (videoCreated: VideoModel, videoObject: VideoTorrentObject) {
2017-11-16 14:22:39 +00:00
const mimeTypes = Object.keys(VIDEO_MIMETYPE_EXT)
const fileUrls = videoObject.url.filter(u => {
return mimeTypes.indexOf(u.mimeType) !== -1 && u.mimeType.startsWith('video/')
})
if (fileUrls.length === 0) {
throw new Error('Cannot find video files for ' + videoCreated.url)
}
2017-11-10 13:34:45 +00:00
2017-12-12 16:53:50 +00:00
const attributes = []
2017-11-16 14:22:39 +00:00
for (const fileUrl of fileUrls) {
2017-11-10 13:34:45 +00:00
// Fetch associated magnet uri
2017-11-16 14:22:39 +00:00
const magnet = videoObject.url.find(u => {
return u.mimeType === 'application/x-bittorrent;x-scheme-handler/magnet' && u.width === fileUrl.width
})
if (!magnet) throw new Error('Cannot find associated magnet uri for file ' + fileUrl.url)
2017-11-10 13:34:45 +00:00
const parsed = magnetUtil.decode(magnet.url)
if (!parsed || isVideoFileInfoHashValid(parsed.infoHash) === false) throw new Error('Cannot parse magnet URI ' + magnet.url)
const attribute = {
2017-11-16 14:22:39 +00:00
extname: VIDEO_MIMETYPE_EXT[fileUrl.mimeType],
2017-11-10 13:34:45 +00:00
infoHash: parsed.infoHash,
2017-11-16 14:22:39 +00:00
resolution: fileUrl.width,
size: fileUrl.size,
2017-11-10 13:34:45 +00:00
videoId: videoCreated.id
}
attributes.push(attribute)
}
return attributes
}
2017-12-28 10:16:08 +00:00
async function videoCommentActivityObjectToDBAttributes (video: VideoModel, actor: ActorModel, comment: VideoCommentObject) {
let originCommentId: number = null
let inReplyToCommentId: number = null
// If this is not a reply to the video (thread), create or get the parent comment
if (video.url !== comment.inReplyTo) {
const [ parent ] = await addVideoComment(video, comment.inReplyTo)
if (!parent) {
logger.warn('Cannot fetch or get parent comment %s of comment %s.', comment.inReplyTo, comment.id)
return undefined
}
originCommentId = parent.originCommentId || parent.id
inReplyToCommentId = parent.id
}
return {
url: comment.url,
text: comment.content,
videoId: video.id,
accountId: actor.Account.id,
inReplyToCommentId,
originCommentId,
createdAt: new Date(comment.published),
updatedAt: new Date(comment.updated)
}
}
async function addVideoShares (instance: VideoModel, shareUrls: string[]) {
for (const shareUrl of shareUrls) {
// Fetch url
2017-12-28 10:16:08 +00:00
const { body } = await doRequest({
uri: shareUrl,
json: true,
activityPub: true
})
2017-12-28 10:16:08 +00:00
const actorUrl = body.actor
2017-12-14 16:38:41 +00:00
if (!actorUrl) continue
2017-12-14 16:38:41 +00:00
const actor = await getOrCreateActorAndServerAndModel(actorUrl)
const entry = {
2017-12-14 16:38:41 +00:00
actorId: actor.id,
videoId: instance.id
}
2017-12-12 16:53:50 +00:00
await VideoShareModel.findOrCreate({
where: entry,
defaults: entry
})
}
}
2017-12-28 10:16:08 +00:00
async function addVideoComments (instance: VideoModel, commentUrls: string[]) {
for (const commentUrl of commentUrls) {
await addVideoComment(instance, commentUrl)
}
}
async function addVideoComment (instance: VideoModel, commentUrl: string) {
// Fetch url
const { body } = await doRequest({
uri: commentUrl,
json: true,
activityPub: true
})
const actorUrl = body.attributedTo
if (!actorUrl) return []
const actor = await getOrCreateActorAndServerAndModel(actorUrl)
const entry = await videoCommentActivityObjectToDBAttributes(instance, actor, body)
if (!entry) return []
return VideoCommentModel.findOrCreate({
where: {
url: body.id
},
defaults: entry
})
}
2017-11-10 13:34:45 +00:00
// ---------------------------------------------------------------------------
export {
videoFileActivityUrlToDBAttributes,
2017-11-16 14:22:39 +00:00
videoActivityObjectToDBAttributes,
2017-12-28 10:16:08 +00:00
addVideoShares,
addVideoComments
2017-11-10 13:34:45 +00:00
}