Move AP video channel creation
This commit is contained in:
parent
08a47c75f9
commit
c56faf0d94
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue