import { Transaction } from 'sequelize/types'
-import { resetSequelizeInstance } from '@server/helpers/database-utils'
-import { logger } from '@server/helpers/logger'
-import { sequelizeTypescript } from '@server/initializers/database'
+import { resetSequelizeInstance, runInReadCommittedTransaction } from '@server/helpers/database-utils'
+import { logger, loggerTagsFactory, LoggerTagsFn } from '@server/helpers/logger'
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'
-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[]
- }) {
- super()
+ protected lTags: LoggerTagsFn
- this.video = options.video
- this.videoObject = options.videoObject
- this.channel = options.channel
- this.overrideTo = options.overrideTo
+ constructor (
+ protected readonly videoObject: VideoObject,
+ private readonly video: MVideoAccountLightBlacklistAllFiles
+ ) {
+ 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()
+
+ this.lTags = loggerTagsFactory('ap', 'video', 'update', video.uuid, video.url)
}
- 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, ...this.lTags() }
+ )
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, undefined, overrideTo)
- if (thumbnailModel) await videoUpdated.addAndSaveThumbnail(thumbnailModel, t)
+ if (thumbnailModel) await videoUpdated.addAndSaveThumbnail(thumbnailModel)
- await this.setPreview(videoUpdated, t)
+ await runInReadCommittedTransaction(async 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 Promise.all([
+ runInReadCommittedTransaction(t => this.setTags(videoUpdated, t)),
+ runInReadCommittedTransaction(t => this.setTrackers(videoUpdated, t)),
+ this.setOrDeleteLive(videoUpdated),
+ this.setPreview(videoUpdated)
+ ])
+
+ await runInReadCommittedTransaction(t => this.setCaptions(videoUpdated, t))
+
await autoBlacklistVideoIfNeeded({
video: videoUpdated,
user: undefined,
if (videoUpdated.isLive) {
PeerTubeSocket.Instance.sendVideoLiveNewState(videoUpdated)
- PeerTubeSocket.Instance.sendVideoViewsUpdate(videoUpdated)
}
- logger.info('Remote video with uuid %s updated', this.videoObject.uuid)
+ logger.info('Remote video with uuid %s updated', this.videoObject.uuid, this.lTags())
return videoUpdated
} catch (err) {
}
// 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
this.video.views = videoData.views
this.video.isLive = videoData.isLive
- // Ensures we update the updated video attribute
+ // Ensures we update the updatedAt attribute, even if main attributes did not change
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) {
+ private async setOrDeleteLive (videoUpdated: MVideoFullLight, transaction?: Transaction) {
+ if (!this.video.isLive) return
+
if (this.video.isLive) return this.insertOrReplaceLive(videoUpdated, transaction)
// Delete existing live if it exists
}
// This is just a debug because we will retry the insert
- logger.debug('Cannot update the remote video.', { err })
+ logger.debug('Cannot update the remote video.', { err, ...this.lTags() })
throw err
}
}