diff options
author | Chocobozzz <me@florianbigard.com> | 2019-07-18 14:28:37 +0200 |
---|---|---|
committer | Chocobozzz <chocobozzz@cpy.re> | 2019-07-24 10:58:16 +0200 |
commit | b4055e1c23eeefb0c8a85a77f312b2827d98f483 (patch) | |
tree | 51b6b04c1ad10897047817d2eaaa037d1331fa6a /server/lib/activitypub/videos.ts | |
parent | 66e001c848c009412c65cbce41be344d8985fd83 (diff) | |
download | PeerTube-b4055e1c23eeefb0c8a85a77f312b2827d98f483.tar.gz PeerTube-b4055e1c23eeefb0c8a85a77f312b2827d98f483.tar.zst PeerTube-b4055e1c23eeefb0c8a85a77f312b2827d98f483.zip |
Add server hooks
Diffstat (limited to 'server/lib/activitypub/videos.ts')
-rw-r--r-- | server/lib/activitypub/videos.ts | 132 |
1 files changed, 67 insertions, 65 deletions
diff --git a/server/lib/activitypub/videos.ts b/server/lib/activitypub/videos.ts index 4f26cb6be..dade6b55f 100644 --- a/server/lib/activitypub/videos.ts +++ b/server/lib/activitypub/videos.ts | |||
@@ -54,6 +54,8 @@ import { ThumbnailModel } from '../../models/video/thumbnail' | |||
54 | import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type' | 54 | import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type' |
55 | import { join } from 'path' | 55 | import { join } from 'path' |
56 | import { FilteredModelAttributes } from '../../typings/sequelize' | 56 | import { FilteredModelAttributes } from '../../typings/sequelize' |
57 | import { Hooks } from '../plugins/hooks' | ||
58 | import { autoBlacklistVideoIfNeeded } from '../video-blacklist' | ||
57 | 59 | ||
58 | async function federateVideoIfNeeded (video: VideoModel, isNewVideo: boolean, transaction?: sequelize.Transaction) { | 60 | async function federateVideoIfNeeded (video: VideoModel, isNewVideo: boolean, transaction?: sequelize.Transaction) { |
59 | // If the video is not private and is published, we federate it | 61 | // If the video is not private and is published, we federate it |
@@ -236,72 +238,74 @@ async function updateVideoFromAP (options: { | |||
236 | channel: VideoChannelModel, | 238 | channel: VideoChannelModel, |
237 | overrideTo?: string[] | 239 | overrideTo?: string[] |
238 | }) { | 240 | }) { |
241 | const { video, videoObject, account, channel, overrideTo } = options | ||
242 | |||
239 | logger.debug('Updating remote video "%s".', options.videoObject.uuid) | 243 | logger.debug('Updating remote video "%s".', options.videoObject.uuid) |
240 | 244 | ||
241 | let videoFieldsSave: any | 245 | let videoFieldsSave: any |
242 | const wasPrivateVideo = options.video.privacy === VideoPrivacy.PRIVATE | 246 | const wasPrivateVideo = video.privacy === VideoPrivacy.PRIVATE |
243 | const wasUnlistedVideo = options.video.privacy === VideoPrivacy.UNLISTED | 247 | const wasUnlistedVideo = video.privacy === VideoPrivacy.UNLISTED |
244 | 248 | ||
245 | try { | 249 | try { |
246 | let thumbnailModel: ThumbnailModel | 250 | let thumbnailModel: ThumbnailModel |
247 | 251 | ||
248 | try { | 252 | try { |
249 | thumbnailModel = await createVideoMiniatureFromUrl(options.videoObject.icon.url, options.video, ThumbnailType.MINIATURE) | 253 | thumbnailModel = await createVideoMiniatureFromUrl(videoObject.icon.url, video, ThumbnailType.MINIATURE) |
250 | } catch (err) { | 254 | } catch (err) { |
251 | logger.warn('Cannot generate thumbnail of %s.', options.videoObject.id, { err }) | 255 | logger.warn('Cannot generate thumbnail of %s.', videoObject.id, { err }) |
252 | } | 256 | } |
253 | 257 | ||
254 | await sequelizeTypescript.transaction(async t => { | 258 | await sequelizeTypescript.transaction(async t => { |
255 | const sequelizeOptions = { transaction: t } | 259 | const sequelizeOptions = { transaction: t } |
256 | 260 | ||
257 | videoFieldsSave = options.video.toJSON() | 261 | videoFieldsSave = video.toJSON() |
258 | 262 | ||
259 | // Check actor has the right to update the video | 263 | // Check actor has the right to update the video |
260 | const videoChannel = options.video.VideoChannel | 264 | const videoChannel = video.VideoChannel |
261 | if (videoChannel.Account.id !== options.account.id) { | 265 | if (videoChannel.Account.id !== account.id) { |
262 | throw new Error('Account ' + options.account.Actor.url + ' does not own video channel ' + videoChannel.Actor.url) | 266 | throw new Error('Account ' + account.Actor.url + ' does not own video channel ' + videoChannel.Actor.url) |
263 | } | 267 | } |
264 | 268 | ||
265 | const to = options.overrideTo ? options.overrideTo : options.videoObject.to | 269 | const to = overrideTo ? overrideTo : videoObject.to |
266 | const videoData = await videoActivityObjectToDBAttributes(options.channel, options.videoObject, to) | 270 | const videoData = await videoActivityObjectToDBAttributes(channel, videoObject, to) |
267 | options.video.set('name', videoData.name) | 271 | video.name = videoData.name |
268 | options.video.set('uuid', videoData.uuid) | 272 | video.uuid = videoData.uuid |
269 | options.video.set('url', videoData.url) | 273 | video.url = videoData.url |
270 | options.video.set('category', videoData.category) | 274 | video.category = videoData.category |
271 | options.video.set('licence', videoData.licence) | 275 | video.licence = videoData.licence |
272 | options.video.set('language', videoData.language) | 276 | video.language = videoData.language |
273 | options.video.set('description', videoData.description) | 277 | video.description = videoData.description |
274 | options.video.set('support', videoData.support) | 278 | video.support = videoData.support |
275 | options.video.set('nsfw', videoData.nsfw) | 279 | video.nsfw = videoData.nsfw |
276 | options.video.set('commentsEnabled', videoData.commentsEnabled) | 280 | video.commentsEnabled = videoData.commentsEnabled |
277 | options.video.set('downloadEnabled', videoData.downloadEnabled) | 281 | video.downloadEnabled = videoData.downloadEnabled |
278 | options.video.set('waitTranscoding', videoData.waitTranscoding) | 282 | video.waitTranscoding = videoData.waitTranscoding |
279 | options.video.set('state', videoData.state) | 283 | video.state = videoData.state |
280 | options.video.set('duration', videoData.duration) | 284 | video.duration = videoData.duration |
281 | options.video.set('createdAt', videoData.createdAt) | 285 | video.createdAt = videoData.createdAt |
282 | options.video.set('publishedAt', videoData.publishedAt) | 286 | video.publishedAt = videoData.publishedAt |
283 | options.video.set('originallyPublishedAt', videoData.originallyPublishedAt) | 287 | video.originallyPublishedAt = videoData.originallyPublishedAt |
284 | options.video.set('privacy', videoData.privacy) | 288 | video.privacy = videoData.privacy |
285 | options.video.set('channelId', videoData.channelId) | 289 | video.channelId = videoData.channelId |
286 | options.video.set('views', videoData.views) | 290 | video.views = videoData.views |
287 | 291 | ||
288 | await options.video.save(sequelizeOptions) | 292 | await video.save(sequelizeOptions) |
289 | 293 | ||
290 | if (thumbnailModel) if (thumbnailModel) await options.video.addAndSaveThumbnail(thumbnailModel, t) | 294 | if (thumbnailModel) await video.addAndSaveThumbnail(thumbnailModel, t) |
291 | 295 | ||
292 | // FIXME: use icon URL instead | 296 | // FIXME: use icon URL instead |
293 | const previewUrl = buildRemoteBaseUrl(options.video, join(STATIC_PATHS.PREVIEWS, options.video.getPreview().filename)) | 297 | const previewUrl = buildRemoteBaseUrl(video, join(STATIC_PATHS.PREVIEWS, video.getPreview().filename)) |
294 | const previewModel = createPlaceholderThumbnail(previewUrl, options.video, ThumbnailType.PREVIEW, PREVIEWS_SIZE) | 298 | const previewModel = createPlaceholderThumbnail(previewUrl, video, ThumbnailType.PREVIEW, PREVIEWS_SIZE) |
295 | await options.video.addAndSaveThumbnail(previewModel, t) | 299 | await video.addAndSaveThumbnail(previewModel, t) |
296 | 300 | ||
297 | { | 301 | { |
298 | const videoFileAttributes = videoFileActivityUrlToDBAttributes(options.video, options.videoObject) | 302 | const videoFileAttributes = videoFileActivityUrlToDBAttributes(video, videoObject) |
299 | const newVideoFiles = videoFileAttributes.map(a => new VideoFileModel(a)) | 303 | const newVideoFiles = videoFileAttributes.map(a => new VideoFileModel(a)) |
300 | 304 | ||
301 | // Remove video files that do not exist anymore | 305 | // Remove video files that do not exist anymore |
302 | const destroyTasks = options.video.VideoFiles | 306 | const destroyTasks = video.VideoFiles |
303 | .filter(f => !newVideoFiles.find(newFile => newFile.hasSameUniqueKeysThan(f))) | 307 | .filter(f => !newVideoFiles.find(newFile => newFile.hasSameUniqueKeysThan(f))) |
304 | .map(f => f.destroy(sequelizeOptions)) | 308 | .map(f => f.destroy(sequelizeOptions)) |
305 | await Promise.all(destroyTasks) | 309 | await Promise.all(destroyTasks) |
306 | 310 | ||
307 | // Update or add other one | 311 | // Update or add other one |
@@ -310,21 +314,17 @@ async function updateVideoFromAP (options: { | |||
310 | .then(([ file ]) => file) | 314 | .then(([ file ]) => file) |
311 | }) | 315 | }) |
312 | 316 | ||
313 | options.video.VideoFiles = await Promise.all(upsertTasks) | 317 | video.VideoFiles = await Promise.all(upsertTasks) |
314 | } | 318 | } |
315 | 319 | ||
316 | { | 320 | { |
317 | const streamingPlaylistAttributes = streamingPlaylistActivityUrlToDBAttributes( | 321 | const streamingPlaylistAttributes = streamingPlaylistActivityUrlToDBAttributes(video, videoObject, video.VideoFiles) |
318 | options.video, | ||
319 | options.videoObject, | ||
320 | options.video.VideoFiles | ||
321 | ) | ||
322 | const newStreamingPlaylists = streamingPlaylistAttributes.map(a => new VideoStreamingPlaylistModel(a)) | 322 | const newStreamingPlaylists = streamingPlaylistAttributes.map(a => new VideoStreamingPlaylistModel(a)) |
323 | 323 | ||
324 | // Remove video files that do not exist anymore | 324 | // Remove video files that do not exist anymore |
325 | const destroyTasks = options.video.VideoStreamingPlaylists | 325 | const destroyTasks = video.VideoStreamingPlaylists |
326 | .filter(f => !newStreamingPlaylists.find(newPlaylist => newPlaylist.hasSameUniqueKeysThan(f))) | 326 | .filter(f => !newStreamingPlaylists.find(newPlaylist => newPlaylist.hasSameUniqueKeysThan(f))) |
327 | .map(f => f.destroy(sequelizeOptions)) | 327 | .map(f => f.destroy(sequelizeOptions)) |
328 | await Promise.all(destroyTasks) | 328 | await Promise.all(destroyTasks) |
329 | 329 | ||
330 | // Update or add other one | 330 | // Update or add other one |
@@ -333,36 +333,36 @@ async function updateVideoFromAP (options: { | |||
333 | .then(([ streamingPlaylist ]) => streamingPlaylist) | 333 | .then(([ streamingPlaylist ]) => streamingPlaylist) |
334 | }) | 334 | }) |
335 | 335 | ||
336 | options.video.VideoStreamingPlaylists = await Promise.all(upsertTasks) | 336 | video.VideoStreamingPlaylists = await Promise.all(upsertTasks) |
337 | } | 337 | } |
338 | 338 | ||
339 | { | 339 | { |
340 | // Update Tags | 340 | // Update Tags |
341 | const tags = options.videoObject.tag.map(tag => tag.name) | 341 | const tags = videoObject.tag.map(tag => tag.name) |
342 | const tagInstances = await TagModel.findOrCreateTags(tags, t) | 342 | const tagInstances = await TagModel.findOrCreateTags(tags, t) |
343 | await options.video.$set('Tags', tagInstances, sequelizeOptions) | 343 | await video.$set('Tags', tagInstances, sequelizeOptions) |
344 | } | 344 | } |
345 | 345 | ||
346 | { | 346 | { |
347 | // Update captions | 347 | // Update captions |
348 | await VideoCaptionModel.deleteAllCaptionsOfRemoteVideo(options.video.id, t) | 348 | await VideoCaptionModel.deleteAllCaptionsOfRemoteVideo(video.id, t) |
349 | 349 | ||
350 | const videoCaptionsPromises = options.videoObject.subtitleLanguage.map(c => { | 350 | const videoCaptionsPromises = videoObject.subtitleLanguage.map(c => { |
351 | return VideoCaptionModel.insertOrReplaceLanguage(options.video.id, c.identifier, t) | 351 | return VideoCaptionModel.insertOrReplaceLanguage(video.id, c.identifier, t) |
352 | }) | 352 | }) |
353 | options.video.VideoCaptions = await Promise.all(videoCaptionsPromises) | 353 | video.VideoCaptions = await Promise.all(videoCaptionsPromises) |
354 | } | 354 | } |
355 | }) | 355 | }) |
356 | 356 | ||
357 | // Notify our users? | 357 | const autoBlacklisted = await autoBlacklistVideoIfNeeded(video, undefined, undefined) |
358 | if (wasPrivateVideo || wasUnlistedVideo) { | 358 | |
359 | Notifier.Instance.notifyOnNewVideo(options.video) | 359 | if (autoBlacklisted) Notifier.Instance.notifyOnVideoAutoBlacklist(video) |
360 | } | 360 | else if (!wasPrivateVideo || wasUnlistedVideo) Notifier.Instance.notifyOnNewVideo(video) // Notify our users? |
361 | 361 | ||
362 | logger.info('Remote video with uuid %s updated', options.videoObject.uuid) | 362 | logger.info('Remote video with uuid %s updated', videoObject.uuid) |
363 | } catch (err) { | 363 | } catch (err) { |
364 | if (options.video !== undefined && videoFieldsSave !== undefined) { | 364 | if (video !== undefined && videoFieldsSave !== undefined) { |
365 | resetSequelizeInstance(options.video, videoFieldsSave) | 365 | resetSequelizeInstance(video, videoFieldsSave) |
366 | } | 366 | } |
367 | 367 | ||
368 | // This is just a debug because we will retry the insert | 368 | // This is just a debug because we will retry the insert |
@@ -379,7 +379,9 @@ async function refreshVideoIfNeeded (options: { | |||
379 | if (!options.video.isOutdated()) return options.video | 379 | if (!options.video.isOutdated()) return options.video |
380 | 380 | ||
381 | // We need more attributes if the argument video was fetched with not enough joints | 381 | // We need more attributes if the argument video was fetched with not enough joints |
382 | const video = options.fetchedType === 'all' ? options.video : await VideoModel.loadByUrlAndPopulateAccount(options.video.url) | 382 | const video = options.fetchedType === 'all' |
383 | ? options.video | ||
384 | : await VideoModel.loadByUrlAndPopulateAccount(options.video.url) | ||
383 | 385 | ||
384 | try { | 386 | try { |
385 | const { response, videoObject } = await fetchRemoteVideo(video.url) | 387 | const { response, videoObject } = await fetchRemoteVideo(video.url) |