aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/lib/activitypub/videos.ts
diff options
context:
space:
mode:
Diffstat (limited to 'server/lib/activitypub/videos.ts')
-rw-r--r--server/lib/activitypub/videos.ts132
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'
54import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type' 54import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type'
55import { join } from 'path' 55import { join } from 'path'
56import { FilteredModelAttributes } from '../../typings/sequelize' 56import { FilteredModelAttributes } from '../../typings/sequelize'
57import { Hooks } from '../plugins/hooks'
58import { autoBlacklistVideoIfNeeded } from '../video-blacklist'
57 59
58async function federateVideoIfNeeded (video: VideoModel, isNewVideo: boolean, transaction?: sequelize.Transaction) { 60async 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)