1 import * as Bluebird from 'bluebird'
2 import { ActivityUpdate } from '../../../../shared/models/activitypub'
3 import { ActivityPubActor } from '../../../../shared/models/activitypub/activitypub-actor'
4 import { VideoTorrentObject } from '../../../../shared/models/activitypub/objects'
5 import { retryTransactionWrapper } from '../../../helpers/database-utils'
6 import { logger } from '../../../helpers/logger'
7 import { resetSequelizeInstance } from '../../../helpers/utils'
8 import { sequelizeTypescript } from '../../../initializers'
9 import { AccountModel } from '../../../models/account/account'
10 import { ActorModel } from '../../../models/activitypub/actor'
11 import { TagModel } from '../../../models/video/tag'
12 import { VideoFileModel } from '../../../models/video/video-file'
13 import { fetchAvatarIfExists, getOrCreateActorAndServerAndModel, updateActorAvatarInstance, updateActorInstance } from '../actor'
14 import { getOrCreateAccountAndVideoAndChannel, videoActivityObjectToDBAttributes, videoFileActivityUrlToDBAttributes } from '../videos'
16 async function processUpdateActivity (activity: ActivityUpdate) {
17 const actor = await getOrCreateActorAndServerAndModel(activity.actor)
19 if (activity.object.type === 'Video') {
20 return processUpdateVideo(actor, activity)
21 } else if (activity.object.type === 'Person') {
22 return processUpdateAccount(actor, activity)
28 // ---------------------------------------------------------------------------
34 // ---------------------------------------------------------------------------
36 function processUpdateVideo (actor: ActorModel, activity: ActivityUpdate) {
38 arguments: [ actor, activity ],
39 errorMessage: 'Cannot update the remote video with many retries'
42 return retryTransactionWrapper(updateRemoteVideo, options)
45 async function updateRemoteVideo (actor: ActorModel, activity: ActivityUpdate) {
46 const videoAttributesToUpdate = activity.object as VideoTorrentObject
48 const res = await getOrCreateAccountAndVideoAndChannel(videoAttributesToUpdate.id)
50 logger.debug('Updating remote video "%s".', videoAttributesToUpdate.uuid)
51 let videoInstance = res.video
52 let videoFieldsSave: any
55 await sequelizeTypescript.transaction(async t => {
56 const sequelizeOptions = {
60 videoFieldsSave = videoInstance.toJSON()
62 const videoChannel = videoInstance.VideoChannel
63 if (videoChannel.Account.Actor.id !== actor.id) {
64 throw new Error('Account ' + actor.url + ' does not own video channel ' + videoChannel.Actor.url)
67 const videoData = await videoActivityObjectToDBAttributes(videoChannel, videoAttributesToUpdate, activity.to, activity.cc)
68 videoInstance.set('name', videoData.name)
69 videoInstance.set('category', videoData.category)
70 videoInstance.set('licence', videoData.licence)
71 videoInstance.set('language', videoData.language)
72 videoInstance.set('nsfw', videoData.nsfw)
73 videoInstance.set('commentsEnabled', videoData.commentsEnabled)
74 videoInstance.set('privacy', videoData.privacy)
75 videoInstance.set('description', videoData.description)
76 videoInstance.set('duration', videoData.duration)
77 videoInstance.set('createdAt', videoData.createdAt)
78 videoInstance.set('updatedAt', videoData.updatedAt)
79 videoInstance.set('views', videoData.views)
81 await videoInstance.save(sequelizeOptions)
83 // Remove old video files
84 const videoFileDestroyTasks: Bluebird<void>[] = []
85 for (const videoFile of videoInstance.VideoFiles) {
86 videoFileDestroyTasks.push(videoFile.destroy(sequelizeOptions))
88 await Promise.all(videoFileDestroyTasks)
90 const videoFileAttributes = videoFileActivityUrlToDBAttributes(videoInstance, videoAttributesToUpdate)
91 const tasks: Bluebird<any>[] = videoFileAttributes.map(f => VideoFileModel.create(f))
92 await Promise.all(tasks)
94 const tags = videoAttributesToUpdate.tag.map(t => t.name)
95 const tagInstances = await TagModel.findOrCreateTags(tags, t)
96 await videoInstance.$set('Tags', tagInstances, sequelizeOptions)
99 logger.info('Remote video with uuid %s updated', videoAttributesToUpdate.uuid)
101 if (videoInstance !== undefined && videoFieldsSave !== undefined) {
102 resetSequelizeInstance(videoInstance, videoFieldsSave)
105 // This is just a debug because we will retry the insert
106 logger.debug('Cannot update the remote video.', err)
111 function processUpdateAccount (actor: ActorModel, activity: ActivityUpdate) {
113 arguments: [ actor, activity ],
114 errorMessage: 'Cannot update the remote account with many retries'
117 return retryTransactionWrapper(updateRemoteAccount, options)
120 async function updateRemoteAccount (actor: ActorModel, activity: ActivityUpdate) {
121 const accountAttributesToUpdate = activity.object as ActivityPubActor
123 logger.debug('Updating remote account "%s".', accountAttributesToUpdate.uuid)
124 let accountInstance: AccountModel
125 let actorFieldsSave: object
126 let accountFieldsSave: object
129 const avatarName = await fetchAvatarIfExists(accountAttributesToUpdate)
132 await sequelizeTypescript.transaction(async t => {
133 actorFieldsSave = actor.toJSON()
134 accountInstance = actor.Account
135 accountFieldsSave = actor.Account.toJSON()
137 await updateActorInstance(actor, accountAttributesToUpdate)
139 if (avatarName !== undefined) {
140 await updateActorAvatarInstance(actor, avatarName, t)
143 await actor.save({ transaction: t })
145 actor.Account.set('name', accountAttributesToUpdate.name || accountAttributesToUpdate.preferredUsername)
146 await actor.Account.save({ transaction: t })
149 logger.info('Remote account with uuid %s updated', accountAttributesToUpdate.uuid)
151 if (actor !== undefined && actorFieldsSave !== undefined) {
152 resetSequelizeInstance(actor, actorFieldsSave)
155 if (accountInstance !== undefined && accountFieldsSave !== undefined) {
156 resetSequelizeInstance(accountInstance, accountFieldsSave)
159 // This is just a debug because we will retry the insert
160 logger.debug('Cannot update the remote account.', err)