aboutsummaryrefslogtreecommitdiffhomepage
path: root/server
diff options
context:
space:
mode:
Diffstat (limited to 'server')
-rw-r--r--server/controllers/api/videos/index.ts4
-rw-r--r--server/lib/activitypub/audience.ts10
-rw-r--r--server/lib/activitypub/process/process-delete.ts2
-rw-r--r--server/lib/activitypub/send/send-announce.ts4
-rw-r--r--server/lib/activitypub/send/send-create.ts95
-rw-r--r--server/lib/activitypub/send/send-delete.ts13
-rw-r--r--server/lib/activitypub/send/send-like.ts23
-rw-r--r--server/lib/activitypub/send/send-undo.ts85
-rw-r--r--server/lib/activitypub/send/send-update.ts16
-rw-r--r--server/lib/activitypub/send/utils.ts30
-rw-r--r--server/tests/api/server/stats.ts3
11 files changed, 135 insertions, 150 deletions
diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts
index 0c9e6c2d1..8353a649a 100644
--- a/server/controllers/api/videos/index.ts
+++ b/server/controllers/api/videos/index.ts
@@ -393,9 +393,9 @@ async function viewVideo (req: express.Request, res: express.Response) {
393 Redis.Instance.setIPVideoView(ip, videoInstance.uuid) 393 Redis.Instance.setIPVideoView(ip, videoInstance.uuid)
394 ]) 394 ])
395 395
396 const serverAccount = await getServerActor() 396 const serverActor = await getServerActor()
397 397
398 await sendCreateView(serverAccount, videoInstance, undefined) 398 await sendCreateView(serverActor, videoInstance, undefined)
399 399
400 return res.status(204).end() 400 return res.status(204).end()
401} 401}
diff --git a/server/lib/activitypub/audience.ts b/server/lib/activitypub/audience.ts
index 7b4067c11..a86428461 100644
--- a/server/lib/activitypub/audience.ts
+++ b/server/lib/activitypub/audience.ts
@@ -6,7 +6,7 @@ import { VideoModel } from '../../models/video/video'
6import { VideoCommentModel } from '../../models/video/video-comment' 6import { VideoCommentModel } from '../../models/video/video-comment'
7import { VideoShareModel } from '../../models/video/video-share' 7import { VideoShareModel } from '../../models/video/video-share'
8 8
9function getVideoAudience (video: VideoModel, actorsInvolvedInVideo: ActorModel[]) { 9function getRemoteVideoAudience (video: VideoModel, actorsInvolvedInVideo: ActorModel[]): ActivityAudience {
10 return { 10 return {
11 to: [ video.VideoChannel.Account.Actor.url ], 11 to: [ video.VideoChannel.Account.Actor.url ],
12 cc: actorsInvolvedInVideo.map(a => a.followersUrl) 12 cc: actorsInvolvedInVideo.map(a => a.followersUrl)
@@ -18,7 +18,7 @@ function getVideoCommentAudience (
18 threadParentComments: VideoCommentModel[], 18 threadParentComments: VideoCommentModel[],
19 actorsInvolvedInVideo: ActorModel[], 19 actorsInvolvedInVideo: ActorModel[],
20 isOrigin = false 20 isOrigin = false
21) { 21): ActivityAudience {
22 const to = [ ACTIVITY_PUB.PUBLIC ] 22 const to = [ ACTIVITY_PUB.PUBLIC ]
23 const cc: string[] = [] 23 const cc: string[] = []
24 24
@@ -41,7 +41,7 @@ function getVideoCommentAudience (
41 } 41 }
42} 42}
43 43
44function getObjectFollowersAudience (actorsInvolvedInObject: ActorModel[]) { 44function getAudienceFromFollowersOf (actorsInvolvedInObject: ActorModel[]): ActivityAudience {
45 return { 45 return {
46 to: [ ACTIVITY_PUB.PUBLIC ].concat(actorsInvolvedInObject.map(a => a.followersUrl)), 46 to: [ ACTIVITY_PUB.PUBLIC ].concat(actorsInvolvedInObject.map(a => a.followersUrl)),
47 cc: [] 47 cc: []
@@ -83,9 +83,9 @@ function audiencify<T> (object: T, audience: ActivityAudience) {
83export { 83export {
84 buildAudience, 84 buildAudience,
85 getAudience, 85 getAudience,
86 getVideoAudience, 86 getRemoteVideoAudience,
87 getActorsInvolvedInVideo, 87 getActorsInvolvedInVideo,
88 getObjectFollowersAudience, 88 getAudienceFromFollowersOf,
89 audiencify, 89 audiencify,
90 getVideoCommentAudience 90 getVideoCommentAudience
91} 91}
diff --git a/server/lib/activitypub/process/process-delete.ts b/server/lib/activitypub/process/process-delete.ts
index 3c830abea..4c034a81c 100644
--- a/server/lib/activitypub/process/process-delete.ts
+++ b/server/lib/activitypub/process/process-delete.ts
@@ -41,6 +41,8 @@ async function processDeleteActivity (activity: ActivityDelete) {
41 { 41 {
42 const videoInstance = await VideoModel.loadByUrlAndPopulateAccount(objectUrl) 42 const videoInstance = await VideoModel.loadByUrlAndPopulateAccount(objectUrl)
43 if (videoInstance) { 43 if (videoInstance) {
44 if (videoInstance.isOwned()) throw new Error(`Remote instance cannot delete owned video ${videoInstance.url}.`)
45
44 return retryTransactionWrapper(processDeleteVideo, actor, videoInstance) 46 return retryTransactionWrapper(processDeleteVideo, actor, videoInstance)
45 } 47 }
46 } 48 }
diff --git a/server/lib/activitypub/send/send-announce.ts b/server/lib/activitypub/send/send-announce.ts
index f137217f8..cd0cab7ee 100644
--- a/server/lib/activitypub/send/send-announce.ts
+++ b/server/lib/activitypub/send/send-announce.ts
@@ -4,14 +4,14 @@ import { ActorModel } from '../../../models/activitypub/actor'
4import { VideoModel } from '../../../models/video/video' 4import { VideoModel } from '../../../models/video/video'
5import { VideoShareModel } from '../../../models/video/video-share' 5import { VideoShareModel } from '../../../models/video/video-share'
6import { broadcastToFollowers } from './utils' 6import { broadcastToFollowers } from './utils'
7import { audiencify, getActorsInvolvedInVideo, getAudience, getObjectFollowersAudience } from '../audience' 7import { audiencify, getActorsInvolvedInVideo, getAudience, getAudienceFromFollowersOf } from '../audience'
8import { logger } from '../../../helpers/logger' 8import { logger } from '../../../helpers/logger'
9 9
10async function buildAnnounceWithVideoAudience (byActor: ActorModel, videoShare: VideoShareModel, video: VideoModel, t: Transaction) { 10async function buildAnnounceWithVideoAudience (byActor: ActorModel, videoShare: VideoShareModel, video: VideoModel, t: Transaction) {
11 const announcedObject = video.url 11 const announcedObject = video.url
12 12
13 const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t) 13 const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t)
14 const audience = getObjectFollowersAudience(actorsInvolvedInVideo) 14 const audience = getAudienceFromFollowersOf(actorsInvolvedInVideo)
15 15
16 const activity = buildAnnounceActivity(videoShare.url, byActor, announcedObject, audience) 16 const activity = buildAnnounceActivity(videoShare.url, byActor, announcedObject, audience)
17 17
diff --git a/server/lib/activitypub/send/send-create.ts b/server/lib/activitypub/send/send-create.ts
index 6f89b1a22..285edba3b 100644
--- a/server/lib/activitypub/send/send-create.ts
+++ b/server/lib/activitypub/send/send-create.ts
@@ -1,21 +1,13 @@
1import { Transaction } from 'sequelize' 1import { Transaction } from 'sequelize'
2import { ActivityAudience, ActivityCreate } from '../../../../shared/models/activitypub' 2import { ActivityAudience, ActivityCreate } from '../../../../shared/models/activitypub'
3import { VideoPrivacy } from '../../../../shared/models/videos' 3import { VideoPrivacy } from '../../../../shared/models/videos'
4import { getServerActor } from '../../../helpers/utils'
5import { ActorModel } from '../../../models/activitypub/actor' 4import { ActorModel } from '../../../models/activitypub/actor'
6import { VideoModel } from '../../../models/video/video' 5import { VideoModel } from '../../../models/video/video'
7import { VideoAbuseModel } from '../../../models/video/video-abuse' 6import { VideoAbuseModel } from '../../../models/video/video-abuse'
8import { VideoCommentModel } from '../../../models/video/video-comment' 7import { VideoCommentModel } from '../../../models/video/video-comment'
9import { getVideoAbuseActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoViewActivityPubUrl } from '../url' 8import { getVideoAbuseActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoViewActivityPubUrl } from '../url'
10import { broadcastToActors, broadcastToFollowers, unicastTo } from './utils' 9import { broadcastToActors, broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils'
11import { 10import { audiencify, getActorsInvolvedInVideo, getAudience, getAudienceFromFollowersOf, getVideoCommentAudience } from '../audience'
12 audiencify,
13 getActorsInvolvedInVideo,
14 getAudience,
15 getObjectFollowersAudience,
16 getVideoAudience,
17 getVideoCommentAudience
18} from '../audience'
19import { logger } from '../../../helpers/logger' 11import { logger } from '../../../helpers/logger'
20import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy' 12import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy'
21 13
@@ -40,6 +32,7 @@ async function sendVideoAbuse (byActor: ActorModel, videoAbuse: VideoAbuseModel,
40 32
41 logger.info('Creating job to send video abuse %s.', url) 33 logger.info('Creating job to send video abuse %s.', url)
42 34
35 // Custom audience, we only send the abuse to the origin instance
43 const audience = { to: [ video.VideoChannel.Account.Actor.url ], cc: [] } 36 const audience = { to: [ video.VideoChannel.Account.Actor.url ], cc: [] }
44 const createActivity = buildCreateActivity(url, byActor, videoAbuse.toActivityPubObject(), audience) 37 const createActivity = buildCreateActivity(url, byActor, videoAbuse.toActivityPubObject(), audience)
45 38
@@ -49,15 +42,15 @@ async function sendVideoAbuse (byActor: ActorModel, videoAbuse: VideoAbuseModel,
49async function sendCreateCacheFile (byActor: ActorModel, fileRedundancy: VideoRedundancyModel) { 42async function sendCreateCacheFile (byActor: ActorModel, fileRedundancy: VideoRedundancyModel) {
50 logger.info('Creating job to send file cache of %s.', fileRedundancy.url) 43 logger.info('Creating job to send file cache of %s.', fileRedundancy.url)
51 44
52 const redundancyObject = fileRedundancy.toActivityPubObject()
53
54 const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(fileRedundancy.VideoFile.Video.id) 45 const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(fileRedundancy.VideoFile.Video.id)
55 const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, undefined) 46 const redundancyObject = fileRedundancy.toActivityPubObject()
56
57 const audience = getVideoAudience(video, actorsInvolvedInVideo)
58 const createActivity = buildCreateActivity(fileRedundancy.url, byActor, redundancyObject, audience)
59 47
60 return unicastTo(createActivity, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl) 48 return sendVideoRelatedCreateActivity({
49 byActor,
50 video,
51 url: fileRedundancy.url,
52 object: redundancyObject
53 })
61} 54}
62 55
63async function sendCreateVideoComment (comment: VideoCommentModel, t: Transaction) { 56async function sendCreateVideoComment (comment: VideoCommentModel, t: Transaction) {
@@ -70,6 +63,7 @@ async function sendCreateVideoComment (comment: VideoCommentModel, t: Transactio
70 const commentObject = comment.toActivityPubObject(threadParentComments) 63 const commentObject = comment.toActivityPubObject(threadParentComments)
71 64
72 const actorsInvolvedInComment = await getActorsInvolvedInVideo(comment.Video, t) 65 const actorsInvolvedInComment = await getActorsInvolvedInVideo(comment.Video, t)
66 // Add the actor that commented too
73 actorsInvolvedInComment.push(byActor) 67 actorsInvolvedInComment.push(byActor)
74 68
75 const parentsCommentActors = threadParentComments.map(c => c.Account.Actor) 69 const parentsCommentActors = threadParentComments.map(c => c.Account.Actor)
@@ -78,7 +72,7 @@ async function sendCreateVideoComment (comment: VideoCommentModel, t: Transactio
78 if (isOrigin) { 72 if (isOrigin) {
79 audience = getVideoCommentAudience(comment, threadParentComments, actorsInvolvedInComment, isOrigin) 73 audience = getVideoCommentAudience(comment, threadParentComments, actorsInvolvedInComment, isOrigin)
80 } else { 74 } else {
81 audience = getObjectFollowersAudience(actorsInvolvedInComment.concat(parentsCommentActors)) 75 audience = getAudienceFromFollowersOf(actorsInvolvedInComment.concat(parentsCommentActors))
82 } 76 }
83 77
84 const createActivity = buildCreateActivity(comment.url, byActor, commentObject, audience) 78 const createActivity = buildCreateActivity(comment.url, byActor, commentObject, audience)
@@ -103,24 +97,14 @@ async function sendCreateView (byActor: ActorModel, video: VideoModel, t: Transa
103 const url = getVideoViewActivityPubUrl(byActor, video) 97 const url = getVideoViewActivityPubUrl(byActor, video)
104 const viewActivity = buildViewActivity(byActor, video) 98 const viewActivity = buildViewActivity(byActor, video)
105 99
106 const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t) 100 return sendVideoRelatedCreateActivity({
107 101 // Use the server actor to send the view
108 // Send to origin 102 byActor,
109 if (video.isOwned() === false) { 103 video,
110 const audience = getVideoAudience(video, actorsInvolvedInVideo) 104 url,
111 const createActivity = buildCreateActivity(url, byActor, viewActivity, audience) 105 object: viewActivity,
112 106 transaction: t
113 return unicastTo(createActivity, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl) 107 })
114 }
115
116 // Send to followers
117 const audience = getObjectFollowersAudience(actorsInvolvedInVideo)
118 const createActivity = buildCreateActivity(url, byActor, viewActivity, audience)
119
120 // Use the server actor to send the view
121 const serverActor = await getServerActor()
122 const actorsException = [ byActor ]
123 return broadcastToFollowers(createActivity, serverActor, actorsInvolvedInVideo, t, actorsException)
124} 108}
125 109
126async function sendCreateDislike (byActor: ActorModel, video: VideoModel, t: Transaction) { 110async function sendCreateDislike (byActor: ActorModel, video: VideoModel, t: Transaction) {
@@ -129,22 +113,13 @@ async function sendCreateDislike (byActor: ActorModel, video: VideoModel, t: Tra
129 const url = getVideoDislikeActivityPubUrl(byActor, video) 113 const url = getVideoDislikeActivityPubUrl(byActor, video)
130 const dislikeActivity = buildDislikeActivity(byActor, video) 114 const dislikeActivity = buildDislikeActivity(byActor, video)
131 115
132 const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t) 116 return sendVideoRelatedCreateActivity({
133 117 byActor,
134 // Send to origin 118 video,
135 if (video.isOwned() === false) { 119 url,
136 const audience = getVideoAudience(video, actorsInvolvedInVideo) 120 object: dislikeActivity,
137 const createActivity = buildCreateActivity(url, byActor, dislikeActivity, audience) 121 transaction: t
138 122 })
139 return unicastTo(createActivity, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl)
140 }
141
142 // Send to followers
143 const audience = getObjectFollowersAudience(actorsInvolvedInVideo)
144 const createActivity = buildCreateActivity(url, byActor, dislikeActivity, audience)
145
146 const actorsException = [ byActor ]
147 return broadcastToFollowers(createActivity, byActor, actorsInvolvedInVideo, t, actorsException)
148} 123}
149 124
150function buildCreateActivity (url: string, byActor: ActorModel, object: any, audience?: ActivityAudience): ActivityCreate { 125function buildCreateActivity (url: string, byActor: ActorModel, object: any, audience?: ActivityAudience): ActivityCreate {
@@ -189,3 +164,19 @@ export {
189 sendCreateVideoComment, 164 sendCreateVideoComment,
190 sendCreateCacheFile 165 sendCreateCacheFile
191} 166}
167
168// ---------------------------------------------------------------------------
169
170async function sendVideoRelatedCreateActivity (options: {
171 byActor: ActorModel,
172 video: VideoModel,
173 url: string,
174 object: any,
175 transaction?: Transaction
176}) {
177 const activityBuilder = (audience: ActivityAudience) => {
178 return buildCreateActivity(options.url, options.byActor, options.object, audience)
179 }
180
181 return sendVideoRelatedActivity(activityBuilder, options)
182}
diff --git a/server/lib/activitypub/send/send-delete.ts b/server/lib/activitypub/send/send-delete.ts
index 479182543..18969433a 100644
--- a/server/lib/activitypub/send/send-delete.ts
+++ b/server/lib/activitypub/send/send-delete.ts
@@ -5,21 +5,22 @@ import { VideoModel } from '../../../models/video/video'
5import { VideoCommentModel } from '../../../models/video/video-comment' 5import { VideoCommentModel } from '../../../models/video/video-comment'
6import { VideoShareModel } from '../../../models/video/video-share' 6import { VideoShareModel } from '../../../models/video/video-share'
7import { getDeleteActivityPubUrl } from '../url' 7import { getDeleteActivityPubUrl } from '../url'
8import { broadcastToActors, broadcastToFollowers, unicastTo } from './utils' 8import { broadcastToActors, broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils'
9import { audiencify, getActorsInvolvedInVideo, getVideoCommentAudience } from '../audience' 9import { audiencify, getActorsInvolvedInVideo, getVideoCommentAudience } from '../audience'
10import { logger } from '../../../helpers/logger' 10import { logger } from '../../../helpers/logger'
11 11
12async function sendDeleteVideo (video: VideoModel, t: Transaction) { 12async function sendDeleteVideo (video: VideoModel, transaction: Transaction) {
13 logger.info('Creating job to broadcast delete of video %s.', video.url) 13 logger.info('Creating job to broadcast delete of video %s.', video.url)
14 14
15 const url = getDeleteActivityPubUrl(video.url)
16 const byActor = video.VideoChannel.Account.Actor 15 const byActor = video.VideoChannel.Account.Actor
17 16
18 const activity = buildDeleteActivity(url, video.url, byActor) 17 const activityBuilder = (audience: ActivityAudience) => {
18 const url = getDeleteActivityPubUrl(video.url)
19 19
20 const actorsInvolved = await getActorsInvolvedInVideo(video, t) 20 return buildDeleteActivity(url, video.url, byActor, audience)
21 }
21 22
22 return broadcastToFollowers(activity, byActor, actorsInvolved, t) 23 return sendVideoRelatedActivity(activityBuilder, { byActor, video, transaction })
23} 24}
24 25
25async function sendDeleteActor (byActor: ActorModel, t: Transaction) { 26async function sendDeleteActor (byActor: ActorModel, t: Transaction) {
diff --git a/server/lib/activitypub/send/send-like.ts b/server/lib/activitypub/send/send-like.ts
index a5408ac6a..89307acc6 100644
--- a/server/lib/activitypub/send/send-like.ts
+++ b/server/lib/activitypub/send/send-like.ts
@@ -3,31 +3,20 @@ import { ActivityAudience, ActivityLike } from '../../../../shared/models/activi
3import { ActorModel } from '../../../models/activitypub/actor' 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 { broadcastToFollowers, unicastTo } from './utils' 6import { sendVideoRelatedActivity } from './utils'
7import { audiencify, getActorsInvolvedInVideo, getAudience, getObjectFollowersAudience, getVideoAudience } from '../audience' 7import { audiencify, getAudience } from '../audience'
8import { logger } from '../../../helpers/logger' 8import { logger } from '../../../helpers/logger'
9 9
10async function sendLike (byActor: ActorModel, video: VideoModel, t: Transaction) { 10async function sendLike (byActor: ActorModel, video: VideoModel, t: Transaction) {
11 logger.info('Creating job to like %s.', video.url) 11 logger.info('Creating job to like %s.', video.url)
12 12
13 const url = getVideoLikeActivityPubUrl(byActor, video) 13 const activityBuilder = (audience: ActivityAudience) => {
14 const url = getVideoLikeActivityPubUrl(byActor, video)
14 15
15 const accountsInvolvedInVideo = await getActorsInvolvedInVideo(video, t) 16 return buildLikeActivity(url, byActor, video, audience)
16
17 // Send to origin
18 if (video.isOwned() === false) {
19 const audience = getVideoAudience(video, accountsInvolvedInVideo)
20 const data = buildLikeActivity(url, byActor, video, audience)
21
22 return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl)
23 } 17 }
24 18
25 // Send to followers 19 return sendVideoRelatedActivity(activityBuilder, { byActor, video, transaction: t })
26 const audience = getObjectFollowersAudience(accountsInvolvedInVideo)
27 const activity = buildLikeActivity(url, byActor, video, audience)
28
29 const followersException = [ byActor ]
30 return broadcastToFollowers(activity, byActor, accountsInvolvedInVideo, t, followersException)
31} 20}
32 21
33function buildLikeActivity (url: string, byActor: ActorModel, video: VideoModel, audience?: ActivityAudience): ActivityLike { 22function buildLikeActivity (url: string, byActor: ActorModel, video: VideoModel, audience?: ActivityAudience): ActivityLike {
diff --git a/server/lib/activitypub/send/send-undo.ts b/server/lib/activitypub/send/send-undo.ts
index a50673c79..5236d2cb3 100644
--- a/server/lib/activitypub/send/send-undo.ts
+++ b/server/lib/activitypub/send/send-undo.ts
@@ -11,8 +11,8 @@ import { ActorModel } from '../../../models/activitypub/actor'
11import { ActorFollowModel } from '../../../models/activitypub/actor-follow' 11import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
12import { VideoModel } from '../../../models/video/video' 12import { VideoModel } from '../../../models/video/video'
13import { getActorFollowActivityPubUrl, getUndoActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoLikeActivityPubUrl } from '../url' 13import { getActorFollowActivityPubUrl, getUndoActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoLikeActivityPubUrl } from '../url'
14import { broadcastToFollowers, unicastTo } from './utils' 14import { broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils'
15import { audiencify, getActorsInvolvedInVideo, getAudience, getObjectFollowersAudience, getVideoAudience } from '../audience' 15import { audiencify, getAudience } from '../audience'
16import { buildCreateActivity, buildDislikeActivity } from './send-create' 16import { buildCreateActivity, buildDislikeActivity } from './send-create'
17import { buildFollowActivity } from './send-follow' 17import { buildFollowActivity } from './send-follow'
18import { buildLikeActivity } from './send-like' 18import { buildLikeActivity } from './send-like'
@@ -39,79 +39,44 @@ async function sendUndoFollow (actorFollow: ActorFollowModel, t: Transaction) {
39 return unicastTo(undoActivity, me, following.inboxUrl) 39 return unicastTo(undoActivity, me, following.inboxUrl)
40} 40}
41 41
42async function sendUndoLike (byActor: ActorModel, video: VideoModel, t: Transaction) { 42async function sendUndoAnnounce (byActor: ActorModel, videoShare: VideoShareModel, video: VideoModel, t: Transaction) {
43 logger.info('Creating job to undo a like of video %s.', video.url) 43 logger.info('Creating job to undo announce %s.', videoShare.url)
44 44
45 const likeUrl = getVideoLikeActivityPubUrl(byActor, video) 45 const undoUrl = getUndoActivityPubUrl(videoShare.url)
46 const undoUrl = getUndoActivityPubUrl(likeUrl)
47 46
48 const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t) 47 const { activity: announceActivity, actorsInvolvedInVideo } = await buildAnnounceWithVideoAudience(byActor, videoShare, video, t)
49 const likeActivity = buildLikeActivity(likeUrl, byActor, video) 48 const undoActivity = undoActivityData(undoUrl, byActor, announceActivity)
50 49
51 // Send to origin 50 const followersException = [ byActor ]
52 if (video.isOwned() === false) { 51 return broadcastToFollowers(undoActivity, byActor, actorsInvolvedInVideo, t, followersException)
53 const audience = getVideoAudience(video, actorsInvolvedInVideo) 52}
54 const undoActivity = undoActivityData(undoUrl, byActor, likeActivity, audience)
55 53
56 return unicastTo(undoActivity, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl) 54async function sendUndoLike (byActor: ActorModel, video: VideoModel, t: Transaction) {
57 } 55 logger.info('Creating job to undo a like of video %s.', video.url)
58 56
59 const audience = getObjectFollowersAudience(actorsInvolvedInVideo) 57 const likeUrl = getVideoLikeActivityPubUrl(byActor, video)
60 const undoActivity = undoActivityData(undoUrl, byActor, likeActivity, audience) 58 const likeActivity = buildLikeActivity(likeUrl, byActor, video)
61 59
62 const followersException = [ byActor ] 60 return sendUndoVideoRelatedActivity({ byActor, video, url: likeUrl, activity: likeActivity, transaction: t })
63 return broadcastToFollowers(undoActivity, byActor, actorsInvolvedInVideo, t, followersException)
64} 61}
65 62
66async function sendUndoDislike (byActor: ActorModel, video: VideoModel, t: Transaction) { 63async function sendUndoDislike (byActor: ActorModel, video: VideoModel, t: Transaction) {
67 logger.info('Creating job to undo a dislike of video %s.', video.url) 64 logger.info('Creating job to undo a dislike of video %s.', video.url)
68 65
69 const dislikeUrl = getVideoDislikeActivityPubUrl(byActor, video) 66 const dislikeUrl = getVideoDislikeActivityPubUrl(byActor, video)
70 const undoUrl = getUndoActivityPubUrl(dislikeUrl)
71
72 const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t)
73 const dislikeActivity = buildDislikeActivity(byActor, video) 67 const dislikeActivity = buildDislikeActivity(byActor, video)
74 const createDislikeActivity = buildCreateActivity(dislikeUrl, byActor, dislikeActivity) 68 const createDislikeActivity = buildCreateActivity(dislikeUrl, byActor, dislikeActivity)
75 69
76 if (video.isOwned() === false) { 70 return sendUndoVideoRelatedActivity({ byActor, video, url: dislikeUrl, activity: createDislikeActivity, transaction: t })
77 const audience = getVideoAudience(video, actorsInvolvedInVideo)
78 const undoActivity = undoActivityData(undoUrl, byActor, createDislikeActivity, audience)
79
80 return unicastTo(undoActivity, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl)
81 }
82
83 const undoActivity = undoActivityData(undoUrl, byActor, createDislikeActivity)
84
85 const followersException = [ byActor ]
86 return broadcastToFollowers(undoActivity, byActor, actorsInvolvedInVideo, t, followersException)
87}
88
89async function sendUndoAnnounce (byActor: ActorModel, videoShare: VideoShareModel, video: VideoModel, t: Transaction) {
90 logger.info('Creating job to undo announce %s.', videoShare.url)
91
92 const undoUrl = getUndoActivityPubUrl(videoShare.url)
93
94 const { activity: announceActivity, actorsInvolvedInVideo } = await buildAnnounceWithVideoAudience(byActor, videoShare, video, t)
95 const undoActivity = undoActivityData(undoUrl, byActor, announceActivity)
96
97 const followersException = [ byActor ]
98 return broadcastToFollowers(undoActivity, byActor, actorsInvolvedInVideo, t, followersException)
99} 71}
100 72
101async function sendUndoCacheFile (byActor: ActorModel, redundancyModel: VideoRedundancyModel, t: Transaction) { 73async function sendUndoCacheFile (byActor: ActorModel, redundancyModel: VideoRedundancyModel, t: Transaction) {
102 logger.info('Creating job to undo cache file %s.', redundancyModel.url) 74 logger.info('Creating job to undo cache file %s.', redundancyModel.url)
103 75
104 const undoUrl = getUndoActivityPubUrl(redundancyModel.url)
105
106 const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(redundancyModel.VideoFile.Video.id) 76 const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(redundancyModel.VideoFile.Video.id)
107 const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t)
108
109 const audience = getVideoAudience(video, actorsInvolvedInVideo)
110 const createActivity = buildCreateActivity(redundancyModel.url, byActor, redundancyModel.toActivityPubObject()) 77 const createActivity = buildCreateActivity(redundancyModel.url, byActor, redundancyModel.toActivityPubObject())
111 78
112 const undoActivity = undoActivityData(undoUrl, byActor, createActivity, audience) 79 return sendUndoVideoRelatedActivity({ byActor, video, url: redundancyModel.url, activity: createActivity, transaction: t })
113
114 return unicastTo(undoActivity, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl)
115} 80}
116 81
117// --------------------------------------------------------------------------- 82// ---------------------------------------------------------------------------
@@ -144,3 +109,19 @@ function undoActivityData (
144 audience 109 audience
145 ) 110 )
146} 111}
112
113async function sendUndoVideoRelatedActivity (options: {
114 byActor: ActorModel,
115 video: VideoModel,
116 url: string,
117 activity: ActivityFollow | ActivityLike | ActivityCreate | ActivityAnnounce,
118 transaction: Transaction
119}) {
120 const activityBuilder = (audience: ActivityAudience) => {
121 const undoUrl = getUndoActivityPubUrl(options.url)
122
123 return undoActivityData(undoUrl, options.byActor, options.activity, audience)
124 }
125
126 return sendVideoRelatedActivity(activityBuilder, options)
127}
diff --git a/server/lib/activitypub/send/send-update.ts b/server/lib/activitypub/send/send-update.ts
index 605473338..ec46789b7 100644
--- a/server/lib/activitypub/send/send-update.ts
+++ b/server/lib/activitypub/send/send-update.ts
@@ -7,8 +7,8 @@ import { VideoModel } from '../../../models/video/video'
7import { VideoChannelModel } from '../../../models/video/video-channel' 7import { VideoChannelModel } from '../../../models/video/video-channel'
8import { VideoShareModel } from '../../../models/video/video-share' 8import { VideoShareModel } from '../../../models/video/video-share'
9import { getUpdateActivityPubUrl } from '../url' 9import { getUpdateActivityPubUrl } from '../url'
10import { broadcastToFollowers, unicastTo } from './utils' 10import { broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils'
11import { audiencify, getActorsInvolvedInVideo, getAudience, getObjectFollowersAudience } from '../audience' 11import { audiencify, getActorsInvolvedInVideo, getAudience, getAudienceFromFollowersOf } from '../audience'
12import { logger } from '../../../helpers/logger' 12import { logger } from '../../../helpers/logger'
13import { VideoCaptionModel } from '../../../models/video/video-caption' 13import { VideoCaptionModel } from '../../../models/video/video-caption'
14import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy' 14import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy'
@@ -61,16 +61,16 @@ async function sendUpdateActor (accountOrChannel: AccountModel | VideoChannelMod
61async function sendUpdateCacheFile (byActor: ActorModel, redundancyModel: VideoRedundancyModel) { 61async function sendUpdateCacheFile (byActor: ActorModel, redundancyModel: VideoRedundancyModel) {
62 logger.info('Creating job to update cache file %s.', redundancyModel.url) 62 logger.info('Creating job to update cache file %s.', redundancyModel.url)
63 63
64 const url = getUpdateActivityPubUrl(redundancyModel.url, redundancyModel.updatedAt.toISOString())
65 const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(redundancyModel.VideoFile.Video.id) 64 const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(redundancyModel.VideoFile.Video.id)
66 65
67 const redundancyObject = redundancyModel.toActivityPubObject() 66 const activityBuilder = (audience: ActivityAudience) => {
67 const redundancyObject = redundancyModel.toActivityPubObject()
68 const url = getUpdateActivityPubUrl(redundancyModel.url, redundancyModel.updatedAt.toISOString())
68 69
69 const accountsInvolvedInVideo = await getActorsInvolvedInVideo(video, undefined) 70 return buildUpdateActivity(url, byActor, redundancyObject, audience)
70 const audience = getObjectFollowersAudience(accountsInvolvedInVideo) 71 }
71 72
72 const updateActivity = buildUpdateActivity(url, byActor, redundancyObject, audience) 73 return sendVideoRelatedActivity(activityBuilder, { byActor, video })
73 return unicastTo(updateActivity, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl)
74} 74}
75 75
76// --------------------------------------------------------------------------- 76// ---------------------------------------------------------------------------
diff --git a/server/lib/activitypub/send/utils.ts b/server/lib/activitypub/send/utils.ts
index c20c15633..69706e620 100644
--- a/server/lib/activitypub/send/utils.ts
+++ b/server/lib/activitypub/send/utils.ts
@@ -1,13 +1,36 @@
1import { Transaction } from 'sequelize' 1import { Transaction } from 'sequelize'
2import { Activity } from '../../../../shared/models/activitypub' 2import { Activity, ActivityAudience } from '../../../../shared/models/activitypub'
3import { logger } from '../../../helpers/logger' 3import { logger } from '../../../helpers/logger'
4import { ActorModel } from '../../../models/activitypub/actor' 4import { ActorModel } from '../../../models/activitypub/actor'
5import { ActorFollowModel } from '../../../models/activitypub/actor-follow' 5import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
6import { JobQueue } from '../../job-queue' 6import { JobQueue } from '../../job-queue'
7import { VideoModel } from '../../../models/video/video' 7import { VideoModel } from '../../../models/video/video'
8import { getActorsInvolvedInVideo } from '../audience' 8import { getActorsInvolvedInVideo, getAudienceFromFollowersOf, getRemoteVideoAudience } from '../audience'
9import { getServerActor } from '../../../helpers/utils' 9import { getServerActor } from '../../../helpers/utils'
10 10
11async function sendVideoRelatedActivity (activityBuilder: (audience: ActivityAudience) => Activity, options: {
12 byActor: ActorModel,
13 video: VideoModel,
14 transaction?: Transaction
15}) {
16 const actorsInvolvedInVideo = await getActorsInvolvedInVideo(options.video, options.transaction)
17
18 // Send to origin
19 if (options.video.isOwned() === false) {
20 const audience = getRemoteVideoAudience(options.video, actorsInvolvedInVideo)
21 const activity = activityBuilder(audience)
22
23 return unicastTo(activity, options.byActor, options.video.VideoChannel.Account.Actor.sharedInboxUrl)
24 }
25
26 // Send to followers
27 const audience = getAudienceFromFollowersOf(actorsInvolvedInVideo)
28 const activity = activityBuilder(audience)
29
30 const actorsException = [ options.byActor ]
31 return broadcastToFollowers(activity, options.byActor, actorsInvolvedInVideo, options.transaction, actorsException)
32}
33
11async function forwardVideoRelatedActivity ( 34async function forwardVideoRelatedActivity (
12 activity: Activity, 35 activity: Activity,
13 t: Transaction, 36 t: Transaction,
@@ -110,7 +133,8 @@ export {
110 unicastTo, 133 unicastTo,
111 forwardActivity, 134 forwardActivity,
112 broadcastToActors, 135 broadcastToActors,
113 forwardVideoRelatedActivity 136 forwardVideoRelatedActivity,
137 sendVideoRelatedActivity
114} 138}
115 139
116// --------------------------------------------------------------------------- 140// ---------------------------------------------------------------------------
diff --git a/server/tests/api/server/stats.ts b/server/tests/api/server/stats.ts
index d8a3268bb..cb229e876 100644
--- a/server/tests/api/server/stats.ts
+++ b/server/tests/api/server/stats.ts
@@ -65,7 +65,6 @@ describe('Test stats (excluding redundancy)', function () {
65 expect(data.totalVideos).to.equal(1) 65 expect(data.totalVideos).to.equal(1)
66 expect(data.totalInstanceFollowers).to.equal(2) 66 expect(data.totalInstanceFollowers).to.equal(2)
67 expect(data.totalInstanceFollowing).to.equal(1) 67 expect(data.totalInstanceFollowing).to.equal(1)
68 expect(data.videosRedundancy).to.have.lengthOf(0)
69 }) 68 })
70 69
71 it('Should have the correct stats on instance 2', async function () { 70 it('Should have the correct stats on instance 2', async function () {
@@ -80,7 +79,6 @@ describe('Test stats (excluding redundancy)', function () {
80 expect(data.totalVideos).to.equal(1) 79 expect(data.totalVideos).to.equal(1)
81 expect(data.totalInstanceFollowers).to.equal(1) 80 expect(data.totalInstanceFollowers).to.equal(1)
82 expect(data.totalInstanceFollowing).to.equal(1) 81 expect(data.totalInstanceFollowing).to.equal(1)
83 expect(data.videosRedundancy).to.have.lengthOf(0)
84 }) 82 })
85 83
86 it('Should have the correct stats on instance 3', async function () { 84 it('Should have the correct stats on instance 3', async function () {
@@ -95,7 +93,6 @@ describe('Test stats (excluding redundancy)', function () {
95 expect(data.totalVideos).to.equal(1) 93 expect(data.totalVideos).to.equal(1)
96 expect(data.totalInstanceFollowing).to.equal(1) 94 expect(data.totalInstanceFollowing).to.equal(1)
97 expect(data.totalInstanceFollowers).to.equal(0) 95 expect(data.totalInstanceFollowers).to.equal(0)
98 expect(data.videosRedundancy).to.have.lengthOf(0)
99 }) 96 })
100 97
101 after(async function () { 98 after(async function () {