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