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