]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/commitdiff
Move AP video channel creation
authorChocobozzz <me@florianbigard.com>
Wed, 2 Jun 2021 09:54:29 +0000 (11:54 +0200)
committerChocobozzz <me@florianbigard.com>
Wed, 2 Jun 2021 14:57:53 +0000 (16:57 +0200)
server/lib/activitypub/process/process-update.ts
server/lib/activitypub/videos/fetch.ts
server/lib/activitypub/videos/shared/abstract-builder.ts
server/lib/activitypub/videos/shared/creator.ts
server/lib/activitypub/videos/updater.ts

index de1ff5d905d5aec1839f8069f1652546a8d9dafc..516bd8d706818c37cb6b1331392bfc323ec3adef 100644 (file)
@@ -12,12 +12,12 @@ import { AccountModel } from '../../../models/account/account'
 import { ActorModel } from '../../../models/actor/actor'
 import { VideoChannelModel } from '../../../models/video/video-channel'
 import { APProcessorOptions } from '../../../types/activitypub-processor.model'
-import { MAccountIdActor, MActorSignature } from '../../../types/models'
+import { MActorSignature } from '../../../types/models'
 import { getImageInfoIfExists, updateActorImageInstance, updateActorInstance } from '../actor'
 import { createOrUpdateCacheFile } from '../cache-file'
 import { createOrUpdateVideoPlaylist } from '../playlist'
 import { forwardVideoRelatedActivity } from '../send/utils'
