diff --git a/server/lib/activitypub/process/process-update.ts b/server/lib/activitypub/process/process-update.ts index de1ff5d90..516bd8d70 100644 --- a/server/lib/activitypub/process/process-update.ts +++ b/server/lib/activitypub/process/process-update.ts @@ -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) { const { activity, byActor } = options @@ -25,7 +25,7 @@ async function processUpdateActivity (options: APProcessorOptions 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 = 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 } diff --git a/server/lib/activitypub/videos/shared/abstract-builder.ts b/server/lib/activitypub/videos/shared/abstract-builder.ts index 9d5f37e5f..c7631cd45 100644 --- a/server/lib/activitypub/videos/shared/abstract-builder.ts +++ b/server/lib/activitypub/videos/shared/abstract-builder.ts @@ -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 { return createVideoMiniatureFromUrl({ downloadUrl: getThumbnailFromIcons(this.videoObject).url, diff --git a/server/lib/activitypub/videos/shared/creator.ts b/server/lib/activitypub/videos/shared/creator.ts index 4f2d79374..dd9bfb508 100644 --- a/server/lib/activitypub/videos/shared/creator.ts +++ b/server/lib/activitypub/videos/shared/creator.ts @@ -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, diff --git a/server/lib/activitypub/videos/updater.ts b/server/lib/activitypub/videos/updater.ts index 4338d1e22..11c177a68 100644 --- a/server/lib/activitypub/videos/updater.ts +++ b/server/lib/activitypub/videos/updater.ts @@ -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