PeerTube/server/lib/activitypub/videos/updater.ts

167 lines
6.1 KiB
TypeScript
Raw Normal View History

2021-06-02 10:41:46 +02:00
import { Transaction } from 'sequelize/types'
import { resetSequelizeInstance } from '@server/helpers/database-utils'
2021-06-03 16:56:42 +02:00
import { logger, loggerTagsFactory, LoggerTagsFn } from '@server/helpers/logger'
2021-06-02 10:41:46 +02:00
import { sequelizeTypescript } from '@server/initializers/database'
import { Notifier } from '@server/lib/notifier'
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'
2021-06-02 11:54:29 +02:00
import { MActor, MChannelAccountLight, MChannelId, MVideoAccountLightBlacklistAllFiles, MVideoFullLight } from '@server/types/models'
2021-06-02 10:41:46 +02:00
import { VideoObject, VideoPrivacy } from '@shared/models'
import { APVideoAbstractBuilder, getVideoAttributesFromObject } from './shared'
export class APVideoUpdater extends APVideoAbstractBuilder {
private readonly wasPrivateVideo: boolean
private readonly wasUnlistedVideo: boolean
private readonly videoFieldsSave: any
private readonly oldVideoChannel: MChannelAccountLight
2021-06-03 16:56:42 +02:00
protected lTags: LoggerTagsFn
2021-06-02 16:49:59 +02:00
2021-06-02 11:54:29 +02:00
constructor (
protected readonly videoObject: VideoObject,
private readonly video: MVideoAccountLightBlacklistAllFiles
) {
2021-06-02 10:41:46 +02:00
super()
this.wasPrivateVideo = this.video.privacy === VideoPrivacy.PRIVATE
this.wasUnlistedVideo = this.video.privacy === VideoPrivacy.UNLISTED
this.oldVideoChannel = this.video.VideoChannel
this.videoFieldsSave = this.video.toJSON()
2021-06-03 16:56:42 +02:00
this.lTags = loggerTagsFactory('ap', 'video', 'update', video.uuid, video.url)
2021-06-02 10:41:46 +02:00
}
2021-06-02 11:54:29 +02:00
async update (overrideTo?: string[]) {
2021-06-02 16:49:59 +02:00
logger.debug(
'Updating remote video "%s".', this.videoObject.uuid,
2021-06-03 16:56:42 +02:00
{ videoObject: this.videoObject, ...this.lTags() }
2021-06-02 16:49:59 +02:00
)
2021-06-02 10:41:46 +02:00
try {
2021-06-02 11:54:29 +02:00
const channelActor = await this.getOrCreateVideoChannelFromVideoObject()
2021-06-02 10:41:46 +02:00
const thumbnailModel = await this.tryToGenerateThumbnail(this.video)
const videoUpdated = await sequelizeTypescript.transaction(async t => {
2021-06-02 11:54:29 +02:00
this.checkChannelUpdateOrThrow(channelActor)
2021-06-02 10:41:46 +02:00
2021-06-02 11:54:29 +02:00
const videoUpdated = await this.updateVideo(channelActor.VideoChannel, t, overrideTo)
2021-06-02 10:41:46 +02:00
if (thumbnailModel) await videoUpdated.addAndSaveThumbnail(thumbnailModel, t)
await this.setPreview(videoUpdated, t)
await this.setWebTorrentFiles(videoUpdated, t)
await this.setStreamingPlaylists(videoUpdated, t)
await this.setTags(videoUpdated, t)
await this.setTrackers(videoUpdated, t)
await this.setCaptions(videoUpdated, t)
await this.setOrDeleteLive(videoUpdated, t)
return videoUpdated
})
await autoBlacklistVideoIfNeeded({
video: videoUpdated,
user: undefined,
isRemote: true,
isNew: false,
transaction: undefined
})
// Notify our users?
if (this.wasPrivateVideo || this.wasUnlistedVideo) {
Notifier.Instance.notifyOnNewVideoIfNeeded(videoUpdated)
}
if (videoUpdated.isLive) {
PeerTubeSocket.Instance.sendVideoLiveNewState(videoUpdated)
PeerTubeSocket.Instance.sendVideoViewsUpdate(videoUpdated)
}
2021-06-03 16:56:42 +02:00
logger.info('Remote video with uuid %s updated', this.videoObject.uuid, this.lTags())
2021-06-02 10:41:46 +02:00
return videoUpdated
} catch (err) {
this.catchUpdateError(err)
}
}
// Check we can update the channel: we trust the remote server
2021-06-02 11:54:29 +02:00
private checkChannelUpdateOrThrow (newChannelActor: MActor) {
if (!this.oldVideoChannel.Actor.serverId || !newChannelActor.serverId) {
2021-06-02 10:41:46 +02:00
throw new Error('Cannot check old channel/new channel validity because `serverId` is null')
}
2021-06-02 11:54:29 +02:00
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}`)
2021-06-02 10:41:46 +02:00
}
}
2021-06-02 11:54:29 +02:00
private updateVideo (channel: MChannelId, transaction: Transaction, overrideTo?: string[]) {
const to = overrideTo || this.videoObject.to
const videoData = getVideoAttributesFromObject(channel, this.videoObject, to)
2021-06-02 10:41:46 +02:00
this.video.name = videoData.name
this.video.uuid = videoData.uuid
this.video.url = videoData.url
this.video.category = videoData.category
this.video.licence = videoData.licence
this.video.language = videoData.language
this.video.description = videoData.description
this.video.support = videoData.support
this.video.nsfw = videoData.nsfw
this.video.commentsEnabled = videoData.commentsEnabled
this.video.downloadEnabled = videoData.downloadEnabled
this.video.waitTranscoding = videoData.waitTranscoding
this.video.state = videoData.state
this.video.duration = videoData.duration
this.video.createdAt = videoData.createdAt
this.video.publishedAt = videoData.publishedAt
this.video.originallyPublishedAt = videoData.originallyPublishedAt
this.video.privacy = videoData.privacy
this.video.channelId = videoData.channelId
this.video.views = videoData.views
this.video.isLive = videoData.isLive
2021-06-03 16:02:29 +02:00
// Ensures we update the updatedAt attribute, even if main attributes did not change
2021-06-02 10:41:46 +02:00
this.video.changed('updatedAt', true)
return this.video.save({ transaction }) as Promise<MVideoFullLight>
}
private async setCaptions (videoUpdated: MVideoFullLight, t: Transaction) {
await VideoCaptionModel.deleteAllCaptionsOfRemoteVideo(videoUpdated.id, t)
await this.insertOrReplaceCaptions(videoUpdated, t)
}
private async setOrDeleteLive (videoUpdated: MVideoFullLight, transaction: Transaction) {
if (this.video.isLive) return this.insertOrReplaceLive(videoUpdated, transaction)
// Delete existing live if it exists
await VideoLiveModel.destroy({
where: {
videoId: this.video.id
},
transaction
})
videoUpdated.VideoLive = null
}
private catchUpdateError (err: Error) {
if (this.video !== undefined && this.videoFieldsSave !== undefined) {
resetSequelizeInstance(this.video, this.videoFieldsSave)
}
// This is just a debug because we will retry the insert
2021-06-03 16:56:42 +02:00
logger.debug('Cannot update the remote video.', { err, ...this.lTags() })
2021-06-02 10:41:46 +02:00
throw err
}
}