]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blobdiff - server/lib/activitypub/videos/shared/abstract-builder.ts
Merge branch 'release/3.3.0' into develop
[github/Chocobozzz/PeerTube.git] / server / lib / activitypub / videos / shared / abstract-builder.ts
index 9d5f37e5f954091fd572407a478fbbb7df120e63..f995fe637a385c1fa8f960f7d198b34a3639280f 100644 (file)
@@ -1,7 +1,8 @@
 import { Transaction } from 'sequelize/types'
-import { deleteNonExistingModels } from '@server/helpers/database-utils'
-import { logger } from '@server/helpers/logger'
-import { createPlaceholderThumbnail, createVideoMiniatureFromUrl } from '@server/lib/thumbnail'
+import { checkUrlsSameHost } from '@server/helpers/activitypub'
+import { deleteAllModels, filterNonExistingModels } from '@server/helpers/database-utils'
+import { logger, LoggerTagsFn } from '@server/helpers/logger'
+import { updatePlaceholderThumbnail, updateVideoMiniatureFromUrl } from '@server/lib/thumbnail'
 import { setVideoTags } from '@server/lib/video'
 import { VideoCaptionModel } from '@server/models/video/video-caption'
 import { VideoFileModel } from '@server/models/video/video-file'
@@ -9,6 +10,7 @@ import { VideoLiveModel } from '@server/models/video/video-live'
 import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist'
 import { MStreamingPlaylistFilesVideo, MThumbnail, MVideoCaption, MVideoFile, MVideoFullLight, MVideoThumbnail } from '@server/types/models'
 import { ActivityTagObject, ThumbnailType, VideoObject, VideoStreamingPlaylistType } from '@shared/models'
+import { getOrCreateAPActor } from '../../actors'
 import {
   getCaptionAttributesFromObject,
   getFileAttributesFromUrl,
@@ -22,25 +24,37 @@ import { getTrackerUrls, setVideoTrackers } from './trackers'
 
 export abstract class APVideoAbstractBuilder {
   protected abstract videoObject: VideoObject
+  protected abstract lTags: LoggerTagsFn
+
+  protected async getOrCreateVideoChannelFromVideoObject () {
+    const channel = this.videoObject.attributedTo.find(a => a.type === 'Group')
+    if (!channel) throw new Error('Cannot find associated video channel to video ' + this.videoObject.url)
+
+    if (checkUrlsSameHost(channel.id, this.videoObject.id) !== true) {
+      throw new Error(`Video channel url ${channel.id} does not have the same host than video object id ${this.videoObject.id}`)
+    }
+
+    return getOrCreateAPActor(channel.id, 'all')
+  }
 
   protected tryToGenerateThumbnail (video: MVideoThumbnail): Promise<MThumbnail> {
-    return createVideoMiniatureFromUrl({
+    return updateVideoMiniatureFromUrl({
       downloadUrl: getThumbnailFromIcons(this.videoObject).url,
       video,
       type: ThumbnailType.MINIATURE
     }).catch(err => {
-      logger.warn('Cannot generate thumbnail of %s.', this.videoObject.id, { err })
+      logger.warn('Cannot generate thumbnail of %s.', this.videoObject.id, { err, ...this.lTags() })
 
       return undefined
     })
   }
 
-  protected async setPreview (video: MVideoFullLight, t: Transaction) {
+  protected async setPreview (video: MVideoFullLight, t?: Transaction) {
     // Don't fetch the preview that could be big, create a placeholder instead
     const previewIcon = getPreviewFromIcons(this.videoObject)
     if (!previewIcon) return
 
-    const previewModel = createPlaceholderThumbnail({
+    const previewModel = updatePlaceholderThumbnail({
       fileUrl: previewIcon.url,
       video,
       type: ThumbnailType.PREVIEW,
@@ -61,11 +75,28 @@ export abstract class APVideoAbstractBuilder {
   }
 
   protected async insertOrReplaceCaptions (video: MVideoFullLight, t: Transaction) {
-    const videoCaptionsPromises = getCaptionAttributesFromObject(video, this.videoObject)
-      .map(a => new VideoCaptionModel(a) as MVideoCaption)
-      .map(c => VideoCaptionModel.insertOrReplaceLanguage(c, t))
+    const existingCaptions = await VideoCaptionModel.listVideoCaptions(video.id, t)
+
+    let captionsToCreate = getCaptionAttributesFromObject(video, this.videoObject)
+                            .map(a => new VideoCaptionModel(a) as MVideoCaption)
 
-    await Promise.all(videoCaptionsPromises)
+    for (const existingCaption of existingCaptions) {
+      // Only keep captions that do not already exist
+      const filtered = captionsToCreate.filter(c => !c.isEqual(existingCaption))
+
+      // This caption already exists, we don't need to destroy and create it
+      if (filtered.length !== captionsToCreate.length) {
+        captionsToCreate = filtered
+        continue
+      }
+
+      // Destroy this caption that does not exist anymore
+      await existingCaption.destroy({ transaction: t })
+    }
+
+    for (const captionToCreate of captionsToCreate) {
+      await captionToCreate.save({ transaction: t })
+    }
   }
 
   protected async insertOrReplaceLive (video: MVideoFullLight, transaction: Transaction) {
@@ -80,8 +111,7 @@ export abstract class APVideoAbstractBuilder {
     const newVideoFiles = videoFileAttributes.map(a => new VideoFileModel(a))
 
     // Remove video files that do not exist anymore
-    const destroyTasks = deleteNonExistingModels(video.VideoFiles || [], newVideoFiles, t)
-    await Promise.all(destroyTasks)
+    await deleteAllModels(filterNonExistingModels(video.VideoFiles || [], newVideoFiles), t)
 
     // Update or add other one
     const upsertTasks = newVideoFiles.map(f => VideoFileModel.customUpsert(f, 'video', t))
@@ -93,13 +123,11 @@ export abstract class APVideoAbstractBuilder {
     const newStreamingPlaylists = streamingPlaylistAttributes.map(a => new VideoStreamingPlaylistModel(a))
 
     // Remove video playlists that do not exist anymore
-    const destroyTasks = deleteNonExistingModels(video.VideoStreamingPlaylists || [], newStreamingPlaylists, t)
-    await Promise.all(destroyTasks)
+    await deleteAllModels(filterNonExistingModels(video.VideoStreamingPlaylists || [], newStreamingPlaylists), t)
 
     video.VideoStreamingPlaylists = []
 
     for (const playlistAttributes of streamingPlaylistAttributes) {
-
       const streamingPlaylistModel = await this.insertOrReplaceStreamingPlaylist(playlistAttributes, t)
       streamingPlaylistModel.Video = video
 
@@ -132,8 +160,7 @@ export abstract class APVideoAbstractBuilder {
 
     const newVideoFiles: MVideoFile[] = getFileAttributesFromUrl(playlistModel, tagObjects).map(a => new VideoFileModel(a))
 
-    const destroyTasks = deleteNonExistingModels(oldStreamingPlaylistFiles, newVideoFiles, t)
-    await Promise.all(destroyTasks)
+    await deleteAllModels(filterNonExistingModels(oldStreamingPlaylistFiles, newVideoFiles), t)
 
     // Update or add other one
     const upsertTasks = newVideoFiles.map(f => VideoFileModel.customUpsert(f, 'streaming-playlist', t))