aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/lib/activitypub
diff options
context:
space:
mode:
authorChocobozzz <florian.bigard@gmail.com>2017-11-22 16:25:03 +0100
committerChocobozzz <florian.bigard@gmail.com>2017-11-27 19:40:53 +0100
commit40ff57078e15d5b86ee6b71e198b95d3feb78eaf (patch)
tree88031d4eac6a26597e8a1f2fc63674664e3eae26 /server/lib/activitypub
parentc46edbc2f6ca310b2f0331f979ac6caf27f6eb92 (diff)
downloadPeerTube-40ff57078e15d5b86ee6b71e198b95d3feb78eaf.tar.gz
PeerTube-40ff57078e15d5b86ee6b71e198b95d3feb78eaf.tar.zst
PeerTube-40ff57078e15d5b86ee6b71e198b95d3feb78eaf.zip
Federate video views
Diffstat (limited to 'server/lib/activitypub')
-rw-r--r--server/lib/activitypub/process/misc.ts7
-rw-r--r--server/lib/activitypub/process/process-create.ts19
-rw-r--r--server/lib/activitypub/process/process-follow.ts6
-rw-r--r--server/lib/activitypub/send/misc.ts14
-rw-r--r--server/lib/activitypub/send/send-create.ts64
-rw-r--r--server/lib/activitypub/url.ts7
6 files changed, 104 insertions, 13 deletions
diff --git a/server/lib/activitypub/process/misc.ts b/server/lib/activitypub/process/misc.ts
index e90a793fc..eefbe2884 100644
--- a/server/lib/activitypub/process/misc.ts
+++ b/server/lib/activitypub/process/misc.ts
@@ -33,13 +33,18 @@ async function videoActivityObjectToDBAttributes (
33 else if (cc.indexOf(ACTIVITY_PUB.PUBLIC) !== -1) privacy = VideoPrivacy.UNLISTED 33 else if (cc.indexOf(ACTIVITY_PUB.PUBLIC) !== -1) privacy = VideoPrivacy.UNLISTED
34 34
35 const duration = videoObject.duration.replace(/[^\d]+/, '') 35 const duration = videoObject.duration.replace(/[^\d]+/, '')
36 let language = null
37 if (videoObject.language) {
38 language = parseInt(videoObject.language.identifier, 10)
39 }
40
36 const videoData: VideoAttributes = { 41 const videoData: VideoAttributes = {
37 name: videoObject.name, 42 name: videoObject.name,
38 uuid: videoObject.uuid, 43 uuid: videoObject.uuid,
39 url: videoObject.id, 44 url: videoObject.id,
40 category: parseInt(videoObject.category.identifier, 10), 45 category: parseInt(videoObject.category.identifier, 10),
41 licence: parseInt(videoObject.licence.identifier, 10), 46 licence: parseInt(videoObject.licence.identifier, 10),
42 language: parseInt(videoObject.language.identifier, 10), 47 language,
43 nsfw: videoObject.nsfw, 48 nsfw: videoObject.nsfw,
44 description: videoObject.content, 49 description: videoObject.content,
45 channelId: videoChannel.id, 50 channelId: videoChannel.id,
diff --git a/server/lib/activitypub/process/process-create.ts b/server/lib/activitypub/process/process-create.ts
index ddf7c74f6..1777733a0 100644
--- a/server/lib/activitypub/process/process-create.ts
+++ b/server/lib/activitypub/process/process-create.ts
@@ -1,9 +1,11 @@
1import { ActivityCreate, VideoChannelObject } from '../../../../shared' 1import { ActivityCreate, VideoChannelObject } from '../../../../shared'
2import { VideoAbuseObject } from '../../../../shared/models/activitypub/objects/video-abuse-object' 2import { VideoAbuseObject } from '../../../../shared/models/activitypub/objects/video-abuse-object'
3import { ViewObject } from '../../../../shared/models/activitypub/objects/view-object'
3import { logger, retryTransactionWrapper } from '../../../helpers' 4import { logger, retryTransactionWrapper } from '../../../helpers'
4import { database as db } from '../../../initializers' 5import { database as db } from '../../../initializers'
5import { AccountInstance } from '../../../models/account/account-interface' 6import { AccountInstance } from '../../../models/account/account-interface'
6import { getOrCreateAccountAndServer } from '../account' 7import { getOrCreateAccountAndServer } from '../account'
8import { sendCreateViewToVideoFollowers } from '../send/send-create'
7import { getVideoChannelActivityPubUrl } from '../url' 9import { getVideoChannelActivityPubUrl } from '../url'
8import { videoChannelActivityObjectToDBAttributes } from './misc' 10import { videoChannelActivityObjectToDBAttributes } from './misc'
9 11
@@ -12,7 +14,9 @@ async function processCreateActivity (activity: ActivityCreate) {
12 const activityType = activityObject.type 14 const activityType = activityObject.type
13 const account = await getOrCreateAccountAndServer(activity.actor) 15 const account = await getOrCreateAccountAndServer(activity.actor)
14 16
15 if (activityType === 'VideoChannel') { 17 if (activityType === 'View') {
18 return processCreateView(activityObject as ViewObject)
19 } else if (activityType === 'VideoChannel') {
16 return processCreateVideoChannel(account, activityObject as VideoChannelObject) 20 return processCreateVideoChannel(account, activityObject as VideoChannelObject)
17 } else if (activityType === 'Flag') { 21 } else if (activityType === 'Flag') {
18 return processCreateVideoAbuse(account, activityObject as VideoAbuseObject) 22 return processCreateVideoAbuse(account, activityObject as VideoAbuseObject)
@@ -30,6 +34,19 @@ export {
30 34
31// --------------------------------------------------------------------------- 35// ---------------------------------------------------------------------------
32 36
37async function processCreateView (view: ViewObject) {
38 const video = await db.Video.loadByUrlAndPopulateAccount(view.object)
39
40 if (!video) throw new Error('Unknown video ' + view.object)
41
42 const account = await db.Account.loadByUrl(view.actor)
43 if (!account) throw new Error('Unknown account ' + view.actor)
44
45 await video.increment('views')
46
47 if (video.isOwned()) await sendCreateViewToVideoFollowers(account, video, undefined)
48}
49
33function processCreateVideoChannel (account: AccountInstance, videoChannelToCreateData: VideoChannelObject) { 50function processCreateVideoChannel (account: AccountInstance, videoChannelToCreateData: VideoChannelObject) {
34 const options = { 51 const options = {
35 arguments: [ account, videoChannelToCreateData ], 52 arguments: [ account, videoChannelToCreateData ],
diff --git a/server/lib/activitypub/process/process-follow.ts b/server/lib/activitypub/process/process-follow.ts
index 248004226..320dc1138 100644
--- a/server/lib/activitypub/process/process-follow.ts
+++ b/server/lib/activitypub/process/process-follow.ts
@@ -49,6 +49,12 @@ async function follow (account: AccountInstance, targetAccountURL: string) {
49 }, 49 },
50 transaction: t 50 transaction: t
51 }) 51 })
52
53 if (accountFollow.state !== 'accepted') {
54 accountFollow.state = 'accepted'
55 await accountFollow.save({ transaction: t })
56 }
57
52 accountFollow.AccountFollower = account 58 accountFollow.AccountFollower = account
53 accountFollow.AccountFollowing = targetAccount 59 accountFollow.AccountFollowing = targetAccount
54 60
diff --git a/server/lib/activitypub/send/misc.ts b/server/lib/activitypub/send/misc.ts
index bea955b67..f3dc5c148 100644
--- a/server/lib/activitypub/send/misc.ts
+++ b/server/lib/activitypub/send/misc.ts
@@ -4,16 +4,26 @@ import { ACTIVITY_PUB, database as db } from '../../../initializers'
4import { AccountInstance } from '../../../models/account/account-interface' 4import { AccountInstance } from '../../../models/account/account-interface'
5import { activitypubHttpJobScheduler } from '../../jobs/activitypub-http-job-scheduler/activitypub-http-job-scheduler' 5import { activitypubHttpJobScheduler } from '../../jobs/activitypub-http-job-scheduler/activitypub-http-job-scheduler'
6 6
7async function broadcastToFollowers (data: any, byAccount: AccountInstance, toAccountFollowers: AccountInstance[], t: Transaction) { 7async function broadcastToFollowers (
8 data: any,
9 byAccount: AccountInstance,
10 toAccountFollowers: AccountInstance[],
11 t: Transaction,
12 followersException: AccountInstance[] = []
13) {
8 const toAccountFollowerIds = toAccountFollowers.map(a => a.id) 14 const toAccountFollowerIds = toAccountFollowers.map(a => a.id)
15
9 const result = await db.AccountFollow.listAcceptedFollowerSharedInboxUrls(toAccountFollowerIds) 16 const result = await db.AccountFollow.listAcceptedFollowerSharedInboxUrls(toAccountFollowerIds)
10 if (result.data.length === 0) { 17 if (result.data.length === 0) {
11 logger.info('Not broadcast because of 0 followers for %s.', toAccountFollowerIds.join(', ')) 18 logger.info('Not broadcast because of 0 followers for %s.', toAccountFollowerIds.join(', '))
12 return undefined 19 return undefined
13 } 20 }
14 21
22 const followersSharedInboxException = followersException.map(f => f.sharedInboxUrl)
23 const uris = result.data.filter(sharedInbox => followersSharedInboxException.indexOf(sharedInbox) === -1)
24
15 const jobPayload = { 25 const jobPayload = {
16 uris: result.data, 26 uris,
17 signatureAccountId: byAccount.id, 27 signatureAccountId: byAccount.id,
18 body: data 28 body: data
19 } 29 }
diff --git a/server/lib/activitypub/send/send-create.ts b/server/lib/activitypub/send/send-create.ts
index df8e0a642..e5fb212b7 100644
--- a/server/lib/activitypub/send/send-create.ts
+++ b/server/lib/activitypub/send/send-create.ts
@@ -3,7 +3,9 @@ import { ActivityCreate } from '../../../../shared/models/activitypub/activity'
3import { AccountInstance, VideoChannelInstance, VideoInstance } from '../../../models' 3import { AccountInstance, VideoChannelInstance, VideoInstance } from '../../../models'
4import { VideoAbuseInstance } from '../../../models/video/video-abuse-interface' 4import { VideoAbuseInstance } from '../../../models/video/video-abuse-interface'
5import { broadcastToFollowers, getAudience, unicastTo } from './misc' 5import { broadcastToFollowers, getAudience, unicastTo } from './misc'
6import { getVideoAbuseActivityPubUrl } from '../url' 6import { getVideoAbuseActivityPubUrl, getVideoViewActivityPubUrl } from '../url'
7import { getServerAccount } from '../../../helpers/utils'
8import { database as db } from '../../../initializers'
7 9
8async function sendCreateVideoChannel (videoChannel: VideoChannelInstance, t: Transaction) { 10async function sendCreateVideoChannel (videoChannel: VideoChannelInstance, t: Transaction) {
9 const byAccount = videoChannel.Account 11 const byAccount = videoChannel.Account
@@ -16,21 +18,53 @@ async function sendCreateVideoChannel (videoChannel: VideoChannelInstance, t: Tr
16 18
17async function sendVideoAbuse (byAccount: AccountInstance, videoAbuse: VideoAbuseInstance, video: VideoInstance, t: Transaction) { 19async function sendVideoAbuse (byAccount: AccountInstance, videoAbuse: VideoAbuseInstance, video: VideoInstance, t: Transaction) {
18 const url = getVideoAbuseActivityPubUrl(videoAbuse) 20 const url = getVideoAbuseActivityPubUrl(videoAbuse)
19 const data = await createActivityData(url, byAccount, videoAbuse.toActivityPubObject()) 21
22 const audience = { to: [ video.VideoChannel.Account.url ], cc: [] }
23 const data = await createActivityData(url, byAccount, videoAbuse.toActivityPubObject(), audience)
24
25 return unicastTo(data, byAccount, video.VideoChannel.Account.sharedInboxUrl, t)
26}
27
28async function sendCreateViewToOrigin (byAccount: AccountInstance, video: VideoInstance, t: Transaction) {
29 const url = getVideoViewActivityPubUrl(byAccount, video)
30 const viewActivity = createViewActivityData(byAccount, video)
31
32 const audience = { to: [ video.VideoChannel.Account.url ], cc: [ video.VideoChannel.Account.url + '/followers' ] }
33 const data = await createActivityData(url, byAccount, viewActivity, audience)
20 34
21 return unicastTo(data, byAccount, video.VideoChannel.Account.sharedInboxUrl, t) 35 return unicastTo(data, byAccount, video.VideoChannel.Account.sharedInboxUrl, t)
22} 36}
23 37
24// async function sendCreateView () 38async function sendCreateViewToVideoFollowers (byAccount: AccountInstance, video: VideoInstance, t: Transaction) {
39 const url = getVideoViewActivityPubUrl(byAccount, video)
40 const viewActivity = createViewActivityData(byAccount, video)
41
42 const audience = { to: [ video.VideoChannel.Account.url + '/followers' ], cc: [] }
43 const data = await createActivityData(url, byAccount, viewActivity, audience)
44
45 const serverAccount = await getServerAccount()
46 const accountsToForwardView = await db.VideoShare.loadAccountsByShare(video.id)
47 accountsToForwardView.push(video.VideoChannel.Account)
48
49 // Don't forward view to server that sent it to us
50 const index = accountsToForwardView.findIndex(a => a.id === byAccount.id)
51 if (index) accountsToForwardView.splice(index, 1)
52
53 const followersException = [ byAccount ]
54 return broadcastToFollowers(data, serverAccount, accountsToForwardView, t, followersException)
55}
56
57async function createActivityData (url: string, byAccount: AccountInstance, object: any, audience?: { to: string[], cc: string[] }) {
58 if (!audience) {
59 audience = await getAudience(byAccount)
60 }
25 61
26async function createActivityData (url: string, byAccount: AccountInstance, object: any) {
27 const { to, cc } = await getAudience(byAccount)
28 const activity: ActivityCreate = { 62 const activity: ActivityCreate = {
29 type: 'Create', 63 type: 'Create',
30 id: url, 64 id: url,
31 actor: byAccount.url, 65 actor: byAccount.url,
32 to, 66 to: audience.to,
33 cc, 67 cc: audience.cc,
34 object 68 object
35 } 69 }
36 70
@@ -42,5 +76,19 @@ async function createActivityData (url: string, byAccount: AccountInstance, obje
42export { 76export {
43 sendCreateVideoChannel, 77 sendCreateVideoChannel,
44 sendVideoAbuse, 78 sendVideoAbuse,
45 createActivityData 79 createActivityData,
80 sendCreateViewToOrigin,
81 sendCreateViewToVideoFollowers
82}
83
84// ---------------------------------------------------------------------------
85
86function createViewActivityData (byAccount: AccountInstance, video: VideoInstance) {
87 const obj = {
88 type: 'View',
89 actor: byAccount.url,
90 object: video.url
91 }
92
93 return obj
46} 94}
diff --git a/server/lib/activitypub/url.ts b/server/lib/activitypub/url.ts
index 41ac0f9a8..d98561e33 100644
--- a/server/lib/activitypub/url.ts
+++ b/server/lib/activitypub/url.ts
@@ -21,6 +21,10 @@ function getVideoAbuseActivityPubUrl (videoAbuse: VideoAbuseInstance) {
21 return CONFIG.WEBSERVER.URL + '/admin/video-abuses/' + videoAbuse.id 21 return CONFIG.WEBSERVER.URL + '/admin/video-abuses/' + videoAbuse.id
22} 22}
23 23
24function getVideoViewActivityPubUrl (byAccount: AccountInstance, video: VideoInstance) {
25 return video.url + '#views/' + byAccount.uuid + '/' + new Date().toISOString()
26}
27
24function getAccountFollowActivityPubUrl (accountFollow: AccountFollowInstance) { 28function getAccountFollowActivityPubUrl (accountFollow: AccountFollowInstance) {
25 const me = accountFollow.AccountFollower 29 const me = accountFollow.AccountFollower
26 const following = accountFollow.AccountFollowing 30 const following = accountFollow.AccountFollowing
@@ -56,5 +60,6 @@ export {
56 getAccountFollowAcceptActivityPubUrl, 60 getAccountFollowAcceptActivityPubUrl,
57 getAnnounceActivityPubUrl, 61 getAnnounceActivityPubUrl,
58 getUpdateActivityPubUrl, 62 getUpdateActivityPubUrl,
59 getUndoActivityPubUrl 63 getUndoActivityPubUrl,
64 getVideoViewActivityPubUrl
60} 65}