Redis.Instance.setIPVideoView(ip, videoInstance.uuid)
])
- const serverAccount = await getServerActor()
+ const serverActor = await getServerActor()
- await sendCreateView(serverAccount, videoInstance, undefined)
+ await sendCreateView(serverActor, videoInstance, undefined)
return res.status(204).end()
}
import { VideoCommentModel } from '../../models/video/video-comment'
import { VideoShareModel } from '../../models/video/video-share'
-function getVideoAudience (video: VideoModel, actorsInvolvedInVideo: ActorModel[]) {
+function getRemoteVideoAudience (video: VideoModel, actorsInvolvedInVideo: ActorModel[]): ActivityAudience {
return {
to: [ video.VideoChannel.Account.Actor.url ],
cc: actorsInvolvedInVideo.map(a => a.followersUrl)
threadParentComments: VideoCommentModel[],
actorsInvolvedInVideo: ActorModel[],
isOrigin = false
-) {
+): ActivityAudience {
const to = [ ACTIVITY_PUB.PUBLIC ]
const cc: string[] = []
}
}
-function getObjectFollowersAudience (actorsInvolvedInObject: ActorModel[]) {
+function getAudienceFromFollowersOf (actorsInvolvedInObject: ActorModel[]): ActivityAudience {
return {
to: [ ACTIVITY_PUB.PUBLIC ].concat(actorsInvolvedInObject.map(a => a.followersUrl)),
cc: []
export {
buildAudience,
getAudience,
- getVideoAudience,
+ getRemoteVideoAudience,
getActorsInvolvedInVideo,
- getObjectFollowersAudience,
+ getAudienceFromFollowersOf,
audiencify,
getVideoCommentAudience
}
{
const videoInstance = await VideoModel.loadByUrlAndPopulateAccount(objectUrl)
if (videoInstance) {
+ if (videoInstance.isOwned()) throw new Error(`Remote instance cannot delete owned video ${videoInstance.url}.`)
+
return retryTransactionWrapper(processDeleteVideo, actor, videoInstance)
}
}
import { VideoModel } from '../../../models/video/video'
import { VideoShareModel } from '../../../models/video/video-share'
import { broadcastToFollowers } from './utils'
-import { audiencify, getActorsInvolvedInVideo, getAudience, getObjectFollowersAudience } from '../audience'
+import { audiencify, getActorsInvolvedInVideo, getAudience, getAudienceFromFollowersOf } from '../audience'
import { logger } from '../../../helpers/logger'
async function buildAnnounceWithVideoAudience (byActor: ActorModel, videoShare: VideoShareModel, video: VideoModel, t: Transaction) {
const announcedObject = video.url
const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t)
- const audience = getObjectFollowersAudience(actorsInvolvedInVideo)
+ const audience = getAudienceFromFollowersOf(actorsInvolvedInVideo)
const activity = buildAnnounceActivity(videoShare.url, byActor, announcedObject, audience)
import { Transaction } from 'sequelize'
import { ActivityAudience, ActivityCreate } from '../../../../shared/models/activitypub'
import { VideoPrivacy } from '../../../../shared/models/videos'
-import { getServerActor } from '../../../helpers/utils'
import { ActorModel } from '../../../models/activitypub/actor'
import { VideoModel } from '../../../models/video/video'
import { VideoAbuseModel } from '../../../models/video/video-abuse'
import { VideoCommentModel } from '../../../models/video/video-comment'
import { getVideoAbuseActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoViewActivityPubUrl } from '../url'
-import { broadcastToActors, broadcastToFollowers, unicastTo } from './utils'
-import {
- audiencify,
- getActorsInvolvedInVideo,
- getAudience,
- getObjectFollowersAudience,
- getVideoAudience,
- getVideoCommentAudience
-} from '../audience'
+import { broadcastToActors, broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils'
+import { audiencify, getActorsInvolvedInVideo, getAudience, getAudienceFromFollowersOf, getVideoCommentAudience } from '../audience'
import { logger } from '../../../helpers/logger'
import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy'
logger.info('Creating job to send video abuse %s.', url)
+ // Custom audience, we only send the abuse to the origin instance
const audience = { to: [ video.VideoChannel.Account.Actor.url ], cc: [] }
const createActivity = buildCreateActivity(url, byActor, videoAbuse.toActivityPubObject(), audience)
async function sendCreateCacheFile (byActor: ActorModel, fileRedundancy: VideoRedundancyModel) {
logger.info('Creating job to send file cache of %s.', fileRedundancy.url)
- const redundancyObject = fileRedundancy.toActivityPubObject()
-
const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(fileRedundancy.VideoFile.Video.id)
- const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, undefined)
-
- const audience = getVideoAudience(video, actorsInvolvedInVideo)
- const createActivity = buildCreateActivity(fileRedundancy.url, byActor, redundancyObject, audience)
+ const redundancyObject = fileRedundancy.toActivityPubObject()
- return unicastTo(createActivity, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl)
+ return sendVideoRelatedCreateActivity({
+ byActor,
+ video,
+ url: fileRedundancy.url,
+ object: redundancyObject
+ })
}
async function sendCreateVideoComment (comment: VideoCommentModel, t: Transaction) {
const commentObject = comment.toActivityPubObject(threadParentComments)
const actorsInvolvedInComment = await getActorsInvolvedInVideo(comment.Video, t)
+ // Add the actor that commented too
actorsInvolvedInComment.push(byActor)
const parentsCommentActors = threadParentComments.map(c => c.Account.Actor)
if (isOrigin) {
audience = getVideoCommentAudience(comment, threadParentComments, actorsInvolvedInComment, isOrigin)
} else {
- audience = getObjectFollowersAudience(actorsInvolvedInComment.concat(parentsCommentActors))
+ audience = getAudienceFromFollowersOf(actorsInvolvedInComment.concat(parentsCommentActors))
}
const createActivity = buildCreateActivity(comment.url, byActor, commentObject, audience)
const url = getVideoViewActivityPubUrl(byActor, video)
const viewActivity = buildViewActivity(byActor, video)
- const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t)
-
- // Send to origin
- if (video.isOwned() === false) {
- const audience = getVideoAudience(video, actorsInvolvedInVideo)
- const createActivity = buildCreateActivity(url, byActor, viewActivity, audience)
-
- return unicastTo(createActivity, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl)
- }
-
- // Send to followers
- const audience = getObjectFollowersAudience(actorsInvolvedInVideo)
- const createActivity = buildCreateActivity(url, byActor, viewActivity, audience)
-
- // Use the server actor to send the view
- const serverActor = await getServerActor()
- const actorsException = [ byActor ]
- return broadcastToFollowers(createActivity, serverActor, actorsInvolvedInVideo, t, actorsException)
+ return sendVideoRelatedCreateActivity({
+ // Use the server actor to send the view
+ byActor,
+ video,
+ url,
+ object: viewActivity,
+ transaction: t
+ })
}
async function sendCreateDislike (byActor: ActorModel, video: VideoModel, t: Transaction) {
const url = getVideoDislikeActivityPubUrl(byActor, video)
const dislikeActivity = buildDislikeActivity(byActor, video)
- const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t)
-
- // Send to origin
- if (video.isOwned() === false) {
- const audience = getVideoAudience(video, actorsInvolvedInVideo)
- const createActivity = buildCreateActivity(url, byActor, dislikeActivity, audience)
-
- return unicastTo(createActivity, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl)
- }
-
- // Send to followers
- const audience = getObjectFollowersAudience(actorsInvolvedInVideo)
- const createActivity = buildCreateActivity(url, byActor, dislikeActivity, audience)
-
- const actorsException = [ byActor ]
- return broadcastToFollowers(createActivity, byActor, actorsInvolvedInVideo, t, actorsException)
+ return sendVideoRelatedCreateActivity({
+ byActor,
+ video,
+ url,
+ object: dislikeActivity,
+ transaction: t
+ })
}
function buildCreateActivity (url: string, byActor: ActorModel, object: any, audience?: ActivityAudience): ActivityCreate {
sendCreateVideoComment,
sendCreateCacheFile
}
+
+// ---------------------------------------------------------------------------
+
+async function sendVideoRelatedCreateActivity (options: {
+ byActor: ActorModel,
+ video: VideoModel,
+ url: string,
+ object: any,
+ transaction?: Transaction
+}) {
+ const activityBuilder = (audience: ActivityAudience) => {
+ return buildCreateActivity(options.url, options.byActor, options.object, audience)
+ }
+
+ return sendVideoRelatedActivity(activityBuilder, options)
+}
import { VideoCommentModel } from '../../../models/video/video-comment'
import { VideoShareModel } from '../../../models/video/video-share'
import { getDeleteActivityPubUrl } from '../url'
-import { broadcastToActors, broadcastToFollowers, unicastTo } from './utils'
+import { broadcastToActors, broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils'
import { audiencify, getActorsInvolvedInVideo, getVideoCommentAudience } from '../audience'
import { logger } from '../../../helpers/logger'
-async function sendDeleteVideo (video: VideoModel, t: Transaction) {
+async function sendDeleteVideo (video: VideoModel, transaction: Transaction) {
logger.info('Creating job to broadcast delete of video %s.', video.url)
- const url = getDeleteActivityPubUrl(video.url)
const byActor = video.VideoChannel.Account.Actor
- const activity = buildDeleteActivity(url, video.url, byActor)
+ const activityBuilder = (audience: ActivityAudience) => {
+ const url = getDeleteActivityPubUrl(video.url)
- const actorsInvolved = await getActorsInvolvedInVideo(video, t)
+ return buildDeleteActivity(url, video.url, byActor, audience)
+ }
- return broadcastToFollowers(activity, byActor, actorsInvolved, t)
+ return sendVideoRelatedActivity(activityBuilder, { byActor, video, transaction })
}
async function sendDeleteActor (byActor: ActorModel, t: Transaction) {
import { ActorModel } from '../../../models/activitypub/actor'
import { VideoModel } from '../../../models/video/video'
import { getVideoLikeActivityPubUrl } from '../url'
-import { broadcastToFollowers, unicastTo } from './utils'
-import { audiencify, getActorsInvolvedInVideo, getAudience, getObjectFollowersAudience, getVideoAudience } from '../audience'
+import { sendVideoRelatedActivity } from './utils'
+import { audiencify, getAudience } from '../audience'
import { logger } from '../../../helpers/logger'
async function sendLike (byActor: ActorModel, video: VideoModel, t: Transaction) {
logger.info('Creating job to like %s.', video.url)
- const url = getVideoLikeActivityPubUrl(byActor, video)
+ const activityBuilder = (audience: ActivityAudience) => {
+ const url = getVideoLikeActivityPubUrl(byActor, video)
- const accountsInvolvedInVideo = await getActorsInvolvedInVideo(video, t)
-
- // Send to origin
- if (video.isOwned() === false) {
- const audience = getVideoAudience(video, accountsInvolvedInVideo)
- const data = buildLikeActivity(url, byActor, video, audience)
-
- return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl)
+ return buildLikeActivity(url, byActor, video, audience)
}
- // Send to followers
- const audience = getObjectFollowersAudience(accountsInvolvedInVideo)
- const activity = buildLikeActivity(url, byActor, video, audience)
-
- const followersException = [ byActor ]
- return broadcastToFollowers(activity, byActor, accountsInvolvedInVideo, t, followersException)
+ return sendVideoRelatedActivity(activityBuilder, { byActor, video, transaction: t })
}
function buildLikeActivity (url: string, byActor: ActorModel, video: VideoModel, audience?: ActivityAudience): ActivityLike {
import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
import { VideoModel } from '../../../models/video/video'
import { getActorFollowActivityPubUrl, getUndoActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoLikeActivityPubUrl } from '../url'
-import { broadcastToFollowers, unicastTo } from './utils'
-import { audiencify, getActorsInvolvedInVideo, getAudience, getObjectFollowersAudience, getVideoAudience } from '../audience'
+import { broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils'
+import { audiencify, getAudience } from '../audience'
import { buildCreateActivity, buildDislikeActivity } from './send-create'
import { buildFollowActivity } from './send-follow'
import { buildLikeActivity } from './send-like'
return unicastTo(undoActivity, me, following.inboxUrl)
}
-async function sendUndoLike (byActor: ActorModel, video: VideoModel, t: Transaction) {
- logger.info('Creating job to undo a like of video %s.', video.url)
+async function sendUndoAnnounce (byActor: ActorModel, videoShare: VideoShareModel, video: VideoModel, t: Transaction) {
+ logger.info('Creating job to undo announce %s.', videoShare.url)
- const likeUrl = getVideoLikeActivityPubUrl(byActor, video)
- const undoUrl = getUndoActivityPubUrl(likeUrl)
+ const undoUrl = getUndoActivityPubUrl(videoShare.url)
- const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t)
- const likeActivity = buildLikeActivity(likeUrl, byActor, video)
+ const { activity: announceActivity, actorsInvolvedInVideo } = await buildAnnounceWithVideoAudience(byActor, videoShare, video, t)
+ const undoActivity = undoActivityData(undoUrl, byActor, announceActivity)
- // Send to origin
- if (video.isOwned() === false) {
- const audience = getVideoAudience(video, actorsInvolvedInVideo)
- const undoActivity = undoActivityData(undoUrl, byActor, likeActivity, audience)
+ const followersException = [ byActor ]
+ return broadcastToFollowers(undoActivity, byActor, actorsInvolvedInVideo, t, followersException)
+}
- return unicastTo(undoActivity, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl)
- }
+async function sendUndoLike (byActor: ActorModel, video: VideoModel, t: Transaction) {
+ logger.info('Creating job to undo a like of video %s.', video.url)
- const audience = getObjectFollowersAudience(actorsInvolvedInVideo)
- const undoActivity = undoActivityData(undoUrl, byActor, likeActivity, audience)
+ const likeUrl = getVideoLikeActivityPubUrl(byActor, video)
+ const likeActivity = buildLikeActivity(likeUrl, byActor, video)
- const followersException = [ byActor ]
- return broadcastToFollowers(undoActivity, byActor, actorsInvolvedInVideo, t, followersException)
+ return sendUndoVideoRelatedActivity({ byActor, video, url: likeUrl, activity: likeActivity, transaction: t })
}
async function sendUndoDislike (byActor: ActorModel, video: VideoModel, t: Transaction) {
logger.info('Creating job to undo a dislike of video %s.', video.url)
const dislikeUrl = getVideoDislikeActivityPubUrl(byActor, video)
- const undoUrl = getUndoActivityPubUrl(dislikeUrl)
-
- const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t)
const dislikeActivity = buildDislikeActivity(byActor, video)
const createDislikeActivity = buildCreateActivity(dislikeUrl, byActor, dislikeActivity)
- if (video.isOwned() === false) {
- const audience = getVideoAudience(video, actorsInvolvedInVideo)
- const undoActivity = undoActivityData(undoUrl, byActor, createDislikeActivity, audience)
-
- return unicastTo(undoActivity, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl)
- }
-
- const undoActivity = undoActivityData(undoUrl, byActor, createDislikeActivity)
-
- const followersException = [ byActor ]
- return broadcastToFollowers(undoActivity, byActor, actorsInvolvedInVideo, t, followersException)
-}
-
-async function sendUndoAnnounce (byActor: ActorModel, videoShare: VideoShareModel, video: VideoModel, t: Transaction) {
- logger.info('Creating job to undo announce %s.', videoShare.url)
-
- const undoUrl = getUndoActivityPubUrl(videoShare.url)
-
- const { activity: announceActivity, actorsInvolvedInVideo } = await buildAnnounceWithVideoAudience(byActor, videoShare, video, t)
- const undoActivity = undoActivityData(undoUrl, byActor, announceActivity)
-
- const followersException = [ byActor ]
- return broadcastToFollowers(undoActivity, byActor, actorsInvolvedInVideo, t, followersException)
+ return sendUndoVideoRelatedActivity({ byActor, video, url: dislikeUrl, activity: createDislikeActivity, transaction: t })
}
async function sendUndoCacheFile (byActor: ActorModel, redundancyModel: VideoRedundancyModel, t: Transaction) {
logger.info('Creating job to undo cache file %s.', redundancyModel.url)
- const undoUrl = getUndoActivityPubUrl(redundancyModel.url)
-
const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(redundancyModel.VideoFile.Video.id)
- const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t)
-
- const audience = getVideoAudience(video, actorsInvolvedInVideo)
const createActivity = buildCreateActivity(redundancyModel.url, byActor, redundancyModel.toActivityPubObject())
- const undoActivity = undoActivityData(undoUrl, byActor, createActivity, audience)
-
- return unicastTo(undoActivity, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl)
+ return sendUndoVideoRelatedActivity({ byActor, video, url: redundancyModel.url, activity: createActivity, transaction: t })
}
// ---------------------------------------------------------------------------
audience
)
}
+
+async function sendUndoVideoRelatedActivity (options: {
+ byActor: ActorModel,
+ video: VideoModel,
+ url: string,
+ activity: ActivityFollow | ActivityLike | ActivityCreate | ActivityAnnounce,
+ transaction: Transaction
+}) {
+ const activityBuilder = (audience: ActivityAudience) => {
+ const undoUrl = getUndoActivityPubUrl(options.url)
+
+ return undoActivityData(undoUrl, options.byActor, options.activity, audience)
+ }
+
+ return sendVideoRelatedActivity(activityBuilder, options)
+}
import { VideoChannelModel } from '../../../models/video/video-channel'
import { VideoShareModel } from '../../../models/video/video-share'
import { getUpdateActivityPubUrl } from '../url'
-import { broadcastToFollowers, unicastTo } from './utils'
-import { audiencify, getActorsInvolvedInVideo, getAudience, getObjectFollowersAudience } from '../audience'
+import { broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils'
+import { audiencify, getActorsInvolvedInVideo, getAudience, getAudienceFromFollowersOf } from '../audience'
import { logger } from '../../../helpers/logger'
import { VideoCaptionModel } from '../../../models/video/video-caption'
import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy'
async function sendUpdateCacheFile (byActor: ActorModel, redundancyModel: VideoRedundancyModel) {
logger.info('Creating job to update cache file %s.', redundancyModel.url)
- const url = getUpdateActivityPubUrl(redundancyModel.url, redundancyModel.updatedAt.toISOString())
const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(redundancyModel.VideoFile.Video.id)
- const redundancyObject = redundancyModel.toActivityPubObject()
+ const activityBuilder = (audience: ActivityAudience) => {
+ const redundancyObject = redundancyModel.toActivityPubObject()
+ const url = getUpdateActivityPubUrl(redundancyModel.url, redundancyModel.updatedAt.toISOString())
- const accountsInvolvedInVideo = await getActorsInvolvedInVideo(video, undefined)
- const audience = getObjectFollowersAudience(accountsInvolvedInVideo)
+ return buildUpdateActivity(url, byActor, redundancyObject, audience)
+ }
- const updateActivity = buildUpdateActivity(url, byActor, redundancyObject, audience)
- return unicastTo(updateActivity, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl)
+ return sendVideoRelatedActivity(activityBuilder, { byActor, video })
}
// ---------------------------------------------------------------------------
import { Transaction } from 'sequelize'
-import { Activity } from '../../../../shared/models/activitypub'
+import { Activity, ActivityAudience } from '../../../../shared/models/activitypub'
import { logger } from '../../../helpers/logger'
import { ActorModel } from '../../../models/activitypub/actor'
import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
import { JobQueue } from '../../job-queue'
import { VideoModel } from '../../../models/video/video'
-import { getActorsInvolvedInVideo } from '../audience'
+import { getActorsInvolvedInVideo, getAudienceFromFollowersOf, getRemoteVideoAudience } from '../audience'
import { getServerActor } from '../../../helpers/utils'
+async function sendVideoRelatedActivity (activityBuilder: (audience: ActivityAudience) => Activity, options: {
+ byActor: ActorModel,
+ video: VideoModel,
+ transaction?: Transaction
+}) {
+ const actorsInvolvedInVideo = await getActorsInvolvedInVideo(options.video, options.transaction)
+
+ // Send to origin
+ if (options.video.isOwned() === false) {
+ const audience = getRemoteVideoAudience(options.video, actorsInvolvedInVideo)
+ const activity = activityBuilder(audience)
+
+ return unicastTo(activity, options.byActor, options.video.VideoChannel.Account.Actor.sharedInboxUrl)
+ }
+
+ // Send to followers
+ const audience = getAudienceFromFollowersOf(actorsInvolvedInVideo)
+ const activity = activityBuilder(audience)
+
+ const actorsException = [ options.byActor ]
+ return broadcastToFollowers(activity, options.byActor, actorsInvolvedInVideo, options.transaction, actorsException)
+}
+
async function forwardVideoRelatedActivity (
activity: Activity,
t: Transaction,
unicastTo,
forwardActivity,
broadcastToActors,
- forwardVideoRelatedActivity
+ forwardVideoRelatedActivity,
+ sendVideoRelatedActivity
}
// ---------------------------------------------------------------------------
expect(data.totalVideos).to.equal(1)
expect(data.totalInstanceFollowers).to.equal(2)
expect(data.totalInstanceFollowing).to.equal(1)
- expect(data.videosRedundancy).to.have.lengthOf(0)
})
it('Should have the correct stats on instance 2', async function () {
expect(data.totalVideos).to.equal(1)
expect(data.totalInstanceFollowers).to.equal(1)
expect(data.totalInstanceFollowing).to.equal(1)
- expect(data.videosRedundancy).to.have.lengthOf(0)
})
it('Should have the correct stats on instance 3', async function () {
expect(data.totalVideos).to.equal(1)
expect(data.totalInstanceFollowing).to.equal(1)
expect(data.totalInstanceFollowers).to.equal(0)
- expect(data.videosRedundancy).to.have.lengthOf(0)
})
after(async function () {