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('uuid', videoData.uuid)
70 videoInstance.set('url', videoData.url)
71 videoInstance.set('category', videoData.category)
72 videoInstance.set('licence', videoData.licence)
73 videoInstance.set('language', videoData.language)
74 videoInstance.set('description', videoData.description)
75 videoInstance.set('nsfw', videoData.nsfw)
76 videoInstance.set('commentsEnabled', videoData.commentsEnabled)
77 videoInstance.set('duration', videoData.duration)
78 videoInstance.set('createdAt', videoData.createdAt)
79 videoInstance.set('updatedAt', videoData.updatedAt)
80 videoInstance.set('views', videoData.views)
81 videoInstance.set('privacy', videoData.privacy)
83 await videoInstance.save(sequelizeOptions)
85 // Remove old video files
86 const videoFileDestroyTasks: Bluebird<void>[] = []
87 for (const videoFile of videoInstance.VideoFiles) {
88 videoFileDestroyTasks.push(videoFile.destroy(sequelizeOptions))
90 await Promise.all(videoFileDestroyTasks)
92 const videoFileAttributes = videoFileActivityUrlToDBAttributes(videoInstance, videoAttributesToUpdate)
93 const tasks = videoFileAttributes.map(f => VideoFileModel.create(f))
94 await Promise.all(tasks)
96 const tags = videoAttributesToUpdate.tag.map(t => t.name)
97 const tagInstances = await TagModel.findOrCreateTags(tags, t)
98 await videoInstance.$set('Tags', tagInstances, sequelizeOptions)
101 logger.info('Remote video with uuid %s updated', videoAttributesToUpdate.uuid)
103 if (videoInstance !== undefined && videoFieldsSave !== undefined) {
104 resetSequelizeInstance(videoInstance, videoFieldsSave)
107 // This is just a debug because we will retry the insert
108 logger.debug('Cannot update the remote video.', err)
113 function processUpdateAccount (actor: ActorModel, activity: ActivityUpdate) {
115 arguments: [ actor, activity ],
116 errorMessage: 'Cannot update the remote account with many retries'
119 return retryTransactionWrapper(updateRemoteAccount, options)
122 async function updateRemoteAccount (actor: ActorModel, activity: ActivityUpdate) {
123 const accountAttributesToUpdate = activity.object as ActivityPubActor
125 logger.debug('Updating remote account "%s".', accountAttributesToUpdate.uuid)
126 let accountInstance: AccountModel
127 let actorFieldsSave: object
128 let accountFieldsSave: object
131 const avatarName = await fetchAvatarIfExists(accountAttributesToUpdate)
134 await sequelizeTypescript.transaction(async t => {
135 actorFieldsSave = actor.toJSON()
136 accountInstance = actor.Account
137 accountFieldsSave = actor.Account.toJSON()
139 await updateActorInstance(actor, accountAttributesToUpdate)
141 if (avatarName !== undefined) {
142 await updateActorAvatarInstance(actor, avatarName, t)
145 await actor.save({ transaction: t })
147 actor.Account.set('name', accountAttributesToUpdate.name || accountAttributesToUpdate.preferredUsername)
148 await actor.Account.save({ transaction: t })
151 logger.info('Remote account with uuid %s updated', accountAttributesToUpdate.uuid)
153 if (actor !== undefined && actorFieldsSave !== undefined) {
154 resetSequelizeInstance(actor, actorFieldsSave)
157 if (accountInstance !== undefined && accountFieldsSave !== undefined) {
158 resetSequelizeInstance(accountInstance, accountFieldsSave)
161 // This is just a debug because we will retry the insert
162 logger.debug('Cannot update the remote account.', err)