]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/lib/activitypub/process/process-update.ts
Fetch remote AP objects
[github/Chocobozzz/PeerTube.git] / server / lib / activitypub / process / process-update.ts
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'
15
16 async function processUpdateActivity (activity: ActivityUpdate) {
17 const actor = await getOrCreateActorAndServerAndModel(activity.actor)
18
19 if (activity.object.type === 'Video') {
20 return processUpdateVideo(actor, activity)
21 } else if (activity.object.type === 'Person') {
22 return processUpdateAccount(actor, activity)
23 }
24
25 return
26 }
27
28 // ---------------------------------------------------------------------------
29
30 export {
31 processUpdateActivity
32 }
33
34 // ---------------------------------------------------------------------------
35
36 function processUpdateVideo (actor: ActorModel, activity: ActivityUpdate) {
37 const options = {
38 arguments: [ actor, activity ],
39 errorMessage: 'Cannot update the remote video with many retries'
40 }
41
42 return retryTransactionWrapper(updateRemoteVideo, options)
43 }
44
45 async function updateRemoteVideo (actor: ActorModel, activity: ActivityUpdate) {
46 const videoAttributesToUpdate = activity.object as VideoTorrentObject
47
48 const res = await getOrCreateAccountAndVideoAndChannel(videoAttributesToUpdate.id)
49
50 logger.debug('Updating remote video "%s".', videoAttributesToUpdate.uuid)
51 let videoInstance = res.video
52 let videoFieldsSave: any
53
54 try {
55 await sequelizeTypescript.transaction(async t => {
56 const sequelizeOptions = {
57 transaction: t
58 }
59
60 videoFieldsSave = videoInstance.toJSON()
61
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)
65 }
66
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)
80
81 await videoInstance.save(sequelizeOptions)
82
83 // Remove old video files
84 const videoFileDestroyTasks: Bluebird<void>[] = []
85 for (const videoFile of videoInstance.VideoFiles) {
86 videoFileDestroyTasks.push(videoFile.destroy(sequelizeOptions))
87 }
88 await Promise.all(videoFileDestroyTasks)
89
90 const videoFileAttributes = videoFileActivityUrlToDBAttributes(videoInstance, videoAttributesToUpdate)
91 const tasks: Bluebird<any>[] = videoFileAttributes.map(f => VideoFileModel.create(f))
92 await Promise.all(tasks)
93
94 const tags = videoAttributesToUpdate.tag.map(t => t.name)
95 const tagInstances = await TagModel.findOrCreateTags(tags, t)
96 await videoInstance.$set('Tags', tagInstances, sequelizeOptions)
97 })
98
99 logger.info('Remote video with uuid %s updated', videoAttributesToUpdate.uuid)
100 } catch (err) {
101 if (videoInstance !== undefined && videoFieldsSave !== undefined) {
102 resetSequelizeInstance(videoInstance, videoFieldsSave)
103 }
104
105 // This is just a debug because we will retry the insert
106 logger.debug('Cannot update the remote video.', err)
107 throw err
108 }
109 }
110
111 function processUpdateAccount (actor: ActorModel, activity: ActivityUpdate) {
112 const options = {
113 arguments: [ actor, activity ],
114 errorMessage: 'Cannot update the remote account with many retries'
115 }
116
117 return retryTransactionWrapper(updateRemoteAccount, options)
118 }
119
120 async function updateRemoteAccount (actor: ActorModel, activity: ActivityUpdate) {
121 const accountAttributesToUpdate = activity.object as ActivityPubActor
122
123 logger.debug('Updating remote account "%s".', accountAttributesToUpdate.uuid)
124 let accountInstance: AccountModel
125 let actorFieldsSave: object
126 let accountFieldsSave: object
127
128 // Fetch icon?
129 const avatarName = await fetchAvatarIfExists(accountAttributesToUpdate)
130
131 try {
132 await sequelizeTypescript.transaction(async t => {
133 actorFieldsSave = actor.toJSON()
134 accountInstance = actor.Account
135 accountFieldsSave = actor.Account.toJSON()
136
137 await updateActorInstance(actor, accountAttributesToUpdate)
138
139 if (avatarName !== undefined) {
140 await updateActorAvatarInstance(actor, avatarName, t)
141 }
142
143 await actor.save({ transaction: t })
144
145 actor.Account.set('name', accountAttributesToUpdate.name || accountAttributesToUpdate.preferredUsername)
146 await actor.Account.save({ transaction: t })
147 })
148
149 logger.info('Remote account with uuid %s updated', accountAttributesToUpdate.uuid)
150 } catch (err) {
151 if (actor !== undefined && actorFieldsSave !== undefined) {
152 resetSequelizeInstance(actor, actorFieldsSave)
153 }
154
155 if (accountInstance !== undefined && accountFieldsSave !== undefined) {
156 resetSequelizeInstance(accountInstance, accountFieldsSave)
157 }
158
159 // This is just a debug because we will retry the insert
160 logger.debug('Cannot update the remote account.', err)
161 throw err
162 }
163 }