aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/lib/activitypub
diff options
context:
space:
mode:
Diffstat (limited to 'server/lib/activitypub')
-rw-r--r--server/lib/activitypub/actor.ts2
-rw-r--r--server/lib/activitypub/process/process-undo.ts25
-rw-r--r--server/lib/activitypub/process/process-update.ts10
-rw-r--r--server/lib/activitypub/send/send-undo.ts29
-rw-r--r--server/lib/activitypub/share.ts45
-rw-r--r--server/lib/activitypub/videos.ts13
6 files changed, 101 insertions, 23 deletions
diff --git a/server/lib/activitypub/actor.ts b/server/lib/activitypub/actor.ts
index b0cf9bb17..5773fc34f 100644
--- a/server/lib/activitypub/actor.ts
+++ b/server/lib/activitypub/actor.ts
@@ -353,7 +353,7 @@ async function saveVideoChannel (actor: ActorModel, result: FetchRemoteActorResu
353 return videoChannelCreated 353 return videoChannelCreated
354} 354}
355 355
356async function refreshActorIfNeeded (actor: ActorModel) { 356async function refreshActorIfNeeded (actor: ActorModel): Promise<ActorModel> {
357 if (!actor.isOutdated()) return actor 357 if (!actor.isOutdated()) return actor
358 358
359 try { 359 try {
diff --git a/server/lib/activitypub/process/process-undo.ts b/server/lib/activitypub/process/process-undo.ts
index 565e70289..9b024d15f 100644
--- a/server/lib/activitypub/process/process-undo.ts
+++ b/server/lib/activitypub/process/process-undo.ts
@@ -1,4 +1,4 @@
1import { ActivityFollow, ActivityLike, ActivityUndo } from '../../../../shared/models/activitypub' 1import { ActivityAnnounce, ActivityFollow, ActivityLike, ActivityUndo } from '../../../../shared/models/activitypub'
2import { DislikeObject } from '../../../../shared/models/activitypub/objects' 2import { DislikeObject } from '../../../../shared/models/activitypub/objects'
3import { getActorUrl } from '../../../helpers/activitypub' 3import { getActorUrl } from '../../../helpers/activitypub'
4import { retryTransactionWrapper } from '../../../helpers/database-utils' 4import { retryTransactionWrapper } from '../../../helpers/database-utils'
@@ -10,6 +10,7 @@ import { ActorModel } from '../../../models/activitypub/actor'
10import { ActorFollowModel } from '../../../models/activitypub/actor-follow' 10import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
11import { forwardActivity } from '../send/misc' 11import { forwardActivity } from '../send/misc'
12import { getOrCreateAccountAndVideoAndChannel } from '../videos' 12import { getOrCreateAccountAndVideoAndChannel } from '../videos'
13import { VideoShareModel } from '../../../models/video/video-share'
13 14
14async function processUndoActivity (activity: ActivityUndo) { 15async function processUndoActivity (activity: ActivityUndo) {
15 const activityToUndo = activity.object 16 const activityToUndo = activity.object
@@ -22,6 +23,8 @@ async function processUndoActivity (activity: ActivityUndo) {
22 return processUndoDislike(actorUrl, activity) 23 return processUndoDislike(actorUrl, activity)
23 } else if (activityToUndo.type === 'Follow') { 24 } else if (activityToUndo.type === 'Follow') {
24 return processUndoFollow(actorUrl, activityToUndo) 25 return processUndoFollow(actorUrl, activityToUndo)
26 } else if (activityToUndo.type === 'Announce') {
27 return processUndoAnnounce(actorUrl, activityToUndo)
25 } 28 }
26 29
27 logger.warn('Unknown activity object type %s -> %s when undo activity.', activityToUndo.type, { activity: activity.id }) 30 logger.warn('Unknown activity object type %s -> %s when undo activity.', activityToUndo.type, { activity: activity.id })
@@ -123,3 +126,23 @@ function undoFollow (actorUrl: string, followActivity: ActivityFollow) {
123 return undefined 126 return undefined
124 }) 127 })
125} 128}
129
130function processUndoAnnounce (actorUrl: string, announceActivity: ActivityAnnounce) {
131 const options = {
132 arguments: [ actorUrl, announceActivity ],
133 errorMessage: 'Cannot undo announce with many retries.'
134 }
135
136 return retryTransactionWrapper(undoAnnounce, options)
137}
138
139function undoAnnounce (actorUrl: string, announceActivity: ActivityAnnounce) {
140 return sequelizeTypescript.transaction(async t => {
141 const share = await VideoShareModel.loadByUrl(announceActivity.id, t)
142 if (!share) throw new Error(`'Unknown video share ${announceActivity.id}.`)
143
144 await share.destroy({ transaction: t })
145
146 return undefined
147 })
148}
diff --git a/server/lib/activitypub/process/process-update.ts b/server/lib/activitypub/process/process-update.ts
index 51e3cc4e3..0dd657c2b 100644
--- a/server/lib/activitypub/process/process-update.ts
+++ b/server/lib/activitypub/process/process-update.ts
@@ -14,7 +14,7 @@ import { VideoFileModel } from '../../../models/video/video-file'
14import { fetchAvatarIfExists, getOrCreateActorAndServerAndModel, updateActorAvatarInstance, updateActorInstance } from '../actor' 14import { fetchAvatarIfExists, getOrCreateActorAndServerAndModel, updateActorAvatarInstance, updateActorInstance } from '../actor'
15import { 15import {
16 generateThumbnailFromUrl, 16 generateThumbnailFromUrl,
17 getOrCreateAccountAndVideoAndChannel, 17 getOrCreateAccountAndVideoAndChannel, getOrCreateVideoChannel,
18 videoActivityObjectToDBAttributes, 18 videoActivityObjectToDBAttributes,
19 videoFileActivityUrlToDBAttributes 19 videoFileActivityUrlToDBAttributes
20} from '../videos' 20} from '../videos'
@@ -54,6 +54,10 @@ async function updateRemoteVideo (actor: ActorModel, activity: ActivityUpdate) {
54 54
55 const res = await getOrCreateAccountAndVideoAndChannel(videoAttributesToUpdate.id) 55 const res = await getOrCreateAccountAndVideoAndChannel(videoAttributesToUpdate.id)
56 56
57 // Fetch video channel outside the transaction
58 const newVideoChannelActor = await getOrCreateVideoChannel(videoAttributesToUpdate)
59 const newVideoChannel = newVideoChannelActor.VideoChannel
60
57 logger.debug('Updating remote video "%s".', videoAttributesToUpdate.uuid) 61 logger.debug('Updating remote video "%s".', videoAttributesToUpdate.uuid)
58 let videoInstance = res.video 62 let videoInstance = res.video
59 let videoFieldsSave: any 63 let videoFieldsSave: any
@@ -66,12 +70,13 @@ async function updateRemoteVideo (actor: ActorModel, activity: ActivityUpdate) {
66 70
67 videoFieldsSave = videoInstance.toJSON() 71 videoFieldsSave = videoInstance.toJSON()
68 72
73 // Check actor has the right to update the video
69 const videoChannel = videoInstance.VideoChannel 74 const videoChannel = videoInstance.VideoChannel
70 if (videoChannel.Account.Actor.id !== actor.id) { 75 if (videoChannel.Account.Actor.id !== actor.id) {
71 throw new Error('Account ' + actor.url + ' does not own video channel ' + videoChannel.Actor.url) 76 throw new Error('Account ' + actor.url + ' does not own video channel ' + videoChannel.Actor.url)
72 } 77 }
73 78
74 const videoData = await videoActivityObjectToDBAttributes(videoChannel, videoAttributesToUpdate, activity.to) 79 const videoData = await videoActivityObjectToDBAttributes(newVideoChannel, videoAttributesToUpdate, activity.to)
75 videoInstance.set('name', videoData.name) 80 videoInstance.set('name', videoData.name)
76 videoInstance.set('uuid', videoData.uuid) 81 videoInstance.set('uuid', videoData.uuid)
77 videoInstance.set('url', videoData.url) 82 videoInstance.set('url', videoData.url)
@@ -87,6 +92,7 @@ async function updateRemoteVideo (actor: ActorModel, activity: ActivityUpdate) {
87 videoInstance.set('updatedAt', videoData.updatedAt) 92 videoInstance.set('updatedAt', videoData.updatedAt)
88 videoInstance.set('views', videoData.views) 93 videoInstance.set('views', videoData.views)
89 videoInstance.set('privacy', videoData.privacy) 94 videoInstance.set('privacy', videoData.privacy)
95 videoInstance.set('channelId', videoData.channelId)
90 96
91 await videoInstance.save(sequelizeOptions) 97 await videoInstance.save(sequelizeOptions)
92 98
diff --git a/server/lib/activitypub/send/send-undo.ts b/server/lib/activitypub/send/send-undo.ts
index bd49d452e..adee2192f 100644
--- a/server/lib/activitypub/send/send-undo.ts
+++ b/server/lib/activitypub/send/send-undo.ts
@@ -1,5 +1,12 @@
1import { Transaction } from 'sequelize' 1import { Transaction } from 'sequelize'
2import { ActivityAudience, ActivityCreate, ActivityFollow, ActivityLike, ActivityUndo } from '../../../../shared/models/activitypub' 2import {
3 ActivityAnnounce,
4 ActivityAudience,
5 ActivityCreate,
6 ActivityFollow,
7 ActivityLike,
8 ActivityUndo
9} from '../../../../shared/models/activitypub'
3import { ActorModel } from '../../../models/activitypub/actor' 10import { ActorModel } from '../../../models/activitypub/actor'
4import { ActorFollowModel } from '../../../models/activitypub/actor-follow' 11import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
5import { VideoModel } from '../../../models/video/video' 12import { VideoModel } from '../../../models/video/video'
@@ -16,6 +23,8 @@ import {
16import { createActivityData, createDislikeActivityData } from './send-create' 23import { createActivityData, createDislikeActivityData } from './send-create'
17import { followActivityData } from './send-follow' 24import { followActivityData } from './send-follow'
18import { likeActivityData } from './send-like' 25import { likeActivityData } from './send-like'
26import { VideoShareModel } from '../../../models/video/video-share'
27import { buildVideoAnnounce } from './send-announce'
19 28
20async function sendUndoFollow (actorFollow: ActorFollowModel, t: Transaction) { 29async function sendUndoFollow (actorFollow: ActorFollowModel, t: Transaction) {
21 const me = actorFollow.ActorFollower 30 const me = actorFollow.ActorFollower
@@ -58,7 +67,7 @@ async function sendUndoDislike (byActor: ActorModel, video: VideoModel, t: Trans
58 67
59 const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t) 68 const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t)
60 const dislikeActivity = createDislikeActivityData(byActor, video) 69 const dislikeActivity = createDislikeActivityData(byActor, video)
61 const object = await createActivityData(undoUrl, byActor, dislikeActivity, t) 70 const object = await createActivityData(dislikeUrl, byActor, dislikeActivity, t)
62 71
63 if (video.isOwned() === false) { 72 if (video.isOwned() === false) {
64 const audience = getOriginVideoAudience(video, actorsInvolvedInVideo) 73 const audience = getOriginVideoAudience(video, actorsInvolvedInVideo)
@@ -73,12 +82,24 @@ async function sendUndoDislike (byActor: ActorModel, video: VideoModel, t: Trans
73 return broadcastToFollowers(data, byActor, actorsInvolvedInVideo, t, followersException) 82 return broadcastToFollowers(data, byActor, actorsInvolvedInVideo, t, followersException)
74} 83}
75 84
85async function sendUndoAnnounce (byActor: ActorModel, videoShare: VideoShareModel, video: VideoModel, t: Transaction) {
86 const undoUrl = getUndoActivityPubUrl(videoShare.url)
87
88 const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t)
89 const object = await buildVideoAnnounce(byActor, videoShare, video, t)
90 const data = await undoActivityData(undoUrl, byActor, object, t)
91
92 const followersException = [ byActor ]
93 return broadcastToFollowers(data, byActor, actorsInvolvedInVideo, t, followersException)
94}
95
76// --------------------------------------------------------------------------- 96// ---------------------------------------------------------------------------
77 97
78export { 98export {
79 sendUndoFollow, 99 sendUndoFollow,
80 sendUndoLike, 100 sendUndoLike,
81 sendUndoDislike 101 sendUndoDislike,
102 sendUndoAnnounce
82} 103}
83 104
84// --------------------------------------------------------------------------- 105// ---------------------------------------------------------------------------
@@ -86,7 +107,7 @@ export {
86async function undoActivityData ( 107async function undoActivityData (
87 url: string, 108 url: string,
88 byActor: ActorModel, 109 byActor: ActorModel,
89 object: ActivityFollow | ActivityLike | ActivityCreate, 110 object: ActivityFollow | ActivityLike | ActivityCreate | ActivityAnnounce,
90 t: Transaction, 111 t: Transaction,
91 audience?: ActivityAudience 112 audience?: ActivityAudience
92): Promise<ActivityUndo> { 113): Promise<ActivityUndo> {
diff --git a/server/lib/activitypub/share.ts b/server/lib/activitypub/share.ts
index f256f8d21..698414867 100644
--- a/server/lib/activitypub/share.ts
+++ b/server/lib/activitypub/share.ts
@@ -3,16 +3,37 @@ import { VideoPrivacy } from '../../../shared/models/videos'
3import { getServerActor } from '../../helpers/utils' 3import { getServerActor } from '../../helpers/utils'
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 { sendVideoAnnounce } from './send' 6import { sendUndoAnnounce, sendVideoAnnounce } from './send'
7import { getAnnounceActivityPubUrl } from './url' 7import { getAnnounceActivityPubUrl } from './url'
8import { VideoChannelModel } from '../../models/video/video-channel'
8 9
9async function shareVideoByServerAndChannel (video: VideoModel, t: Transaction) { 10async function shareVideoByServerAndChannel (video: VideoModel, t: Transaction) {
10 if (video.privacy === VideoPrivacy.PRIVATE) return undefined 11 if (video.privacy === VideoPrivacy.PRIVATE) return undefined
11 12
13 return Promise.all([
14 shareByServer(video, t),
15 shareByVideoChannel(video, t)
16 ])
17}
18
19async function changeVideoChannelShare (video: VideoModel, oldVideoChannel: VideoChannelModel, t: Transaction) {
20 await undoShareByVideoChannel(video, oldVideoChannel, t)
21
22 await shareByVideoChannel(video, t)
23}
24
25export {
26 changeVideoChannelShare,
27 shareVideoByServerAndChannel
28}
29
30// ---------------------------------------------------------------------------
31
32async function shareByServer (video: VideoModel, t: Transaction) {
12 const serverActor = await getServerActor() 33 const serverActor = await getServerActor()
13 34
14 const serverShareUrl = getAnnounceActivityPubUrl(video.url, serverActor) 35 const serverShareUrl = getAnnounceActivityPubUrl(video.url, serverActor)
15 const serverSharePromise = VideoShareModel.findOrCreate({ 36 return VideoShareModel.findOrCreate({
16 defaults: { 37 defaults: {
17 actorId: serverActor.id, 38 actorId: serverActor.id,
18 videoId: video.id, 39 videoId: video.id,
@@ -27,9 +48,11 @@ async function shareVideoByServerAndChannel (video: VideoModel, t: Transaction)
27 48
28 return undefined 49 return undefined
29 }) 50 })
51}
30 52
53async function shareByVideoChannel (video: VideoModel, t: Transaction) {
31 const videoChannelShareUrl = getAnnounceActivityPubUrl(video.url, video.VideoChannel.Actor) 54 const videoChannelShareUrl = getAnnounceActivityPubUrl(video.url, video.VideoChannel.Actor)
32 const videoChannelSharePromise = VideoShareModel.findOrCreate({ 55 return VideoShareModel.findOrCreate({
33 defaults: { 56 defaults: {
34 actorId: video.VideoChannel.actorId, 57 actorId: video.VideoChannel.actorId,
35 videoId: video.id, 58 videoId: video.id,
@@ -40,17 +63,17 @@ async function shareVideoByServerAndChannel (video: VideoModel, t: Transaction)
40 }, 63 },
41 transaction: t 64 transaction: t
42 }).then(([ videoChannelShare, created ]) => { 65 }).then(([ videoChannelShare, created ]) => {
43 if (created) return sendVideoAnnounce(serverActor, videoChannelShare, video, t) 66 if (created) return sendVideoAnnounce(video.VideoChannel.Actor, videoChannelShare, video, t)
44 67
45 return undefined 68 return undefined
46 }) 69 })
47
48 return Promise.all([
49 serverSharePromise,
50 videoChannelSharePromise
51 ])
52} 70}
53 71
54export { 72async function undoShareByVideoChannel (video: VideoModel, oldVideoChannel: VideoChannelModel, t: Transaction) {
55 shareVideoByServerAndChannel 73 // Load old share
74 const oldShare = await VideoShareModel.load(oldVideoChannel.actorId, video.id, t)
75 if (!oldShare) return new Error('Cannot find old video channel share ' + oldVideoChannel.actorId + ' for video ' + video.id)
76
77 await sendUndoAnnounce(oldVideoChannel.Actor, oldShare, video, t)
78 await oldShare.destroy({ transaction: t })
56} 79}
diff --git a/server/lib/activitypub/videos.ts b/server/lib/activitypub/videos.ts
index b81acbb35..2899acff3 100644
--- a/server/lib/activitypub/videos.ts
+++ b/server/lib/activitypub/videos.ts
@@ -137,6 +137,13 @@ function videoFileActivityUrlToDBAttributes (videoCreated: VideoModel, videoObje
137 return attributes 137 return attributes
138} 138}
139 139
140function getOrCreateVideoChannel (videoObject: VideoTorrentObject) {
141 const channel = videoObject.attributedTo.find(a => a.type === 'Group')
142 if (!channel) throw new Error('Cannot find associated video channel to video ' + videoObject.url)
143
144 return getOrCreateActorAndServerAndModel(channel.id)
145}
146
140async function getOrCreateVideo (videoObject: VideoTorrentObject, channelActor: ActorModel) { 147async function getOrCreateVideo (videoObject: VideoTorrentObject, channelActor: ActorModel) {
141 logger.debug('Adding remote video %s.', videoObject.id) 148 logger.debug('Adding remote video %s.', videoObject.id)
142 149
@@ -199,10 +206,7 @@ async function getOrCreateAccountAndVideoAndChannel (videoObject: VideoTorrentOb
199 actor = await getOrCreateActorAndServerAndModel(actorObj.id) 206 actor = await getOrCreateActorAndServerAndModel(actorObj.id)
200 } 207 }
201 208
202 const channel = videoObject.attributedTo.find(a => a.type === 'Group') 209 const channelActor = await getOrCreateVideoChannel(videoObject)
203 if (!channel) throw new Error('Cannot find associated video channel to video ' + videoObject.url)
204
205 const channelActor = await getOrCreateActorAndServerAndModel(channel.id)
206 210
207 const options = { 211 const options = {
208 arguments: [ videoObject, channelActor ], 212 arguments: [ videoObject, channelActor ],
@@ -301,6 +305,7 @@ export {
301 videoActivityObjectToDBAttributes, 305 videoActivityObjectToDBAttributes,
302 videoFileActivityUrlToDBAttributes, 306 videoFileActivityUrlToDBAttributes,
303 getOrCreateVideo, 307 getOrCreateVideo,
308 getOrCreateVideoChannel,
304 addVideoShares} 309 addVideoShares}
305 310
306// --------------------------------------------------------------------------- 311// ---------------------------------------------------------------------------