-import { getOrCreateVideoAndAccountAndChannel, getOrCreateVideoChannelFromVideoObject, APVideoUpdater } from '../videos'
+import { APVideoUpdater, getOrCreateVideoAndAccountAndChannel } from '../videos'
 
 async function processUpdateActivity (options: APProcessorOptions<ActivityUpdate>) {
   const { activity, byActor } = options
@@ -25,7 +25,7 @@ async function processUpdateActivity (options: APProcessorOptions<ActivityUpdate
   const objectType = activity.object.type
 
   if (objectType === 'Video') {
-    return retryTransactionWrapper(processUpdateVideo, byActor, activity)
+    return retryTransactionWrapper(processUpdateVideo, activity)
   }
 
   if (objectType === 'Person' || objectType === 'Application' || objectType === 'Group') {
@@ -55,7 +55,7 @@ export {
 
 // ---------------------------------------------------------------------------
 
-async function processUpdateVideo (actor: MActorSignature, activity: ActivityUpdate) {
+async function processUpdateVideo (activity: ActivityUpdate) {
   const videoObject = activity.object as VideoObject
 
   if (sanitizeAndCheckVideoTorrentObject(videoObject) === false) {
@@ -71,19 +71,8 @@ async function processUpdateVideo (actor: MActorSignature, activity: ActivityUpd
   // We did not have this video, it has been created so no need to update
   if (created) return
 
-  // Load new channel
-  const channelActor = await getOrCreateVideoChannelFromVideoObject(videoObject)
-
-  const account = actor.Account as MAccountIdActor
-  account.Actor = actor
-
-  const updater = new APVideoUpdater({
-    video,
-    videoObject,
-    channel: channelActor.VideoChannel,
-    overrideTo: activity.to
-  })
-  return updater.update()
+  const updater = new APVideoUpdater(videoObject, video)
+  return updater.update(activity.to)
 }
 
 async function processUpdateCacheFile (byActor: MActorSignature, activity: ActivityUpdate) {
index 5e7f8552b5c991e37e62b2fc789f52774865278d..5113c9d7e01157376694661d4e2cb7de34d472f7 100644 (file)
@@ -11,7 +11,6 @@ import { VideoModel } from '@server/models/video/video'
 import { MVideoAccountLight, MVideoAccountLightBlacklistAllFiles, MVideoImmutable, MVideoThumbnail } from '@server/types/models'
 import { HttpStatusCode } from '@shared/core-utils'
 import { VideoObject } from '@shared/models'
-import { getOrCreateActorAndServerAndModel } from '../actor'
 import { APVideoCreator, SyncParam, syncVideoExternalAttributes } from './shared'
 import { APVideoUpdater } from './updater'
 
@@ -37,17 +36,6 @@ async function fetchRemoteVideoDescription (video: MVideoAccountLight) {
   return body.description || ''
 }
 
-function getOrCreateVideoChannelFromVideoObject (videoObject: VideoObject) {
-  const channel = videoObject.attributedTo.find(a => a.type === 'Group')
-  if (!channel) throw new Error('Cannot find associated video channel to video ' + videoObject.url)
-
-  if (checkUrlsSameHost(channel.id, videoObject.id) !== true) {
-    throw new Error(`Video channel url ${channel.id} does not have the same host than video object id ${videoObject.id}`)
-  }
-
-  return getOrCreateActorAndServerAndModel(channel.id, 'all')
-}
-
 type GetVideoResult <T> = Promise<{
   video: T
   created: boolean
@@ -117,11 +105,8 @@ async function getOrCreateVideoAndAccountAndChannel (
   const { videoObject } = await fetchRemoteVideo(videoUrl)
   if (!videoObject) throw new Error('Cannot fetch remote video with url: ' + videoUrl)
 
-  const actor = await getOrCreateVideoChannelFromVideoObject(videoObject)
-  const videoChannel = actor.VideoChannel
-
   try {
-    const creator = new APVideoCreator({ videoObject, channel: videoChannel })
+    const creator = new APVideoCreator(videoObject)
     const { autoBlacklisted, videoCreated } = await retryTransactionWrapper(creator.create.bind(creator), syncParam.thumbnail)
 
     await syncVideoExternalAttributes(videoCreated, videoObject, syncParam)
@@ -160,13 +145,7 @@ async function refreshVideoIfNeeded (options: {
       return video
     }
 
-    const channelActor = await getOrCreateVideoChannelFromVideoObject(videoObject)
-
-    const videoUpdater = new APVideoUpdater({
-      video,
-      videoObject,
-      channel: channelActor.VideoChannel
-    })
+    const videoUpdater = new APVideoUpdater(videoObject, video)
     await videoUpdater.update()
 
     await syncVideoExternalAttributes(video, videoObject, options.syncParam)
@@ -197,6 +176,5 @@ export {
   fetchRemoteVideo,
   fetchRemoteVideoDescription,
   refreshVideoIfNeeded,
-  getOrCreateVideoChannelFromVideoObject,
   getOrCreateVideoAndAccountAndChannel
 }
index 9d5f37e5f954091fd572407a478fbbb7df120e63..c7631cd457a8823595f21c85bc051280172dd1c2 100644 (file)
@@ -1,4 +1,5 @@
 import { Transaction } from 'sequelize/types'
+import { checkUrlsSameHost } from '@server/helpers/activitypub'
 import { deleteNonExistingModels } from '@server/helpers/database-utils'
 import { logger } from '@server/helpers/logger'
 import { createPlaceholderThumbnail, createVideoMiniatureFromUrl } from '@server/lib/thumbnail'
@@ -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 { getOrCreateActorAndServerAndModel } from '../../actor'
 import {
   getCaptionAttributesFromObject,
   getFileAttributesFromUrl,
@@ -23,6 +25,17 @@ import { getTrackerUrls, setVideoTrackers } from './trackers'
 export abstract class APVideoAbstractBuilder {
   protected abstract videoObject: VideoObject
 
+  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 getOrCreateActorAndServerAndModel(channel.id, 'all')
+  }
+
   protected tryToGenerateThumbnail (video: MVideoThumbnail): Promise<MThumbnail> {
     return createVideoMiniatureFromUrl({
       downloadUrl: getThumbnailFromIcons(this.videoObject).url,
index 4f2d79374109554c84241fcbc68d1316c581e12c..dd9bfb508528a6a6437738b478cd6a3afb02882a 100644 (file)
@@ -3,29 +3,24 @@ import { logger } from '@server/helpers/logger'
 import { sequelizeTypescript } from '@server/initializers/database'
 import { autoBlacklistVideoIfNeeded } from '@server/lib/video-blacklist'
 import { VideoModel } from '@server/models/video/video'
-import { MChannelAccountLight, MThumbnail, MVideoFullLight, MVideoThumbnail } from '@server/types/models'
+import { MThumbnail, MVideoFullLight, MVideoThumbnail } from '@server/types/models'
 import { VideoObject } from '@shared/models'
 import { APVideoAbstractBuilder } from './abstract-builder'
 import { getVideoAttributesFromObject } from './object-to-model-attributes'
 
 export class APVideoCreator extends APVideoAbstractBuilder {
-  protected readonly videoObject: VideoObject
-  private readonly channel: MChannelAccountLight
 
-  constructor (options: {
-    videoObject: VideoObject
-    channel: MChannelAccountLight
-  }) {
+  constructor (protected readonly videoObject: VideoObject) {
     super()
-
-    this.videoObject = options.videoObject
-    this.channel = options.channel
   }
 
   async create (waitThumbnail = false) {
     logger.debug('Adding remote video %s.', this.videoObject.id)
 
-    const videoData = await getVideoAttributesFromObject(this.channel, this.videoObject, this.videoObject.to)
+    const channelActor = await this.getOrCreateVideoChannelFromVideoObject()
+    const channel = channelActor.VideoChannel
+
+    const videoData = await getVideoAttributesFromObject(channel, this.videoObject, this.videoObject.to)
     const video = VideoModel.build(videoData) as MVideoThumbnail
 
     const promiseThumbnail = this.tryToGenerateThumbnail(video)
@@ -38,7 +33,7 @@ export class APVideoCreator extends APVideoAbstractBuilder {
     const { autoBlacklisted, videoCreated } = await sequelizeTypescript.transaction(async t => {
       try {
         const videoCreated = await video.save({ transaction: t }) as MVideoFullLight
-        videoCreated.VideoChannel = this.channel
+        videoCreated.VideoChannel = channel
 
         if (thumbnailModel) await videoCreated.addAndSaveThumbnail(thumbnailModel, t)
 
@@ -51,7 +46,7 @@ export class APVideoCreator extends APVideoAbstractBuilder {
         await this.insertOrReplaceLive(videoCreated, t)
 
         // We added a video in this channel, set it as updated
-        await this.channel.setAsUpdated(t)
+        await channel.setAsUpdated(t)
 
         const autoBlacklisted = await autoBlacklistVideoIfNeeded({
           video: videoCreated,
index 4338d1e2226ddd28d551c6a6e40629e6d1e1f332..11c177a68278a4fa1e79b715dd1874ed8f55b3d2 100644 (file)
@@ -7,17 +7,11 @@ import { PeerTubeSocket } from '@server/lib/peertube-socket'
 import { autoBlacklistVideoIfNeeded } from '@server/lib/video-blacklist'
 import { VideoCaptionModel } from '@server/models/video/video-caption'
 import { VideoLiveModel } from '@server/models/video/video-live'
-import { MChannelAccountLight, MChannelDefault, MVideoAccountLightBlacklistAllFiles, MVideoFullLight } from '@server/types/models'
+import { MActor, MChannelAccountLight, MChannelId, MVideoAccountLightBlacklistAllFiles, MVideoFullLight } from '@server/types/models'
 import { VideoObject, VideoPrivacy } from '@shared/models'
 import { APVideoAbstractBuilder, getVideoAttributesFromObject } from './shared'
 
 export class APVideoUpdater extends APVideoAbstractBuilder {
-  protected readonly videoObject: VideoObject
-
-  private readonly video: MVideoAccountLightBlacklistAllFiles
-  private readonly channel: MChannelDefault
-  private readonly overrideTo: string[]
-
   private readonly wasPrivateVideo: boolean
   private readonly wasUnlistedVideo: boolean
 
@@ -25,19 +19,12 @@ export class APVideoUpdater extends APVideoAbstractBuilder {
 
   private readonly oldVideoChannel: MChannelAccountLight
 
-  constructor (options: {
-    video: MVideoAccountLightBlacklistAllFiles
-    videoObject: VideoObject
-    channel: MChannelDefault
-    overrideTo?: string[]
-  }) {
+  constructor (
+    protected readonly videoObject: VideoObject,
+    private readonly video: MVideoAccountLightBlacklistAllFiles
+  ) {
     super()
 
-    this.video = options.video
-    this.videoObject = options.videoObject
-    this.channel = options.channel
-    this.overrideTo = options.overrideTo
-
     this.wasPrivateVideo = this.video.privacy === VideoPrivacy.PRIVATE
     this.wasUnlistedVideo = this.video.privacy === VideoPrivacy.UNLISTED
 
@@ -46,16 +33,18 @@ export class APVideoUpdater extends APVideoAbstractBuilder {
     this.videoFieldsSave = this.video.toJSON()
   }
 
-  async update () {
-    logger.debug('Updating remote video "%s".', this.videoObject.uuid, { videoObject: this.videoObject, channel: this.channel })
+  async update (overrideTo?: string[]) {
+    logger.debug('Updating remote video "%s".', this.videoObject.uuid, { videoObject: this.videoObject })
 
     try {
+      const channelActor = await this.getOrCreateVideoChannelFromVideoObject()
+
       const thumbnailModel = await this.tryToGenerateThumbnail(this.video)
 
       const videoUpdated = await sequelizeTypescript.transaction(async t => {
-        this.checkChannelUpdateOrThrow()
+        this.checkChannelUpdateOrThrow(channelActor)
 
-        const videoUpdated = await this.updateVideo(t)
+        const videoUpdated = await this.updateVideo(channelActor.VideoChannel, t, overrideTo)
 
         if (thumbnailModel) await videoUpdated.addAndSaveThumbnail(thumbnailModel, t)
 
@@ -97,19 +86,19 @@ export class APVideoUpdater extends APVideoAbstractBuilder {
   }
 
   // Check we can update the channel: we trust the remote server
-  private checkChannelUpdateOrThrow () {
-    if (!this.oldVideoChannel.Actor.serverId || !this.channel.Actor.serverId) {
+  private checkChannelUpdateOrThrow (newChannelActor: MActor) {
+    if (!this.oldVideoChannel.Actor.serverId || !newChannelActor.serverId) {
       throw new Error('Cannot check old channel/new channel validity because `serverId` is null')
     }
 
-    if (this.oldVideoChannel.Actor.serverId !== this.channel.Actor.serverId) {
-      throw new Error(`New channel ${this.channel.Actor.url} is not on the same server than new channel ${this.oldVideoChannel.Actor.url}`)
+    if (this.oldVideoChannel.Actor.serverId !== newChannelActor.serverId) {
+      throw new Error(`New channel ${newChannelActor.url} is not on the same server than new channel ${this.oldVideoChannel.Actor.url}`)
     }
   }
 
-  private updateVideo (transaction: Transaction) {
-    const to = this.overrideTo || this.videoObject.to
-    const videoData = getVideoAttributesFromObject(this.channel, this.videoObject, to)
+  private updateVideo (channel: MChannelId, transaction: Transaction, overrideTo?: string[]) {
+    const to = overrideTo || this.videoObject.to
+    const videoData = getVideoAttributesFromObject(channel, this.videoObject, to)
     this.video.name = videoData.name
     this.video.uuid = videoData.uuid
     this.video.url = videoData.url