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 { VideoFile } from '../../../../shared/models/videos'
6 import { retryTransactionWrapper } from '../../../helpers/database-utils'
7 import { logger } from '../../../helpers/logger'
8 import { resetSequelizeInstance } from '../../../helpers/utils'
9 import { sequelizeTypescript } from '../../../initializers'
10 import { AccountModel } from '../../../models/account/account'
11 import { ActorModel } from '../../../models/activitypub/actor'
12 import { TagModel } from '../../../models/video/tag'
13 import { VideoFileModel } from '../../../models/video/video-file'
14 import { fetchAvatarIfExists, getOrCreateActorAndServerAndModel, updateActorAvatarInstance, updateActorInstance } from '../actor'
15 import { getOrCreateAccountAndVideoAndChannel, videoActivityObjectToDBAttributes, videoFileActivityUrlToDBAttributes } from '../videos'
17 async function processUpdateActivity (activity: ActivityUpdate) {
18 const actor = await getOrCreateActorAndServerAndModel(activity.actor)
20 if (activity.object.type === 'Video') {
21 return processUpdateVideo(actor, activity)
22 } else if (activity.object.type === 'Person') {
23 return processUpdateAccount(actor, activity)
29 // ---------------------------------------------------------------------------
35 // ---------------------------------------------------------------------------
37 function processUpdateVideo (actor: ActorModel, activity: ActivityUpdate) {
39 arguments: [ actor, activity ],
40 errorMessage: 'Cannot update the remote video with many retries'
43 return retryTransactionWrapper(updateRemoteVideo, options)
46 async function updateRemoteVideo (actor: ActorModel, activity: ActivityUpdate) {
47 const videoAttributesToUpdate = activity.object as VideoTorrentObject
49 const res = await getOrCreateAccountAndVideoAndChannel(videoAttributesToUpdate.id)
51 logger.debug('Updating remote video "%s".', videoAttributesToUpdate.uuid)
52 let videoInstance = res.video
53 let videoFieldsSave: any
56 await sequelizeTypescript.transaction(async t => {
57 const sequelizeOptions = {
61 videoFieldsSave = videoInstance.toJSON()
63 const videoChannel = videoInstance.VideoChannel
64 if (videoChannel.Account.Actor.id !== actor.id) {
65 throw new Error('Account ' + actor.url + ' does not own video channel ' + videoChannel.Actor.url)
68 const videoData = await videoActivityObjectToDBAttributes(videoChannel, videoAttributesToUpdate, activity.to, activity.cc)
69 videoInstance.set('name', videoData.name)
70 videoInstance.set('uuid', videoData.uuid)
71 videoInstance.set('url', videoData.url)
72 videoInstance.set('category', videoData.category)
73 videoInstance.set('licence', videoData.licence)
74 videoInstance.set('language', videoData.language)
75 videoInstance.set('description', videoData.description)
76 videoInstance.set('nsfw', videoData.nsfw)
77 videoInstance.set('commentsEnabled', videoData.commentsEnabled)
78 videoInstance.set('duration', videoData.duration)
79 videoInstance.set('createdAt', videoData.createdAt)
80 videoInstance.set('updatedAt', videoData.updatedAt)
81 videoInstance.set('views', videoData.views)
82 videoInstance.set('privacy', videoData.privacy)
84 await videoInstance.save(sequelizeOptions)
86 // Remove old video files
87 const videoFileDestroyTasks: Bluebird<void>[] = []
88 for (const videoFile of videoInstance.VideoFiles) {
89 videoFileDestroyTasks.push(videoFile.destroy(sequelizeOptions))
91 await Promise.all(videoFileDestroyTasks)
93 const videoFileAttributes = videoFileActivityUrlToDBAttributes(videoInstance, videoAttributesToUpdate)
94 const tasks = videoFileAttributes.map(f => VideoFileModel.create(f))
95 await Promise.all(tasks)
97 const tags = videoAttributesToUpdate.tag.map(t => t.name)
98 const tagInstances = await TagModel.findOrCreateTags(tags, t)
99 await videoInstance.$set('Tags', tagInstances, sequelizeOptions)
102 logger.info('Remote video with uuid %s updated', videoAttributesToUpdate.uuid)
104 if (videoInstance !== undefined && videoFieldsSave !== undefined) {
105 resetSequelizeInstance(videoInstance, videoFieldsSave)
108 // This is just a debug because we will retry the insert
109 logger.debug('Cannot update the remote video.', err)
114 function processUpdateAccount (actor: ActorModel, activity: ActivityUpdate) {
116 arguments: [ actor, activity ],
117 errorMessage: 'Cannot update the remote account with many retries'
120 return retryTransactionWrapper(updateRemoteAccount, options)
123 async function updateRemoteAccount (actor: ActorModel, activity: ActivityUpdate) {
124 const accountAttributesToUpdate = activity.object as ActivityPubActor
126 logger.debug('Updating remote account "%s".', accountAttributesToUpdate.uuid)
127 let accountInstance: AccountModel
128 let actorFieldsSave: object
129 let accountFieldsSave: object
132 const avatarName = await fetchAvatarIfExists(accountAttributesToUpdate)
135 await sequelizeTypescript.transaction(async t => {
136 actorFieldsSave = actor.toJSON()
137 accountInstance = actor.Account
138 accountFieldsSave = actor.Account.toJSON()
140 await updateActorInstance(actor, accountAttributesToUpdate)
142 if (avatarName !== undefined) {
143 await updateActorAvatarInstance(actor, avatarName, t)
146 await actor.save({ transaction: t })
148 actor.Account.set('name', accountAttributesToUpdate.name || accountAttributesToUpdate.preferredUsername)
149 await actor.Account.save({ transaction: t })
152 logger.info('Remote account with uuid %s updated', accountAttributesToUpdate.uuid)
154 if (actor !== undefined && actorFieldsSave !== undefined) {
155 resetSequelizeInstance(actor, actorFieldsSave)
158 if (accountInstance !== undefined && accountFieldsSave !== undefined) {
159 resetSequelizeInstance(accountInstance, accountFieldsSave)
162 // This is just a debug because we will retry the insert
163 logger.debug('Cannot update the remote account.', err)