aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/lib
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2017-12-14 17:38:41 +0100
committerChocobozzz <me@florianbigard.com>2017-12-19 10:53:16 +0100
commit50d6de9c286abcb34ff4234d56d9cbb803db7665 (patch)
treef1732b27edcd05c7877a8358b8312f1e38c287ed /server/lib
parentfadf619ad61a016c1c7fc53de5a8f398a4f77519 (diff)
downloadPeerTube-50d6de9c286abcb34ff4234d56d9cbb803db7665.tar.gz
PeerTube-50d6de9c286abcb34ff4234d56d9cbb803db7665.tar.zst
PeerTube-50d6de9c286abcb34ff4234d56d9cbb803db7665.zip
Begin moving video channel to actor
Diffstat (limited to 'server/lib')
-rw-r--r--server/lib/activitypub/account.ts127
-rw-r--r--server/lib/activitypub/actor.ts229
-rw-r--r--server/lib/activitypub/fetch.ts6
-rw-r--r--server/lib/activitypub/index.ts3
-rw-r--r--server/lib/activitypub/process/index.ts1
-rw-r--r--server/lib/activitypub/process/misc.ts52
-rw-r--r--server/lib/activitypub/process/process-accept.ts18
-rw-r--r--server/lib/activitypub/process/process-add.ts137
-rw-r--r--server/lib/activitypub/process/process-announce.ts67
-rw-r--r--server/lib/activitypub/process/process-create.ts187
-rw-r--r--server/lib/activitypub/process/process-delete.ts72
-rw-r--r--server/lib/activitypub/process/process-follow.ts48
-rw-r--r--server/lib/activitypub/process/process-like.ts19
-rw-r--r--server/lib/activitypub/process/process-undo.ts43
-rw-r--r--server/lib/activitypub/process/process-update.ts63
-rw-r--r--server/lib/activitypub/process/process.ts14
-rw-r--r--server/lib/activitypub/send/index.ts1
-rw-r--r--server/lib/activitypub/send/misc.ts80
-rw-r--r--server/lib/activitypub/send/send-accept.ts22
-rw-r--r--server/lib/activitypub/send/send-add.ts45
-rw-r--r--server/lib/activitypub/send/send-announce.ts84
-rw-r--r--server/lib/activitypub/send/send-create.ts107
-rw-r--r--server/lib/activitypub/send/send-delete.ts38
-rw-r--r--server/lib/activitypub/send/send-follow.ts20
-rw-r--r--server/lib/activitypub/send/send-like.ts34
-rw-r--r--server/lib/activitypub/send/send-undo.ts84
-rw-r--r--server/lib/activitypub/send/send-update.ts52
-rw-r--r--server/lib/activitypub/share.ts24
-rw-r--r--server/lib/activitypub/url.ts46
-rw-r--r--server/lib/activitypub/video-channels.ts59
-rw-r--r--server/lib/activitypub/videos.ts28
-rw-r--r--server/lib/index.ts6
-rw-r--r--server/lib/jobs/activitypub-http-job-scheduler/activitypub-http-job-scheduler.ts12
-rw-r--r--server/lib/jobs/transcoding-job-scheduler/video-file-optimizer-handler.ts5
-rw-r--r--server/lib/user.ts45
-rw-r--r--server/lib/video-channel.ts19
36 files changed, 821 insertions, 1076 deletions
diff --git a/server/lib/activitypub/account.ts b/server/lib/activitypub/account.ts
deleted file mode 100644
index 45690b88d..000000000
--- a/server/lib/activitypub/account.ts
+++ /dev/null
@@ -1,127 +0,0 @@
1import * as Bluebird from 'bluebird'
2import { Transaction } from 'sequelize'
3import * as url from 'url'
4import { ActivityPubActor } from '../../../shared/models/activitypub'
5import { doRequest, logger, retryTransactionWrapper } from '../../helpers'
6import { isRemoteAccountValid } from '../../helpers/custom-validators/activitypub'
7import { ACTIVITY_PUB, sequelizeTypescript } from '../../initializers'
8import { AccountModel } from '../../models/account/account'
9import { ServerModel } from '../../models/server/server'
10
11async function getOrCreateAccountAndServer (accountUrl: string) {
12 let account = await AccountModel.loadByUrl(accountUrl)
13
14 // We don't have this account in our database, fetch it on remote
15 if (!account) {
16 account = await fetchRemoteAccount(accountUrl)
17 if (account === undefined) throw new Error('Cannot fetch remote account.')
18
19 const options = {
20 arguments: [ account ],
21 errorMessage: 'Cannot save account and server with many retries.'
22 }
23 account = await retryTransactionWrapper(saveAccountAndServerIfNotExist, options)
24 }
25
26 return account
27}
28
29function saveAccountAndServerIfNotExist (account: AccountModel, t?: Transaction): Bluebird<AccountModel> | Promise<AccountModel> {
30 if (t !== undefined) {
31 return save(t)
32 } else {
33 return sequelizeTypescript.transaction(t => {
34 return save(t)
35 })
36 }
37
38 async function save (t: Transaction) {
39 const accountHost = url.parse(account.url).host
40
41 const serverOptions = {
42 where: {
43 host: accountHost
44 },
45 defaults: {
46 host: accountHost
47 },
48 transaction: t
49 }
50 const [ server ] = await ServerModel.findOrCreate(serverOptions)
51
52 // Save our new account in database
53 account.set('serverId', server.id)
54 account = await account.save({ transaction: t })
55
56 return account
57 }
58}
59
60async function fetchRemoteAccount (accountUrl: string) {
61 const options = {
62 uri: accountUrl,
63 method: 'GET',
64 headers: {
65 'Accept': ACTIVITY_PUB.ACCEPT_HEADER
66 }
67 }
68
69 logger.info('Fetching remote account %s.', accountUrl)
70
71 let requestResult
72 try {
73 requestResult = await doRequest(options)
74 } catch (err) {
75 logger.warn('Cannot fetch remote account %s.', accountUrl, err)
76 return undefined
77 }
78
79 const accountJSON: ActivityPubActor = JSON.parse(requestResult.body)
80 if (isRemoteAccountValid(accountJSON) === false) {
81 logger.debug('Remote account JSON is not valid.', { accountJSON })
82 return undefined
83 }
84
85 const followersCount = await fetchAccountCount(accountJSON.followers)
86 const followingCount = await fetchAccountCount(accountJSON.following)
87
88 return new AccountModel({
89 uuid: accountJSON.uuid,
90 name: accountJSON.preferredUsername,
91 url: accountJSON.url,
92 publicKey: accountJSON.publicKey.publicKeyPem,
93 privateKey: null,
94 followersCount: followersCount,
95 followingCount: followingCount,
96 inboxUrl: accountJSON.inbox,
97 outboxUrl: accountJSON.outbox,
98 sharedInboxUrl: accountJSON.endpoints.sharedInbox,
99 followersUrl: accountJSON.followers,
100 followingUrl: accountJSON.following
101 })
102}
103
104export {
105 getOrCreateAccountAndServer,
106 fetchRemoteAccount,
107 saveAccountAndServerIfNotExist
108}
109
110// ---------------------------------------------------------------------------
111
112async function fetchAccountCount (url: string) {
113 const options = {
114 uri: url,
115 method: 'GET'
116 }
117
118 let requestResult
119 try {
120 requestResult = await doRequest(options)
121 } catch (err) {
122 logger.warn('Cannot fetch remote account count %s.', url, err)
123 return undefined
124 }
125
126 return requestResult.totalItems ? requestResult.totalItems : 0
127}
diff --git a/server/lib/activitypub/actor.ts b/server/lib/activitypub/actor.ts
new file mode 100644
index 000000000..c3de4bdce
--- /dev/null
+++ b/server/lib/activitypub/actor.ts
@@ -0,0 +1,229 @@
1import * as Bluebird from 'bluebird'
2import { Transaction } from 'sequelize'
3import * as url from 'url'
4import { ActivityPubActor, ActivityPubActorType } from '../../../shared/models/activitypub'
5import { ActivityPubAttributedTo } from '../../../shared/models/activitypub/objects'
6import { createPrivateAndPublicKeys, doRequest, logger, retryTransactionWrapper } from '../../helpers'
7import { isRemoteActorValid } from '../../helpers/custom-validators/activitypub'
8import { ACTIVITY_PUB, CONFIG, sequelizeTypescript } from '../../initializers'
9import { AccountModel } from '../../models/account/account'
10import { ActorModel } from '../../models/activitypub/actor'
11import { ServerModel } from '../../models/server/server'
12import { VideoChannelModel } from '../../models/video/video-channel'
13
14 // Set account keys, this could be long so process after the account creation and do not block the client
15function setAsyncActorKeys (actor: ActorModel) {
16 return createPrivateAndPublicKeys()
17 .then(({ publicKey, privateKey }) => {
18 actor.set('publicKey', publicKey)
19 actor.set('privateKey', privateKey)
20 return actor.save()
21 })
22 .catch(err => {
23 logger.error('Cannot set public/private keys of actor %d.', actor.uuid, err)
24 return actor
25 })
26}
27
28async function getOrCreateActorAndServerAndModel (actorUrl: string, recurseIfNeeded = true) {
29 let actor = await ActorModel.loadByUrl(actorUrl)
30
31 // We don't have this actor in our database, fetch it on remote
32 if (!actor) {
33 const result = await fetchRemoteActor(actorUrl)
34 if (result === undefined) throw new Error('Cannot fetch remote actor.')
35
36 // Create the attributed to actor
37 // In PeerTube a video channel is owned by an account
38 let ownerActor: ActorModel = undefined
39 if (recurseIfNeeded === true && result.actor.type === 'Group') {
40 const accountAttributedTo = result.attributedTo.find(a => a.type === 'Person')
41 if (!accountAttributedTo) throw new Error('Cannot find account attributed to video channel ' + actor.url)
42
43 try {
44 // Assert we don't recurse another time
45 ownerActor = await getOrCreateActorAndServerAndModel(accountAttributedTo.id, false)
46 } catch (err) {
47 logger.error('Cannot get or create account attributed to video channel ' + actor.url)
48 throw new Error(err)
49 }
50 }
51
52 const options = {
53 arguments: [ result, ownerActor ],
54 errorMessage: 'Cannot save actor and server with many retries.'
55 }
56 actor = await retryTransactionWrapper(saveActorAndServerAndModelIfNotExist, options)
57 }
58
59 return actor
60}
61
62function saveActorAndServerAndModelIfNotExist (
63 result: FetchRemoteActorResult,
64 ownerActor?: ActorModel,
65 t?: Transaction
66): Bluebird<ActorModel> | Promise<ActorModel> {
67 let actor = result.actor
68
69 if (t !== undefined) return save(t)
70
71 return sequelizeTypescript.transaction(t => save(t))
72
73 async function save (t: Transaction) {
74 const actorHost = url.parse(actor.url).host
75
76 const serverOptions = {
77 where: {
78 host: actorHost
79 },
80 defaults: {
81 host: actorHost
82 },
83 transaction: t
84 }
85 const [ server ] = await ServerModel.findOrCreate(serverOptions)
86
87 // Save our new account in database
88 actor.set('serverId', server.id)
89
90 // Force the actor creation, sometimes Sequelize skips the save() when it thinks the instance already exists
91 // (which could be false in a retried query)
92 const actorCreated = await ActorModel.create(actor.toJSON(), { transaction: t })
93
94 if (actorCreated.type === 'Person' || actorCreated.type === 'Application') {
95 const account = await saveAccount(actorCreated, result, t)
96 actorCreated.Account = account
97 actorCreated.Account.Actor = actorCreated
98 } else if (actorCreated.type === 'Group') { // Video channel
99 const videoChannel = await saveVideoChannel(actorCreated, result, ownerActor, t)
100 actorCreated.VideoChannel = videoChannel
101 actorCreated.VideoChannel.Actor = actorCreated
102 }
103
104 return actorCreated
105 }
106}
107
108type FetchRemoteActorResult = {
109 actor: ActorModel
110 preferredUsername: string
111 summary: string
112 attributedTo: ActivityPubAttributedTo[]
113}
114async function fetchRemoteActor (actorUrl: string): Promise<FetchRemoteActorResult> {
115 const options = {
116 uri: actorUrl,
117 method: 'GET',
118 headers: {
119 'Accept': ACTIVITY_PUB.ACCEPT_HEADER
120 }
121 }
122
123 logger.info('Fetching remote actor %s.', actorUrl)
124
125 let requestResult
126 try {
127 requestResult = await doRequest(options)
128 } catch (err) {
129 logger.warn('Cannot fetch remote actor %s.', actorUrl, err)
130 return undefined
131 }
132
133 const actorJSON: ActivityPubActor = JSON.parse(requestResult.body)
134 if (isRemoteActorValid(actorJSON) === false) {
135 logger.debug('Remote actor JSON is not valid.', { actorJSON: actorJSON })
136 return undefined
137 }
138
139 const followersCount = await fetchActorTotalItems(actorJSON.followers)
140 const followingCount = await fetchActorTotalItems(actorJSON.following)
141
142 const actor = new ActorModel({
143 type: actorJSON.type,
144 uuid: actorJSON.uuid,
145 name: actorJSON.name,
146 url: actorJSON.url,
147 publicKey: actorJSON.publicKey.publicKeyPem,
148 privateKey: null,
149 followersCount: followersCount,
150 followingCount: followingCount,
151 inboxUrl: actorJSON.inbox,
152 outboxUrl: actorJSON.outbox,
153 sharedInboxUrl: actorJSON.endpoints.sharedInbox,
154 followersUrl: actorJSON.followers,
155 followingUrl: actorJSON.following
156 })
157
158 return {
159 actor,
160 preferredUsername: actorJSON.preferredUsername,
161 summary: actorJSON.summary,
162 attributedTo: actorJSON.attributedTo
163 }
164}
165
166function buildActorInstance (type: ActivityPubActorType, url: string, name: string, uuid?: string) {
167 return new ActorModel({
168 type,
169 url,
170 name,
171 uuid,
172 publicKey: null,
173 privateKey: null,
174 followersCount: 0,
175 followingCount: 0,
176 inboxUrl: url + '/inbox',
177 outboxUrl: url + '/outbox',
178 sharedInboxUrl: CONFIG.WEBSERVER.URL + '/inbox',
179 followersUrl: url + '/followers',
180 followingUrl: url + '/following'
181 })
182}
183
184export {
185 getOrCreateActorAndServerAndModel,
186 saveActorAndServerAndModelIfNotExist,
187 fetchRemoteActor,
188 buildActorInstance,
189 setAsyncActorKeys
190}
191
192// ---------------------------------------------------------------------------
193
194async function fetchActorTotalItems (url: string) {
195 const options = {
196 uri: url,
197 method: 'GET'
198 }
199
200 let requestResult
201 try {
202 requestResult = await doRequest(options)
203 } catch (err) {
204 logger.warn('Cannot fetch remote actor count %s.', url, err)
205 return undefined
206 }
207
208 return requestResult.totalItems ? requestResult.totalItems : 0
209}
210
211function saveAccount (actor: ActorModel, result: FetchRemoteActorResult, t: Transaction) {
212 const account = new AccountModel({
213 name: result.preferredUsername,
214 actorId: actor.id
215 })
216
217 return account.save({ transaction: t })
218}
219
220async function saveVideoChannel (actor: ActorModel, result: FetchRemoteActorResult, ownerActor: ActorModel, t: Transaction) {
221 const videoChannel = new VideoChannelModel({
222 name: result.preferredUsername,
223 description: result.summary,
224 actorId: actor.id,
225 accountId: ownerActor.Account.id
226 })
227
228 return videoChannel.save({ transaction: t })
229}
diff --git a/server/lib/activitypub/fetch.ts b/server/lib/activitypub/fetch.ts
index aa4dea8e0..4fc97cc38 100644
--- a/server/lib/activitypub/fetch.ts
+++ b/server/lib/activitypub/fetch.ts
@@ -1,10 +1,10 @@
1import { Transaction } from 'sequelize' 1import { Transaction } from 'sequelize'
2import { AccountModel } from '../../models/account/account' 2import { ActorModel } from '../../models/activitypub/actor'
3import { activitypubHttpJobScheduler, ActivityPubHttpPayload } from '../jobs/activitypub-http-job-scheduler' 3import { activitypubHttpJobScheduler, ActivityPubHttpPayload } from '../jobs/activitypub-http-job-scheduler'
4 4
5async function addFetchOutboxJob (account: AccountModel, t: Transaction) { 5async function addFetchOutboxJob (actor: ActorModel, t: Transaction) {
6 const jobPayload: ActivityPubHttpPayload = { 6 const jobPayload: ActivityPubHttpPayload = {
7 uris: [ account.outboxUrl ] 7 uris: [ actor.outboxUrl ]
8 } 8 }
9 9
10 return activitypubHttpJobScheduler.createJob(t, 'activitypubHttpFetcherHandler', jobPayload) 10 return activitypubHttpJobScheduler.createJob(t, 'activitypubHttpFetcherHandler', jobPayload)
diff --git a/server/lib/activitypub/index.ts b/server/lib/activitypub/index.ts
index fcea662a6..94ed1edaa 100644
--- a/server/lib/activitypub/index.ts
+++ b/server/lib/activitypub/index.ts
@@ -1,8 +1,7 @@
1export * from './process' 1export * from './process'
2export * from './send' 2export * from './send'
3export * from './account' 3export * from './actor'
4export * from './fetch' 4export * from './fetch'
5export * from './share' 5export * from './share'
6export * from './video-channels'
7export * from './videos' 6export * from './videos'
8export * from './url' 7export * from './url'
diff --git a/server/lib/activitypub/process/index.ts b/server/lib/activitypub/process/index.ts
index e25c261cc..db4980a72 100644
--- a/server/lib/activitypub/process/index.ts
+++ b/server/lib/activitypub/process/index.ts
@@ -1,6 +1,5 @@
1export * from './process' 1export * from './process'
2export * from './process-accept' 2export * from './process-accept'
3export * from './process-add'
4export * from './process-announce' 3export * from './process-announce'
5export * from './process-create' 4export * from './process-create'
6export * from './process-delete' 5export * from './process-delete'
diff --git a/server/lib/activitypub/process/misc.ts b/server/lib/activitypub/process/misc.ts
index a775c858a..a9c6f913c 100644
--- a/server/lib/activitypub/process/misc.ts
+++ b/server/lib/activitypub/process/misc.ts
@@ -1,29 +1,13 @@
1import * as magnetUtil from 'magnet-uri' 1import * as magnetUtil from 'magnet-uri'
2import { VideoTorrentObject } from '../../../../shared' 2import { VideoTorrentObject } from '../../../../shared'
3import { VideoChannelObject } from '../../../../shared/models/activitypub/objects'
4import { VideoPrivacy } from '../../../../shared/models/videos' 3import { VideoPrivacy } from '../../../../shared/models/videos'
5import { doRequest } from '../../../helpers' 4import { doRequest } from '../../../helpers'
6import { isVideoFileInfoHashValid } from '../../../helpers/custom-validators/videos' 5import { isVideoFileInfoHashValid } from '../../../helpers/custom-validators/videos'
7import { ACTIVITY_PUB, VIDEO_MIMETYPE_EXT } from '../../../initializers' 6import { ACTIVITY_PUB, VIDEO_MIMETYPE_EXT } from '../../../initializers'
8import { AccountModel } from '../../../models/account/account'
9import { VideoModel } from '../../../models/video/video' 7import { VideoModel } from '../../../models/video/video'
10import { VideoChannelModel } from '../../../models/video/video-channel' 8import { VideoChannelModel } from '../../../models/video/video-channel'
11import { VideoChannelShareModel } from '../../../models/video/video-channel-share'
12import { VideoShareModel } from '../../../models/video/video-share' 9import { VideoShareModel } from '../../../models/video/video-share'
13import { getOrCreateAccountAndServer } from '../account' 10import { getOrCreateActorAndServerAndModel } from '../actor'
14
15function videoChannelActivityObjectToDBAttributes (videoChannelObject: VideoChannelObject, account: AccountModel) {
16 return {
17 name: videoChannelObject.name,
18 description: videoChannelObject.content,
19 uuid: videoChannelObject.uuid,
20 url: videoChannelObject.id,
21 createdAt: new Date(videoChannelObject.published),
22 updatedAt: new Date(videoChannelObject.updated),
23 remote: true,
24 accountId: account.id
25 }
26}
27 11
28async function videoActivityObjectToDBAttributes ( 12async function videoActivityObjectToDBAttributes (
29 videoChannel: VideoChannelModel, 13 videoChannel: VideoChannelModel,
@@ -120,13 +104,13 @@ async function addVideoShares (instance: VideoModel, shares: string[]) {
120 uri: share, 104 uri: share,
121 json: true 105 json: true
122 }) 106 })
123 const actor = json['actor'] 107 const actorUrl = json['actor']
124 if (!actor) continue 108 if (!actorUrl) continue
125 109
126 const account = await getOrCreateAccountAndServer(actor) 110 const actor = await getOrCreateActorAndServerAndModel(actorUrl)
127 111
128 const entry = { 112 const entry = {
129 accountId: account.id, 113 actorId: actor.id,
130 videoId: instance.id 114 videoId: instance.id
131 } 115 }
132 116
@@ -137,36 +121,10 @@ async function addVideoShares (instance: VideoModel, shares: string[]) {
137 } 121 }
138} 122}
139 123
140async function addVideoChannelShares (instance: VideoChannelModel, shares: string[]) {
141 for (const share of shares) {
142 // Fetch url
143 const json = await doRequest({
144 uri: share,
145 json: true
146 })
147 const actor = json['actor']
148 if (!actor) continue
149
150 const account = await getOrCreateAccountAndServer(actor)
151
152 const entry = {
153 accountId: account.id,
154 videoChannelId: instance.id
155 }
156
157 await VideoChannelShareModel.findOrCreate({
158 where: entry,
159 defaults: entry
160 })
161 }
162}
163
164// --------------------------------------------------------------------------- 124// ---------------------------------------------------------------------------
165 125
166export { 126export {
167 videoFileActivityUrlToDBAttributes, 127 videoFileActivityUrlToDBAttributes,
168 videoActivityObjectToDBAttributes, 128 videoActivityObjectToDBAttributes,
169 videoChannelActivityObjectToDBAttributes,
170 addVideoChannelShares,
171 addVideoShares 129 addVideoShares
172} 130}
diff --git a/server/lib/activitypub/process/process-accept.ts b/server/lib/activitypub/process/process-accept.ts
index 5b321f771..b9d906ec9 100644
--- a/server/lib/activitypub/process/process-accept.ts
+++ b/server/lib/activitypub/process/process-accept.ts
@@ -1,14 +1,14 @@
1import { ActivityAccept } from '../../../../shared/models/activitypub' 1import { ActivityAccept } from '../../../../shared/models/activitypub'
2import { AccountModel } from '../../../models/account/account' 2import { ActorModel } from '../../../models/activitypub/actor'
3import { AccountFollowModel } from '../../../models/account/account-follow' 3import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
4import { addFetchOutboxJob } from '../fetch' 4import { addFetchOutboxJob } from '../fetch'
5 5
6async function processAcceptActivity (activity: ActivityAccept, inboxAccount?: AccountModel) { 6async function processAcceptActivity (activity: ActivityAccept, inboxActor?: ActorModel) {
7 if (inboxAccount === undefined) throw new Error('Need to accept on explicit inbox.') 7 if (inboxActor === undefined) throw new Error('Need to accept on explicit inbox.')
8 8
9 const targetAccount = await AccountModel.loadByUrl(activity.actor) 9 const targetActor = await ActorModel.loadByUrl(activity.actor)
10 10
11 return processAccept(inboxAccount, targetAccount) 11 return processAccept(inboxActor, targetActor)
12} 12}
13 13
14// --------------------------------------------------------------------------- 14// ---------------------------------------------------------------------------
@@ -19,11 +19,11 @@ export {
19 19
20// --------------------------------------------------------------------------- 20// ---------------------------------------------------------------------------
21 21
22async function processAccept (account: AccountModel, targetAccount: AccountModel) { 22async function processAccept (actor: ActorModel, targetActor: ActorModel) {
23 const follow = await AccountFollowModel.loadByAccountAndTarget(account.id, targetAccount.id) 23 const follow = await ActorFollowModel.loadByActorAndTarget(actor.id, targetActor.id)
24 if (!follow) throw new Error('Cannot find associated follow.') 24 if (!follow) throw new Error('Cannot find associated follow.')
25 25
26 follow.set('state', 'accepted') 26 follow.set('state', 'accepted')
27 await follow.save() 27 await follow.save()
28 await addFetchOutboxJob(targetAccount, undefined) 28 await addFetchOutboxJob(targetActor, undefined)
29} 29}
diff --git a/server/lib/activitypub/process/process-add.ts b/server/lib/activitypub/process/process-add.ts
deleted file mode 100644
index 550593eab..000000000
--- a/server/lib/activitypub/process/process-add.ts
+++ /dev/null
@@ -1,137 +0,0 @@
1import * as Bluebird from 'bluebird'
2import { VideoTorrentObject } from '../../../../shared'
3import { ActivityAdd } from '../../../../shared/models/activitypub'
4import { VideoRateType } from '../../../../shared/models/videos'
5import { logger, retryTransactionWrapper } from '../../../helpers'
6import { sequelizeTypescript } from '../../../initializers'
7import { AccountModel } from '../../../models/account/account'
8import { AccountVideoRateModel } from '../../../models/account/account-video-rate'
9import { TagModel } from '../../../models/video/tag'
10import { VideoModel } from '../../../models/video/video'
11import { VideoChannelModel } from '../../../models/video/video-channel'
12import { VideoFileModel } from '../../../models/video/video-file'
13import { getOrCreateAccountAndServer } from '../account'
14import { getOrCreateVideoChannel } from '../video-channels'
15import { generateThumbnailFromUrl } from '../videos'
16import { addVideoShares, videoActivityObjectToDBAttributes, videoFileActivityUrlToDBAttributes } from './misc'
17
18async function processAddActivity (activity: ActivityAdd) {
19 const activityObject = activity.object
20 const activityType = activityObject.type
21 const account = await getOrCreateAccountAndServer(activity.actor)
22
23 if (activityType === 'Video') {
24 const videoChannelUrl = activity.target
25 const videoChannel = await getOrCreateVideoChannel(account, videoChannelUrl)
26
27 return processAddVideo(account, activity, videoChannel, activityObject)
28 }
29
30 logger.warn('Unknown activity object type %s when creating activity.', activityType, { activity: activity.id })
31 return Promise.resolve(undefined)
32}
33
34// ---------------------------------------------------------------------------
35
36export {
37 processAddActivity
38}
39
40// ---------------------------------------------------------------------------
41
42async function processAddVideo (account: AccountModel,
43 activity: ActivityAdd,
44 videoChannel: VideoChannelModel,
45 videoToCreateData: VideoTorrentObject) {
46 const options = {
47 arguments: [ account, activity, videoChannel, videoToCreateData ],
48 errorMessage: 'Cannot insert the remote video with many retries.'
49 }
50
51 const video = await retryTransactionWrapper(addRemoteVideo, options)
52
53 // Process outside the transaction because we could fetch remote data
54 if (videoToCreateData.likes && Array.isArray(videoToCreateData.likes.orderedItems)) {
55 await createRates(videoToCreateData.likes.orderedItems, video, 'like')
56 }
57
58 if (videoToCreateData.dislikes && Array.isArray(videoToCreateData.dislikes.orderedItems)) {
59 await createRates(videoToCreateData.dislikes.orderedItems, video, 'dislike')
60 }
61
62 if (videoToCreateData.shares && Array.isArray(videoToCreateData.shares.orderedItems)) {
63 await addVideoShares(video, videoToCreateData.shares.orderedItems)
64 }
65
66 return video
67}
68
69function addRemoteVideo (account: AccountModel,
70 activity: ActivityAdd,
71 videoChannel: VideoChannelModel,
72 videoToCreateData: VideoTorrentObject) {
73 logger.debug('Adding remote video %s.', videoToCreateData.id)
74
75 return sequelizeTypescript.transaction(async t => {
76 const sequelizeOptions = {
77 transaction: t
78 }
79
80 if (videoChannel.Account.id !== account.id) throw new Error('Video channel is not owned by this account.')
81
82 const videoFromDatabase = await VideoModel.loadByUUIDOrURL(videoToCreateData.uuid, videoToCreateData.id, t)
83 if (videoFromDatabase) return videoFromDatabase
84
85 const videoData = await videoActivityObjectToDBAttributes(videoChannel, videoToCreateData, activity.to, activity.cc)
86 const video = VideoModel.build(videoData)
87
88 // Don't block on request
89 generateThumbnailFromUrl(video, videoToCreateData.icon)
90 .catch(err => logger.warn('Cannot generate thumbnail of %s.', videoToCreateData.id, err))
91
92 const videoCreated = await video.save(sequelizeOptions)
93
94 const videoFileAttributes = videoFileActivityUrlToDBAttributes(videoCreated, videoToCreateData)
95 if (videoFileAttributes.length === 0) {
96 throw new Error('Cannot find valid files for video %s ' + videoToCreateData.url)
97 }
98
99 const tasks: Bluebird<any>[] = videoFileAttributes.map(f => VideoFileModel.create(f, { transaction: t }))
100 await Promise.all(tasks)
101
102 const tags = videoToCreateData.tag.map(t => t.name)
103 const tagInstances = await TagModel.findOrCreateTags(tags, t)
104 await videoCreated.$set('Tags', tagInstances, sequelizeOptions)
105
106 logger.info('Remote video with uuid %s inserted.', videoToCreateData.uuid)
107
108 return videoCreated
109 })
110}
111
112async function createRates (accountUrls: string[], video: VideoModel, rate: VideoRateType) {
113 let rateCounts = 0
114 const tasks: Bluebird<any>[] = []
115
116 for (const accountUrl of accountUrls) {
117 const account = await getOrCreateAccountAndServer(accountUrl)
118 const p = AccountVideoRateModel
119 .create({
120 videoId: video.id,
121 accountId: account.id,
122 type: rate
123 })
124 .then(() => rateCounts += 1)
125
126 tasks.push(p)
127 }
128
129 await Promise.all(tasks)
130
131 logger.info('Adding %d %s to video %s.', rateCounts, rate, video.uuid)
132
133 // This is "likes" and "dislikes"
134 await video.increment(rate + 's', { by: rateCounts })
135
136 return
137}
diff --git a/server/lib/activitypub/process/process-announce.ts b/server/lib/activitypub/process/process-announce.ts
index ff2c6d708..7dfee2f60 100644
--- a/server/lib/activitypub/process/process-announce.ts
+++ b/server/lib/activitypub/process/process-announce.ts
@@ -1,24 +1,19 @@
1import { ActivityAdd, ActivityAnnounce, ActivityCreate } from '../../../../shared/models/activitypub' 1import { ActivityAnnounce } from '../../../../shared/models/activitypub'
2import { logger, retryTransactionWrapper } from '../../../helpers' 2import { logger, retryTransactionWrapper } from '../../../helpers'
3import { sequelizeTypescript } from '../../../initializers' 3import { sequelizeTypescript } from '../../../initializers'
4import { AccountModel } from '../../../models/account/account' 4import { ActorModel } from '../../../models/activitypub/actor'
5import { VideoModel } from '../../../models/video/video' 5import { VideoModel } from '../../../models/video/video'
6import { VideoChannelModel } from '../../../models/video/video-channel'
7import { VideoChannelShareModel } from '../../../models/video/video-channel-share'
8import { VideoShareModel } from '../../../models/video/video-share' 6import { VideoShareModel } from '../../../models/video/video-share'
9import { getOrCreateAccountAndServer } from '../account' 7import { getOrCreateActorAndServerAndModel } from '../actor'
10import { forwardActivity } from '../send/misc' 8import { forwardActivity } from '../send/misc'
11import { processAddActivity } from './process-add'
12import { processCreateActivity } from './process-create' 9import { processCreateActivity } from './process-create'
13 10
14async function processAnnounceActivity (activity: ActivityAnnounce) { 11async function processAnnounceActivity (activity: ActivityAnnounce) {
15 const announcedActivity = activity.object 12 const announcedActivity = activity.object
16 const accountAnnouncer = await getOrCreateAccountAndServer(activity.actor) 13 const actorAnnouncer = await getOrCreateActorAndServerAndModel(activity.actor)
17 14
18 if (announcedActivity.type === 'Create' && announcedActivity.object.type === 'VideoChannel') { 15 if (announcedActivity.type === 'Create' && announcedActivity.object.type === 'Video') {
19 return processVideoChannelShare(accountAnnouncer, activity) 16 return processVideoShare(actorAnnouncer, activity)
20 } else if (announcedActivity.type === 'Add' && announcedActivity.object.type === 'Video') {
21 return processVideoShare(accountAnnouncer, activity)
22 } 17 }
23 18
24 logger.warn( 19 logger.warn(
@@ -37,60 +32,24 @@ export {
37 32
38// --------------------------------------------------------------------------- 33// ---------------------------------------------------------------------------
39 34
40function processVideoChannelShare (accountAnnouncer: AccountModel, activity: ActivityAnnounce) { 35function processVideoShare (actorAnnouncer: ActorModel, activity: ActivityAnnounce) {
41 const options = { 36 const options = {
42 arguments: [ accountAnnouncer, activity ], 37 arguments: [ actorAnnouncer, activity ],
43 errorMessage: 'Cannot share the video channel with many retries.'
44 }
45
46 return retryTransactionWrapper(shareVideoChannel, options)
47}
48
49async function shareVideoChannel (accountAnnouncer: AccountModel, activity: ActivityAnnounce) {
50 const announcedActivity = activity.object as ActivityCreate
51
52 return sequelizeTypescript.transaction(async t => {
53 // Add share entry
54 const videoChannel: VideoChannelModel = await processCreateActivity(announcedActivity)
55 const share = {
56 accountId: accountAnnouncer.id,
57 videoChannelId: videoChannel.id
58 }
59
60 const [ , created ] = await VideoChannelShareModel.findOrCreate({
61 where: share,
62 defaults: share,
63 transaction: t
64 })
65
66 if (videoChannel.isOwned() && created === true) {
67 // Don't resend the activity to the sender
68 const exceptions = [ accountAnnouncer ]
69 await forwardActivity(activity, t, exceptions)
70 }
71
72 return undefined
73 })
74}
75
76function processVideoShare (accountAnnouncer: AccountModel, activity: ActivityAnnounce) {
77 const options = {
78 arguments: [ accountAnnouncer, activity ],
79 errorMessage: 'Cannot share the video with many retries.' 38 errorMessage: 'Cannot share the video with many retries.'
80 } 39 }
81 40
82 return retryTransactionWrapper(shareVideo, options) 41 return retryTransactionWrapper(shareVideo, options)
83} 42}
84 43
85function shareVideo (accountAnnouncer: AccountModel, activity: ActivityAnnounce) { 44function shareVideo (actorAnnouncer: ActorModel, activity: ActivityAnnounce) {
86 const announcedActivity = activity.object as ActivityAdd 45 const announcedActivity = activity.object
87 46
88 return sequelizeTypescript.transaction(async t => { 47 return sequelizeTypescript.transaction(async t => {
89 // Add share entry 48 // Add share entry
90 const video: VideoModel = await processAddActivity(announcedActivity) 49 const video: VideoModel = await processCreateActivity(announcedActivity)
91 50
92 const share = { 51 const share = {
93 accountId: accountAnnouncer.id, 52 actorId: actorAnnouncer.id,
94 videoId: video.id 53 videoId: video.id
95 } 54 }
96 55
@@ -102,7 +61,7 @@ function shareVideo (accountAnnouncer: AccountModel, activity: ActivityAnnounce)
102 61
103 if (video.isOwned() && created === true) { 62 if (video.isOwned() && created === true) {
104 // Don't resend the activity to the sender 63 // Don't resend the activity to the sender
105 const exceptions = [ accountAnnouncer ] 64 const exceptions = [ actorAnnouncer ]
106 await forwardActivity(activity, t, exceptions) 65 await forwardActivity(activity, t, exceptions)
107 } 66 }
108 67
diff --git a/server/lib/activitypub/process/process-create.ts b/server/lib/activitypub/process/process-create.ts
index c1eb2a8ab..1ddd817db 100644
--- a/server/lib/activitypub/process/process-create.ts
+++ b/server/lib/activitypub/process/process-create.ts
@@ -1,30 +1,33 @@
1import { ActivityCreate, VideoChannelObject } from '../../../../shared' 1import * as Bluebird from 'bluebird'
2import { ActivityCreate, VideoTorrentObject } from '../../../../shared'
2import { DislikeObject, VideoAbuseObject, ViewObject } from '../../../../shared/models/activitypub/objects' 3import { DislikeObject, VideoAbuseObject, ViewObject } from '../../../../shared/models/activitypub/objects'
4import { VideoRateType } from '../../../../shared/models/videos'
3import { logger, retryTransactionWrapper } from '../../../helpers' 5import { logger, retryTransactionWrapper } from '../../../helpers'
4import { sequelizeTypescript } from '../../../initializers' 6import { sequelizeTypescript } from '../../../initializers'
5import { AccountModel } from '../../../models/account/account'
6import { AccountVideoRateModel } from '../../../models/account/account-video-rate' 7import { AccountVideoRateModel } from '../../../models/account/account-video-rate'
8import { ActorModel } from '../../../models/activitypub/actor'
9import { TagModel } from '../../../models/video/tag'
7import { VideoModel } from '../../../models/video/video' 10import { VideoModel } from '../../../models/video/video'
8import { VideoAbuseModel } from '../../../models/video/video-abuse' 11import { VideoAbuseModel } from '../../../models/video/video-abuse'
9import { VideoChannelModel } from '../../../models/video/video-channel' 12import { VideoFileModel } from '../../../models/video/video-file'
10import { getOrCreateAccountAndServer } from '../account' 13import { getOrCreateActorAndServerAndModel } from '../actor'
11import { forwardActivity } from '../send/misc' 14import { forwardActivity } from '../send/misc'
12import { getVideoChannelActivityPubUrl } from '../url' 15import { generateThumbnailFromUrl } from '../videos'
13import { addVideoChannelShares, videoChannelActivityObjectToDBAttributes } from './misc' 16import { addVideoShares, videoActivityObjectToDBAttributes, videoFileActivityUrlToDBAttributes } from './misc'
14 17
15async function processCreateActivity (activity: ActivityCreate) { 18async function processCreateActivity (activity: ActivityCreate) {
16 const activityObject = activity.object 19 const activityObject = activity.object
17 const activityType = activityObject.type 20 const activityType = activityObject.type
18 const account = await getOrCreateAccountAndServer(activity.actor) 21 const actor = await getOrCreateActorAndServerAndModel(activity.actor)
19 22
20 if (activityType === 'View') { 23 if (activityType === 'View') {
21 return processCreateView(account, activity) 24 return processCreateView(actor, activity)
22 } else if (activityType === 'Dislike') { 25 } else if (activityType === 'Dislike') {
23 return processCreateDislike(account, activity) 26 return processCreateDislike(actor, activity)
24 } else if (activityType === 'VideoChannel') { 27 } else if (activityType === 'Video') {
25 return processCreateVideoChannel(account, activityObject as VideoChannelObject) 28 return processCreateVideo(actor, activity)
26 } else if (activityType === 'Flag') { 29 } else if (activityType === 'Flag') {
27 return processCreateVideoAbuse(account, activityObject as VideoAbuseObject) 30 return processCreateVideoAbuse(actor, activityObject as VideoAbuseObject)
28 } 31 }
29 32
30 logger.warn('Unknown activity object type %s when creating activity.', activityType, { activity: activity.id }) 33 logger.warn('Unknown activity object type %s when creating activity.', activityType, { activity: activity.id })
@@ -39,17 +42,123 @@ export {
39 42
40// --------------------------------------------------------------------------- 43// ---------------------------------------------------------------------------
41 44
42async function processCreateDislike (byAccount: AccountModel, activity: ActivityCreate) { 45async function processCreateVideo (
46 actor: ActorModel,
47 activity: ActivityCreate
48) {
49 const videoToCreateData = activity.object as VideoTorrentObject
50
51 const channel = videoToCreateData.attributedTo.find(a => a.type === 'Group')
52 if (!channel) throw new Error('Cannot find associated video channel to video ' + videoToCreateData.url)
53
54 const channelActor = await getOrCreateActorAndServerAndModel(channel.id)
55
56 const options = {
57 arguments: [ actor, activity, videoToCreateData, channelActor ],
58 errorMessage: 'Cannot insert the remote video with many retries.'
59 }
60
61 const video = await retryTransactionWrapper(createRemoteVideo, options)
62
63 // Process outside the transaction because we could fetch remote data
64 if (videoToCreateData.likes && Array.isArray(videoToCreateData.likes.orderedItems)) {
65 await createRates(videoToCreateData.likes.orderedItems, video, 'like')
66 }
67
68 if (videoToCreateData.dislikes && Array.isArray(videoToCreateData.dislikes.orderedItems)) {
69 await createRates(videoToCreateData.dislikes.orderedItems, video, 'dislike')
70 }
71
72 if (videoToCreateData.shares && Array.isArray(videoToCreateData.shares.orderedItems)) {
73 await addVideoShares(video, videoToCreateData.shares.orderedItems)
74 }
75
76 return video
77}
78
79function createRemoteVideo (
80 account: ActorModel,
81 activity: ActivityCreate,
82 videoToCreateData: VideoTorrentObject,
83 channelActor: ActorModel
84) {
85 logger.debug('Adding remote video %s.', videoToCreateData.id)
86
87 return sequelizeTypescript.transaction(async t => {
88 const sequelizeOptions = {
89 transaction: t
90 }
91 const videoFromDatabase = await VideoModel.loadByUUIDOrURL(videoToCreateData.uuid, videoToCreateData.id, t)
92 if (videoFromDatabase) return videoFromDatabase
93
94 const videoData = await videoActivityObjectToDBAttributes(channelActor.VideoChannel, videoToCreateData, activity.to, activity.cc)
95 const video = VideoModel.build(videoData)
96
97 // Don't block on request
98 generateThumbnailFromUrl(video, videoToCreateData.icon)
99 .catch(err => logger.warn('Cannot generate thumbnail of %s.', videoToCreateData.id, err))
100
101 const videoCreated = await video.save(sequelizeOptions)
102
103 const videoFileAttributes = videoFileActivityUrlToDBAttributes(videoCreated, videoToCreateData)
104 if (videoFileAttributes.length === 0) {
105 throw new Error('Cannot find valid files for video %s ' + videoToCreateData.url)
106 }
107
108 const tasks: Bluebird<any>[] = videoFileAttributes.map(f => VideoFileModel.create(f, { transaction: t }))
109 await Promise.all(tasks)
110
111 const tags = videoToCreateData.tag.map(t => t.name)
112 const tagInstances = await TagModel.findOrCreateTags(tags, t)
113 await videoCreated.$set('Tags', tagInstances, sequelizeOptions)
114
115 logger.info('Remote video with uuid %s inserted.', videoToCreateData.uuid)
116
117 return videoCreated
118 })
119}
120
121async function createRates (actorUrls: string[], video: VideoModel, rate: VideoRateType) {
122 let rateCounts = 0
123 const tasks: Bluebird<any>[] = []
124
125 for (const actorUrl of actorUrls) {
126 const actor = await getOrCreateActorAndServerAndModel(actorUrl)
127 const p = AccountVideoRateModel
128 .create({
129 videoId: video.id,
130 accountId: actor.Account.id,
131 type: rate
132 })
133 .then(() => rateCounts += 1)
134
135 tasks.push(p)
136 }
137
138 await Promise.all(tasks)
139
140 logger.info('Adding %d %s to video %s.', rateCounts, rate, video.uuid)
141
142 // This is "likes" and "dislikes"
143 await video.increment(rate + 's', { by: rateCounts })
144
145 return
146}
147
148async function processCreateDislike (byActor: ActorModel, activity: ActivityCreate) {
43 const options = { 149 const options = {
44 arguments: [ byAccount, activity ], 150 arguments: [ byActor, activity ],
45 errorMessage: 'Cannot dislike the video with many retries.' 151 errorMessage: 'Cannot dislike the video with many retries.'
46 } 152 }
47 153
48 return retryTransactionWrapper(createVideoDislike, options) 154 return retryTransactionWrapper(createVideoDislike, options)
49} 155}
50 156
51function createVideoDislike (byAccount: AccountModel, activity: ActivityCreate) { 157function createVideoDislike (byActor: ActorModel, activity: ActivityCreate) {
52 const dislike = activity.object as DislikeObject 158 const dislike = activity.object as DislikeObject
159 const byAccount = byActor.Account
160
161 if (!byAccount) throw new Error('Cannot create dislike with the non account actor ' + byActor.url)
53 162
54 return sequelizeTypescript.transaction(async t => { 163 return sequelizeTypescript.transaction(async t => {
55 const video = await VideoModel.loadByUrlAndPopulateAccount(dislike.object, t) 164 const video = await VideoModel.loadByUrlAndPopulateAccount(dislike.object, t)
@@ -69,20 +178,20 @@ function createVideoDislike (byAccount: AccountModel, activity: ActivityCreate)
69 178
70 if (video.isOwned() && created === true) { 179 if (video.isOwned() && created === true) {
71 // Don't resend the activity to the sender 180 // Don't resend the activity to the sender
72 const exceptions = [ byAccount ] 181 const exceptions = [ byActor ]
73 await forwardActivity(activity, t, exceptions) 182 await forwardActivity(activity, t, exceptions)
74 } 183 }
75 }) 184 })
76} 185}
77 186
78async function processCreateView (byAccount: AccountModel, activity: ActivityCreate) { 187async function processCreateView (byAccount: ActorModel, activity: ActivityCreate) {
79 const view = activity.object as ViewObject 188 const view = activity.object as ViewObject
80 189
81 const video = await VideoModel.loadByUrlAndPopulateAccount(view.object) 190 const video = await VideoModel.loadByUrlAndPopulateAccount(view.object)
82 191
83 if (!video) throw new Error('Unknown video ' + view.object) 192 if (!video) throw new Error('Unknown video ' + view.object)
84 193
85 const account = await AccountModel.loadByUrl(view.actor) 194 const account = await ActorModel.loadByUrl(view.actor)
86 if (!account) throw new Error('Unknown account ' + view.actor) 195 if (!account) throw new Error('Unknown account ' + view.actor)
87 196
88 await video.increment('views') 197 await video.increment('views')
@@ -94,51 +203,21 @@ async function processCreateView (byAccount: AccountModel, activity: ActivityCre
94 } 203 }
95} 204}
96 205
97async function processCreateVideoChannel (account: AccountModel, videoChannelToCreateData: VideoChannelObject) { 206function processCreateVideoAbuse (actor: ActorModel, videoAbuseToCreateData: VideoAbuseObject) {
98 const options = {
99 arguments: [ account, videoChannelToCreateData ],
100 errorMessage: 'Cannot insert the remote video channel with many retries.'
101 }
102
103 const videoChannel = await retryTransactionWrapper(addRemoteVideoChannel, options)
104
105 if (videoChannelToCreateData.shares && Array.isArray(videoChannelToCreateData.shares.orderedItems)) {
106 await addVideoChannelShares(videoChannel, videoChannelToCreateData.shares.orderedItems)
107 }
108
109 return videoChannel
110}
111
112function addRemoteVideoChannel (account: AccountModel, videoChannelToCreateData: VideoChannelObject) {
113 logger.debug('Adding remote video channel "%s".', videoChannelToCreateData.uuid)
114
115 return sequelizeTypescript.transaction(async t => {
116 let videoChannel = await VideoChannelModel.loadByUUIDOrUrl(videoChannelToCreateData.uuid, videoChannelToCreateData.id, t)
117 if (videoChannel) return videoChannel
118
119 const videoChannelData = videoChannelActivityObjectToDBAttributes(videoChannelToCreateData, account)
120 videoChannel = new VideoChannelModel(videoChannelData)
121 videoChannel.url = getVideoChannelActivityPubUrl(videoChannel)
122
123 videoChannel = await videoChannel.save({ transaction: t })
124 logger.info('Remote video channel with uuid %s inserted.', videoChannelToCreateData.uuid)
125
126 return videoChannel
127 })
128}
129
130function processCreateVideoAbuse (account: AccountModel, videoAbuseToCreateData: VideoAbuseObject) {
131 const options = { 207 const options = {
132 arguments: [ account, videoAbuseToCreateData ], 208 arguments: [ actor, videoAbuseToCreateData ],
133 errorMessage: 'Cannot insert the remote video abuse with many retries.' 209 errorMessage: 'Cannot insert the remote video abuse with many retries.'
134 } 210 }
135 211
136 return retryTransactionWrapper(addRemoteVideoAbuse, options) 212 return retryTransactionWrapper(addRemoteVideoAbuse, options)
137} 213}
138 214
139function addRemoteVideoAbuse (account: AccountModel, videoAbuseToCreateData: VideoAbuseObject) { 215function addRemoteVideoAbuse (actor: ActorModel, videoAbuseToCreateData: VideoAbuseObject) {
140 logger.debug('Reporting remote abuse for video %s.', videoAbuseToCreateData.object) 216 logger.debug('Reporting remote abuse for video %s.', videoAbuseToCreateData.object)
141 217
218 const account = actor.Account
219 if (!account) throw new Error('Cannot create dislike with the non account actor ' + actor.url)
220
142 return sequelizeTypescript.transaction(async t => { 221 return sequelizeTypescript.transaction(async t => {
143 const video = await VideoModel.loadByUrlAndPopulateAccount(videoAbuseToCreateData.object, t) 222 const video = await VideoModel.loadByUrlAndPopulateAccount(videoAbuseToCreateData.object, t)
144 if (!video) { 223 if (!video) {
diff --git a/server/lib/activitypub/process/process-delete.ts b/server/lib/activitypub/process/process-delete.ts
index 8f280d37f..65a4e5bcc 100644
--- a/server/lib/activitypub/process/process-delete.ts
+++ b/server/lib/activitypub/process/process-delete.ts
@@ -2,28 +2,30 @@ import { ActivityDelete } from '../../../../shared/models/activitypub'
2import { logger, retryTransactionWrapper } from '../../../helpers' 2import { logger, retryTransactionWrapper } from '../../../helpers'
3import { sequelizeTypescript } from '../../../initializers' 3import { sequelizeTypescript } from '../../../initializers'
4import { AccountModel } from '../../../models/account/account' 4import { AccountModel } from '../../../models/account/account'
5import { ActorModel } from '../../../models/activitypub/actor'
5import { VideoModel } from '../../../models/video/video' 6import { VideoModel } from '../../../models/video/video'
6import { VideoChannelModel } from '../../../models/video/video-channel' 7import { VideoChannelModel } from '../../../models/video/video-channel'
7import { getOrCreateAccountAndServer } from '../account' 8import { getOrCreateActorAndServerAndModel } from '../actor'
8 9
9async function processDeleteActivity (activity: ActivityDelete) { 10async function processDeleteActivity (activity: ActivityDelete) {
10 const account = await getOrCreateAccountAndServer(activity.actor) 11 const actor = await getOrCreateActorAndServerAndModel(activity.actor)
11 12
12 if (account.url === activity.id) { 13 if (actor.url === activity.id) {
13 return processDeleteAccount(account) 14 if (actor.type === 'Person') {
14 } 15 if (!actor.Account) throw new Error('Actor ' + actor.url + ' is a person but we cannot find it in database.')
15 16
16 { 17 return processDeleteAccount(actor.Account)
17 let videoObject = await VideoModel.loadByUrlAndPopulateAccount(activity.id) 18 } else if (actor.type === 'Group') {
18 if (videoObject !== undefined) { 19 if (!actor.VideoChannel) throw new Error('Actor ' + actor.url + ' is a group but we cannot find it in database.')
19 return processDeleteVideo(account, videoObject) 20
21 return processDeleteVideoChannel(actor.VideoChannel)
20 } 22 }
21 } 23 }
22 24
23 { 25 {
24 let videoChannelObject = await VideoChannelModel.loadByUrl(activity.id) 26 let videoObject = await VideoModel.loadByUrlAndPopulateAccount(activity.id)
25 if (videoChannelObject !== undefined) { 27 if (videoObject !== undefined) {
26 return processDeleteVideoChannel(account, videoChannelObject) 28 return processDeleteVideo(actor, videoObject)
27 } 29 }
28 } 30 }
29 31
@@ -38,21 +40,21 @@ export {
38 40
39// --------------------------------------------------------------------------- 41// ---------------------------------------------------------------------------
40 42
41async function processDeleteVideo (account: AccountModel, videoToDelete: VideoModel) { 43async function processDeleteVideo (actor: ActorModel, videoToDelete: VideoModel) {
42 const options = { 44 const options = {
43 arguments: [ account, videoToDelete ], 45 arguments: [ actor, videoToDelete ],
44 errorMessage: 'Cannot remove the remote video with many retries.' 46 errorMessage: 'Cannot remove the remote video with many retries.'
45 } 47 }
46 48
47 await retryTransactionWrapper(deleteRemoteVideo, options) 49 await retryTransactionWrapper(deleteRemoteVideo, options)
48} 50}
49 51
50async function deleteRemoteVideo (account: AccountModel, videoToDelete: VideoModel) { 52async function deleteRemoteVideo (actor: ActorModel, videoToDelete: VideoModel) {
51 logger.debug('Removing remote video "%s".', videoToDelete.uuid) 53 logger.debug('Removing remote video "%s".', videoToDelete.uuid)
52 54
53 await sequelizeTypescript.transaction(async t => { 55 await sequelizeTypescript.transaction(async t => {
54 if (videoToDelete.VideoChannel.Account.id !== account.id) { 56 if (videoToDelete.VideoChannel.Account.Actor.id !== actor.id) {
55 throw new Error('Account ' + account.url + ' does not own video channel ' + videoToDelete.VideoChannel.url) 57 throw new Error('Account ' + actor.url + ' does not own video channel ' + videoToDelete.VideoChannel.Actor.url)
56 } 58 }
57 59
58 await videoToDelete.destroy({ transaction: t }) 60 await videoToDelete.destroy({ transaction: t })
@@ -61,44 +63,40 @@ async function deleteRemoteVideo (account: AccountModel, videoToDelete: VideoMod
61 logger.info('Remote video with uuid %s removed.', videoToDelete.uuid) 63 logger.info('Remote video with uuid %s removed.', videoToDelete.uuid)
62} 64}
63 65
64async function processDeleteVideoChannel (account: AccountModel, videoChannelToRemove: VideoChannelModel) { 66async function processDeleteAccount (accountToRemove: AccountModel) {
65 const options = { 67 const options = {
66 arguments: [ account, videoChannelToRemove ], 68 arguments: [ accountToRemove ],
67 errorMessage: 'Cannot remove the remote video channel with many retries.' 69 errorMessage: 'Cannot remove the remote account with many retries.'
68 } 70 }
69 71
70 await retryTransactionWrapper(deleteRemoteVideoChannel, options) 72 await retryTransactionWrapper(deleteRemoteAccount, options)
71} 73}
72 74
73async function deleteRemoteVideoChannel (account: AccountModel, videoChannelToRemove: VideoChannelModel) { 75async function deleteRemoteAccount (accountToRemove: AccountModel) {
74 logger.debug('Removing remote video channel "%s".', videoChannelToRemove.uuid) 76 logger.debug('Removing remote account "%s".', accountToRemove.Actor.uuid)
75 77
76 await sequelizeTypescript.transaction(async t => { 78 await sequelizeTypescript.transaction(async t => {
77 if (videoChannelToRemove.Account.id !== account.id) { 79 await accountToRemove.destroy({ transaction: t })
78 throw new Error('Account ' + account.url + ' does not own video channel ' + videoChannelToRemove.url)
79 }
80
81 await videoChannelToRemove.destroy({ transaction: t })
82 }) 80 })
83 81
84 logger.info('Remote video channel with uuid %s removed.', videoChannelToRemove.uuid) 82 logger.info('Remote account with uuid %s removed.', accountToRemove.Actor.uuid)
85} 83}
86 84
87async function processDeleteAccount (accountToRemove: AccountModel) { 85async function processDeleteVideoChannel (videoChannelToRemove: VideoChannelModel) {
88 const options = { 86 const options = {
89 arguments: [ accountToRemove ], 87 arguments: [ videoChannelToRemove ],
90 errorMessage: 'Cannot remove the remote account with many retries.' 88 errorMessage: 'Cannot remove the remote video channel with many retries.'
91 } 89 }
92 90
93 await retryTransactionWrapper(deleteRemoteAccount, options) 91 await retryTransactionWrapper(deleteRemoteVideoChannel, options)
94} 92}
95 93
96async function deleteRemoteAccount (accountToRemove: AccountModel) { 94async function deleteRemoteVideoChannel (videoChannelToRemove: VideoChannelModel) {
97 logger.debug('Removing remote account "%s".', accountToRemove.uuid) 95 logger.debug('Removing remote video channel "%s".', videoChannelToRemove.Actor.uuid)
98 96
99 await sequelizeTypescript.transaction(async t => { 97 await sequelizeTypescript.transaction(async t => {
100 await accountToRemove.destroy({ transaction: t }) 98 await videoChannelToRemove.destroy({ transaction: t })
101 }) 99 })
102 100
103 logger.info('Remote account with uuid %s removed.', accountToRemove.uuid) 101 logger.info('Remote video channel with uuid %s removed.', videoChannelToRemove.Actor.uuid)
104} 102}
diff --git a/server/lib/activitypub/process/process-follow.ts b/server/lib/activitypub/process/process-follow.ts
index ccaee43a6..ec7a331f3 100644
--- a/server/lib/activitypub/process/process-follow.ts
+++ b/server/lib/activitypub/process/process-follow.ts
@@ -1,16 +1,16 @@
1import { ActivityFollow } from '../../../../shared/models/activitypub' 1import { ActivityFollow } from '../../../../shared/models/activitypub'
2import { logger, retryTransactionWrapper } from '../../../helpers' 2import { logger, retryTransactionWrapper } from '../../../helpers'
3import { sequelizeTypescript } from '../../../initializers' 3import { sequelizeTypescript } from '../../../initializers'
4import { AccountModel } from '../../../models/account/account' 4import { ActorModel } from '../../../models/activitypub/actor'
5import { AccountFollowModel } from '../../../models/account/account-follow' 5import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
6import { getOrCreateAccountAndServer } from '../account' 6import { getOrCreateActorAndServerAndModel } from '../actor'
7import { sendAccept } from '../send' 7import { sendAccept } from '../send'
8 8
9async function processFollowActivity (activity: ActivityFollow) { 9async function processFollowActivity (activity: ActivityFollow) {
10 const activityObject = activity.object 10 const activityObject = activity.object
11 const account = await getOrCreateAccountAndServer(activity.actor) 11 const actor = await getOrCreateActorAndServerAndModel(activity.actor)
12 12
13 return processFollow(account, activityObject) 13 return processFollow(actor, activityObject)
14} 14}
15 15
16// --------------------------------------------------------------------------- 16// ---------------------------------------------------------------------------
@@ -21,46 +21,46 @@ export {
21 21
22// --------------------------------------------------------------------------- 22// ---------------------------------------------------------------------------
23 23
24function processFollow (account: AccountModel, targetAccountURL: string) { 24function processFollow (actor: ActorModel, targetActorURL: string) {
25 const options = { 25 const options = {
26 arguments: [ account, targetAccountURL ], 26 arguments: [ actor, targetActorURL ],
27 errorMessage: 'Cannot follow with many retries.' 27 errorMessage: 'Cannot follow with many retries.'
28 } 28 }
29 29
30 return retryTransactionWrapper(follow, options) 30 return retryTransactionWrapper(follow, options)
31} 31}
32 32
33async function follow (account: AccountModel, targetAccountURL: string) { 33async function follow (actor: ActorModel, targetActorURL: string) {
34 await sequelizeTypescript.transaction(async t => { 34 await sequelizeTypescript.transaction(async t => {
35 const targetAccount = await AccountModel.loadByUrl(targetAccountURL, t) 35 const targetActor = await ActorModel.loadByUrl(targetActorURL, t)
36 36
37 if (!targetAccount) throw new Error('Unknown account') 37 if (!targetActor) throw new Error('Unknown actor')
38 if (targetAccount.isOwned() === false) throw new Error('This is not a local account.') 38 if (targetActor.isOwned() === false) throw new Error('This is not a local actor.')
39 39
40 const [ accountFollow ] = await AccountFollowModel.findOrCreate({ 40 const [ actorFollow ] = await ActorFollowModel.findOrCreate({
41 where: { 41 where: {
42 accountId: account.id, 42 actorId: actor.id,
43 targetAccountId: targetAccount.id 43 targetActorId: targetActor.id
44 }, 44 },
45 defaults: { 45 defaults: {
46 accountId: account.id, 46 actorId: actor.id,
47 targetAccountId: targetAccount.id, 47 targetActorId: targetActor.id,
48 state: 'accepted' 48 state: 'accepted'
49 }, 49 },
50 transaction: t 50 transaction: t
51 }) 51 })
52 52
53 if (accountFollow.state !== 'accepted') { 53 if (actorFollow.state !== 'accepted') {
54 accountFollow.state = 'accepted' 54 actorFollow.state = 'accepted'
55 await accountFollow.save({ transaction: t }) 55 await actorFollow.save({ transaction: t })
56 } 56 }
57 57
58 accountFollow.AccountFollower = account 58 actorFollow.ActorFollower = actor
59 accountFollow.AccountFollowing = targetAccount 59 actorFollow.ActorFollowing = targetActor
60 60
61 // Target sends to account he accepted the follow request 61 // Target sends to actor he accepted the follow request
62 return sendAccept(accountFollow, t) 62 return sendAccept(actorFollow, t)
63 }) 63 })
64 64
65 logger.info('Account uuid %s is followed by account %s.', account.url, targetAccountURL) 65 logger.info('Actor uuid %s is followed by actor %s.', actor.url, targetActorURL)
66} 66}
diff --git a/server/lib/activitypub/process/process-like.ts b/server/lib/activitypub/process/process-like.ts
index a6e391f1e..a7fcec21c 100644
--- a/server/lib/activitypub/process/process-like.ts
+++ b/server/lib/activitypub/process/process-like.ts
@@ -1,16 +1,16 @@
1import { ActivityLike } from '../../../../shared/models/activitypub' 1import { ActivityLike } from '../../../../shared/models/activitypub'
2import { retryTransactionWrapper } from '../../../helpers' 2import { retryTransactionWrapper } from '../../../helpers'
3import { sequelizeTypescript } from '../../../initializers' 3import { sequelizeTypescript } from '../../../initializers'
4import { AccountModel } from '../../../models/account/account'
5import { AccountVideoRateModel } from '../../../models/account/account-video-rate' 4import { AccountVideoRateModel } from '../../../models/account/account-video-rate'
5import { ActorModel } from '../../../models/activitypub/actor'
6import { VideoModel } from '../../../models/video/video' 6import { VideoModel } from '../../../models/video/video'
7import { getOrCreateAccountAndServer } from '../account' 7import { getOrCreateActorAndServerAndModel } from '../actor'
8import { forwardActivity } from '../send/misc' 8import { forwardActivity } from '../send/misc'
9 9
10async function processLikeActivity (activity: ActivityLike) { 10async function processLikeActivity (activity: ActivityLike) {
11 const account = await getOrCreateAccountAndServer(activity.actor) 11 const actor = await getOrCreateActorAndServerAndModel(activity.actor)
12 12
13 return processLikeVideo(account, activity) 13 return processLikeVideo(actor, activity)
14} 14}
15 15
16// --------------------------------------------------------------------------- 16// ---------------------------------------------------------------------------
@@ -21,18 +21,21 @@ export {
21 21
22// --------------------------------------------------------------------------- 22// ---------------------------------------------------------------------------
23 23
24async function processLikeVideo (byAccount: AccountModel, activity: ActivityLike) { 24async function processLikeVideo (actor: ActorModel, activity: ActivityLike) {
25 const options = { 25 const options = {
26 arguments: [ byAccount, activity ], 26 arguments: [ actor, activity ],
27 errorMessage: 'Cannot like the video with many retries.' 27 errorMessage: 'Cannot like the video with many retries.'
28 } 28 }
29 29
30 return retryTransactionWrapper(createVideoLike, options) 30 return retryTransactionWrapper(createVideoLike, options)
31} 31}
32 32
33function createVideoLike (byAccount: AccountModel, activity: ActivityLike) { 33function createVideoLike (byActor: ActorModel, activity: ActivityLike) {
34 const videoUrl = activity.object 34 const videoUrl = activity.object
35 35
36 const byAccount = byActor.Account
37 if (!byAccount) throw new Error('Cannot create like with the non account actor ' + byActor.url)
38
36 return sequelizeTypescript.transaction(async t => { 39 return sequelizeTypescript.transaction(async t => {
37 const video = await VideoModel.loadByUrlAndPopulateAccount(videoUrl) 40 const video = await VideoModel.loadByUrlAndPopulateAccount(videoUrl)
38 41
@@ -52,7 +55,7 @@ function createVideoLike (byAccount: AccountModel, activity: ActivityLike) {
52 55
53 if (video.isOwned() && created === true) { 56 if (video.isOwned() && created === true) {
54 // Don't resend the activity to the sender 57 // Don't resend the activity to the sender
55 const exceptions = [ byAccount ] 58 const exceptions = [ byActor ]
56 await forwardActivity(activity, t, exceptions) 59 await forwardActivity(activity, t, exceptions)
57 } 60 }
58 }) 61 })
diff --git a/server/lib/activitypub/process/process-undo.ts b/server/lib/activitypub/process/process-undo.ts
index efa63122b..4a0181137 100644
--- a/server/lib/activitypub/process/process-undo.ts
+++ b/server/lib/activitypub/process/process-undo.ts
@@ -3,8 +3,9 @@ import { DislikeObject } from '../../../../shared/models/activitypub/objects'
3import { logger, retryTransactionWrapper } from '../../../helpers' 3import { logger, retryTransactionWrapper } from '../../../helpers'
4import { sequelizeTypescript } from '../../../initializers' 4import { sequelizeTypescript } from '../../../initializers'
5import { AccountModel } from '../../../models/account/account' 5import { AccountModel } from '../../../models/account/account'
6import { AccountFollowModel } from '../../../models/account/account-follow'
7import { AccountVideoRateModel } from '../../../models/account/account-video-rate' 6import { AccountVideoRateModel } from '../../../models/account/account-video-rate'
7import { ActorModel } from '../../../models/activitypub/actor'
8import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
8import { VideoModel } from '../../../models/video/video' 9import { VideoModel } from '../../../models/video/video'
9import { forwardActivity } from '../send/misc' 10import { forwardActivity } from '../send/misc'
10 11
@@ -32,21 +33,21 @@ export {
32 33
33// --------------------------------------------------------------------------- 34// ---------------------------------------------------------------------------
34 35
35function processUndoLike (actor: string, activity: ActivityUndo) { 36function processUndoLike (actorUrl: string, activity: ActivityUndo) {
36 const options = { 37 const options = {
37 arguments: [ actor, activity ], 38 arguments: [ actorUrl, activity ],
38 errorMessage: 'Cannot undo like with many retries.' 39 errorMessage: 'Cannot undo like with many retries.'
39 } 40 }
40 41
41 return retryTransactionWrapper(undoLike, options) 42 return retryTransactionWrapper(undoLike, options)
42} 43}
43 44
44function undoLike (actor: string, activity: ActivityUndo) { 45function undoLike (actorUrl: string, activity: ActivityUndo) {
45 const likeActivity = activity.object as ActivityLike 46 const likeActivity = activity.object as ActivityLike
46 47
47 return sequelizeTypescript.transaction(async t => { 48 return sequelizeTypescript.transaction(async t => {
48 const byAccount = await AccountModel.loadByUrl(actor, t) 49 const byAccount = await AccountModel.loadByUrl(actorUrl, t)
49 if (!byAccount) throw new Error('Unknown account ' + actor) 50 if (!byAccount) throw new Error('Unknown account ' + actorUrl)
50 51
51 const video = await VideoModel.loadByUrlAndPopulateAccount(likeActivity.object, t) 52 const video = await VideoModel.loadByUrlAndPopulateAccount(likeActivity.object, t)
52 if (!video) throw new Error('Unknown video ' + likeActivity.actor) 53 if (!video) throw new Error('Unknown video ' + likeActivity.actor)
@@ -59,27 +60,27 @@ function undoLike (actor: string, activity: ActivityUndo) {
59 60
60 if (video.isOwned()) { 61 if (video.isOwned()) {
61 // Don't resend the activity to the sender 62 // Don't resend the activity to the sender
62 const exceptions = [ byAccount ] 63 const exceptions = [ byAccount.Actor ]
63 await forwardActivity(activity, t, exceptions) 64 await forwardActivity(activity, t, exceptions)
64 } 65 }
65 }) 66 })
66} 67}
67 68
68function processUndoDislike (actor: string, activity: ActivityUndo) { 69function processUndoDislike (actorUrl: string, activity: ActivityUndo) {
69 const options = { 70 const options = {
70 arguments: [ actor, activity ], 71 arguments: [ actorUrl, activity ],
71 errorMessage: 'Cannot undo dislike with many retries.' 72 errorMessage: 'Cannot undo dislike with many retries.'
72 } 73 }
73 74
74 return retryTransactionWrapper(undoDislike, options) 75 return retryTransactionWrapper(undoDislike, options)
75} 76}
76 77
77function undoDislike (actor: string, activity: ActivityUndo) { 78function undoDislike (actorUrl: string, activity: ActivityUndo) {
78 const dislike = activity.object.object as DislikeObject 79 const dislike = activity.object.object as DislikeObject
79 80
80 return sequelizeTypescript.transaction(async t => { 81 return sequelizeTypescript.transaction(async t => {
81 const byAccount = await AccountModel.loadByUrl(actor, t) 82 const byAccount = await AccountModel.loadByUrl(actorUrl, t)
82 if (!byAccount) throw new Error('Unknown account ' + actor) 83 if (!byAccount) throw new Error('Unknown account ' + actorUrl)
83 84
84 const video = await VideoModel.loadByUrlAndPopulateAccount(dislike.object, t) 85 const video = await VideoModel.loadByUrlAndPopulateAccount(dislike.object, t)
85 if (!video) throw new Error('Unknown video ' + dislike.actor) 86 if (!video) throw new Error('Unknown video ' + dislike.actor)
@@ -92,30 +93,30 @@ function undoDislike (actor: string, activity: ActivityUndo) {
92 93
93 if (video.isOwned()) { 94 if (video.isOwned()) {
94 // Don't resend the activity to the sender 95 // Don't resend the activity to the sender
95 const exceptions = [ byAccount ] 96 const exceptions = [ byAccount.Actor ]
96 await forwardActivity(activity, t, exceptions) 97 await forwardActivity(activity, t, exceptions)
97 } 98 }
98 }) 99 })
99} 100}
100 101
101function processUndoFollow (actor: string, followActivity: ActivityFollow) { 102function processUndoFollow (actorUrl: string, followActivity: ActivityFollow) {
102 const options = { 103 const options = {
103 arguments: [ actor, followActivity ], 104 arguments: [ actorUrl, followActivity ],
104 errorMessage: 'Cannot undo follow with many retries.' 105 errorMessage: 'Cannot undo follow with many retries.'
105 } 106 }
106 107
107 return retryTransactionWrapper(undoFollow, options) 108 return retryTransactionWrapper(undoFollow, options)
108} 109}
109 110
110function undoFollow (actor: string, followActivity: ActivityFollow) { 111function undoFollow (actorUrl: string, followActivity: ActivityFollow) {
111 return sequelizeTypescript.transaction(async t => { 112 return sequelizeTypescript.transaction(async t => {
112 const follower = await AccountModel.loadByUrl(actor, t) 113 const follower = await ActorModel.loadByUrl(actorUrl, t)
113 const following = await AccountModel.loadByUrl(followActivity.object, t) 114 const following = await ActorModel.loadByUrl(followActivity.object, t)
114 const accountFollow = await AccountFollowModel.loadByAccountAndTarget(follower.id, following.id, t) 115 const actorFollow = await ActorFollowModel.loadByActorAndTarget(follower.id, following.id, t)
115 116
116 if (!accountFollow) throw new Error(`'Unknown account follow ${follower.id} -> ${following.id}.`) 117 if (!actorFollow) throw new Error(`'Unknown actor follow ${follower.id} -> ${following.id}.`)
117 118
118 await accountFollow.destroy({ transaction: t }) 119 await actorFollow.destroy({ transaction: t })
119 120
120 return undefined 121 return undefined
121 }) 122 })
diff --git a/server/lib/activitypub/process/process-update.ts b/server/lib/activitypub/process/process-update.ts
index 771021f0c..35912ee87 100644
--- a/server/lib/activitypub/process/process-update.ts
+++ b/server/lib/activitypub/process/process-update.ts
@@ -1,23 +1,19 @@
1import * as Bluebird from 'bluebird' 1import * as Bluebird from 'bluebird'
2import { VideoChannelObject, VideoTorrentObject } from '../../../../shared'
3import { ActivityUpdate } from '../../../../shared/models/activitypub' 2import { ActivityUpdate } from '../../../../shared/models/activitypub'
4import { logger, resetSequelizeInstance, retryTransactionWrapper } from '../../../helpers' 3import { logger, resetSequelizeInstance, retryTransactionWrapper } from '../../../helpers'
5import { sequelizeTypescript } from '../../../initializers' 4import { sequelizeTypescript } from '../../../initializers'
6import { AccountModel } from '../../../models/account/account' 5import { ActorModel } from '../../../models/activitypub/actor'
7import { TagModel } from '../../../models/video/tag' 6import { TagModel } from '../../../models/video/tag'
8import { VideoModel } from '../../../models/video/video' 7import { VideoModel } from '../../../models/video/video'
9import { VideoChannelModel } from '../../../models/video/video-channel'
10import { VideoFileModel } from '../../../models/video/video-file' 8import { VideoFileModel } from '../../../models/video/video-file'
11import { getOrCreateAccountAndServer } from '../account' 9import { getOrCreateActorAndServerAndModel } from '../actor'
12import { videoActivityObjectToDBAttributes, videoFileActivityUrlToDBAttributes } from './misc' 10import { videoActivityObjectToDBAttributes, videoFileActivityUrlToDBAttributes } from './misc'
13 11
14async function processUpdateActivity (activity: ActivityUpdate) { 12async function processUpdateActivity (activity: ActivityUpdate) {
15 const account = await getOrCreateAccountAndServer(activity.actor) 13 const actor = await getOrCreateActorAndServerAndModel(activity.actor)
16 14
17 if (activity.object.type === 'Video') { 15 if (activity.object.type === 'Video') {
18 return processUpdateVideo(account, activity.object) 16 return processUpdateVideo(actor, activity)
19 } else if (activity.object.type === 'VideoChannel') {
20 return processUpdateVideoChannel(account, activity.object)
21 } 17 }
22 18
23 return 19 return
@@ -31,16 +27,18 @@ export {
31 27
32// --------------------------------------------------------------------------- 28// ---------------------------------------------------------------------------
33 29
34function processUpdateVideo (account: AccountModel, video: VideoTorrentObject) { 30function processUpdateVideo (actor: ActorModel, activity: ActivityUpdate) {
35 const options = { 31 const options = {
36 arguments: [ account, video ], 32 arguments: [ actor, activity ],
37 errorMessage: 'Cannot update the remote video with many retries' 33 errorMessage: 'Cannot update the remote video with many retries'
38 } 34 }
39 35
40 return retryTransactionWrapper(updateRemoteVideo, options) 36 return retryTransactionWrapper(updateRemoteVideo, options)
41} 37}
42 38
43async function updateRemoteVideo (account: AccountModel, videoAttributesToUpdate: VideoTorrentObject) { 39async function updateRemoteVideo (actor: ActorModel, activity: ActivityUpdate) {
40 const videoAttributesToUpdate = activity.object
41
44 logger.debug('Updating remote video "%s".', videoAttributesToUpdate.uuid) 42 logger.debug('Updating remote video "%s".', videoAttributesToUpdate.uuid)
45 let videoInstance: VideoModel 43 let videoInstance: VideoModel
46 let videoFieldsSave: object 44 let videoFieldsSave: object
@@ -54,23 +52,23 @@ async function updateRemoteVideo (account: AccountModel, videoAttributesToUpdate
54 const videoInstance = await VideoModel.loadByUrlAndPopulateAccount(videoAttributesToUpdate.id, t) 52 const videoInstance = await VideoModel.loadByUrlAndPopulateAccount(videoAttributesToUpdate.id, t)
55 if (!videoInstance) throw new Error('Video ' + videoAttributesToUpdate.id + ' not found.') 53 if (!videoInstance) throw new Error('Video ' + videoAttributesToUpdate.id + ' not found.')
56 54
57 if (videoInstance.VideoChannel.Account.id !== account.id) { 55 const videoChannel = videoInstance.VideoChannel
58 throw new Error('Account ' + account.url + ' does not own video channel ' + videoInstance.VideoChannel.url) 56 if (videoChannel.Account.Actor.id !== actor.id) {
57 throw new Error('Account ' + actor.url + ' does not own video channel ' + videoChannel.Actor.url)
59 } 58 }
60 59
61 const videoData = await videoActivityObjectToDBAttributes(videoInstance.VideoChannel, videoAttributesToUpdate) 60 const videoData = await videoActivityObjectToDBAttributes(videoChannel, videoAttributesToUpdate, activity.to, activity.cc)
62 videoInstance.set('name', videoData.name) 61 videoInstance.set('name', videoData.name)
63 videoInstance.set('category', videoData.category) 62 videoInstance.set('category', videoData.category)
64 videoInstance.set('licence', videoData.licence) 63 videoInstance.set('licence', videoData.licence)
65 videoInstance.set('language', videoData.language) 64 videoInstance.set('language', videoData.language)
66 videoInstance.set('nsfw', videoData.nsfw) 65 videoInstance.set('nsfw', videoData.nsfw)
66 videoInstance.set('privacy', videoData.privacy)
67 videoInstance.set('description', videoData.description) 67 videoInstance.set('description', videoData.description)
68 videoInstance.set('duration', videoData.duration) 68 videoInstance.set('duration', videoData.duration)
69 videoInstance.set('createdAt', videoData.createdAt) 69 videoInstance.set('createdAt', videoData.createdAt)
70 videoInstance.set('updatedAt', videoData.updatedAt) 70 videoInstance.set('updatedAt', videoData.updatedAt)
71 videoInstance.set('views', videoData.views) 71 videoInstance.set('views', videoData.views)
72 // videoInstance.set('likes', videoData.likes)
73 // videoInstance.set('dislikes', videoData.dislikes)
74 72
75 await videoInstance.save(sequelizeOptions) 73 await videoInstance.save(sequelizeOptions)
76 74
@@ -101,36 +99,3 @@ async function updateRemoteVideo (account: AccountModel, videoAttributesToUpdate
101 throw err 99 throw err
102 } 100 }
103} 101}
104
105async function processUpdateVideoChannel (account: AccountModel, videoChannel: VideoChannelObject) {
106 const options = {
107 arguments: [ account, videoChannel ],
108 errorMessage: 'Cannot update the remote video channel with many retries.'
109 }
110
111 await retryTransactionWrapper(updateRemoteVideoChannel, options)
112}
113
114async function updateRemoteVideoChannel (account: AccountModel, videoChannel: VideoChannelObject) {
115 logger.debug('Updating remote video channel "%s".', videoChannel.uuid)
116
117 await sequelizeTypescript.transaction(async t => {
118 const sequelizeOptions = { transaction: t }
119
120 const videoChannelInstance = await VideoChannelModel.loadByUrl(videoChannel.id)
121 if (!videoChannelInstance) throw new Error('Video ' + videoChannel.id + ' not found.')
122
123 if (videoChannelInstance.Account.id !== account.id) {
124 throw new Error('Account ' + account.id + ' does not own video channel ' + videoChannelInstance.url)
125 }
126
127 videoChannelInstance.set('name', videoChannel.name)
128 videoChannelInstance.set('description', videoChannel.content)
129 videoChannelInstance.set('createdAt', videoChannel.published)
130 videoChannelInstance.set('updatedAt', videoChannel.updated)
131
132 await videoChannelInstance.save(sequelizeOptions)
133 })
134
135 logger.info('Remote video channel with uuid %s updated', videoChannel.uuid)
136}
diff --git a/server/lib/activitypub/process/process.ts b/server/lib/activitypub/process/process.ts
index bfbf8053c..dfb60c1bf 100644
--- a/server/lib/activitypub/process/process.ts
+++ b/server/lib/activitypub/process/process.ts
@@ -1,8 +1,7 @@
1import { Activity, ActivityType } from '../../../../shared/models/activitypub' 1import { Activity, ActivityType } from '../../../../shared/models/activitypub'
2import { logger } from '../../../helpers' 2import { logger } from '../../../helpers'
3import { AccountModel } from '../../../models/account/account' 3import { ActorModel } from '../../../models/activitypub/actor'
4import { processAcceptActivity } from './process-accept' 4import { processAcceptActivity } from './process-accept'
5import { processAddActivity } from './process-add'
6import { processAnnounceActivity } from './process-announce' 5import { processAnnounceActivity } from './process-announce'
7import { processCreateActivity } from './process-create' 6import { processCreateActivity } from './process-create'
8import { processDeleteActivity } from './process-delete' 7import { processDeleteActivity } from './process-delete'
@@ -11,9 +10,8 @@ import { processLikeActivity } from './process-like'
11import { processUndoActivity } from './process-undo' 10import { processUndoActivity } from './process-undo'
12import { processUpdateActivity } from './process-update' 11import { processUpdateActivity } from './process-update'
13 12
14const processActivity: { [ P in ActivityType ]: (activity: Activity, inboxAccount?: AccountModel) => Promise<any> } = { 13const processActivity: { [ P in ActivityType ]: (activity: Activity, inboxActor?: ActorModel) => Promise<any> } = {
15 Create: processCreateActivity, 14 Create: processCreateActivity,
16 Add: processAddActivity,
17 Update: processUpdateActivity, 15 Update: processUpdateActivity,
18 Delete: processDeleteActivity, 16 Delete: processDeleteActivity,
19 Follow: processFollowActivity, 17 Follow: processFollowActivity,
@@ -23,11 +21,11 @@ const processActivity: { [ P in ActivityType ]: (activity: Activity, inboxAccoun
23 Like: processLikeActivity 21 Like: processLikeActivity
24} 22}
25 23
26async function processActivities (activities: Activity[], signatureAccount?: AccountModel, inboxAccount?: AccountModel) { 24async function processActivities (activities: Activity[], signatureActor?: ActorModel, inboxActor?: ActorModel) {
27 for (const activity of activities) { 25 for (const activity of activities) {
28 // When we fetch remote data, we don't have signature 26 // When we fetch remote data, we don't have signature
29 if (signatureAccount && activity.actor !== signatureAccount.url) { 27 if (signatureActor && activity.actor !== signatureActor.url) {
30 logger.warn('Signature mismatch between %s and %s.', activity.actor, signatureAccount.url) 28 logger.warn('Signature mismatch between %s and %s.', activity.actor, signatureActor.url)
31 continue 29 continue
32 } 30 }
33 31
@@ -38,7 +36,7 @@ async function processActivities (activities: Activity[], signatureAccount?: Acc
38 } 36 }
39 37
40 try { 38 try {
41 await activityProcessor(activity, inboxAccount) 39 await activityProcessor(activity, inboxActor)
42 } catch (err) { 40 } catch (err) {
43 logger.warn('Cannot process activity %s.', activity.type, err) 41 logger.warn('Cannot process activity %s.', activity.type, err)
44 } 42 }
diff --git a/server/lib/activitypub/send/index.ts b/server/lib/activitypub/send/index.ts
index ee8f3ad7e..79ba6c7fe 100644
--- a/server/lib/activitypub/send/index.ts
+++ b/server/lib/activitypub/send/index.ts
@@ -1,5 +1,4 @@
1export * from './send-accept' 1export * from './send-accept'
2export * from './send-add'
3export * from './send-announce' 2export * from './send-announce'
4export * from './send-create' 3export * from './send-create'
5export * from './send-delete' 4export * from './send-delete'
diff --git a/server/lib/activitypub/send/misc.ts b/server/lib/activitypub/send/misc.ts
index ffc221477..14101e630 100644
--- a/server/lib/activitypub/send/misc.ts
+++ b/server/lib/activitypub/send/misc.ts
@@ -2,18 +2,16 @@ import { Transaction } from 'sequelize'
2import { Activity } from '../../../../shared/models/activitypub' 2import { Activity } from '../../../../shared/models/activitypub'
3import { logger } from '../../../helpers' 3import { logger } from '../../../helpers'
4import { ACTIVITY_PUB } from '../../../initializers' 4import { ACTIVITY_PUB } from '../../../initializers'
5import { AccountModel } from '../../../models/account/account' 5import { ActorModel } from '../../../models/activitypub/actor'
6import { AccountFollowModel } from '../../../models/account/account-follow' 6import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
7import { VideoModel } from '../../../models/video/video' 7import { VideoModel } from '../../../models/video/video'
8import { VideoChannelModel } from '../../../models/video/video-channel'
9import { VideoChannelShareModel } from '../../../models/video/video-channel-share'
10import { VideoShareModel } from '../../../models/video/video-share' 8import { VideoShareModel } from '../../../models/video/video-share'
11import { activitypubHttpJobScheduler, ActivityPubHttpPayload } from '../../jobs/activitypub-http-job-scheduler' 9import { activitypubHttpJobScheduler, ActivityPubHttpPayload } from '../../jobs/activitypub-http-job-scheduler'
12 10
13async function forwardActivity ( 11async function forwardActivity (
14 activity: Activity, 12 activity: Activity,
15 t: Transaction, 13 t: Transaction,
16 followersException: AccountModel[] = [] 14 followersException: ActorModel[] = []
17) { 15) {
18 const to = activity.to || [] 16 const to = activity.to || []
19 const cc = activity.cc || [] 17 const cc = activity.cc || []
@@ -25,11 +23,11 @@ async function forwardActivity (
25 } 23 }
26 } 24 }
27 25
28 const toAccountFollowers = await AccountModel.listByFollowersUrls(followersUrls, t) 26 const toActorFollowers = await ActorModel.listByFollowersUrls(followersUrls, t)
29 const uris = await computeFollowerUris(toAccountFollowers, followersException, t) 27 const uris = await computeFollowerUris(toActorFollowers, followersException, t)
30 28
31 if (uris.length === 0) { 29 if (uris.length === 0) {
32 logger.info('0 followers for %s, no forwarding.', toAccountFollowers.map(a => a.id).join(', ')) 30 logger.info('0 followers for %s, no forwarding.', toActorFollowers.map(a => a.id).join(', '))
33 return undefined 31 return undefined
34 } 32 }
35 33
@@ -45,14 +43,14 @@ async function forwardActivity (
45 43
46async function broadcastToFollowers ( 44async function broadcastToFollowers (
47 data: any, 45 data: any,
48 byAccount: AccountModel, 46 byActor: ActorModel,
49 toAccountFollowers: AccountModel[], 47 toActorFollowers: ActorModel[],
50 t: Transaction, 48 t: Transaction,
51 followersException: AccountModel[] = [] 49 followersException: ActorModel[] = []
52) { 50) {
53 const uris = await computeFollowerUris(toAccountFollowers, followersException, t) 51 const uris = await computeFollowerUris(toActorFollowers, followersException, t)
54 if (uris.length === 0) { 52 if (uris.length === 0) {
55 logger.info('0 followers for %s, no broadcasting.', toAccountFollowers.map(a => a.id).join(', ')) 53 logger.info('0 followers for %s, no broadcasting.', toActorFollowers.map(a => a.id).join(', '))
56 return undefined 54 return undefined
57 } 55 }
58 56
@@ -60,62 +58,48 @@ async function broadcastToFollowers (
60 58
61 const jobPayload: ActivityPubHttpPayload = { 59 const jobPayload: ActivityPubHttpPayload = {
62 uris, 60 uris,
63 signatureAccountId: byAccount.id, 61 signatureActorId: byActor.id,
64 body: data 62 body: data
65 } 63 }
66 64
67 return activitypubHttpJobScheduler.createJob(t, 'activitypubHttpBroadcastHandler', jobPayload) 65 return activitypubHttpJobScheduler.createJob(t, 'activitypubHttpBroadcastHandler', jobPayload)
68} 66}
69 67
70async function unicastTo (data: any, byAccount: AccountModel, toAccountUrl: string, t: Transaction) { 68async function unicastTo (data: any, byActor: ActorModel, toActorUrl: string, t: Transaction) {
71 logger.debug('Creating unicast job.', { uri: toAccountUrl }) 69 logger.debug('Creating unicast job.', { uri: toActorUrl })
72 70
73 const jobPayload: ActivityPubHttpPayload = { 71 const jobPayload: ActivityPubHttpPayload = {
74 uris: [ toAccountUrl ], 72 uris: [ toActorUrl ],
75 signatureAccountId: byAccount.id, 73 signatureActorId: byActor.id,
76 body: data 74 body: data
77 } 75 }
78 76
79 return activitypubHttpJobScheduler.createJob(t, 'activitypubHttpUnicastHandler', jobPayload) 77 return activitypubHttpJobScheduler.createJob(t, 'activitypubHttpUnicastHandler', jobPayload)
80} 78}
81 79
82function getOriginVideoAudience (video: VideoModel, accountsInvolvedInVideo: AccountModel[]) { 80function getOriginVideoAudience (video: VideoModel, actorsInvolvedInVideo: ActorModel[]) {
83 return { 81 return {
84 to: [ video.VideoChannel.Account.url ], 82 to: [ video.VideoChannel.Account.Actor.url ],
85 cc: accountsInvolvedInVideo.map(a => a.followersUrl) 83 cc: actorsInvolvedInVideo.map(a => a.followersUrl)
86 } 84 }
87} 85}
88 86
89function getOriginVideoChannelAudience (videoChannel: VideoChannelModel, accountsInvolved: AccountModel[]) { 87function getObjectFollowersAudience (actorsInvolvedInObject: ActorModel[]) {
90 return { 88 return {
91 to: [ videoChannel.Account.url ], 89 to: actorsInvolvedInObject.map(a => a.followersUrl),
92 cc: accountsInvolved.map(a => a.followersUrl)
93 }
94}
95
96function getObjectFollowersAudience (accountsInvolvedInObject: AccountModel[]) {
97 return {
98 to: accountsInvolvedInObject.map(a => a.followersUrl),
99 cc: [] 90 cc: []
100 } 91 }
101} 92}
102 93
103async function getAccountsInvolvedInVideo (video: VideoModel, t: Transaction) { 94async function getActorsInvolvedInVideo (video: VideoModel, t: Transaction) {
104 const accountsToForwardView = await VideoShareModel.loadAccountsByShare(video.id, t) 95 const actorsToForwardView = await VideoShareModel.loadActorsByShare(video.id, t)
105 accountsToForwardView.push(video.VideoChannel.Account) 96 actorsToForwardView.push(video.VideoChannel.Account.Actor)
106
107 return accountsToForwardView
108}
109
110async function getAccountsInvolvedInVideoChannel (videoChannel: VideoChannelModel, t: Transaction) {
111 const accountsToForwardView = await VideoChannelShareModel.loadAccountsByShare(videoChannel.id, t)
112 accountsToForwardView.push(videoChannel.Account)
113 97
114 return accountsToForwardView 98 return actorsToForwardView
115} 99}
116 100
117async function getAudience (accountSender: AccountModel, t: Transaction, isPublic = true) { 101async function getAudience (actorSender: ActorModel, t: Transaction, isPublic = true) {
118 const followerInboxUrls = await accountSender.getFollowerSharedInboxUrls(t) 102 const followerInboxUrls = await actorSender.getFollowerSharedInboxUrls(t)
119 103
120 // Thanks Mastodon: https://github.com/tootsuite/mastodon/blob/master/app/lib/activitypub/tag_manager.rb#L47 104 // Thanks Mastodon: https://github.com/tootsuite/mastodon/blob/master/app/lib/activitypub/tag_manager.rb#L47
121 let to = [] 105 let to = []
@@ -132,10 +116,10 @@ async function getAudience (accountSender: AccountModel, t: Transaction, isPubli
132 return { to, cc } 116 return { to, cc }
133} 117}
134 118
135async function computeFollowerUris (toAccountFollower: AccountModel[], followersException: AccountModel[], t: Transaction) { 119async function computeFollowerUris (toActorFollower: ActorModel[], followersException: ActorModel[], t: Transaction) {
136 const toAccountFollowerIds = toAccountFollower.map(a => a.id) 120 const toActorFollowerIds = toActorFollower.map(a => a.id)
137 121
138 const result = await AccountFollowModel.listAcceptedFollowerSharedInboxUrls(toAccountFollowerIds, t) 122 const result = await ActorFollowModel.listAcceptedFollowerSharedInboxUrls(toActorFollowerIds, t)
139 const followersSharedInboxException = followersException.map(f => f.sharedInboxUrl) 123 const followersSharedInboxException = followersException.map(f => f.sharedInboxUrl)
140 return result.data.filter(sharedInbox => followersSharedInboxException.indexOf(sharedInbox) === -1) 124 return result.data.filter(sharedInbox => followersSharedInboxException.indexOf(sharedInbox) === -1)
141} 125}
@@ -144,12 +128,10 @@ async function computeFollowerUris (toAccountFollower: AccountModel[], followers
144 128
145export { 129export {
146 broadcastToFollowers, 130 broadcastToFollowers,
147 getOriginVideoChannelAudience,
148 unicastTo, 131 unicastTo,
149 getAudience, 132 getAudience,
150 getOriginVideoAudience, 133 getOriginVideoAudience,
151 getAccountsInvolvedInVideo, 134 getActorsInvolvedInVideo,
152 getAccountsInvolvedInVideoChannel,
153 getObjectFollowersAudience, 135 getObjectFollowersAudience,
154 forwardActivity 136 forwardActivity
155} 137}
diff --git a/server/lib/activitypub/send/send-accept.ts b/server/lib/activitypub/send/send-accept.ts
index f160af3c9..7579884a7 100644
--- a/server/lib/activitypub/send/send-accept.ts
+++ b/server/lib/activitypub/send/send-accept.ts
@@ -1,15 +1,15 @@
1import { Transaction } from 'sequelize' 1import { Transaction } from 'sequelize'
2import { ActivityAccept } from '../../../../shared/models/activitypub' 2import { ActivityAccept } from '../../../../shared/models/activitypub'
3import { AccountModel } from '../../../models/account/account' 3import { ActorModel } from '../../../models/activitypub/actor'
4import { AccountFollowModel } from '../../../models/account/account-follow' 4import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
5import { getAccountFollowAcceptActivityPubUrl } from '../url' 5import { getActorFollowAcceptActivityPubUrl } from '../url'
6import { unicastTo } from './misc' 6import { unicastTo } from './misc'
7 7
8async function sendAccept (accountFollow: AccountFollowModel, t: Transaction) { 8async function sendAccept (actorFollow: ActorFollowModel, t: Transaction) {
9 const follower = accountFollow.AccountFollower 9 const follower = actorFollow.ActorFollower
10 const me = accountFollow.AccountFollowing 10 const me = actorFollow.ActorFollowing
11 11
12 const url = getAccountFollowAcceptActivityPubUrl(accountFollow) 12 const url = getActorFollowAcceptActivityPubUrl(actorFollow)
13 const data = acceptActivityData(url, me) 13 const data = acceptActivityData(url, me)
14 14
15 return unicastTo(data, me, follower.inboxUrl, t) 15 return unicastTo(data, me, follower.inboxUrl, t)
@@ -23,12 +23,10 @@ export {
23 23
24// --------------------------------------------------------------------------- 24// ---------------------------------------------------------------------------
25 25
26function acceptActivityData (url: string, byAccount: AccountModel) { 26function acceptActivityData (url: string, byActor: ActorModel): ActivityAccept {
27 const activity: ActivityAccept = { 27 return {
28 type: 'Accept', 28 type: 'Accept',
29 id: url, 29 id: url,
30 actor: byAccount.url 30 actor: byActor.url
31 } 31 }
32
33 return activity
34} 32}
diff --git a/server/lib/activitypub/send/send-add.ts b/server/lib/activitypub/send/send-add.ts
deleted file mode 100644
index fd614db75..000000000
--- a/server/lib/activitypub/send/send-add.ts
+++ /dev/null
@@ -1,45 +0,0 @@
1import { Transaction } from 'sequelize'
2import { ActivityAdd } from '../../../../shared/models/activitypub'
3import { VideoPrivacy } from '../../../../shared/models/videos'
4import { AccountModel } from '../../../models/account/account'
5import { VideoModel } from '../../../models/video/video'
6import { broadcastToFollowers, getAudience } from './misc'
7
8async function sendAddVideo (video: VideoModel, t: Transaction) {
9 const byAccount = video.VideoChannel.Account
10
11 const videoObject = video.toActivityPubObject()
12 const data = await addActivityData(video.url, byAccount, video, video.VideoChannel.url, videoObject, t)
13
14 return broadcastToFollowers(data, byAccount, [ byAccount ], t)
15}
16
17async function addActivityData (
18 url: string,
19 byAccount: AccountModel,
20 video: VideoModel,
21 target: string,
22 object: any,
23 t: Transaction
24): Promise<ActivityAdd> {
25 const videoPublic = video.privacy === VideoPrivacy.PUBLIC
26
27 const { to, cc } = await getAudience(byAccount, t, videoPublic)
28
29 return {
30 type: 'Add',
31 id: url,
32 actor: byAccount.url,
33 to,
34 cc,
35 object,
36 target
37 }
38}
39
40// ---------------------------------------------------------------------------
41
42export {
43 addActivityData,
44 sendAddVideo
45}
diff --git a/server/lib/activitypub/send/send-announce.ts b/server/lib/activitypub/send/send-announce.ts
index e685323e8..578fbc630 100644
--- a/server/lib/activitypub/send/send-announce.ts
+++ b/server/lib/activitypub/send/send-announce.ts
@@ -1,88 +1,59 @@
1import { Transaction } from 'sequelize' 1import { Transaction } from 'sequelize'
2import { ActivityAdd } from '../../../../shared/index'
3import { ActivityAnnounce, ActivityAudience, ActivityCreate } from '../../../../shared/models/activitypub' 2import { ActivityAnnounce, ActivityAudience, ActivityCreate } from '../../../../shared/models/activitypub'
4import { AccountModel } from '../../../models/account/account' 3import { VideoPrivacy } from '../../../../shared/models/videos'
4import { ActorModel } from '../../../models/activitypub/actor'
5import { VideoModel } from '../../../models/video/video' 5import { VideoModel } from '../../../models/video/video'
6import { VideoChannelModel } from '../../../models/video/video-channel'
7import { getAnnounceActivityPubUrl } from '../url' 6import { getAnnounceActivityPubUrl } from '../url'
8import { 7import {
9 broadcastToFollowers, 8 broadcastToFollowers,
10 getAccountsInvolvedInVideo, 9 getActorsInvolvedInVideo,
11 getAccountsInvolvedInVideoChannel,
12 getAudience, 10 getAudience,
13 getObjectFollowersAudience, 11 getObjectFollowersAudience,
14 getOriginVideoAudience, 12 getOriginVideoAudience,
15 getOriginVideoChannelAudience,
16 unicastTo 13 unicastTo
17} from './misc' 14} from './misc'
18import { addActivityData } from './send-add'
19import { createActivityData } from './send-create' 15import { createActivityData } from './send-create'
20 16
21async function buildVideoAnnounceToFollowers (byAccount: AccountModel, video: VideoModel, t: Transaction) { 17async function buildVideoAnnounceToFollowers (byActor: ActorModel, video: VideoModel, t: Transaction) {
22 const url = getAnnounceActivityPubUrl(video.url, byAccount) 18 const url = getAnnounceActivityPubUrl(video.url, byActor)
19 const videoObject = video.toActivityPubObject()
23 20
24 const videoChannel = video.VideoChannel 21 const announcedAudience = await getAudience(byActor, t, video.privacy === VideoPrivacy.PUBLIC)
25 const announcedActivity = await addActivityData(url, videoChannel.Account, video, videoChannel.url, video.toActivityPubObject(), t) 22 const announcedActivity = await createActivityData(url, video.VideoChannel.Account.Actor, videoObject, t, announcedAudience)
26 23
27 const accountsToForwardView = await getAccountsInvolvedInVideo(video, t) 24 const accountsToForwardView = await getActorsInvolvedInVideo(video, t)
28 const audience = getObjectFollowersAudience(accountsToForwardView) 25 const audience = getObjectFollowersAudience(accountsToForwardView)
29 return announceActivityData(url, byAccount, announcedActivity, t, audience) 26 return announceActivityData(url, byActor, announcedActivity, t, audience)
30} 27}
31 28
32async function sendVideoAnnounceToFollowers (byAccount: AccountModel, video: VideoModel, t: Transaction) { 29async function sendVideoAnnounceToFollowers (byActor: ActorModel, video: VideoModel, t: Transaction) {
33 const data = await buildVideoAnnounceToFollowers(byAccount, video, t) 30 const data = await buildVideoAnnounceToFollowers(byActor, video, t)
34 31
35 return broadcastToFollowers(data, byAccount, [ byAccount ], t) 32 return broadcastToFollowers(data, byActor, [ byActor ], t)
36} 33}
37 34
38async function sendVideoAnnounceToOrigin (byAccount: AccountModel, video: VideoModel, t: Transaction) { 35async function sendVideoAnnounceToOrigin (byActor: ActorModel, video: VideoModel, t: Transaction) {
39 const url = getAnnounceActivityPubUrl(video.url, byAccount) 36 const url = getAnnounceActivityPubUrl(video.url, byActor)
40 37
41 const videoChannel = video.VideoChannel 38 const videoObject = video.toActivityPubObject()
42 const announcedActivity = await addActivityData(url, videoChannel.Account, video, videoChannel.url, video.toActivityPubObject(), t) 39 const announcedActivity = await createActivityData(url, video.VideoChannel.Account.Actor, videoObject, t)
43 40
44 const accountsInvolvedInVideo = await getAccountsInvolvedInVideo(video, t) 41 const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t)
45 const audience = getOriginVideoAudience(video, accountsInvolvedInVideo) 42 const audience = getOriginVideoAudience(video, actorsInvolvedInVideo)
46 const data = await createActivityData(url, byAccount, announcedActivity, t, audience) 43 const data = await createActivityData(url, byActor, announcedActivity, t, audience)
47 44
48 return unicastTo(data, byAccount, videoChannel.Account.sharedInboxUrl, t) 45 return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl, t)
49}
50
51async function buildVideoChannelAnnounceToFollowers (byAccount: AccountModel, videoChannel: VideoChannelModel, t: Transaction) {
52 const url = getAnnounceActivityPubUrl(videoChannel.url, byAccount)
53 const announcedActivity = await createActivityData(url, videoChannel.Account, videoChannel.toActivityPubObject(), t)
54
55 const accountsToForwardView = await getAccountsInvolvedInVideoChannel(videoChannel, t)
56 const audience = getObjectFollowersAudience(accountsToForwardView)
57 return announceActivityData(url, byAccount, announcedActivity, t, audience)
58}
59
60async function sendVideoChannelAnnounceToFollowers (byAccount: AccountModel, videoChannel: VideoChannelModel, t: Transaction) {
61 const data = await buildVideoChannelAnnounceToFollowers(byAccount, videoChannel, t)
62
63 return broadcastToFollowers(data, byAccount, [ byAccount ], t)
64}
65
66async function sendVideoChannelAnnounceToOrigin (byAccount: AccountModel, videoChannel: VideoChannelModel, t: Transaction) {
67 const url = getAnnounceActivityPubUrl(videoChannel.url, byAccount)
68 const announcedActivity = await createActivityData(url, videoChannel.Account, videoChannel.toActivityPubObject(), t)
69
70 const accountsInvolvedInVideo = await getAccountsInvolvedInVideoChannel(videoChannel, t)
71 const audience = getOriginVideoChannelAudience(videoChannel, accountsInvolvedInVideo)
72 const data = await createActivityData(url, byAccount, announcedActivity, t, audience)
73
74 return unicastTo(data, byAccount, videoChannel.Account.sharedInboxUrl, t)
75} 46}
76 47
77async function announceActivityData ( 48async function announceActivityData (
78 url: string, 49 url: string,
79 byAccount: AccountModel, 50 byActor: ActorModel,
80 object: ActivityCreate | ActivityAdd, 51 object: ActivityCreate,
81 t: Transaction, 52 t: Transaction,
82 audience?: ActivityAudience 53 audience?: ActivityAudience
83): Promise<ActivityAnnounce> { 54): Promise<ActivityAnnounce> {
84 if (!audience) { 55 if (!audience) {
85 audience = await getAudience(byAccount, t) 56 audience = await getAudience(byActor, t)
86 } 57 }
87 58
88 return { 59 return {
@@ -90,7 +61,7 @@ async function announceActivityData (
90 to: audience.to, 61 to: audience.to,
91 cc: audience.cc, 62 cc: audience.cc,
92 id: url, 63 id: url,
93 actor: byAccount.url, 64 actor: byActor.url,
94 object 65 object
95 } 66 }
96} 67}
@@ -99,10 +70,7 @@ async function announceActivityData (
99 70
100export { 71export {
101 sendVideoAnnounceToFollowers, 72 sendVideoAnnounceToFollowers,
102 sendVideoChannelAnnounceToFollowers,
103 sendVideoAnnounceToOrigin, 73 sendVideoAnnounceToOrigin,
104 sendVideoChannelAnnounceToOrigin,
105 announceActivityData, 74 announceActivityData,
106 buildVideoAnnounceToFollowers, 75 buildVideoAnnounceToFollowers
107 buildVideoChannelAnnounceToFollowers
108} 76}
diff --git a/server/lib/activitypub/send/send-create.ts b/server/lib/activitypub/send/send-create.ts
index 9fbaa8196..d26c24838 100644
--- a/server/lib/activitypub/send/send-create.ts
+++ b/server/lib/activitypub/send/send-create.ts
@@ -1,111 +1,112 @@
1import { Transaction } from 'sequelize' 1import { Transaction } from 'sequelize'
2import { ActivityAudience, ActivityCreate } from '../../../../shared/models/activitypub' 2import { ActivityAudience, ActivityCreate } from '../../../../shared/models/activitypub'
3import { getServerAccount } from '../../../helpers' 3import { VideoPrivacy } from '../../../../shared/models/videos'
4import { AccountModel } from '../../../models/account/account' 4import { getServerActor } from '../../../helpers'
5import { ActorModel } from '../../../models/activitypub/actor'
5import { VideoModel } from '../../../models/video/video' 6import { VideoModel } from '../../../models/video/video'
6import { VideoAbuseModel } from '../../../models/video/video-abuse' 7import { VideoAbuseModel } from '../../../models/video/video-abuse'
7import { VideoChannelModel } from '../../../models/video/video-channel'
8import { getVideoAbuseActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoViewActivityPubUrl } from '../url' 8import { getVideoAbuseActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoViewActivityPubUrl } from '../url'
9import { 9import {
10 broadcastToFollowers, 10 broadcastToFollowers,
11 getAccountsInvolvedInVideo, 11 getActorsInvolvedInVideo,
12 getAudience, 12 getAudience,
13 getObjectFollowersAudience, 13 getObjectFollowersAudience,
14 getOriginVideoAudience, 14 getOriginVideoAudience,
15 unicastTo 15 unicastTo
16} from './misc' 16} from './misc'
17 17
18async function sendCreateVideoChannel (videoChannel: VideoChannelModel, t: Transaction) { 18async function sendCreateVideo (video: VideoModel, t: Transaction) {
19 const byAccount = videoChannel.Account 19 const byActor = video.VideoChannel.Account.Actor
20 20
21 const videoChannelObject = videoChannel.toActivityPubObject() 21 const videoObject = video.toActivityPubObject()
22 const data = await createActivityData(videoChannel.url, byAccount, videoChannelObject, t) 22 const audience = await getAudience(byActor, t, video.privacy === VideoPrivacy.PUBLIC)
23 const data = await createActivityData(video.url, byActor, videoObject, t, audience)
23 24
24 return broadcastToFollowers(data, byAccount, [ byAccount ], t) 25 return broadcastToFollowers(data, byActor, [ byActor ], t)
25} 26}
26 27
27async function sendVideoAbuse (byAccount: AccountModel, videoAbuse: VideoAbuseModel, video: VideoModel, t: Transaction) { 28async function sendVideoAbuse (byActor: ActorModel, videoAbuse: VideoAbuseModel, video: VideoModel, t: Transaction) {
28 const url = getVideoAbuseActivityPubUrl(videoAbuse) 29 const url = getVideoAbuseActivityPubUrl(videoAbuse)
29 30
30 const audience = { to: [ video.VideoChannel.Account.url ], cc: [] } 31 const audience = { to: [ video.VideoChannel.Account.Actor.url ], cc: [] }
31 const data = await createActivityData(url, byAccount, videoAbuse.toActivityPubObject(), t, audience) 32 const data = await createActivityData(url, byActor, videoAbuse.toActivityPubObject(), t, audience)
32 33
33 return unicastTo(data, byAccount, video.VideoChannel.Account.sharedInboxUrl, t) 34 return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl, t)
34} 35}
35 36
36async function sendCreateViewToOrigin (byAccount: AccountModel, video: VideoModel, t: Transaction) { 37async function sendCreateViewToOrigin (byActor: ActorModel, video: VideoModel, t: Transaction) {
37 const url = getVideoViewActivityPubUrl(byAccount, video) 38 const url = getVideoViewActivityPubUrl(byActor, video)
38 const viewActivity = createViewActivityData(byAccount, video) 39 const viewActivity = createViewActivityData(byActor, video)
39 40
40 const accountsInvolvedInVideo = await getAccountsInvolvedInVideo(video, t) 41 const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t)
41 const audience = getOriginVideoAudience(video, accountsInvolvedInVideo) 42 const audience = getOriginVideoAudience(video, actorsInvolvedInVideo)
42 const data = await createActivityData(url, byAccount, viewActivity, t, audience) 43 const data = await createActivityData(url, byActor, viewActivity, t, audience)
43 44
44 return unicastTo(data, byAccount, video.VideoChannel.Account.sharedInboxUrl, t) 45 return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl, t)
45} 46}
46 47
47async function sendCreateViewToVideoFollowers (byAccount: AccountModel, video: VideoModel, t: Transaction) { 48async function sendCreateViewToVideoFollowers (byActor: ActorModel, video: VideoModel, t: Transaction) {
48 const url = getVideoViewActivityPubUrl(byAccount, video) 49 const url = getVideoViewActivityPubUrl(byActor, video)
49 const viewActivity = createViewActivityData(byAccount, video) 50 const viewActivity = createViewActivityData(byActor, video)
50 51
51 const accountsToForwardView = await getAccountsInvolvedInVideo(video, t) 52 const actorsToForwardView = await getActorsInvolvedInVideo(video, t)
52 const audience = getObjectFollowersAudience(accountsToForwardView) 53 const audience = getObjectFollowersAudience(actorsToForwardView)
53 const data = await createActivityData(url, byAccount, viewActivity, t, audience) 54 const data = await createActivityData(url, byActor, viewActivity, t, audience)
54 55
55 // Use the server account to send the view, because it could be an unregistered account 56 // Use the server actor to send the view
56 const serverAccount = await getServerAccount() 57 const serverActor = await getServerActor()
57 const followersException = [ byAccount ] 58 const followersException = [ byActor ]
58 return broadcastToFollowers(data, serverAccount, accountsToForwardView, t, followersException) 59 return broadcastToFollowers(data, serverActor, actorsToForwardView, t, followersException)
59} 60}
60 61
61async function sendCreateDislikeToOrigin (byAccount: AccountModel, video: VideoModel, t: Transaction) { 62async function sendCreateDislikeToOrigin (byActor: ActorModel, video: VideoModel, t: Transaction) {
62 const url = getVideoDislikeActivityPubUrl(byAccount, video) 63 const url = getVideoDislikeActivityPubUrl(byActor, video)
63 const dislikeActivity = createDislikeActivityData(byAccount, video) 64 const dislikeActivity = createDislikeActivityData(byActor, video)
64 65
65 const accountsInvolvedInVideo = await getAccountsInvolvedInVideo(video, t) 66 const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t)
66 const audience = getOriginVideoAudience(video, accountsInvolvedInVideo) 67 const audience = getOriginVideoAudience(video, actorsInvolvedInVideo)
67 const data = await createActivityData(url, byAccount, dislikeActivity, t, audience) 68 const data = await createActivityData(url, byActor, dislikeActivity, t, audience)
68 69
69 return unicastTo(data, byAccount, video.VideoChannel.Account.sharedInboxUrl, t) 70 return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl, t)
70} 71}
71 72
72async function sendCreateDislikeToVideoFollowers (byAccount: AccountModel, video: VideoModel, t: Transaction) { 73async function sendCreateDislikeToVideoFollowers (byActor: ActorModel, video: VideoModel, t: Transaction) {
73 const url = getVideoDislikeActivityPubUrl(byAccount, video) 74 const url = getVideoDislikeActivityPubUrl(byActor, video)
74 const dislikeActivity = createDislikeActivityData(byAccount, video) 75 const dislikeActivity = createDislikeActivityData(byActor, video)
75 76
76 const accountsToForwardView = await getAccountsInvolvedInVideo(video, t) 77 const actorsToForwardView = await getActorsInvolvedInVideo(video, t)
77 const audience = getObjectFollowersAudience(accountsToForwardView) 78 const audience = getObjectFollowersAudience(actorsToForwardView)
78 const data = await createActivityData(url, byAccount, dislikeActivity, t, audience) 79 const data = await createActivityData(url, byActor, dislikeActivity, t, audience)
79 80
80 const followersException = [ byAccount ] 81 const followersException = [ byActor ]
81 return broadcastToFollowers(data, byAccount, accountsToForwardView, t, followersException) 82 return broadcastToFollowers(data, byActor, actorsToForwardView, t, followersException)
82} 83}
83 84
84async function createActivityData ( 85async function createActivityData (
85 url: string, 86 url: string,
86 byAccount: AccountModel, 87 byActor: ActorModel,
87 object: any, 88 object: any,
88 t: Transaction, 89 t: Transaction,
89 audience?: ActivityAudience 90 audience?: ActivityAudience
90): Promise<ActivityCreate> { 91): Promise<ActivityCreate> {
91 if (!audience) { 92 if (!audience) {
92 audience = await getAudience(byAccount, t) 93 audience = await getAudience(byActor, t)
93 } 94 }
94 95
95 return { 96 return {
96 type: 'Create', 97 type: 'Create',
97 id: url, 98 id: url,
98 actor: byAccount.url, 99 actor: byActor.url,
99 to: audience.to, 100 to: audience.to,
100 cc: audience.cc, 101 cc: audience.cc,
101 object 102 object
102 } 103 }
103} 104}
104 105
105function createDislikeActivityData (byAccount: AccountModel, video: VideoModel) { 106function createDislikeActivityData (byActor: ActorModel, video: VideoModel) {
106 return { 107 return {
107 type: 'Dislike', 108 type: 'Dislike',
108 actor: byAccount.url, 109 actor: byActor.url,
109 object: video.url 110 object: video.url
110 } 111 }
111} 112}
@@ -113,7 +114,7 @@ function createDislikeActivityData (byAccount: AccountModel, video: VideoModel)
113// --------------------------------------------------------------------------- 114// ---------------------------------------------------------------------------
114 115
115export { 116export {
116 sendCreateVideoChannel, 117 sendCreateVideo,
117 sendVideoAbuse, 118 sendVideoAbuse,
118 createActivityData, 119 createActivityData,
119 sendCreateViewToOrigin, 120 sendCreateViewToOrigin,
@@ -125,10 +126,10 @@ export {
125 126
126// --------------------------------------------------------------------------- 127// ---------------------------------------------------------------------------
127 128
128function createViewActivityData (byAccount: AccountModel, video: VideoModel) { 129function createViewActivityData (byActor: ActorModel, video: VideoModel) {
129 return { 130 return {
130 type: 'View', 131 type: 'View',
131 actor: byAccount.url, 132 actor: byActor.url,
132 object: video.url 133 object: video.url
133 } 134 }
134} 135}
diff --git a/server/lib/activitypub/send/send-delete.ts b/server/lib/activitypub/send/send-delete.ts
index 0a45ea10f..4bc5db77e 100644
--- a/server/lib/activitypub/send/send-delete.ts
+++ b/server/lib/activitypub/send/send-delete.ts
@@ -1,54 +1,40 @@
1import { Transaction } from 'sequelize' 1import { Transaction } from 'sequelize'
2import { ActivityDelete } from '../../../../shared/models/activitypub' 2import { ActivityDelete } from '../../../../shared/models/activitypub'
3import { AccountModel } from '../../../models/account/account' 3import { ActorModel } from '../../../models/activitypub/actor'
4import { VideoModel } from '../../../models/video/video' 4import { VideoModel } from '../../../models/video/video'
5import { VideoChannelModel } from '../../../models/video/video-channel'
6import { VideoChannelShareModel } from '../../../models/video/video-channel-share'
7import { VideoShareModel } from '../../../models/video/video-share' 5import { VideoShareModel } from '../../../models/video/video-share'
8import { broadcastToFollowers } from './misc' 6import { broadcastToFollowers } from './misc'
9 7
10async function sendDeleteVideoChannel (videoChannel: VideoChannelModel, t: Transaction) {
11 const byAccount = videoChannel.Account
12
13 const data = deleteActivityData(videoChannel.url, byAccount)
14
15 const accountsInvolved = await VideoChannelShareModel.loadAccountsByShare(videoChannel.id, t)
16 accountsInvolved.push(byAccount)
17
18 return broadcastToFollowers(data, byAccount, accountsInvolved, t)
19}
20
21async function sendDeleteVideo (video: VideoModel, t: Transaction) { 8async function sendDeleteVideo (video: VideoModel, t: Transaction) {
22 const byAccount = video.VideoChannel.Account 9 const byActor = video.VideoChannel.Account.Actor
23 10
24 const data = deleteActivityData(video.url, byAccount) 11 const data = deleteActivityData(video.url, byActor)
25 12
26 const accountsInvolved = await VideoShareModel.loadAccountsByShare(video.id, t) 13 const actorsInvolved = await VideoShareModel.loadActorsByShare(video.id, t)
27 accountsInvolved.push(byAccount) 14 actorsInvolved.push(byActor)
28 15
29 return broadcastToFollowers(data, byAccount, accountsInvolved, t) 16 return broadcastToFollowers(data, byActor, actorsInvolved, t)
30} 17}
31 18
32async function sendDeleteAccount (account: AccountModel, t: Transaction) { 19async function sendDeleteActor (byActor: ActorModel, t: Transaction) {
33 const data = deleteActivityData(account.url, account) 20 const data = deleteActivityData(byActor.url, byActor)
34 21
35 return broadcastToFollowers(data, account, [ account ], t) 22 return broadcastToFollowers(data, byActor, [ byActor ], t)
36} 23}
37 24
38// --------------------------------------------------------------------------- 25// ---------------------------------------------------------------------------
39 26
40export { 27export {
41 sendDeleteVideoChannel,
42 sendDeleteVideo, 28 sendDeleteVideo,
43 sendDeleteAccount 29 sendDeleteActor
44} 30}
45 31
46// --------------------------------------------------------------------------- 32// ---------------------------------------------------------------------------
47 33
48function deleteActivityData (url: string, byAccount: AccountModel): ActivityDelete { 34function deleteActivityData (url: string, byActor: ActorModel): ActivityDelete {
49 return { 35 return {
50 type: 'Delete', 36 type: 'Delete',
51 id: url, 37 id: url,
52 actor: byAccount.url 38 actor: byActor.url
53 } 39 }
54} 40}
diff --git a/server/lib/activitypub/send/send-follow.ts b/server/lib/activitypub/send/send-follow.ts
index 51735ddfd..eac60e94f 100644
--- a/server/lib/activitypub/send/send-follow.ts
+++ b/server/lib/activitypub/send/send-follow.ts
@@ -1,26 +1,26 @@
1import { Transaction } from 'sequelize' 1import { Transaction } from 'sequelize'
2import { ActivityFollow } from '../../../../shared/models/activitypub' 2import { ActivityFollow } from '../../../../shared/models/activitypub'
3import { AccountModel } from '../../../models/account/account' 3import { ActorModel } from '../../../models/activitypub/actor'
4import { AccountFollowModel } from '../../../models/account/account-follow' 4import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
5import { getAccountFollowActivityPubUrl } from '../url' 5import { getActorFollowActivityPubUrl } from '../url'
6import { unicastTo } from './misc' 6import { unicastTo } from './misc'
7 7
8function sendFollow (accountFollow: AccountFollowModel, t: Transaction) { 8function sendFollow (actorFollow: ActorFollowModel, t: Transaction) {
9 const me = accountFollow.AccountFollower 9 const me = actorFollow.ActorFollower
10 const following = accountFollow.AccountFollowing 10 const following = actorFollow.ActorFollowing
11 11
12 const url = getAccountFollowActivityPubUrl(accountFollow) 12 const url = getActorFollowActivityPubUrl(actorFollow)
13 const data = followActivityData(url, me, following) 13 const data = followActivityData(url, me, following)
14 14
15 return unicastTo(data, me, following.inboxUrl, t) 15 return unicastTo(data, me, following.inboxUrl, t)
16} 16}
17 17
18function followActivityData (url: string, byAccount: AccountModel, targetAccount: AccountModel): ActivityFollow { 18function followActivityData (url: string, byActor: ActorModel, targetActor: ActorModel): ActivityFollow {
19 return { 19 return {
20 type: 'Follow', 20 type: 'Follow',
21 id: url, 21 id: url,
22 actor: byAccount.url, 22 actor: byActor.url,
23 object: targetAccount.url 23 object: targetActor.url
24 } 24 }
25} 25}
26 26
diff --git a/server/lib/activitypub/send/send-like.ts b/server/lib/activitypub/send/send-like.ts
index 1a35d0db0..7e0c73796 100644
--- a/server/lib/activitypub/send/send-like.ts
+++ b/server/lib/activitypub/send/send-like.ts
@@ -1,53 +1,53 @@
1import { Transaction } from 'sequelize' 1import { Transaction } from 'sequelize'
2import { ActivityAudience, ActivityLike } from '../../../../shared/models/activitypub' 2import { ActivityAudience, ActivityLike } from '../../../../shared/models/activitypub'
3import { AccountModel } from '../../../models/account/account' 3import { ActorModel } from '../../../models/activitypub/actor'
4import { VideoModel } from '../../../models/video/video' 4import { VideoModel } from '../../../models/video/video'
5import { getVideoLikeActivityPubUrl } from '../url' 5import { getVideoLikeActivityPubUrl } from '../url'
6import { 6import {
7 broadcastToFollowers, 7 broadcastToFollowers,
8 getAccountsInvolvedInVideo, 8 getActorsInvolvedInVideo,
9 getAudience, 9 getAudience,
10 getOriginVideoAudience,
11 getObjectFollowersAudience, 10 getObjectFollowersAudience,
11 getOriginVideoAudience,
12 unicastTo 12 unicastTo
13} from './misc' 13} from './misc'
14 14
15async function sendLikeToOrigin (byAccount: AccountModel, video: VideoModel, t: Transaction) { 15async function sendLikeToOrigin (byActor: ActorModel, video: VideoModel, t: Transaction) {
16 const url = getVideoLikeActivityPubUrl(byAccount, video) 16 const url = getVideoLikeActivityPubUrl(byActor, video)
17 17
18 const accountsInvolvedInVideo = await getAccountsInvolvedInVideo(video, t) 18 const accountsInvolvedInVideo = await getActorsInvolvedInVideo(video, t)
19 const audience = getOriginVideoAudience(video, accountsInvolvedInVideo) 19 const audience = getOriginVideoAudience(video, accountsInvolvedInVideo)
20 const data = await likeActivityData(url, byAccount, video, t, audience) 20 const data = await likeActivityData(url, byActor, video, t, audience)
21 21
22 return unicastTo(data, byAccount, video.VideoChannel.Account.sharedInboxUrl, t) 22 return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl, t)
23} 23}
24 24
25async function sendLikeToVideoFollowers (byAccount: AccountModel, video: VideoModel, t: Transaction) { 25async function sendLikeToVideoFollowers (byActor: ActorModel, video: VideoModel, t: Transaction) {
26 const url = getVideoLikeActivityPubUrl(byAccount, video) 26 const url = getVideoLikeActivityPubUrl(byActor, video)
27 27
28 const accountsInvolvedInVideo = await getAccountsInvolvedInVideo(video, t) 28 const accountsInvolvedInVideo = await getActorsInvolvedInVideo(video, t)
29 const audience = getObjectFollowersAudience(accountsInvolvedInVideo) 29 const audience = getObjectFollowersAudience(accountsInvolvedInVideo)
30 const data = await likeActivityData(url, byAccount, video, t, audience) 30 const data = await likeActivityData(url, byActor, video, t, audience)
31 31
32 const followersException = [ byAccount ] 32 const followersException = [ byActor ]
33 return broadcastToFollowers(data, byAccount, accountsInvolvedInVideo, t, followersException) 33 return broadcastToFollowers(data, byActor, accountsInvolvedInVideo, t, followersException)
34} 34}
35 35
36async function likeActivityData ( 36async function likeActivityData (
37 url: string, 37 url: string,
38 byAccount: AccountModel, 38 byActor: ActorModel,
39 video: VideoModel, 39 video: VideoModel,
40 t: Transaction, 40 t: Transaction,
41 audience?: ActivityAudience 41 audience?: ActivityAudience
42): Promise<ActivityLike> { 42): Promise<ActivityLike> {
43 if (!audience) { 43 if (!audience) {
44 audience = await getAudience(byAccount, t) 44 audience = await getAudience(byActor, t)
45 } 45 }
46 46
47 return { 47 return {
48 type: 'Like', 48 type: 'Like',
49 id: url, 49 id: url,
50 actor: byAccount.url, 50 actor: byActor.url,
51 to: audience.to, 51 to: audience.to,
52 cc: audience.cc, 52 cc: audience.cc,
53 object: video.url 53 object: video.url
diff --git a/server/lib/activitypub/send/send-undo.ts b/server/lib/activitypub/send/send-undo.ts
index 699f920f0..92271b700 100644
--- a/server/lib/activitypub/send/send-undo.ts
+++ b/server/lib/activitypub/send/send-undo.ts
@@ -6,13 +6,13 @@ import {
6 ActivityLike, 6 ActivityLike,
7 ActivityUndo 7 ActivityUndo
8} from '../../../../shared/models/activitypub' 8} from '../../../../shared/models/activitypub'
9import { AccountModel } from '../../../models/account/account' 9import { ActorModel } from '../../../models/activitypub/actor'
10import { AccountFollowModel } from '../../../models/account/account-follow' 10import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
11import { VideoModel } from '../../../models/video/video' 11import { VideoModel } from '../../../models/video/video'
12import { getAccountFollowActivityPubUrl, getUndoActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoLikeActivityPubUrl } from '../url' 12import { getActorFollowActivityPubUrl, getUndoActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoLikeActivityPubUrl } from '../url'
13import { 13import {
14 broadcastToFollowers, 14 broadcastToFollowers,
15 getAccountsInvolvedInVideo, 15 getActorsInvolvedInVideo,
16 getAudience, 16 getAudience,
17 getObjectFollowersAudience, 17 getObjectFollowersAudience,
18 getOriginVideoAudience, 18 getOriginVideoAudience,
@@ -22,11 +22,11 @@ import { createActivityData, createDislikeActivityData } from './send-create'
22import { followActivityData } from './send-follow' 22import { followActivityData } from './send-follow'
23import { likeActivityData } from './send-like' 23import { likeActivityData } from './send-like'
24 24
25async function sendUndoFollow (accountFollow: AccountFollowModel, t: Transaction) { 25async function sendUndoFollow (actorFollow: ActorFollowModel, t: Transaction) {
26 const me = accountFollow.AccountFollower 26 const me = actorFollow.ActorFollower
27 const following = accountFollow.AccountFollowing 27 const following = actorFollow.ActorFollowing
28 28
29 const followUrl = getAccountFollowActivityPubUrl(accountFollow) 29 const followUrl = getActorFollowActivityPubUrl(actorFollow)
30 const undoUrl = getUndoActivityPubUrl(followUrl) 30 const undoUrl = getUndoActivityPubUrl(followUrl)
31 31
32 const object = followActivityData(followUrl, me, following) 32 const object = followActivityData(followUrl, me, following)
@@ -35,58 +35,58 @@ async function sendUndoFollow (accountFollow: AccountFollowModel, t: Transaction
35 return unicastTo(data, me, following.inboxUrl, t) 35 return unicastTo(data, me, following.inboxUrl, t)
36} 36}
37 37
38async function sendUndoLikeToOrigin (byAccount: AccountModel, video: VideoModel, t: Transaction) { 38async function sendUndoLikeToOrigin (byActor: ActorModel, video: VideoModel, t: Transaction) {
39 const likeUrl = getVideoLikeActivityPubUrl(byAccount, video) 39 const likeUrl = getVideoLikeActivityPubUrl(byActor, video)
40 const undoUrl = getUndoActivityPubUrl(likeUrl) 40 const undoUrl = getUndoActivityPubUrl(likeUrl)
41 41
42 const accountsInvolvedInVideo = await getAccountsInvolvedInVideo(video, t) 42 const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t)
43 const audience = getOriginVideoAudience(video, accountsInvolvedInVideo) 43 const audience = getOriginVideoAudience(video, actorsInvolvedInVideo)
44 const object = await likeActivityData(likeUrl, byAccount, video, t) 44 const object = await likeActivityData(likeUrl, byActor, video, t)
45 const data = await undoActivityData(undoUrl, byAccount, object, t, audience) 45 const data = await undoActivityData(undoUrl, byActor, object, t, audience)
46 46
47 return unicastTo(data, byAccount, video.VideoChannel.Account.sharedInboxUrl, t) 47 return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl, t)
48} 48}
49 49
50async function sendUndoLikeToVideoFollowers (byAccount: AccountModel, video: VideoModel, t: Transaction) { 50async function sendUndoLikeToVideoFollowers (byActor: ActorModel, video: VideoModel, t: Transaction) {
51 const likeUrl = getVideoLikeActivityPubUrl(byAccount, video) 51 const likeUrl = getVideoLikeActivityPubUrl(byActor, video)
52 const undoUrl = getUndoActivityPubUrl(likeUrl) 52 const undoUrl = getUndoActivityPubUrl(likeUrl)
53 53
54 const toAccountsFollowers = await getAccountsInvolvedInVideo(video, t) 54 const toActorsFollowers = await getActorsInvolvedInVideo(video, t)
55 const audience = getObjectFollowersAudience(toAccountsFollowers) 55 const audience = getObjectFollowersAudience(toActorsFollowers)
56 const object = await likeActivityData(likeUrl, byAccount, video, t) 56 const object = await likeActivityData(likeUrl, byActor, video, t)
57 const data = await undoActivityData(undoUrl, byAccount, object, t, audience) 57 const data = await undoActivityData(undoUrl, byActor, object, t, audience)
58 58
59 const followersException = [ byAccount ] 59 const followersException = [ byActor ]
60 return broadcastToFollowers(data, byAccount, toAccountsFollowers, t, followersException) 60 return broadcastToFollowers(data, byActor, toActorsFollowers, t, followersException)
61} 61}
62 62
63async function sendUndoDislikeToOrigin (byAccount: AccountModel, video: VideoModel, t: Transaction) { 63async function sendUndoDislikeToOrigin (byActor: ActorModel, video: VideoModel, t: Transaction) {
64 const dislikeUrl = getVideoDislikeActivityPubUrl(byAccount, video) 64 const dislikeUrl = getVideoDislikeActivityPubUrl(byActor, video)
65 const undoUrl = getUndoActivityPubUrl(dislikeUrl) 65 const undoUrl = getUndoActivityPubUrl(dislikeUrl)
66 66
67 const accountsInvolvedInVideo = await getAccountsInvolvedInVideo(video, t) 67 const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t)
68 const audience = getOriginVideoAudience(video, accountsInvolvedInVideo) 68 const audience = getOriginVideoAudience(video, actorsInvolvedInVideo)
69 const dislikeActivity = createDislikeActivityData(byAccount, video) 69 const dislikeActivity = createDislikeActivityData(byActor, video)
70 const object = await createActivityData(undoUrl, byAccount, dislikeActivity, t) 70 const object = await createActivityData(undoUrl, byActor, dislikeActivity, t)
71 71
72 const data = await undoActivityData(undoUrl, byAccount, object, t, audience) 72 const data = await undoActivityData(undoUrl, byActor, object, t, audience)
73 73
74 return unicastTo(data, byAccount, video.VideoChannel.Account.sharedInboxUrl, t) 74 return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl, t)
75} 75}
76 76
77async function sendUndoDislikeToVideoFollowers (byAccount: AccountModel, video: VideoModel, t: Transaction) { 77async function sendUndoDislikeToVideoFollowers (byActor: ActorModel, video: VideoModel, t: Transaction) {
78 const dislikeUrl = getVideoDislikeActivityPubUrl(byAccount, video) 78 const dislikeUrl = getVideoDislikeActivityPubUrl(byActor, video)
79 const undoUrl = getUndoActivityPubUrl(dislikeUrl) 79 const undoUrl = getUndoActivityPubUrl(dislikeUrl)
80 80
81 const dislikeActivity = createDislikeActivityData(byAccount, video) 81 const dislikeActivity = createDislikeActivityData(byActor, video)
82 const object = await createActivityData(undoUrl, byAccount, dislikeActivity, t) 82 const object = await createActivityData(undoUrl, byActor, dislikeActivity, t)
83 83
84 const data = await undoActivityData(undoUrl, byAccount, object, t) 84 const data = await undoActivityData(undoUrl, byActor, object, t)
85 85
86 const toAccountsFollowers = await getAccountsInvolvedInVideo(video, t) 86 const toActorsFollowers = await getActorsInvolvedInVideo(video, t)
87 87
88 const followersException = [ byAccount ] 88 const followersException = [ byActor ]
89 return broadcastToFollowers(data, byAccount, toAccountsFollowers, t, followersException) 89 return broadcastToFollowers(data, byActor, toActorsFollowers, t, followersException)
90} 90}
91 91
92// --------------------------------------------------------------------------- 92// ---------------------------------------------------------------------------
@@ -103,19 +103,19 @@ export {
103 103
104async function undoActivityData ( 104async function undoActivityData (
105 url: string, 105 url: string,
106 byAccount: AccountModel, 106 byActor: ActorModel,
107 object: ActivityFollow | ActivityLike | ActivityCreate, 107 object: ActivityFollow | ActivityLike | ActivityCreate,
108 t: Transaction, 108 t: Transaction,
109 audience?: ActivityAudience 109 audience?: ActivityAudience
110): Promise<ActivityUndo> { 110): Promise<ActivityUndo> {
111 if (!audience) { 111 if (!audience) {
112 audience = await getAudience(byAccount, t) 112 audience = await getAudience(byActor, t)
113 } 113 }
114 114
115 return { 115 return {
116 type: 'Undo', 116 type: 'Undo',
117 id: url, 117 id: url,
118 actor: byAccount.url, 118 actor: byActor.url,
119 to: audience.to, 119 to: audience.to,
120 cc: audience.cc, 120 cc: audience.cc,
121 object 121 object
diff --git a/server/lib/activitypub/send/send-update.ts b/server/lib/activitypub/send/send-update.ts
index 9baf13a87..48bbbcac1 100644
--- a/server/lib/activitypub/send/send-update.ts
+++ b/server/lib/activitypub/send/send-update.ts
@@ -1,56 +1,52 @@
1import { Transaction } from 'sequelize' 1import { Transaction } from 'sequelize'
2import { ActivityUpdate } from '../../../../shared/models/activitypub' 2import { ActivityAudience, ActivityUpdate } from '../../../../shared/models/activitypub'
3import { AccountModel } from '../../../models/account/account' 3import { VideoPrivacy } from '../../../../shared/models/videos'
4import { ActorModel } from '../../../models/activitypub/actor'
4import { VideoModel } from '../../../models/video/video' 5import { VideoModel } from '../../../models/video/video'
5import { VideoChannelModel } from '../../../models/video/video-channel'
6import { VideoChannelShareModel } from '../../../models/video/video-channel-share'
7import { VideoShareModel } from '../../../models/video/video-share' 6import { VideoShareModel } from '../../../models/video/video-share'
8import { getUpdateActivityPubUrl } from '../url' 7import { getUpdateActivityPubUrl } from '../url'
9import { broadcastToFollowers, getAudience } from './misc' 8import { broadcastToFollowers, getAudience } from './misc'
10 9
11async function sendUpdateVideoChannel (videoChannel: VideoChannelModel, t: Transaction) {
12 const byAccount = videoChannel.Account
13
14 const url = getUpdateActivityPubUrl(videoChannel.url, videoChannel.updatedAt.toISOString())
15 const videoChannelObject = videoChannel.toActivityPubObject()
16 const data = await updateActivityData(url, byAccount, videoChannelObject, t)
17
18 const accountsInvolved = await VideoChannelShareModel.loadAccountsByShare(videoChannel.id, t)
19 accountsInvolved.push(byAccount)
20
21 return broadcastToFollowers(data, byAccount, accountsInvolved, t)
22}
23
24async function sendUpdateVideo (video: VideoModel, t: Transaction) { 10async function sendUpdateVideo (video: VideoModel, t: Transaction) {
25 const byAccount = video.VideoChannel.Account 11 const byActor = video.VideoChannel.Account.Actor
26 12
27 const url = getUpdateActivityPubUrl(video.url, video.updatedAt.toISOString()) 13 const url = getUpdateActivityPubUrl(video.url, video.updatedAt.toISOString())
28 const videoObject = video.toActivityPubObject() 14 const videoObject = video.toActivityPubObject()
29 const data = await updateActivityData(url, byAccount, videoObject, t) 15 const audience = await getAudience(byActor, t, video.privacy === VideoPrivacy.PUBLIC)
16
17 const data = await updateActivityData(url, byActor, videoObject, t, audience)
30 18
31 const accountsInvolved = await VideoShareModel.loadAccountsByShare(video.id, t) 19 const actorsInvolved = await VideoShareModel.loadActorsByShare(video.id, t)
32 accountsInvolved.push(byAccount) 20 actorsInvolved.push(byActor)
33 21
34 return broadcastToFollowers(data, byAccount, accountsInvolved, t) 22 return broadcastToFollowers(data, byActor, actorsInvolved, t)
35} 23}
36 24
37// --------------------------------------------------------------------------- 25// ---------------------------------------------------------------------------
38 26
39export { 27export {
40 sendUpdateVideoChannel,
41 sendUpdateVideo 28 sendUpdateVideo
42} 29}
43 30
44// --------------------------------------------------------------------------- 31// ---------------------------------------------------------------------------
45 32
46async function updateActivityData (url: string, byAccount: AccountModel, object: any, t: Transaction): Promise<ActivityUpdate> { 33async function updateActivityData (
47 const { to, cc } = await getAudience(byAccount, t) 34 url: string,
35 byActor: ActorModel,
36 object: any,
37 t: Transaction,
38 audience?: ActivityAudience
39): Promise<ActivityUpdate> {
40 if (!audience) {
41 audience = await getAudience(byActor, t)
42 }
43
48 return { 44 return {
49 type: 'Update', 45 type: 'Update',
50 id: url, 46 id: url,
51 actor: byAccount.url, 47 actor: byActor.url,
52 to, 48 to: audience.to,
53 cc, 49 cc: audience.cc,
54 object 50 object
55 } 51 }
56} 52}
diff --git a/server/lib/activitypub/share.ts b/server/lib/activitypub/share.ts
index 5bec61c05..f79c4e532 100644
--- a/server/lib/activitypub/share.ts
+++ b/server/lib/activitypub/share.ts
@@ -1,34 +1,20 @@
1import { Transaction } from 'sequelize' 1import { Transaction } from 'sequelize'
2import { getServerAccount } from '../../helpers' 2import { getServerActor } from '../../helpers'
3import { VideoModel } from '../../models/video/video' 3import { VideoModel } from '../../models/video/video'
4import { VideoChannelModel } from '../../models/video/video-channel'
5import { VideoChannelShareModel } from '../../models/video/video-channel-share'
6import { VideoShareModel } from '../../models/video/video-share' 4import { VideoShareModel } from '../../models/video/video-share'
7import { sendVideoAnnounceToFollowers, sendVideoChannelAnnounceToFollowers } from './send' 5import { sendVideoAnnounceToFollowers } from './send'
8
9async function shareVideoChannelByServer (videoChannel: VideoChannelModel, t: Transaction) {
10 const serverAccount = await getServerAccount()
11
12 await VideoChannelShareModel.create({
13 accountId: serverAccount.id,
14 videoChannelId: videoChannel.id
15 }, { transaction: t })
16
17 return sendVideoChannelAnnounceToFollowers(serverAccount, videoChannel, t)
18}
19 6
20async function shareVideoByServer (video: VideoModel, t: Transaction) { 7async function shareVideoByServer (video: VideoModel, t: Transaction) {
21 const serverAccount = await getServerAccount() 8 const serverActor = await getServerActor()
22 9
23 await VideoShareModel.create({ 10 await VideoShareModel.create({
24 accountId: serverAccount.id, 11 actorId: serverActor.id,
25 videoId: video.id 12 videoId: video.id
26 }, { transaction: t }) 13 }, { transaction: t })
27 14
28 return sendVideoAnnounceToFollowers(serverAccount, video, t) 15 return sendVideoAnnounceToFollowers(serverActor, video, t)
29} 16}
30 17
31export { 18export {
32 shareVideoChannelByServer,
33 shareVideoByServer 19 shareVideoByServer
34} 20}
diff --git a/server/lib/activitypub/url.ts b/server/lib/activitypub/url.ts
index 00b4e8852..bb2d4d11e 100644
--- a/server/lib/activitypub/url.ts
+++ b/server/lib/activitypub/url.ts
@@ -1,16 +1,19 @@
1import { CONFIG } from '../../initializers' 1import { CONFIG } from '../../initializers'
2import { AccountModel } from '../../models/account/account' 2import { ActorModel } from '../../models/activitypub/actor'
3import { AccountFollowModel } from '../../models/account/account-follow' 3import { ActorFollowModel } from '../../models/activitypub/actor-follow'
4import { VideoModel } from '../../models/video/video' 4import { VideoModel } from '../../models/video/video'
5import { VideoAbuseModel } from '../../models/video/video-abuse' 5import { VideoAbuseModel } from '../../models/video/video-abuse'
6import { VideoChannelModel } from '../../models/video/video-channel'
7 6
8function getVideoActivityPubUrl (video: VideoModel) { 7function getVideoActivityPubUrl (video: VideoModel) {
9 return CONFIG.WEBSERVER.URL + '/videos/watch/' + video.uuid 8 return CONFIG.WEBSERVER.URL + '/videos/watch/' + video.uuid
10} 9}
11 10
12function getVideoChannelActivityPubUrl (videoChannel: VideoChannelModel) { 11function getVideoChannelActivityPubUrl (videoChannelUUID: string) {
13 return CONFIG.WEBSERVER.URL + '/video-channels/' + videoChannel.uuid 12 return CONFIG.WEBSERVER.URL + '/video-channels/' + videoChannelUUID
13}
14
15function getApplicationActivityPubUrl () {
16 return CONFIG.WEBSERVER.URL + '/application/peertube'
14} 17}
15 18
16function getAccountActivityPubUrl (accountName: string) { 19function getAccountActivityPubUrl (accountName: string) {
@@ -21,34 +24,34 @@ function getVideoAbuseActivityPubUrl (videoAbuse: VideoAbuseModel) {
21 return CONFIG.WEBSERVER.URL + '/admin/video-abuses/' + videoAbuse.id 24 return CONFIG.WEBSERVER.URL + '/admin/video-abuses/' + videoAbuse.id
22} 25}
23 26
24function getVideoViewActivityPubUrl (byAccount: AccountModel, video: VideoModel) { 27function getVideoViewActivityPubUrl (byActor: ActorModel, video: VideoModel) {
25 return video.url + '/views/' + byAccount.uuid + '/' + new Date().toISOString() 28 return video.url + '/views/' + byActor.uuid + '/' + new Date().toISOString()
26} 29}
27 30
28function getVideoLikeActivityPubUrl (byAccount: AccountModel, video: VideoModel) { 31function getVideoLikeActivityPubUrl (byActor: ActorModel, video: VideoModel) {
29 return byAccount.url + '/likes/' + video.id 32 return byActor.url + '/likes/' + video.id
30} 33}
31 34
32function getVideoDislikeActivityPubUrl (byAccount: AccountModel, video: VideoModel) { 35function getVideoDislikeActivityPubUrl (byActor: ActorModel, video: VideoModel) {
33 return byAccount.url + '/dislikes/' + video.id 36 return byActor.url + '/dislikes/' + video.id
34} 37}
35 38
36function getAccountFollowActivityPubUrl (accountFollow: AccountFollowModel) { 39function getActorFollowActivityPubUrl (actorFollow: ActorFollowModel) {
37 const me = accountFollow.AccountFollower 40 const me = actorFollow.ActorFollower
38 const following = accountFollow.AccountFollowing 41 const following = actorFollow.ActorFollowing
39 42
40 return me.url + '/follows/' + following.id 43 return me.url + '/follows/' + following.id
41} 44}
42 45
43function getAccountFollowAcceptActivityPubUrl (accountFollow: AccountFollowModel) { 46function getActorFollowAcceptActivityPubUrl (actorFollow: ActorFollowModel) {
44 const follower = accountFollow.AccountFollower 47 const follower = actorFollow.ActorFollower
45 const me = accountFollow.AccountFollowing 48 const me = actorFollow.ActorFollowing
46 49
47 return follower.url + '/accepts/follows/' + me.id 50 return follower.url + '/accepts/follows/' + me.id
48} 51}
49 52
50function getAnnounceActivityPubUrl (originalUrl: string, byAccount: AccountModel) { 53function getAnnounceActivityPubUrl (originalUrl: string, byActor: ActorModel) {
51 return originalUrl + '/announces/' + byAccount.id 54 return originalUrl + '/announces/' + byActor.id
52} 55}
53 56
54function getUpdateActivityPubUrl (originalUrl: string, updatedAt: string) { 57function getUpdateActivityPubUrl (originalUrl: string, updatedAt: string) {
@@ -60,12 +63,13 @@ function getUndoActivityPubUrl (originalUrl: string) {
60} 63}
61 64
62export { 65export {
66 getApplicationActivityPubUrl,
63 getVideoActivityPubUrl, 67 getVideoActivityPubUrl,
64 getVideoChannelActivityPubUrl, 68 getVideoChannelActivityPubUrl,
65 getAccountActivityPubUrl, 69 getAccountActivityPubUrl,
66 getVideoAbuseActivityPubUrl, 70 getVideoAbuseActivityPubUrl,
67 getAccountFollowActivityPubUrl, 71 getActorFollowActivityPubUrl,
68 getAccountFollowAcceptActivityPubUrl, 72 getActorFollowAcceptActivityPubUrl,
69 getAnnounceActivityPubUrl, 73 getAnnounceActivityPubUrl,
70 getUpdateActivityPubUrl, 74 getUpdateActivityPubUrl,
71 getUndoActivityPubUrl, 75 getUndoActivityPubUrl,
diff --git a/server/lib/activitypub/video-channels.ts b/server/lib/activitypub/video-channels.ts
deleted file mode 100644
index c05a46f95..000000000
--- a/server/lib/activitypub/video-channels.ts
+++ /dev/null
@@ -1,59 +0,0 @@
1import { VideoChannelObject } from '../../../shared/models/activitypub/objects'
2import { doRequest, logger } from '../../helpers'
3import { isVideoChannelObjectValid } from '../../helpers/custom-validators/activitypub'
4import { ACTIVITY_PUB } from '../../initializers'
5import { AccountModel } from '../../models/account/account'
6import { VideoChannelModel } from '../../models/video/video-channel'
7import { videoChannelActivityObjectToDBAttributes } from './process/misc'
8
9async function getOrCreateVideoChannel (ownerAccount: AccountModel, videoChannelUrl: string) {
10 let videoChannel = await VideoChannelModel.loadByUrl(videoChannelUrl)
11
12 // We don't have this account in our database, fetch it on remote
13 if (!videoChannel) {
14 videoChannel = await fetchRemoteVideoChannel(ownerAccount, videoChannelUrl)
15 if (videoChannel === undefined) throw new Error('Cannot fetch remote video channel.')
16
17 // Save our new video channel in database
18 await videoChannel.save()
19 }
20
21 return videoChannel
22}
23
24async function fetchRemoteVideoChannel (ownerAccount: AccountModel, videoChannelUrl: string) {
25 const options = {
26 uri: videoChannelUrl,
27 method: 'GET',
28 headers: {
29 'Accept': ACTIVITY_PUB.ACCEPT_HEADER
30 }
31 }
32
33 logger.info('Fetching remote video channel %s.', videoChannelUrl)
34
35 let requestResult
36 try {
37 requestResult = await doRequest(options)
38 } catch (err) {
39 logger.warn('Cannot fetch remote video channel %s.', videoChannelUrl, err)
40 return undefined
41 }
42
43 const videoChannelJSON: VideoChannelObject = JSON.parse(requestResult.body)
44 if (isVideoChannelObjectValid(videoChannelJSON) === false) {
45 logger.debug('Remote video channel JSON is not valid.', { videoChannelJSON })
46 return undefined
47 }
48
49 const videoChannelAttributes = videoChannelActivityObjectToDBAttributes(videoChannelJSON, ownerAccount)
50 const videoChannel = new VideoChannelModel(videoChannelAttributes)
51 videoChannel.Account = ownerAccount
52
53 return videoChannel
54}
55
56export {
57 getOrCreateVideoChannel,
58 fetchRemoteVideoChannel
59}
diff --git a/server/lib/activitypub/videos.ts b/server/lib/activitypub/videos.ts
index 14c07fec0..fab43757a 100644
--- a/server/lib/activitypub/videos.ts
+++ b/server/lib/activitypub/videos.ts
@@ -19,7 +19,7 @@ import {
19 19
20function fetchRemoteVideoPreview (video: VideoModel) { 20function fetchRemoteVideoPreview (video: VideoModel) {
21 // FIXME: use url 21 // FIXME: use url
22 const host = video.VideoChannel.Account.Server.host 22 const host = video.VideoChannel.Account.Actor.Server.host
23 const path = join(STATIC_PATHS.PREVIEWS, video.getPreviewName()) 23 const path = join(STATIC_PATHS.PREVIEWS, video.getPreviewName())
24 24
25 return request.get(REMOTE_SCHEME.HTTP + '://' + host + path) 25 return request.get(REMOTE_SCHEME.HTTP + '://' + host + path)
@@ -27,7 +27,7 @@ function fetchRemoteVideoPreview (video: VideoModel) {
27 27
28async function fetchRemoteVideoDescription (video: VideoModel) { 28async function fetchRemoteVideoDescription (video: VideoModel) {
29 // FIXME: use url 29 // FIXME: use url
30 const host = video.VideoChannel.Account.Server.host 30 const host = video.VideoChannel.Account.Actor.Server.host
31 const path = video.getDescriptionPath() 31 const path = video.getDescriptionPath()
32 const options = { 32 const options = {
33 uri: REMOTE_SCHEME.HTTP + '://' + host + path, 33 uri: REMOTE_SCHEME.HTTP + '://' + host + path,
@@ -50,43 +50,47 @@ function generateThumbnailFromUrl (video: VideoModel, icon: ActivityIconObject)
50} 50}
51 51
52async function sendVideoRateChangeToFollowers ( 52async function sendVideoRateChangeToFollowers (
53 account: AccountModel, 53 account: AccountModel,
54 video: VideoModel, 54 video: VideoModel,
55 likes: number, 55 likes: number,
56 dislikes: number, 56 dislikes: number,
57 t: Transaction 57 t: Transaction
58) { 58) {
59 const actor = account.Actor
60
59 // Keep the order: first we undo and then we create 61 // Keep the order: first we undo and then we create
60 62
61 // Undo Like 63 // Undo Like
62 if (likes < 0) await sendUndoLikeToVideoFollowers(account, video, t) 64 if (likes < 0) await sendUndoLikeToVideoFollowers(actor, video, t)
63 // Undo Dislike 65 // Undo Dislike
64 if (dislikes < 0) await sendUndoDislikeToVideoFollowers(account, video, t) 66 if (dislikes < 0) await sendUndoDislikeToVideoFollowers(actor, video, t)
65 67
66 // Like 68 // Like
67 if (likes > 0) await sendLikeToVideoFollowers(account, video, t) 69 if (likes > 0) await sendLikeToVideoFollowers(actor, video, t)
68 // Dislike 70 // Dislike
69 if (dislikes > 0) await sendCreateDislikeToVideoFollowers(account, video, t) 71 if (dislikes > 0) await sendCreateDislikeToVideoFollowers(actor, video, t)
70} 72}
71 73
72async function sendVideoRateChangeToOrigin ( 74async function sendVideoRateChangeToOrigin (
73 account: AccountModel, 75 account: AccountModel,
74 video: VideoModel, 76 video: VideoModel,
75 likes: number, 77 likes: number,
76 dislikes: number, 78 dislikes: number,
77 t: Transaction 79 t: Transaction
78) { 80) {
81 const actor = account.Actor
82
79 // Keep the order: first we undo and then we create 83 // Keep the order: first we undo and then we create
80 84
81 // Undo Like 85 // Undo Like
82 if (likes < 0) await sendUndoLikeToOrigin(account, video, t) 86 if (likes < 0) await sendUndoLikeToOrigin(actor, video, t)
83 // Undo Dislike 87 // Undo Dislike
84 if (dislikes < 0) await sendUndoDislikeToOrigin(account, video, t) 88 if (dislikes < 0) await sendUndoDislikeToOrigin(actor, video, t)
85 89
86 // Like 90 // Like
87 if (likes > 0) await sendLikeToOrigin(account, video, t) 91 if (likes > 0) await sendLikeToOrigin(actor, video, t)
88 // Dislike 92 // Dislike
89 if (dislikes > 0) await sendCreateDislikeToOrigin(account, video, t) 93 if (dislikes > 0) await sendCreateDislikeToOrigin(actor, video, t)
90} 94}
91 95
92export { 96export {
diff --git a/server/lib/index.ts b/server/lib/index.ts
deleted file mode 100644
index d22ecb665..000000000
--- a/server/lib/index.ts
+++ /dev/null
@@ -1,6 +0,0 @@
1export * from './activitypub'
2export * from './cache'
3export * from './jobs'
4export * from './oauth-model'
5export * from './user'
6export * from './video-channel'
diff --git a/server/lib/jobs/activitypub-http-job-scheduler/activitypub-http-job-scheduler.ts b/server/lib/jobs/activitypub-http-job-scheduler/activitypub-http-job-scheduler.ts
index 76da5b724..95a5d3ff2 100644
--- a/server/lib/jobs/activitypub-http-job-scheduler/activitypub-http-job-scheduler.ts
+++ b/server/lib/jobs/activitypub-http-job-scheduler/activitypub-http-job-scheduler.ts
@@ -1,7 +1,7 @@
1import { JobCategory } from '../../../../shared' 1import { JobCategory } from '../../../../shared'
2import { buildSignedActivity, logger } from '../../../helpers' 2import { buildSignedActivity, logger } from '../../../helpers'
3import { ACTIVITY_PUB } from '../../../initializers' 3import { ACTIVITY_PUB } from '../../../initializers'
4import { AccountModel } from '../../../models/account/account' 4import { ActorModel } from '../../../models/activitypub/actor'
5import { JobHandler, JobScheduler } from '../job-scheduler' 5import { JobHandler, JobScheduler } from '../job-scheduler'
6 6
7import * as activitypubHttpBroadcastHandler from './activitypub-http-broadcast-handler' 7import * as activitypubHttpBroadcastHandler from './activitypub-http-broadcast-handler'
@@ -10,7 +10,7 @@ import * as activitypubHttpUnicastHandler from './activitypub-http-unicast-handl
10 10
11type ActivityPubHttpPayload = { 11type ActivityPubHttpPayload = {
12 uris: string[] 12 uris: string[]
13 signatureAccountId?: number 13 signatureActorId?: number
14 body?: any 14 body?: any
15 attemptNumber?: number 15 attemptNumber?: number
16} 16}
@@ -44,10 +44,10 @@ function maybeRetryRequestLater (err: Error, payload: ActivityPubHttpPayload, ur
44async function computeBody (payload: ActivityPubHttpPayload) { 44async function computeBody (payload: ActivityPubHttpPayload) {
45 let body = payload.body 45 let body = payload.body
46 46
47 if (payload.signatureAccountId) { 47 if (payload.signatureActorId) {
48 const accountSignature = await AccountModel.load(payload.signatureAccountId) 48 const actorSignature = await ActorModel.load(payload.signatureActorId)
49 if (!accountSignature) throw new Error('Unknown signature account id.') 49 if (!actorSignature) throw new Error('Unknown signature account id.')
50 body = await buildSignedActivity(accountSignature, payload.body) 50 body = await buildSignedActivity(actorSignature, payload.body)
51 } 51 }
52 52
53 return body 53 return body
diff --git a/server/lib/jobs/transcoding-job-scheduler/video-file-optimizer-handler.ts b/server/lib/jobs/transcoding-job-scheduler/video-file-optimizer-handler.ts
index 1786ce971..7df048006 100644
--- a/server/lib/jobs/transcoding-job-scheduler/video-file-optimizer-handler.ts
+++ b/server/lib/jobs/transcoding-job-scheduler/video-file-optimizer-handler.ts
@@ -3,7 +3,7 @@ import { computeResolutionsToTranscode, logger } from '../../../helpers'
3import { sequelizeTypescript } from '../../../initializers' 3import { sequelizeTypescript } from '../../../initializers'
4import { VideoModel } from '../../../models/video/video' 4import { VideoModel } from '../../../models/video/video'
5import { shareVideoByServer } from '../../activitypub' 5import { shareVideoByServer } from '../../activitypub'
6import { sendAddVideo } from '../../activitypub/send' 6import { sendCreateVideo } from '../../activitypub/send'
7import { JobScheduler } from '../job-scheduler' 7import { JobScheduler } from '../job-scheduler'
8import { TranscodingJobPayload } from './transcoding-job-scheduler' 8import { TranscodingJobPayload } from './transcoding-job-scheduler'
9 9
@@ -36,7 +36,8 @@ async function onSuccess (jobId: number, video: VideoModel, jobScheduler: JobSch
36 if (!videoDatabase) return undefined 36 if (!videoDatabase) return undefined
37 37
38 // Now we'll add the video's meta data to our followers 38 // Now we'll add the video's meta data to our followers
39 await sendAddVideo(video, undefined) 39 await sendCreateVideo(video, undefined)
40 // TODO: share by channel
40 await shareVideoByServer(video, undefined) 41 await shareVideoByServer(video, undefined)
41 42
42 const originalFileHeight = await videoDatabase.getOriginalFileHeight() 43 const originalFileHeight = await videoDatabase.getOriginalFileHeight()
diff --git a/server/lib/user.ts b/server/lib/user.ts
index 6aeb198b9..ec1466c6f 100644
--- a/server/lib/user.ts
+++ b/server/lib/user.ts
@@ -1,10 +1,9 @@
1import * as Sequelize from 'sequelize' 1import * as Sequelize from 'sequelize'
2import { createPrivateAndPublicKeys, logger } from '../helpers' 2import { ActivityPubActorType } from '../../shared/models/activitypub'
3import { CONFIG, sequelizeTypescript } from '../initializers' 3import { sequelizeTypescript, SERVER_ACTOR_NAME } from '../initializers'
4import { AccountModel } from '../models/account/account' 4import { AccountModel } from '../models/account/account'
5import { UserModel } from '../models/account/user' 5import { UserModel } from '../models/account/user'
6import { ActorModel } from '../models/activitypub/actor' 6import { buildActorInstance, getAccountActivityPubUrl, setAsyncActorKeys } from './activitypub'
7import { getAccountActivityPubUrl } from './activitypub'
8import { createVideoChannel } from './video-channel' 7import { createVideoChannel } from './video-channel'
9 8
10async function createUserAccountAndChannel (user: UserModel, validateUser = true) { 9async function createUserAccountAndChannel (user: UserModel, validateUser = true) {
@@ -26,31 +25,22 @@ async function createUserAccountAndChannel (user: UserModel, validateUser = true
26 return { account: accountCreated, videoChannel } 25 return { account: accountCreated, videoChannel }
27 }) 26 })
28 27
29 // Set account keys, this could be long so process after the account creation and do not block the client 28 account.Actor = await setAsyncActorKeys(account.Actor)
30 const { publicKey, privateKey } = await createPrivateAndPublicKeys() 29 videoChannel.Actor = await setAsyncActorKeys(videoChannel.Actor)
31 const actor = account.Actor
32 actor.set('publicKey', publicKey)
33 actor.set('privateKey', privateKey)
34 actor.save().catch(err => logger.error('Cannot set public/private keys of actor %d.', actor.uuid, err))
35 30
36 return { account, videoChannel } 31 return { account, videoChannel }
37} 32}
38 33
39async function createLocalAccountWithoutKeys (name: string, userId: number, applicationId: number, t: Sequelize.Transaction) { 34async function createLocalAccountWithoutKeys (
35 name: string,
36 userId: number,
37 applicationId: number,
38 t: Sequelize.Transaction,
39 type: ActivityPubActorType= 'Person'
40) {
40 const url = getAccountActivityPubUrl(name) 41 const url = getAccountActivityPubUrl(name)
41 42
42 const actorInstance = new ActorModel({ 43 const actorInstance = buildActorInstance(type, url, name)
43 url,
44 publicKey: null,
45 privateKey: null,
46 followersCount: 0,
47 followingCount: 0,
48 inboxUrl: url + '/inbox',
49 outboxUrl: url + '/outbox',
50 sharedInboxUrl: CONFIG.WEBSERVER.URL + '/inbox',
51 followersUrl: url + '/followers',
52 followingUrl: url + '/following'
53 })
54 const actorInstanceCreated = await actorInstance.save({ transaction: t }) 44 const actorInstanceCreated = await actorInstance.save({ transaction: t })
55 45
56 const accountInstance = new AccountModel({ 46 const accountInstance = new AccountModel({
@@ -67,9 +57,18 @@ async function createLocalAccountWithoutKeys (name: string, userId: number, appl
67 return accountInstanceCreated 57 return accountInstanceCreated
68} 58}
69 59
60async function createApplicationActor (applicationId: number) {
61 const accountCreated = await createLocalAccountWithoutKeys(SERVER_ACTOR_NAME, null, applicationId, undefined, 'Application')
62
63 accountCreated.Actor = await setAsyncActorKeys(accountCreated.Actor)
64
65 return accountCreated
66}
67
70// --------------------------------------------------------------------------- 68// ---------------------------------------------------------------------------
71 69
72export { 70export {
71 createApplicationActor,
73 createUserAccountAndChannel, 72 createUserAccountAndChannel,
74 createLocalAccountWithoutKeys 73 createLocalAccountWithoutKeys
75} 74}
diff --git a/server/lib/video-channel.ts b/server/lib/video-channel.ts
index 97924aa9e..569b8f29d 100644
--- a/server/lib/video-channel.ts
+++ b/server/lib/video-channel.ts
@@ -1,26 +1,33 @@
1import * as Sequelize from 'sequelize' 1import * as Sequelize from 'sequelize'
2import * as uuidv4 from 'uuid/v4'
2import { VideoChannelCreate } from '../../shared/models' 3import { VideoChannelCreate } from '../../shared/models'
3import { AccountModel } from '../models/account/account' 4import { AccountModel } from '../models/account/account'
4import { VideoChannelModel } from '../models/video/video-channel' 5import { VideoChannelModel } from '../models/video/video-channel'
5import { getVideoChannelActivityPubUrl } from './activitypub' 6import { buildActorInstance, getVideoChannelActivityPubUrl } from './activitypub'
6 7
7async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account: AccountModel, t: Sequelize.Transaction) { 8async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account: AccountModel, t: Sequelize.Transaction) {
9 const uuid = uuidv4()
10 const url = getVideoChannelActivityPubUrl(uuid)
11 // We use the name as uuid
12 const actorInstance = buildActorInstance('Group', url, uuid, uuid)
13
14 const actorInstanceCreated = await actorInstance.save({ transaction: t })
15
8 const videoChannelData = { 16 const videoChannelData = {
9 name: videoChannelInfo.name, 17 name: videoChannelInfo.name,
10 description: videoChannelInfo.description, 18 description: videoChannelInfo.description,
11 remote: false, 19 accountId: account.id,
12 accountId: account.id 20 actorId: actorInstanceCreated.id
13 } 21 }
14 22
15 const videoChannel = VideoChannelModel.build(videoChannelData) 23 const videoChannel = VideoChannelModel.build(videoChannelData)
16 videoChannel.set('url', getVideoChannelActivityPubUrl(videoChannel))
17 24
18 const options = { transaction: t } 25 const options = { transaction: t }
19
20 const videoChannelCreated = await videoChannel.save(options) 26 const videoChannelCreated = await videoChannel.save(options)
21 27
22 // Do not forget to add Account information to the created video channel 28 // Do not forget to add Account/Actor information to the created video channel
23 videoChannelCreated.Account = account 29 videoChannelCreated.Account = account
30 videoChannelCreated.Actor = actorInstanceCreated
24 31
25 // No need to seed this empty video channel to followers 32 // No need to seed this empty video channel to followers
26 return videoChannelCreated 33 return videoChannelCreated