]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame_incremental - server/lib/activitypub/process/process-update.ts
Split files in activitypub server
[github/Chocobozzz/PeerTube.git] / server / lib / activitypub / process / process-update.ts
... / ...
CommitLineData
1import * as Bluebird from 'bluebird'
2import { ActivityUpdate } from '../../../../shared/models/activitypub'
3import { ActivityPubActor } from '../../../../shared/models/activitypub/activitypub-actor'
4import { VideoTorrentObject } from '../../../../shared/models/activitypub/objects'
5import { retryTransactionWrapper } from '../../../helpers/database-utils'
6import { logger } from '../../../helpers/logger'
7import { resetSequelizeInstance } from '../../../helpers/utils'
8import { sequelizeTypescript } from '../../../initializers'
9import { AccountModel } from '../../../models/account/account'
10import { ActorModel } from '../../../models/activitypub/actor'
11import { TagModel } from '../../../models/video/tag'
12import { VideoChannelModel } from '../../../models/video/video-channel'
13import { VideoFileModel } from '../../../models/video/video-file'
14import { fetchAvatarIfExists, getOrCreateActorAndServerAndModel, updateActorAvatarInstance, updateActorInstance } from '../actor'
15import {
16 generateThumbnailFromUrl,
17 getOrCreateAccountAndVideoAndChannel,
18 getOrCreateVideoChannel,
19 videoActivityObjectToDBAttributes,
20 videoFileActivityUrlToDBAttributes
21} from '../videos'
22
23async function processUpdateActivity (activity: ActivityUpdate) {
24 const actor = await getOrCreateActorAndServerAndModel(activity.actor)
25 const objectType = activity.object.type
26
27 if (objectType === 'Video') {
28 return processUpdateVideo(actor, activity)
29 } else if (objectType === 'Person' || objectType === 'Application' || objectType === 'Group') {
30 return processUpdateActor(actor, activity)
31 }
32
33 return
34}
35
36// ---------------------------------------------------------------------------
37
38export {
39 processUpdateActivity
40}
41
42// ---------------------------------------------------------------------------
43
44function processUpdateVideo (actor: ActorModel, activity: ActivityUpdate) {
45 const options = {
46 arguments: [ actor, activity ],
47 errorMessage: 'Cannot update the remote video with many retries'
48 }
49
50 return retryTransactionWrapper(updateRemoteVideo, options)
51}
52
53async function updateRemoteVideo (actor: ActorModel, activity: ActivityUpdate) {
54 const videoAttributesToUpdate = activity.object as VideoTorrentObject
55
56 const res = await getOrCreateAccountAndVideoAndChannel(videoAttributesToUpdate.id)
57
58 // Fetch video channel outside the transaction
59 const newVideoChannelActor = await getOrCreateVideoChannel(videoAttributesToUpdate)
60 const newVideoChannel = newVideoChannelActor.VideoChannel
61
62 logger.debug('Updating remote video "%s".', videoAttributesToUpdate.uuid)
63 let videoInstance = res.video
64 let videoFieldsSave: any
65
66 try {
67 await sequelizeTypescript.transaction(async t => {
68 const sequelizeOptions = {
69 transaction: t
70 }
71
72 videoFieldsSave = videoInstance.toJSON()
73
74 // Check actor has the right to update the video
75 const videoChannel = videoInstance.VideoChannel
76 if (videoChannel.Account.Actor.id !== actor.id) {
77 throw new Error('Account ' + actor.url + ' does not own video channel ' + videoChannel.Actor.url)
78 }
79
80 const videoData = await videoActivityObjectToDBAttributes(newVideoChannel, videoAttributesToUpdate, activity.to)
81 videoInstance.set('name', videoData.name)
82 videoInstance.set('uuid', videoData.uuid)
83 videoInstance.set('url', videoData.url)
84 videoInstance.set('category', videoData.category)
85 videoInstance.set('licence', videoData.licence)
86 videoInstance.set('language', videoData.language)
87 videoInstance.set('description', videoData.description)
88 videoInstance.set('support', videoData.support)
89 videoInstance.set('nsfw', videoData.nsfw)
90 videoInstance.set('commentsEnabled', videoData.commentsEnabled)
91 videoInstance.set('duration', videoData.duration)
92 videoInstance.set('createdAt', videoData.createdAt)
93 videoInstance.set('updatedAt', videoData.updatedAt)
94 videoInstance.set('views', videoData.views)
95 videoInstance.set('privacy', videoData.privacy)
96 videoInstance.set('channelId', videoData.channelId)
97
98 await videoInstance.save(sequelizeOptions)
99
100 // Don't block on request
101 generateThumbnailFromUrl(videoInstance, videoAttributesToUpdate.icon)
102 .catch(err => logger.warn('Cannot generate thumbnail of %s.', videoAttributesToUpdate.id, { err }))
103
104 // Remove old video files
105 const videoFileDestroyTasks: Bluebird<void>[] = []
106 for (const videoFile of videoInstance.VideoFiles) {
107 videoFileDestroyTasks.push(videoFile.destroy(sequelizeOptions))
108 }
109 await Promise.all(videoFileDestroyTasks)
110
111 const videoFileAttributes = videoFileActivityUrlToDBAttributes(videoInstance, videoAttributesToUpdate)
112 const tasks = videoFileAttributes.map(f => VideoFileModel.create(f))
113 await Promise.all(tasks)
114
115 const tags = videoAttributesToUpdate.tag.map(t => t.name)
116 const tagInstances = await TagModel.findOrCreateTags(tags, t)
117 await videoInstance.$set('Tags', tagInstances, sequelizeOptions)
118 })
119
120 logger.info('Remote video with uuid %s updated', videoAttributesToUpdate.uuid)
121 } catch (err) {
122 if (videoInstance !== undefined && videoFieldsSave !== undefined) {
123 resetSequelizeInstance(videoInstance, videoFieldsSave)
124 }
125
126 // This is just a debug because we will retry the insert
127 logger.debug('Cannot update the remote video.', { err })
128 throw err
129 }
130}
131
132function processUpdateActor (actor: ActorModel, activity: ActivityUpdate) {
133 const options = {
134 arguments: [ actor, activity ],
135 errorMessage: 'Cannot update the remote actor with many retries'
136 }
137
138 return retryTransactionWrapper(updateRemoteActor, options)
139}
140
141async function updateRemoteActor (actor: ActorModel, activity: ActivityUpdate) {
142 const actorAttributesToUpdate = activity.object as ActivityPubActor
143
144 logger.debug('Updating remote account "%s".', actorAttributesToUpdate.uuid)
145 let accountOrChannelInstance: AccountModel | VideoChannelModel
146 let actorFieldsSave: object
147 let accountOrChannelFieldsSave: object
148
149 // Fetch icon?
150 const avatarName = await fetchAvatarIfExists(actorAttributesToUpdate)
151
152 try {
153 await sequelizeTypescript.transaction(async t => {
154 actorFieldsSave = actor.toJSON()
155
156 if (actorAttributesToUpdate.type === 'Group') accountOrChannelInstance = actor.VideoChannel
157 else accountOrChannelInstance = actor.Account
158
159 accountOrChannelFieldsSave = accountOrChannelInstance.toJSON()
160
161 await updateActorInstance(actor, actorAttributesToUpdate)
162
163 if (avatarName !== undefined) {
164 await updateActorAvatarInstance(actor, avatarName, t)
165 }
166
167 await actor.save({ transaction: t })
168
169 accountOrChannelInstance.set('name', actorAttributesToUpdate.name || actorAttributesToUpdate.preferredUsername)
170 accountOrChannelInstance.set('description', actorAttributesToUpdate.summary)
171 accountOrChannelInstance.set('support', actorAttributesToUpdate.support)
172 await accountOrChannelInstance.save({ transaction: t })
173 })
174
175 logger.info('Remote account with uuid %s updated', actorAttributesToUpdate.uuid)
176 } catch (err) {
177 if (actor !== undefined && actorFieldsSave !== undefined) {
178 resetSequelizeInstance(actor, actorFieldsSave)
179 }
180
181 if (accountOrChannelInstance !== undefined && accountOrChannelFieldsSave !== undefined) {
182 resetSequelizeInstance(accountOrChannelInstance, accountOrChannelFieldsSave)
183 }
184
185 // This is just a debug because we will retry the insert
186 logger.debug('Cannot update the remote account.', { err })
187 throw err
188 }
189}