diff options
author | Chocobozzz <florian.bigard@gmail.com> | 2017-11-22 16:25:03 +0100 |
---|---|---|
committer | Chocobozzz <florian.bigard@gmail.com> | 2017-11-27 19:40:53 +0100 |
commit | 40ff57078e15d5b86ee6b71e198b95d3feb78eaf (patch) | |
tree | 88031d4eac6a26597e8a1f2fc63674664e3eae26 /server/lib | |
parent | c46edbc2f6ca310b2f0331f979ac6caf27f6eb92 (diff) | |
download | PeerTube-40ff57078e15d5b86ee6b71e198b95d3feb78eaf.tar.gz PeerTube-40ff57078e15d5b86ee6b71e198b95d3feb78eaf.tar.zst PeerTube-40ff57078e15d5b86ee6b71e198b95d3feb78eaf.zip |
Federate video views
Diffstat (limited to 'server/lib')
-rw-r--r-- | server/lib/activitypub/process/misc.ts | 7 | ||||
-rw-r--r-- | server/lib/activitypub/process/process-create.ts | 19 | ||||
-rw-r--r-- | server/lib/activitypub/process/process-follow.ts | 6 | ||||
-rw-r--r-- | server/lib/activitypub/send/misc.ts | 14 | ||||
-rw-r--r-- | server/lib/activitypub/send/send-create.ts | 64 | ||||
-rw-r--r-- | server/lib/activitypub/url.ts | 7 | ||||
-rw-r--r-- | server/lib/jobs/activitypub-http-job-scheduler/activitypub-http-fetcher-handler.ts | 6 |
7 files changed, 106 insertions, 17 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 @@ | |||
1 | import { ActivityCreate, VideoChannelObject } from '../../../../shared' | 1 | import { ActivityCreate, VideoChannelObject } from '../../../../shared' |
2 | import { VideoAbuseObject } from '../../../../shared/models/activitypub/objects/video-abuse-object' | 2 | import { VideoAbuseObject } from '../../../../shared/models/activitypub/objects/video-abuse-object' |
3 | import { ViewObject } from '../../../../shared/models/activitypub/objects/view-object' | ||
3 | import { logger, retryTransactionWrapper } from '../../../helpers' | 4 | import { logger, retryTransactionWrapper } from '../../../helpers' |
4 | import { database as db } from '../../../initializers' | 5 | import { database as db } from '../../../initializers' |
5 | import { AccountInstance } from '../../../models/account/account-interface' | 6 | import { AccountInstance } from '../../../models/account/account-interface' |
6 | import { getOrCreateAccountAndServer } from '../account' | 7 | import { getOrCreateAccountAndServer } from '../account' |
8 | import { sendCreateViewToVideoFollowers } from '../send/send-create' | ||
7 | import { getVideoChannelActivityPubUrl } from '../url' | 9 | import { getVideoChannelActivityPubUrl } from '../url' |
8 | import { videoChannelActivityObjectToDBAttributes } from './misc' | 10 | import { 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 | ||
37 | async 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 | |||
33 | function processCreateVideoChannel (account: AccountInstance, videoChannelToCreateData: VideoChannelObject) { | 50 | function 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' | |||
4 | import { AccountInstance } from '../../../models/account/account-interface' | 4 | import { AccountInstance } from '../../../models/account/account-interface' |
5 | import { activitypubHttpJobScheduler } from '../../jobs/activitypub-http-job-scheduler/activitypub-http-job-scheduler' | 5 | import { activitypubHttpJobScheduler } from '../../jobs/activitypub-http-job-scheduler/activitypub-http-job-scheduler' |
6 | 6 | ||
7 | async function broadcastToFollowers (data: any, byAccount: AccountInstance, toAccountFollowers: AccountInstance[], t: Transaction) { | 7 | async 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' | |||
3 | import { AccountInstance, VideoChannelInstance, VideoInstance } from '../../../models' | 3 | import { AccountInstance, VideoChannelInstance, VideoInstance } from '../../../models' |
4 | import { VideoAbuseInstance } from '../../../models/video/video-abuse-interface' | 4 | import { VideoAbuseInstance } from '../../../models/video/video-abuse-interface' |
5 | import { broadcastToFollowers, getAudience, unicastTo } from './misc' | 5 | import { broadcastToFollowers, getAudience, unicastTo } from './misc' |
6 | import { getVideoAbuseActivityPubUrl } from '../url' | 6 | import { getVideoAbuseActivityPubUrl, getVideoViewActivityPubUrl } from '../url' |
7 | import { getServerAccount } from '../../../helpers/utils' | ||
8 | import { database as db } from '../../../initializers' | ||
7 | 9 | ||
8 | async function sendCreateVideoChannel (videoChannel: VideoChannelInstance, t: Transaction) { | 10 | async 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 | ||
17 | async function sendVideoAbuse (byAccount: AccountInstance, videoAbuse: VideoAbuseInstance, video: VideoInstance, t: Transaction) { | 19 | async 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 | |||
28 | async 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 () | 38 | async 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 | |||
57 | async function createActivityData (url: string, byAccount: AccountInstance, object: any, audience?: { to: string[], cc: string[] }) { | ||
58 | if (!audience) { | ||
59 | audience = await getAudience(byAccount) | ||
60 | } | ||
25 | 61 | ||
26 | async 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 | |||
42 | export { | 76 | export { |
43 | sendCreateVideoChannel, | 77 | sendCreateVideoChannel, |
44 | sendVideoAbuse, | 78 | sendVideoAbuse, |
45 | createActivityData | 79 | createActivityData, |
80 | sendCreateViewToOrigin, | ||
81 | sendCreateViewToVideoFollowers | ||
82 | } | ||
83 | |||
84 | // --------------------------------------------------------------------------- | ||
85 | |||
86 | function 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 | ||
24 | function getVideoViewActivityPubUrl (byAccount: AccountInstance, video: VideoInstance) { | ||
25 | return video.url + '#views/' + byAccount.uuid + '/' + new Date().toISOString() | ||
26 | } | ||
27 | |||
24 | function getAccountFollowActivityPubUrl (accountFollow: AccountFollowInstance) { | 28 | function 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 | } |
diff --git a/server/lib/jobs/activitypub-http-job-scheduler/activitypub-http-fetcher-handler.ts b/server/lib/jobs/activitypub-http-job-scheduler/activitypub-http-fetcher-handler.ts index 09efaa622..bda319592 100644 --- a/server/lib/jobs/activitypub-http-job-scheduler/activitypub-http-fetcher-handler.ts +++ b/server/lib/jobs/activitypub-http-job-scheduler/activitypub-http-fetcher-handler.ts | |||
@@ -1,10 +1,8 @@ | |||
1 | import { logger } from '../../../helpers' | 1 | import { logger } from '../../../helpers' |
2 | import { buildSignedActivity } from '../../../helpers/activitypub' | ||
3 | import { doRequest } from '../../../helpers/requests' | 2 | import { doRequest } from '../../../helpers/requests' |
4 | import { database as db } from '../../../initializers' | ||
5 | import { ActivityPubHttpPayload } from './activitypub-http-job-scheduler' | ||
6 | import { processActivities } from '../../activitypub/process/process' | ||
7 | import { ACTIVITY_PUB } from '../../../initializers/constants' | 3 | import { ACTIVITY_PUB } from '../../../initializers/constants' |
4 | import { processActivities } from '../../activitypub/process/process' | ||
5 | import { ActivityPubHttpPayload } from './activitypub-http-job-scheduler' | ||
8 | 6 | ||
9 | async function process (payload: ActivityPubHttpPayload, jobId: number) { | 7 | async function process (payload: ActivityPubHttpPayload, jobId: number) { |
10 | logger.info('Processing ActivityPub fetcher in job %d.', jobId) | 8 | logger.info('Processing ActivityPub fetcher in job %d.', jobId) |