1 import { ActivityCreate, CacheFileObject, VideoAbuseState, VideoTorrentObject } from '../../../../shared'
2 import { DislikeObject, VideoAbuseObject, ViewObject } from '../../../../shared/models/activitypub/objects'
3 import { VideoCommentObject } from '../../../../shared/models/activitypub/objects/video-comment-object'
4 import { retryTransactionWrapper } from '../../../helpers/database-utils'
5 import { logger } from '../../../helpers/logger'
6 import { sequelizeTypescript } from '../../../initializers'
7 import { AccountVideoRateModel } from '../../../models/account/account-video-rate'
8 import { ActorModel } from '../../../models/activitypub/actor'
9 import { VideoAbuseModel } from '../../../models/video/video-abuse'
10 import { addVideoComment, resolveThread } from '../video-comments'
11 import { getOrCreateVideoAndAccountAndChannel } from '../videos'
12 import { forwardVideoRelatedActivity } from '../send/utils'
13 import { Redis } from '../../redis'
14 import { createOrUpdateCacheFile } from '../cache-file'
15 import { immutableAssign } from '../../../tests/utils'
16 import { getVideoDislikeActivityPubUrl } from '../url'
17 import { VideoModel } from '../../../models/video/video'
19 async function processCreateActivity (activity: ActivityCreate, byActor: ActorModel) {
20 const activityObject = activity.object
21 const activityType = activityObject.type
23 if (activityType === 'View') {
24 return processCreateView(byActor, activity)
25 } else if (activityType === 'Dislike') {
26 return retryTransactionWrapper(processCreateDislike, byActor, activity)
27 } else if (activityType === 'Video') {
28 return processCreateVideo(activity)
29 } else if (activityType === 'Flag') {
30 return retryTransactionWrapper(processCreateVideoAbuse, byActor, activityObject as VideoAbuseObject)
31 } else if (activityType === 'Note') {
32 return retryTransactionWrapper(processCreateVideoComment, byActor, activity)
33 } else if (activityType === 'CacheFile') {
34 return retryTransactionWrapper(processCacheFile, byActor, activity)
37 logger.warn('Unknown activity object type %s when creating activity.', activityType, { activity: activity.id })
38 return Promise.resolve(undefined)
41 // ---------------------------------------------------------------------------
47 // ---------------------------------------------------------------------------
49 async function processCreateVideo (activity: ActivityCreate) {
50 const videoToCreateData = activity.object as VideoTorrentObject
52 const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: videoToCreateData })
57 async function processCreateDislike (byActor: ActorModel, activity: ActivityCreate) {
58 const dislike = activity.object as DislikeObject
59 const byAccount = byActor.Account
61 if (!byAccount) throw new Error('Cannot create dislike with the non account actor ' + byActor.url)
63 const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: dislike.object })
65 return sequelizeTypescript.transaction(async t => {
67 type: 'dislike' as 'dislike',
69 accountId: byAccount.id
72 const [ , created ] = await AccountVideoRateModel.findOrCreate({
74 defaults: immutableAssign(rate, { url: getVideoDislikeActivityPubUrl(byActor, video) }),
77 if (created === true) await video.increment('dislikes', { transaction: t })
79 if (video.isOwned() && created === true) {
80 // Don't resend the activity to the sender
81 const exceptions = [ byActor ]
83 await forwardVideoRelatedActivity(activity, t, exceptions, video)
88 async function processCreateView (byActor: ActorModel, activity: ActivityCreate) {
89 const view = activity.object as ViewObject
91 const video = await VideoModel.loadByUrl(view.object)
92 if (!video || video.isOwned() === false) return
94 await Redis.Instance.addVideoView(video.id)
97 async function processCacheFile (byActor: ActorModel, activity: ActivityCreate) {
98 const cacheFile = activity.object as CacheFileObject
100 const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: cacheFile.object })
102 await sequelizeTypescript.transaction(async t => {
103 return createOrUpdateCacheFile(cacheFile, video, byActor, t)
106 if (video.isOwned()) {
107 // Don't resend the activity to the sender
108 const exceptions = [ byActor ]
109 await forwardVideoRelatedActivity(activity, undefined, exceptions, video)
113 async function processCreateVideoAbuse (byActor: ActorModel, videoAbuseToCreateData: VideoAbuseObject) {
114 logger.debug('Reporting remote abuse for video %s.', videoAbuseToCreateData.object)
116 const account = byActor.Account
117 if (!account) throw new Error('Cannot create dislike with the non account actor ' + byActor.url)
119 const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: videoAbuseToCreateData.object })
121 return sequelizeTypescript.transaction(async t => {
122 const videoAbuseData = {
123 reporterAccountId: account.id,
124 reason: videoAbuseToCreateData.content,
126 state: VideoAbuseState.PENDING
129 await VideoAbuseModel.create(videoAbuseData, { transaction: t })
131 logger.info('Remote abuse for video uuid %s created', videoAbuseToCreateData.object)
135 async function processCreateVideoComment (byActor: ActorModel, activity: ActivityCreate) {
136 const commentObject = activity.object as VideoCommentObject
137 const byAccount = byActor.Account
139 if (!byAccount) throw new Error('Cannot create video comment with the non account actor ' + byActor.url)
141 const { video } = await resolveThread(commentObject.inReplyTo)
143 const { created } = await addVideoComment(video, commentObject.id)
145 if (video.isOwned() && created === true) {
146 // Don't resend the activity to the sender
147 const exceptions = [ byActor ]
149 await forwardVideoRelatedActivity(activity, undefined, exceptions, video)