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 { VideoChannelModel } from '../../../models/video/video-channel'
13 import { VideoFileModel } from '../../../models/video/video-file'
14 import { fetchAvatarIfExists, getOrCreateActorAndServerAndModel, updateActorAvatarInstance, updateActorInstance } from '../actor'
16 generateThumbnailFromUrl,
17 getOrCreateAccountAndVideoAndChannel,
18 videoActivityObjectToDBAttributes,
19 videoFileActivityUrlToDBAttributes
22 async function processUpdateActivity (activity: ActivityUpdate) {
23 const actor = await getOrCreateActorAndServerAndModel(activity.actor)
24 const objectType = activity.object.type
26 if (objectType === 'Video') {
27 return processUpdateVideo(actor, activity)
28 } else if (objectType === 'Person' || objectType === 'Application' || objectType === 'Group') {
29 return processUpdateActor(actor, activity)
35 // ---------------------------------------------------------------------------
41 // ---------------------------------------------------------------------------
43 function processUpdateVideo (actor: ActorModel, activity: ActivityUpdate) {
45 arguments: [ actor, activity ],
46 errorMessage: 'Cannot update the remote video with many retries'
49 return retryTransactionWrapper(updateRemoteVideo, options)
52 async function updateRemoteVideo (actor: ActorModel, activity: ActivityUpdate) {
53 const videoAttributesToUpdate = activity.object as VideoTorrentObject
55 const res = await getOrCreateAccountAndVideoAndChannel(videoAttributesToUpdate.id)
57 logger.debug('Updating remote video "%s".', videoAttributesToUpdate.uuid)
58 let videoInstance = res.video
59 let videoFieldsSave: any
62 await sequelizeTypescript.transaction(async t => {
63 const sequelizeOptions = {
67 videoFieldsSave = videoInstance.toJSON()
69 const videoChannel = videoInstance.VideoChannel
70 if (videoChannel.Account.Actor.id !== actor.id) {
71 throw new Error('Account ' + actor.url + ' does not own video channel ' + videoChannel.Actor.url)
74 const videoData = await videoActivityObjectToDBAttributes(videoChannel, videoAttributesToUpdate, activity.to)
75 videoInstance.set('name', videoData.name)
76 videoInstance.set('uuid', videoData.uuid)
77 videoInstance.set('url', videoData.url)
78 videoInstance.set('category', videoData.category)
79 videoInstance.set('licence', videoData.licence)
80 videoInstance.set('language', videoData.language)
81 videoInstance.set('description', videoData.description)
82 videoInstance.set('support', videoData.support)
83 videoInstance.set('nsfw', videoData.nsfw)
84 videoInstance.set('commentsEnabled', videoData.commentsEnabled)
85 videoInstance.set('duration', videoData.duration)
86 videoInstance.set('createdAt', videoData.createdAt)
87 videoInstance.set('updatedAt', videoData.updatedAt)
88 videoInstance.set('views', videoData.views)
89 videoInstance.set('privacy', videoData.privacy)
91 await videoInstance.save(sequelizeOptions)
93 // Don't block on request
94 generateThumbnailFromUrl(videoInstance, videoAttributesToUpdate.icon)
95 .catch(err => logger.warn('Cannot generate thumbnail of %s.', videoAttributesToUpdate.id, err))
97 // Remove old video files
98 const videoFileDestroyTasks: Bluebird<void>[] = []
99 for (const videoFile of videoInstance.VideoFiles) {
100 videoFileDestroyTasks.push(videoFile.destroy(sequelizeOptions))
102 await Promise.all(videoFileDestroyTasks)
104 const videoFileAttributes = videoFileActivityUrlToDBAttributes(videoInstance, videoAttributesToUpdate)
105 const tasks = videoFileAttributes.map(f => VideoFileModel.create(f))
106 await Promise.all(tasks)
108 const tags = videoAttributesToUpdate.tag.map(t => t.name)
109 const tagInstances = await TagModel.findOrCreateTags(tags, t)
110 await videoInstance.$set('Tags', tagInstances, sequelizeOptions)
113 logger.info('Remote video with uuid %s updated', videoAttributesToUpdate.uuid)
115 if (videoInstance !== undefined && videoFieldsSave !== undefined) {
116 resetSequelizeInstance(videoInstance, videoFieldsSave)
119 // This is just a debug because we will retry the insert
120 logger.debug('Cannot update the remote video.', err)
125 function processUpdateActor (actor: ActorModel, activity: ActivityUpdate) {
127 arguments: [ actor, activity ],
128 errorMessage: 'Cannot update the remote actor with many retries'
131 return retryTransactionWrapper(updateRemoteActor, options)
134 async function updateRemoteActor (actor: ActorModel, activity: ActivityUpdate) {
135 const actorAttributesToUpdate = activity.object as ActivityPubActor
137 logger.debug('Updating remote account "%s".', actorAttributesToUpdate.uuid)
138 let accountOrChannelInstance: AccountModel | VideoChannelModel
139 let actorFieldsSave: object
140 let accountOrChannelFieldsSave: object
143 const avatarName = await fetchAvatarIfExists(actorAttributesToUpdate)
146 await sequelizeTypescript.transaction(async t => {
147 actorFieldsSave = actor.toJSON()
149 if (actorAttributesToUpdate.type === 'Group') accountOrChannelInstance = actor.VideoChannel
150 else accountOrChannelInstance = actor.Account
152 accountOrChannelFieldsSave = accountOrChannelInstance.toJSON()
154 await updateActorInstance(actor, actorAttributesToUpdate)
156 if (avatarName !== undefined) {
157 await updateActorAvatarInstance(actor, avatarName, t)
160 await actor.save({ transaction: t })
162 accountOrChannelInstance.set('name', actorAttributesToUpdate.name || actorAttributesToUpdate.preferredUsername)
163 accountOrChannelInstance.set('description', actorAttributesToUpdate.summary)
164 accountOrChannelInstance.set('support', actorAttributesToUpdate.support)
165 await accountOrChannelInstance.save({ transaction: t })
168 logger.info('Remote account with uuid %s updated', actorAttributesToUpdate.uuid)
170 if (actor !== undefined && actorFieldsSave !== undefined) {
171 resetSequelizeInstance(actor, actorFieldsSave)
174 if (accountOrChannelInstance !== undefined && accountOrChannelFieldsSave !== undefined) {
175 resetSequelizeInstance(accountOrChannelInstance, accountOrChannelFieldsSave)
178 // This is just a debug because we will retry the insert
179 logger.debug('Cannot update the remote account.', err)