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