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
const objectType = activity.object.type
if (objectType === 'Video') {
- return retryTransactionWrapper(processUpdateVideo, byActor, activity)
+ return retryTransactionWrapper(processUpdateVideo, activity)
}
if (objectType === 'Person' || objectType === 'Application' || objectType === 'Group') {
// ---------------------------------------------------------------------------
-async function processUpdateVideo (actor: MActorSignature, activity: ActivityUpdate) {
+async function processUpdateVideo (activity: ActivityUpdate) {
const videoObject = activity.object as VideoObject
if (sanitizeAndCheckVideoTorrentObject(videoObject) === false) {
// 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) {
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'
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
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)
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)
fetchRemoteVideo,
fetchRemoteVideoDescription,
refreshVideoIfNeeded,
- getOrCreateVideoChannelFromVideoObject,
getOrCreateVideoAndAccountAndChannel
}
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'
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,
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,
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)
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)
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,
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
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
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)
}
// 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