aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/lib/activitypub
diff options
context:
space:
mode:
Diffstat (limited to 'server/lib/activitypub')
-rw-r--r--server/lib/activitypub/misc.ts46
-rw-r--r--server/lib/activitypub/process-add.ts22
-rw-r--r--server/lib/activitypub/process-announce.ts29
-rw-r--r--server/lib/activitypub/process-create.ts16
-rw-r--r--server/lib/activitypub/send-request.ts40
5 files changed, 84 insertions, 69 deletions
diff --git a/server/lib/activitypub/misc.ts b/server/lib/activitypub/misc.ts
index 43d26c328..13838fc4c 100644
--- a/server/lib/activitypub/misc.ts
+++ b/server/lib/activitypub/misc.ts
@@ -7,6 +7,21 @@ import { VIDEO_MIMETYPE_EXT } from '../../initializers/constants'
7import { VideoChannelInstance } from '../../models/video/video-channel-interface' 7import { VideoChannelInstance } from '../../models/video/video-channel-interface'
8import { VideoFileAttributes } from '../../models/video/video-file-interface' 8import { VideoFileAttributes } from '../../models/video/video-file-interface'
9import { VideoAttributes, VideoInstance } from '../../models/video/video-interface' 9import { VideoAttributes, VideoInstance } from '../../models/video/video-interface'
10import { VideoChannelObject } from '../../../shared/models/activitypub/objects/video-channel-object'
11import { AccountInstance } from '../../models/account/account-interface'
12
13function videoChannelActivityObjectToDBAttributes (videoChannelObject: VideoChannelObject, account: AccountInstance) {
14 return {
15 name: videoChannelObject.name,
16 description: videoChannelObject.content,
17 uuid: videoChannelObject.uuid,
18 url: videoChannelObject.id,
19 createdAt: new Date(videoChannelObject.published),
20 updatedAt: new Date(videoChannelObject.updated),
21 remote: true,
22 accountId: account.id
23 }
24}
10 25
11async function videoActivityObjectToDBAttributes ( 26async function videoActivityObjectToDBAttributes (
12 videoChannel: VideoChannelInstance, 27 videoChannel: VideoChannelInstance,
@@ -45,26 +60,32 @@ async function videoActivityObjectToDBAttributes (
45} 60}
46 61
47function videoFileActivityUrlToDBAttributes (videoCreated: VideoInstance, videoObject: VideoTorrentObject) { 62function videoFileActivityUrlToDBAttributes (videoCreated: VideoInstance, videoObject: VideoTorrentObject) {
48 const fileUrls = videoObject.url 63 const mimeTypes = Object.keys(VIDEO_MIMETYPE_EXT)
49 .filter(u => Object.keys(VIDEO_MIMETYPE_EXT).indexOf(u.mimeType) !== -1 && u.url.startsWith('video/')) 64 const fileUrls = videoObject.url.filter(u => {
65 return mimeTypes.indexOf(u.mimeType) !== -1 && u.mimeType.startsWith('video/')
66 })
67
68 if (fileUrls.length === 0) {
69 throw new Error('Cannot find video files for ' + videoCreated.url)
70 }
50 71
51 const attributes: VideoFileAttributes[] = [] 72 const attributes: VideoFileAttributes[] = []
52 for (const url of fileUrls) { 73 for (const fileUrl of fileUrls) {
53 // Fetch associated magnet uri 74 // Fetch associated magnet uri
54 const magnet = videoObject.url 75 const magnet = videoObject.url.find(u => {
55 .find(u => { 76 return u.mimeType === 'application/x-bittorrent;x-scheme-handler/magnet' && u.width === fileUrl.width
56 return u.mimeType === 'application/x-bittorrent;x-scheme-handler/magnet' && u.width === url.width 77 })
57 }) 78
58 if (!magnet) throw new Error('Cannot find associated magnet uri for file ' + url.url) 79 if (!magnet) throw new Error('Cannot find associated magnet uri for file ' + fileUrl.url)
59 80
60 const parsed = magnetUtil.decode(magnet.url) 81 const parsed = magnetUtil.decode(magnet.url)
61 if (!parsed || isVideoFileInfoHashValid(parsed.infoHash) === false) throw new Error('Cannot parse magnet URI ' + magnet.url) 82 if (!parsed || isVideoFileInfoHashValid(parsed.infoHash) === false) throw new Error('Cannot parse magnet URI ' + magnet.url)
62 83
63 const attribute = { 84 const attribute = {
64 extname: VIDEO_MIMETYPE_EXT[url.mimeType], 85 extname: VIDEO_MIMETYPE_EXT[fileUrl.mimeType],
65 infoHash: parsed.infoHash, 86 infoHash: parsed.infoHash,
66 resolution: url.width, 87 resolution: fileUrl.width,
67 size: url.size, 88 size: fileUrl.size,
68 videoId: videoCreated.id 89 videoId: videoCreated.id
69 } 90 }
70 attributes.push(attribute) 91 attributes.push(attribute)
@@ -77,5 +98,6 @@ function videoFileActivityUrlToDBAttributes (videoCreated: VideoInstance, videoO
77 98
78export { 99export {
79 videoFileActivityUrlToDBAttributes, 100 videoFileActivityUrlToDBAttributes,
80 videoActivityObjectToDBAttributes 101 videoActivityObjectToDBAttributes,
102 videoChannelActivityObjectToDBAttributes
81} 103}
diff --git a/server/lib/activitypub/process-add.ts b/server/lib/activitypub/process-add.ts
index 98e414dbb..df7139d46 100644
--- a/server/lib/activitypub/process-add.ts
+++ b/server/lib/activitypub/process-add.ts
@@ -5,6 +5,8 @@ import { database as db } from '../../initializers'
5import { AccountInstance } from '../../models/account/account-interface' 5import { AccountInstance } from '../../models/account/account-interface'
6import { videoActivityObjectToDBAttributes, videoFileActivityUrlToDBAttributes } from './misc' 6import { videoActivityObjectToDBAttributes, videoFileActivityUrlToDBAttributes } from './misc'
7import Bluebird = require('bluebird') 7import Bluebird = require('bluebird')
8import { getOrCreateVideoChannel } from '../../helpers/activitypub'
9import { VideoChannelInstance } from '../../models/video/video-channel-interface'
8 10
9async function processAddActivity (activity: ActivityAdd) { 11async function processAddActivity (activity: ActivityAdd) {
10 const activityObject = activity.object 12 const activityObject = activity.object
@@ -12,7 +14,10 @@ async function processAddActivity (activity: ActivityAdd) {
12 const account = await getOrCreateAccount(activity.actor) 14 const account = await getOrCreateAccount(activity.actor)
13 15
14 if (activityType === 'Video') { 16 if (activityType === 'Video') {
15 return processAddVideo(account, activity.id, activityObject as VideoTorrentObject) 17 const videoChannelUrl = activity.target
18 const videoChannel = await getOrCreateVideoChannel(account, videoChannelUrl)
19
20 return processAddVideo(account, videoChannel, activityObject as VideoTorrentObject)
16 } 21 }
17 22
18 logger.warn('Unknown activity object type %s when creating activity.', activityType, { activity: activity.id }) 23 logger.warn('Unknown activity object type %s when creating activity.', activityType, { activity: activity.id })
@@ -27,16 +32,16 @@ export {
27 32
28// --------------------------------------------------------------------------- 33// ---------------------------------------------------------------------------
29 34
30function processAddVideo (account: AccountInstance, videoChannelUrl: string, video: VideoTorrentObject) { 35function processAddVideo (account: AccountInstance, videoChannel: VideoChannelInstance, video: VideoTorrentObject) {
31 const options = { 36 const options = {
32 arguments: [ account, videoChannelUrl, video ], 37 arguments: [ account, videoChannel, video ],
33 errorMessage: 'Cannot insert the remote video with many retries.' 38 errorMessage: 'Cannot insert the remote video with many retries.'
34 } 39 }
35 40
36 return retryTransactionWrapper(addRemoteVideo, options) 41 return retryTransactionWrapper(addRemoteVideo, options)
37} 42}
38 43
39async function addRemoteVideo (account: AccountInstance, videoChannelUrl: string, videoToCreateData: VideoTorrentObject) { 44function addRemoteVideo (account: AccountInstance, videoChannel: VideoChannelInstance, videoToCreateData: VideoTorrentObject) {
40 logger.debug('Adding remote video %s.', videoToCreateData.url) 45 logger.debug('Adding remote video %s.', videoToCreateData.url)
41 46
42 return db.sequelize.transaction(async t => { 47 return db.sequelize.transaction(async t => {
@@ -44,9 +49,6 @@ async function addRemoteVideo (account: AccountInstance, videoChannelUrl: string
44 transaction: t 49 transaction: t
45 } 50 }
46 51
47 const videoChannel = await db.VideoChannel.loadByUrl(videoChannelUrl, t)
48 if (!videoChannel) throw new Error('Video channel not found.')
49
50 if (videoChannel.Account.id !== account.id) throw new Error('Video channel is not owned by this account.') 52 if (videoChannel.Account.id !== account.id) throw new Error('Video channel is not owned by this account.')
51 53
52 const videoData = await videoActivityObjectToDBAttributes(videoChannel, videoToCreateData, t) 54 const videoData = await videoActivityObjectToDBAttributes(videoChannel, videoToCreateData, t)
@@ -59,8 +61,11 @@ async function addRemoteVideo (account: AccountInstance, videoChannelUrl: string
59 const videoCreated = await video.save(sequelizeOptions) 61 const videoCreated = await video.save(sequelizeOptions)
60 62
61 const videoFileAttributes = await videoFileActivityUrlToDBAttributes(videoCreated, videoToCreateData) 63 const videoFileAttributes = await videoFileActivityUrlToDBAttributes(videoCreated, videoToCreateData)
64 if (videoFileAttributes.length === 0) {
65 throw new Error('Cannot find valid files for video %s ' + videoToCreateData.url)
66 }
62 67
63 const tasks: Bluebird<any>[] = videoFileAttributes.map(f => db.VideoFile.create(f)) 68 const tasks: Bluebird<any>[] = videoFileAttributes.map(f => db.VideoFile.create(f, { transaction: t }))
64 await Promise.all(tasks) 69 await Promise.all(tasks)
65 70
66 const tags = videoToCreateData.tag.map(t => t.name) 71 const tags = videoToCreateData.tag.map(t => t.name)
@@ -71,5 +76,4 @@ async function addRemoteVideo (account: AccountInstance, videoChannelUrl: string
71 76
72 return videoCreated 77 return videoCreated
73 }) 78 })
74
75} 79}
diff --git a/server/lib/activitypub/process-announce.ts b/server/lib/activitypub/process-announce.ts
index d67958aec..f9674e095 100644
--- a/server/lib/activitypub/process-announce.ts
+++ b/server/lib/activitypub/process-announce.ts
@@ -10,38 +10,33 @@ import { VideoChannelInstance } from '../../models/video/video-channel-interface
10import { VideoInstance } from '../../models/index' 10import { VideoInstance } from '../../models/index'
11 11
12async function processAnnounceActivity (activity: ActivityAnnounce) { 12async function processAnnounceActivity (activity: ActivityAnnounce) {
13 const activityType = activity.object.type 13 const announcedActivity = activity.object
14 const accountAnnouncer = await getOrCreateAccount(activity.actor) 14 const accountAnnouncer = await getOrCreateAccount(activity.actor)
15 15
16 if (activityType === 'VideoChannel') { 16 if (announcedActivity.type === 'Create' && announcedActivity.object.type === 'VideoChannel') {
17 const activityCreate = Object.assign(activity, {
18 type: 'Create' as 'Create',
19 actor: activity.object.actor,
20 object: activity.object as VideoChannelObject
21 })
22
23 // Add share entry 17 // Add share entry
24 const videoChannel: VideoChannelInstance = await processCreateActivity(activityCreate) 18 const videoChannel: VideoChannelInstance = await processCreateActivity(announcedActivity)
25 await db.VideoChannelShare.create({ 19 await db.VideoChannelShare.create({
26 accountId: accountAnnouncer.id, 20 accountId: accountAnnouncer.id,
27 videoChannelId: videoChannel.id 21 videoChannelId: videoChannel.id
28 }) 22 })
29 } else if (activityType === 'Video') {
30 const activityAdd = Object.assign(activity, {
31 type: 'Add' as 'Add',
32 actor: activity.object.actor,
33 object: activity.object as VideoTorrentObject
34 })
35 23
24 return undefined
25 } else if (announcedActivity.type === 'Add' && announcedActivity.object.type === 'Video') {
36 // Add share entry 26 // Add share entry
37 const video: VideoInstance = await processAddActivity(activityAdd) 27 const video: VideoInstance = await processAddActivity(announcedActivity)
38 await db.VideoShare.create({ 28 await db.VideoShare.create({
39 accountId: accountAnnouncer.id, 29 accountId: accountAnnouncer.id,
40 videoId: video.id 30 videoId: video.id
41 }) 31 })
32
33 return undefined
42 } 34 }
43 35
44 logger.warn('Unknown activity object type %s when announcing activity.', activityType, { activity: activity.id }) 36 logger.warn(
37 'Unknown activity object type %s -> %s when announcing activity.', announcedActivity.type, announcedActivity.object.type,
38 { activity: activity.id }
39 )
45 return Promise.resolve(undefined) 40 return Promise.resolve(undefined)
46} 41}
47 42
diff --git a/server/lib/activitypub/process-create.ts b/server/lib/activitypub/process-create.ts
index 4e4c9f703..c4706a66b 100644
--- a/server/lib/activitypub/process-create.ts
+++ b/server/lib/activitypub/process-create.ts
@@ -4,6 +4,7 @@ import { logger, retryTransactionWrapper } from '../../helpers'
4import { getActivityPubUrl, getOrCreateAccount } from '../../helpers/activitypub' 4import { getActivityPubUrl, getOrCreateAccount } from '../../helpers/activitypub'
5import { database as db } from '../../initializers' 5import { database as db } from '../../initializers'
6import { AccountInstance } from '../../models/account/account-interface' 6import { AccountInstance } from '../../models/account/account-interface'
7import { videoChannelActivityObjectToDBAttributes } from './misc'
7 8
8async function processCreateActivity (activity: ActivityCreate) { 9async function processCreateActivity (activity: ActivityCreate) {
9 const activityObject = activity.object 10 const activityObject = activity.object
@@ -37,23 +38,14 @@ function processCreateVideoChannel (account: AccountInstance, videoChannelToCrea
37 return retryTransactionWrapper(addRemoteVideoChannel, options) 38 return retryTransactionWrapper(addRemoteVideoChannel, options)
38} 39}
39 40
40async function addRemoteVideoChannel (account: AccountInstance, videoChannelToCreateData: VideoChannelObject) { 41function addRemoteVideoChannel (account: AccountInstance, videoChannelToCreateData: VideoChannelObject) {
41 logger.debug('Adding remote video channel "%s".', videoChannelToCreateData.uuid) 42 logger.debug('Adding remote video channel "%s".', videoChannelToCreateData.uuid)
42 43
43 return db.sequelize.transaction(async t => { 44 return db.sequelize.transaction(async t => {
44 let videoChannel = await db.VideoChannel.loadByUUIDOrUrl(videoChannelToCreateData.uuid, videoChannelToCreateData.id, t) 45 let videoChannel = await db.VideoChannel.loadByUUIDOrUrl(videoChannelToCreateData.uuid, videoChannelToCreateData.id, t)
45 if (videoChannel) throw new Error('Video channel with this URL/UUID already exists.') 46 if (videoChannel) throw new Error('Video channel with this URL/UUID already exists.')
46 47
47 const videoChannelData = { 48 const videoChannelData = videoChannelActivityObjectToDBAttributes(videoChannelToCreateData, account)
48 name: videoChannelToCreateData.name,
49 description: videoChannelToCreateData.content,
50 uuid: videoChannelToCreateData.uuid,
51 createdAt: new Date(videoChannelToCreateData.published),
52 updatedAt: new Date(videoChannelToCreateData.updated),
53 remote: true,
54 accountId: account.id
55 }
56
57 videoChannel = db.VideoChannel.build(videoChannelData) 49 videoChannel = db.VideoChannel.build(videoChannelData)
58 videoChannel.url = getActivityPubUrl('videoChannel', videoChannel.uuid) 50 videoChannel.url = getActivityPubUrl('videoChannel', videoChannel.uuid)
59 51
@@ -73,7 +65,7 @@ function processCreateVideoAbuse (account: AccountInstance, videoAbuseToCreateDa
73 return retryTransactionWrapper(addRemoteVideoAbuse, options) 65 return retryTransactionWrapper(addRemoteVideoAbuse, options)
74} 66}
75 67
76async function addRemoteVideoAbuse (account: AccountInstance, videoAbuseToCreateData: VideoAbuseObject) { 68function addRemoteVideoAbuse (account: AccountInstance, videoAbuseToCreateData: VideoAbuseObject) {
77 logger.debug('Reporting remote abuse for video %s.', videoAbuseToCreateData.object) 69 logger.debug('Reporting remote abuse for video %s.', videoAbuseToCreateData.object)
78 70
79 return db.sequelize.transaction(async t => { 71 return db.sequelize.transaction(async t => {
diff --git a/server/lib/activitypub/send-request.ts b/server/lib/activitypub/send-request.ts
index 664b9d826..f9b72f2a8 100644
--- a/server/lib/activitypub/send-request.ts
+++ b/server/lib/activitypub/send-request.ts
@@ -59,24 +59,21 @@ async function sendDeleteAccount (account: AccountInstance, t: Sequelize.Transac
59 return broadcastToFollowers(data, [ account ], t) 59 return broadcastToFollowers(data, [ account ], t)
60} 60}
61 61
62async function sendAnnounce (byAccount: AccountInstance, instance: VideoInstance | VideoChannelInstance, t: Sequelize.Transaction) { 62async function sendVideoChannelAnnounce (byAccount: AccountInstance, videoChannel: VideoChannelInstance, t: Sequelize.Transaction) {
63 const object = instance.toActivityPubObject() 63 const url = getActivityPubUrl('videoChannel', videoChannel.uuid) + '#announce'
64 64 const announcedActivity = await createActivityData(url, videoChannel.Account, videoChannel.toActivityPubObject(), true)
65 let url = '' 65
66 let objectActorUrl: string 66 const data = await announceActivityData(url, byAccount, announcedActivity)
67 if ((instance as any).VideoChannel !== undefined) { 67 return broadcastToFollowers(data, [ byAccount ], t)
68 objectActorUrl = (instance as VideoInstance).VideoChannel.Account.url 68}
69 url = getActivityPubUrl('video', instance.uuid) + '#announce'
70 } else {
71 objectActorUrl = (instance as VideoChannelInstance).Account.url
72 url = getActivityPubUrl('videoChannel', instance.uuid) + '#announce'
73 }
74 69
75 const objectWithActor = Object.assign(object, { 70async function sendVideoAnnounce (byAccount: AccountInstance, video: VideoInstance, t: Sequelize.Transaction) {
76 actor: objectActorUrl 71 const url = getActivityPubUrl('video', video.uuid) + '#announce'
77 })
78 72
79 const data = await announceActivityData(url, byAccount, objectWithActor) 73 const videoChannel = video.VideoChannel
74 const announcedActivity = await addActivityData(url, videoChannel.Account, videoChannel.url, video.toActivityPubObject(), true)
75
76 const data = await announceActivityData(url, byAccount, announcedActivity)
80 return broadcastToFollowers(data, [ byAccount ], t) 77 return broadcastToFollowers(data, [ byAccount ], t)
81} 78}
82 79
@@ -117,7 +114,8 @@ export {
117 sendAccept, 114 sendAccept,
118 sendFollow, 115 sendFollow,
119 sendVideoAbuse, 116 sendVideoAbuse,
120 sendAnnounce 117 sendVideoChannelAnnounce,
118 sendVideoAnnounce
121} 119}
122 120
123// --------------------------------------------------------------------------- 121// ---------------------------------------------------------------------------
@@ -159,7 +157,7 @@ async function getPublicActivityTo (account: AccountInstance) {
159 return inboxUrls.concat('https://www.w3.org/ns/activitystreams#Public') 157 return inboxUrls.concat('https://www.w3.org/ns/activitystreams#Public')
160} 158}
161 159
162async function createActivityData (url: string, byAccount: AccountInstance, object: any) { 160async function createActivityData (url: string, byAccount: AccountInstance, object: any, raw = false) {
163 const to = await getPublicActivityTo(byAccount) 161 const to = await getPublicActivityTo(byAccount)
164 const base = { 162 const base = {
165 type: 'Create', 163 type: 'Create',
@@ -169,6 +167,8 @@ async function createActivityData (url: string, byAccount: AccountInstance, obje
169 object 167 object
170 } 168 }
171 169
170 if (raw === true) return base
171
172 return buildSignedActivity(byAccount, base) 172 return buildSignedActivity(byAccount, base)
173} 173}
174 174
@@ -195,7 +195,7 @@ async function deleteActivityData (url: string, byAccount: AccountInstance) {
195 return buildSignedActivity(byAccount, base) 195 return buildSignedActivity(byAccount, base)
196} 196}
197 197
198async function addActivityData (url: string, byAccount: AccountInstance, target: string, object: any) { 198async function addActivityData (url: string, byAccount: AccountInstance, target: string, object: any, raw = false) {
199 const to = await getPublicActivityTo(byAccount) 199 const to = await getPublicActivityTo(byAccount)
200 const base = { 200 const base = {
201 type: 'Add', 201 type: 'Add',
@@ -206,6 +206,8 @@ async function addActivityData (url: string, byAccount: AccountInstance, target:
206 target 206 target
207 } 207 }
208 208
209 if (raw === true) return base
210
209 return buildSignedActivity(byAccount, base) 211 return buildSignedActivity(byAccount, base)
210} 212}
211 213