diff options
author | Chocobozzz <me@florianbigard.com> | 2018-09-19 11:41:21 +0200 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2018-09-19 11:41:30 +0200 |
commit | d4defe07d26013a75577b30608841fe3f8334308 (patch) | |
tree | ca45a396884c57046cc7f158ee0d38036e49c7cb /server/lib | |
parent | 4157cdb13748cb6e8ce7081d062a8778554cc5a7 (diff) | |
download | PeerTube-d4defe07d26013a75577b30608841fe3f8334308.tar.gz PeerTube-d4defe07d26013a75577b30608841fe3f8334308.tar.zst PeerTube-d4defe07d26013a75577b30608841fe3f8334308.zip |
Optimize video view AP processing
Diffstat (limited to 'server/lib')
-rw-r--r-- | server/lib/activitypub/process/process-create.ts | 10 | ||||
-rw-r--r-- | server/lib/activitypub/process/process-update.ts | 10 | ||||
-rw-r--r-- | server/lib/activitypub/videos.ts | 115 |
3 files changed, 84 insertions, 51 deletions
diff --git a/server/lib/activitypub/process/process-create.ts b/server/lib/activitypub/process/process-create.ts index 99841da14..559a0c23c 100644 --- a/server/lib/activitypub/process/process-create.ts +++ b/server/lib/activitypub/process/process-create.ts | |||
@@ -86,10 +86,14 @@ async function processCreateDislike (byActor: ActorModel, activity: ActivityCrea | |||
86 | async function processCreateView (byActor: ActorModel, activity: ActivityCreate) { | 86 | async function processCreateView (byActor: ActorModel, activity: ActivityCreate) { |
87 | const view = activity.object as ViewObject | 87 | const view = activity.object as ViewObject |
88 | 88 | ||
89 | const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: view.object }) | 89 | const options = { |
90 | videoObject: view.object, | ||
91 | fetchType: 'only-video' as 'only-video' | ||
92 | } | ||
93 | const { video } = await getOrCreateVideoAndAccountAndChannel(options) | ||
90 | 94 | ||
91 | const actor = await ActorModel.loadByUrl(view.actor) | 95 | const actorExists = await ActorModel.isActorUrlExist(view.actor) |
92 | if (!actor) throw new Error('Unknown actor ' + view.actor) | 96 | if (actorExists === false) throw new Error('Unknown actor ' + view.actor) |
93 | 97 | ||
94 | await Redis.Instance.addVideoView(video.id) | 98 | await Redis.Instance.addVideoView(video.id) |
95 | 99 | ||
diff --git a/server/lib/activitypub/process/process-update.ts b/server/lib/activitypub/process/process-update.ts index 935da5a54..0bceb370e 100644 --- a/server/lib/activitypub/process/process-update.ts +++ b/server/lib/activitypub/process/process-update.ts | |||
@@ -51,7 +51,15 @@ async function processUpdateVideo (actor: ActorModel, activity: ActivityUpdate) | |||
51 | const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: videoObject.id }) | 51 | const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: videoObject.id }) |
52 | const channelActor = await getOrCreateVideoChannelFromVideoObject(videoObject) | 52 | const channelActor = await getOrCreateVideoChannelFromVideoObject(videoObject) |
53 | 53 | ||
54 | return updateVideoFromAP(video, videoObject, actor.Account, channelActor.VideoChannel, activity.to) | 54 | const updateOptions = { |
55 | video, | ||
56 | videoObject, | ||
57 | account: actor.Account, | ||
58 | channel: channelActor.VideoChannel, | ||
59 | updateViews: true, | ||
60 | overrideTo: activity.to | ||
61 | } | ||
62 | return updateVideoFromAP(updateOptions) | ||
55 | } | 63 | } |
56 | 64 | ||
57 | async function processUpdateCacheFile (byActor: ActorModel, activity: ActivityUpdate) { | 65 | async function processUpdateCacheFile (byActor: ActorModel, activity: ActivityUpdate) { |
diff --git a/server/lib/activitypub/videos.ts b/server/lib/activitypub/videos.ts index 5aabd3e0d..de22e3584 100644 --- a/server/lib/activitypub/videos.ts +++ b/server/lib/activitypub/videos.ts | |||
@@ -157,18 +157,26 @@ async function syncVideoExternalAttributes (video: VideoModel, fetchedVideo: Vid | |||
157 | async function getOrCreateVideoAndAccountAndChannel (options: { | 157 | async function getOrCreateVideoAndAccountAndChannel (options: { |
158 | videoObject: VideoTorrentObject | string, | 158 | videoObject: VideoTorrentObject | string, |
159 | syncParam?: SyncParam, | 159 | syncParam?: SyncParam, |
160 | fetchType?: VideoFetchByUrlType | 160 | fetchType?: VideoFetchByUrlType, |
161 | refreshViews?: boolean | ||
161 | }) { | 162 | }) { |
162 | // Default params | 163 | // Default params |
163 | const syncParam = options.syncParam || { likes: true, dislikes: true, shares: true, comments: true, thumbnail: true, refreshVideo: false } | 164 | const syncParam = options.syncParam || { likes: true, dislikes: true, shares: true, comments: true, thumbnail: true, refreshVideo: false } |
164 | const fetchType = options.fetchType || 'all' | 165 | const fetchType = options.fetchType || 'all' |
166 | const refreshViews = options.refreshViews || false | ||
165 | 167 | ||
166 | // Get video url | 168 | // Get video url |
167 | const videoUrl = typeof options.videoObject === 'string' ? options.videoObject : options.videoObject.id | 169 | const videoUrl = typeof options.videoObject === 'string' ? options.videoObject : options.videoObject.id |
168 | 170 | ||
169 | let videoFromDatabase = await fetchVideoByUrl(videoUrl, fetchType) | 171 | let videoFromDatabase = await fetchVideoByUrl(videoUrl, fetchType) |
170 | if (videoFromDatabase) { | 172 | if (videoFromDatabase) { |
171 | const p = retryTransactionWrapper(refreshVideoIfNeeded, videoFromDatabase, fetchType, syncParam) | 173 | const refreshOptions = { |
174 | video: videoFromDatabase, | ||
175 | fetchedType: fetchType, | ||
176 | syncParam, | ||
177 | refreshViews | ||
178 | } | ||
179 | const p = retryTransactionWrapper(refreshVideoIfNeeded, refreshOptions) | ||
172 | if (syncParam.refreshVideo === true) videoFromDatabase = await p | 180 | if (syncParam.refreshVideo === true) videoFromDatabase = await p |
173 | 181 | ||
174 | return { video: videoFromDatabase } | 182 | return { video: videoFromDatabase } |
@@ -185,14 +193,15 @@ async function getOrCreateVideoAndAccountAndChannel (options: { | |||
185 | return { video } | 193 | return { video } |
186 | } | 194 | } |
187 | 195 | ||
188 | async function updateVideoFromAP ( | 196 | async function updateVideoFromAP (options: { |
189 | video: VideoModel, | 197 | video: VideoModel, |
190 | videoObject: VideoTorrentObject, | 198 | videoObject: VideoTorrentObject, |
191 | account: AccountModel, | 199 | account: AccountModel, |
192 | channel: VideoChannelModel, | 200 | channel: VideoChannelModel, |
201 | updateViews: boolean, | ||
193 | overrideTo?: string[] | 202 | overrideTo?: string[] |
194 | ) { | 203 | }) { |
195 | logger.debug('Updating remote video "%s".', videoObject.uuid) | 204 | logger.debug('Updating remote video "%s".', options.videoObject.uuid) |
196 | let videoFieldsSave: any | 205 | let videoFieldsSave: any |
197 | 206 | ||
198 | try { | 207 | try { |
@@ -201,72 +210,72 @@ async function updateVideoFromAP ( | |||
201 | transaction: t | 210 | transaction: t |
202 | } | 211 | } |
203 | 212 | ||
204 | videoFieldsSave = video.toJSON() | 213 | videoFieldsSave = options.video.toJSON() |
205 | 214 | ||
206 | // Check actor has the right to update the video | 215 | // Check actor has the right to update the video |
207 | const videoChannel = video.VideoChannel | 216 | const videoChannel = options.video.VideoChannel |
208 | if (videoChannel.Account.id !== account.id) { | 217 | if (videoChannel.Account.id !== options.account.id) { |
209 | throw new Error('Account ' + account.Actor.url + ' does not own video channel ' + videoChannel.Actor.url) | 218 | throw new Error('Account ' + options.account.Actor.url + ' does not own video channel ' + videoChannel.Actor.url) |
210 | } | 219 | } |
211 | 220 | ||
212 | const to = overrideTo ? overrideTo : videoObject.to | 221 | const to = options.overrideTo ? options.overrideTo : options.videoObject.to |
213 | const videoData = await videoActivityObjectToDBAttributes(channel, videoObject, to) | 222 | const videoData = await videoActivityObjectToDBAttributes(options.channel, options.videoObject, to) |
214 | video.set('name', videoData.name) | 223 | options.video.set('name', videoData.name) |
215 | video.set('uuid', videoData.uuid) | 224 | options.video.set('uuid', videoData.uuid) |
216 | video.set('url', videoData.url) | 225 | options.video.set('url', videoData.url) |
217 | video.set('category', videoData.category) | 226 | options.video.set('category', videoData.category) |
218 | video.set('licence', videoData.licence) | 227 | options.video.set('licence', videoData.licence) |
219 | video.set('language', videoData.language) | 228 | options.video.set('language', videoData.language) |
220 | video.set('description', videoData.description) | 229 | options.video.set('description', videoData.description) |
221 | video.set('support', videoData.support) | 230 | options.video.set('support', videoData.support) |
222 | video.set('nsfw', videoData.nsfw) | 231 | options.video.set('nsfw', videoData.nsfw) |
223 | video.set('commentsEnabled', videoData.commentsEnabled) | 232 | options.video.set('commentsEnabled', videoData.commentsEnabled) |
224 | video.set('waitTranscoding', videoData.waitTranscoding) | 233 | options.video.set('waitTranscoding', videoData.waitTranscoding) |
225 | video.set('state', videoData.state) | 234 | options.video.set('state', videoData.state) |
226 | video.set('duration', videoData.duration) | 235 | options.video.set('duration', videoData.duration) |
227 | video.set('createdAt', videoData.createdAt) | 236 | options.video.set('createdAt', videoData.createdAt) |
228 | video.set('publishedAt', videoData.publishedAt) | 237 | options.video.set('publishedAt', videoData.publishedAt) |
229 | video.set('views', videoData.views) | 238 | options.video.set('privacy', videoData.privacy) |
230 | video.set('privacy', videoData.privacy) | 239 | options.video.set('channelId', videoData.channelId) |
231 | video.set('channelId', videoData.channelId) | 240 | |
232 | 241 | if (options.updateViews === true) options.video.set('views', videoData.views) | |
233 | await video.save(sequelizeOptions) | 242 | await options.video.save(sequelizeOptions) |
234 | 243 | ||
235 | // Don't block on request | 244 | // Don't block on request |
236 | generateThumbnailFromUrl(video, videoObject.icon) | 245 | generateThumbnailFromUrl(options.video, options.videoObject.icon) |
237 | .catch(err => logger.warn('Cannot generate thumbnail of %s.', videoObject.id, { err })) | 246 | .catch(err => logger.warn('Cannot generate thumbnail of %s.', options.videoObject.id, { err })) |
238 | 247 | ||
239 | // Remove old video files | 248 | // Remove old video files |
240 | const videoFileDestroyTasks: Bluebird<void>[] = [] | 249 | const videoFileDestroyTasks: Bluebird<void>[] = [] |
241 | for (const videoFile of video.VideoFiles) { | 250 | for (const videoFile of options.video.VideoFiles) { |
242 | videoFileDestroyTasks.push(videoFile.destroy(sequelizeOptions)) | 251 | videoFileDestroyTasks.push(videoFile.destroy(sequelizeOptions)) |
243 | } | 252 | } |
244 | await Promise.all(videoFileDestroyTasks) | 253 | await Promise.all(videoFileDestroyTasks) |
245 | 254 | ||
246 | const videoFileAttributes = videoFileActivityUrlToDBAttributes(video, videoObject) | 255 | const videoFileAttributes = videoFileActivityUrlToDBAttributes(options.video, options.videoObject) |
247 | const tasks = videoFileAttributes.map(f => VideoFileModel.create(f, sequelizeOptions)) | 256 | const tasks = videoFileAttributes.map(f => VideoFileModel.create(f, sequelizeOptions)) |
248 | await Promise.all(tasks) | 257 | await Promise.all(tasks) |
249 | 258 | ||
250 | // Update Tags | 259 | // Update Tags |
251 | const tags = videoObject.tag.map(tag => tag.name) | 260 | const tags = options.videoObject.tag.map(tag => tag.name) |
252 | const tagInstances = await TagModel.findOrCreateTags(tags, t) | 261 | const tagInstances = await TagModel.findOrCreateTags(tags, t) |
253 | await video.$set('Tags', tagInstances, sequelizeOptions) | 262 | await options.video.$set('Tags', tagInstances, sequelizeOptions) |
254 | 263 | ||
255 | // Update captions | 264 | // Update captions |
256 | await VideoCaptionModel.deleteAllCaptionsOfRemoteVideo(video.id, t) | 265 | await VideoCaptionModel.deleteAllCaptionsOfRemoteVideo(options.video.id, t) |
257 | 266 | ||
258 | const videoCaptionsPromises = videoObject.subtitleLanguage.map(c => { | 267 | const videoCaptionsPromises = options.videoObject.subtitleLanguage.map(c => { |
259 | return VideoCaptionModel.insertOrReplaceLanguage(video.id, c.identifier, t) | 268 | return VideoCaptionModel.insertOrReplaceLanguage(options.video.id, c.identifier, t) |
260 | }) | 269 | }) |
261 | await Promise.all(videoCaptionsPromises) | 270 | await Promise.all(videoCaptionsPromises) |
262 | }) | 271 | }) |
263 | 272 | ||
264 | logger.info('Remote video with uuid %s updated', videoObject.uuid) | 273 | logger.info('Remote video with uuid %s updated', options.videoObject.uuid) |
265 | 274 | ||
266 | return updatedVideo | 275 | return updatedVideo |
267 | } catch (err) { | 276 | } catch (err) { |
268 | if (video !== undefined && videoFieldsSave !== undefined) { | 277 | if (options.video !== undefined && videoFieldsSave !== undefined) { |
269 | resetSequelizeInstance(video, videoFieldsSave) | 278 | resetSequelizeInstance(options.video, videoFieldsSave) |
270 | } | 279 | } |
271 | 280 | ||
272 | // This is just a debug because we will retry the insert | 281 | // This is just a debug because we will retry the insert |
@@ -339,9 +348,14 @@ async function createVideo (videoObject: VideoTorrentObject, channelActor: Actor | |||
339 | return videoCreated | 348 | return videoCreated |
340 | } | 349 | } |
341 | 350 | ||
342 | async function refreshVideoIfNeeded (videoArg: VideoModel, fetchedType: VideoFetchByUrlType, syncParam: SyncParam): Promise<VideoModel> { | 351 | async function refreshVideoIfNeeded (options: { |
352 | video: VideoModel, | ||
353 | fetchedType: VideoFetchByUrlType, | ||
354 | syncParam: SyncParam, | ||
355 | refreshViews: boolean | ||
356 | }): Promise<VideoModel> { | ||
343 | // We need more attributes if the argument video was fetched with not enough joints | 357 | // We need more attributes if the argument video was fetched with not enough joints |
344 | const video = fetchedType === 'all' ? videoArg : await VideoModel.loadByUrlAndPopulateAccount(videoArg.url) | 358 | const video = options.fetchedType === 'all' ? options.video : await VideoModel.loadByUrlAndPopulateAccount(options.video.url) |
345 | 359 | ||
346 | if (!video.isOutdated()) return video | 360 | if (!video.isOutdated()) return video |
347 | 361 | ||
@@ -361,8 +375,15 @@ async function refreshVideoIfNeeded (videoArg: VideoModel, fetchedType: VideoFet | |||
361 | const channelActor = await getOrCreateVideoChannelFromVideoObject(videoObject) | 375 | const channelActor = await getOrCreateVideoChannelFromVideoObject(videoObject) |
362 | const account = await AccountModel.load(channelActor.VideoChannel.accountId) | 376 | const account = await AccountModel.load(channelActor.VideoChannel.accountId) |
363 | 377 | ||
364 | await updateVideoFromAP(video, videoObject, account, channelActor.VideoChannel) | 378 | const updateOptions = { |
365 | await syncVideoExternalAttributes(video, videoObject, syncParam) | 379 | video, |
380 | videoObject, | ||
381 | account, | ||
382 | channel: channelActor.VideoChannel, | ||
383 | updateViews: options.refreshViews | ||
384 | } | ||
385 | await updateVideoFromAP(updateOptions) | ||
386 | await syncVideoExternalAttributes(video, videoObject, options.syncParam) | ||
366 | } catch (err) { | 387 | } catch (err) { |
367 | logger.warn('Cannot refresh video.', { err }) | 388 | logger.warn('Cannot refresh video.', { err }) |
368 | return video | 389 | return video |