]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/lib/activitypub/process/misc.ts
Propagate old comment on new follow
[github/Chocobozzz/PeerTube.git] / server / lib / activitypub / process / misc.ts
1 import * as magnetUtil from 'magnet-uri'
2 import { VideoTorrentObject } from '../../../../shared'
3 import { VideoCommentObject } from '../../../../shared/models/activitypub/objects/video-comment-object'
4 import { VideoPrivacy } from '../../../../shared/models/videos'
5 import { isVideoFileInfoHashValid } from '../../../helpers/custom-validators/videos'
6 import { logger } from '../../../helpers/logger'
7 import { doRequest } from '../../../helpers/requests'
8 import { ACTIVITY_PUB, VIDEO_MIMETYPE_EXT } from '../../../initializers'
9 import { ActorModel } from '../../../models/activitypub/actor'
10 import { VideoModel } from '../../../models/video/video'
11 import { VideoChannelModel } from '../../../models/video/video-channel'
12 import { VideoCommentModel } from '../../../models/video/video-comment'
13 import { VideoShareModel } from '../../../models/video/video-share'
14 import { getOrCreateActorAndServerAndModel } from '../actor'
15
16 async function videoActivityObjectToDBAttributes (
17 videoChannel: VideoChannelModel,
18 videoObject: VideoTorrentObject,
19 to: string[] = [],
20 cc: string[] = []
21 ) {
22 let privacy = VideoPrivacy.PRIVATE
23 if (to.indexOf(ACTIVITY_PUB.PUBLIC) !== -1) privacy = VideoPrivacy.PUBLIC
24 else if (cc.indexOf(ACTIVITY_PUB.PUBLIC) !== -1) privacy = VideoPrivacy.UNLISTED
25
26 const duration = videoObject.duration.replace(/[^\d]+/, '')
27 let language = null
28 if (videoObject.language) {
29 language = parseInt(videoObject.language.identifier, 10)
30 }
31
32 let category = null
33 if (videoObject.category) {
34 category = parseInt(videoObject.category.identifier, 10)
35 }
36
37 let licence = null
38 if (videoObject.licence) {
39 licence = parseInt(videoObject.licence.identifier, 10)
40 }
41
42 let description = null
43 if (videoObject.content) {
44 description = videoObject.content
45 }
46
47 return {
48 name: videoObject.name,
49 uuid: videoObject.uuid,
50 url: videoObject.id,
51 category,
52 licence,
53 language,
54 description,
55 nsfw: videoObject.nsfw,
56 channelId: videoChannel.id,
57 duration: parseInt(duration, 10),
58 createdAt: new Date(videoObject.published),
59 // FIXME: updatedAt does not seems to be considered by Sequelize
60 updatedAt: new Date(videoObject.updated),
61 views: videoObject.views,
62 likes: 0,
63 dislikes: 0,
64 remote: true,
65 privacy
66 }
67 }
68
69 function videoFileActivityUrlToDBAttributes (videoCreated: VideoModel, videoObject: VideoTorrentObject) {
70 const mimeTypes = Object.keys(VIDEO_MIMETYPE_EXT)
71 const fileUrls = videoObject.url.filter(u => {
72 return mimeTypes.indexOf(u.mimeType) !== -1 && u.mimeType.startsWith('video/')
73 })
74
75 if (fileUrls.length === 0) {
76 throw new Error('Cannot find video files for ' + videoCreated.url)
77 }
78
79 const attributes = []
80 for (const fileUrl of fileUrls) {
81 // Fetch associated magnet uri
82 const magnet = videoObject.url.find(u => {
83 return u.mimeType === 'application/x-bittorrent;x-scheme-handler/magnet' && u.width === fileUrl.width
84 })
85
86 if (!magnet) throw new Error('Cannot find associated magnet uri for file ' + fileUrl.url)
87
88 const parsed = magnetUtil.decode(magnet.url)
89 if (!parsed || isVideoFileInfoHashValid(parsed.infoHash) === false) throw new Error('Cannot parse magnet URI ' + magnet.url)
90
91 const attribute = {
92 extname: VIDEO_MIMETYPE_EXT[fileUrl.mimeType],
93 infoHash: parsed.infoHash,
94 resolution: fileUrl.width,
95 size: fileUrl.size,
96 videoId: videoCreated.id
97 }
98 attributes.push(attribute)
99 }
100
101 return attributes
102 }
103
104 async function videoCommentActivityObjectToDBAttributes (video: VideoModel, actor: ActorModel, comment: VideoCommentObject) {
105 let originCommentId: number = null
106 let inReplyToCommentId: number = null
107
108 // If this is not a reply to the video (thread), create or get the parent comment
109 if (video.url !== comment.inReplyTo) {
110 const [ parent ] = await addVideoComment(video, comment.inReplyTo)
111 if (!parent) {
112 logger.warn('Cannot fetch or get parent comment %s of comment %s.', comment.inReplyTo, comment.id)
113 return undefined
114 }
115
116 originCommentId = parent.originCommentId || parent.id
117 inReplyToCommentId = parent.id
118 }
119
120 return {
121 url: comment.url,
122 text: comment.content,
123 videoId: video.id,
124 accountId: actor.Account.id,
125 inReplyToCommentId,
126 originCommentId,
127 createdAt: new Date(comment.published),
128 updatedAt: new Date(comment.updated)
129 }
130 }
131
132 async function addVideoShares (instance: VideoModel, shareUrls: string[]) {
133 for (const shareUrl of shareUrls) {
134 // Fetch url
135 const { body } = await doRequest({
136 uri: shareUrl,
137 json: true,
138 activityPub: true
139 })
140 const actorUrl = body.actor
141 if (!actorUrl) continue
142
143 const actor = await getOrCreateActorAndServerAndModel(actorUrl)
144
145 const entry = {
146 actorId: actor.id,
147 videoId: instance.id
148 }
149
150 await VideoShareModel.findOrCreate({
151 where: entry,
152 defaults: entry
153 })
154 }
155 }
156
157 async function addVideoComments (instance: VideoModel, commentUrls: string[]) {
158 for (const commentUrl of commentUrls) {
159 await addVideoComment(instance, commentUrl)
160 }
161 }
162
163 async function addVideoComment (instance: VideoModel, commentUrl: string) {
164 // Fetch url
165 const { body } = await doRequest({
166 uri: commentUrl,
167 json: true,
168 activityPub: true
169 })
170
171 const actorUrl = body.attributedTo
172 if (!actorUrl) return []
173
174 const actor = await getOrCreateActorAndServerAndModel(actorUrl)
175 const entry = await videoCommentActivityObjectToDBAttributes(instance, actor, body)
176 if (!entry) return []
177
178 return VideoCommentModel.findOrCreate({
179 where: {
180 url: body.id
181 },
182 defaults: entry
183 })
184 }
185
186 // ---------------------------------------------------------------------------
187
188 export {
189 videoFileActivityUrlToDBAttributes,
190 videoActivityObjectToDBAttributes,
191 addVideoShares,
192 addVideoComments
193 }