]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/lib/activitypub/videos/updater.ts
Merge branch 'release/4.3.0' into develop
[github/Chocobozzz/PeerTube.git] / server / lib / activitypub / videos / updater.ts
CommitLineData
08a47c75 1import { Transaction } from 'sequelize/types'
28dfb44b 2import { resetSequelizeInstance, runInReadCommittedTransaction } from '@server/helpers/database-utils'
908e6ead 3import { logger, loggerTagsFactory, LoggerTagsFn } from '@server/helpers/logger'
08a47c75
C
4import { Notifier } from '@server/lib/notifier'
5import { PeerTubeSocket } from '@server/lib/peertube-socket'
6import { autoBlacklistVideoIfNeeded } from '@server/lib/video-blacklist'
08a47c75 7import { VideoLiveModel } from '@server/models/video/video-live'
c56faf0d 8import { MActor, MChannelAccountLight, MChannelId, MVideoAccountLightBlacklistAllFiles, MVideoFullLight } from '@server/types/models'
08a47c75 9import { VideoObject, VideoPrivacy } from '@shared/models'
57e4e1c1 10import { APVideoAbstractBuilder, getVideoAttributesFromObject, updateVideoRates } from './shared'
08a47c75
C
11
12export class APVideoUpdater extends APVideoAbstractBuilder {
08a47c75
C
13 private readonly wasPrivateVideo: boolean
14 private readonly wasUnlistedVideo: boolean
15
16 private readonly videoFieldsSave: any
17
18 private readonly oldVideoChannel: MChannelAccountLight
19
908e6ead 20 protected lTags: LoggerTagsFn
46320694 21
c56faf0d
C
22 constructor (
23 protected readonly videoObject: VideoObject,
24 private readonly video: MVideoAccountLightBlacklistAllFiles
25 ) {
08a47c75
C
26 super()
27
08a47c75
C
28 this.wasPrivateVideo = this.video.privacy === VideoPrivacy.PRIVATE
29 this.wasUnlistedVideo = this.video.privacy === VideoPrivacy.UNLISTED
30
31 this.oldVideoChannel = this.video.VideoChannel
32
33 this.videoFieldsSave = this.video.toJSON()
908e6ead
C
34
35 this.lTags = loggerTagsFactory('ap', 'video', 'update', video.uuid, video.url)
08a47c75
C
36 }
37
c56faf0d 38 async update (overrideTo?: string[]) {
46320694
C
39 logger.debug(
40 'Updating remote video "%s".', this.videoObject.uuid,
908e6ead 41 { videoObject: this.videoObject, ...this.lTags() }
46320694 42 )
08a47c75
C
43
44 try {
c56faf0d
C
45 const channelActor = await this.getOrCreateVideoChannelFromVideoObject()
46
08a47c75
C
47 const thumbnailModel = await this.tryToGenerateThumbnail(this.video)
48
28dfb44b 49 this.checkChannelUpdateOrThrow(channelActor)
08a47c75 50
28dfb44b 51 const videoUpdated = await this.updateVideo(channelActor.VideoChannel, undefined, overrideTo)
08a47c75 52
28dfb44b 53 if (thumbnailModel) await videoUpdated.addAndSaveThumbnail(thumbnailModel)
08a47c75 54
28dfb44b 55 await runInReadCommittedTransaction(async t => {
08a47c75
C
56 await this.setWebTorrentFiles(videoUpdated, t)
57 await this.setStreamingPlaylists(videoUpdated, t)
08a47c75
C
58 })
59
28dfb44b
C
60 await Promise.all([
61 runInReadCommittedTransaction(t => this.setTags(videoUpdated, t)),
62 runInReadCommittedTransaction(t => this.setTrackers(videoUpdated, t)),
63 this.setOrDeleteLive(videoUpdated),
64 this.setPreview(videoUpdated)
65 ])
66
67 await runInReadCommittedTransaction(t => this.setCaptions(videoUpdated, t))
68
08a47c75
C
69 await autoBlacklistVideoIfNeeded({
70 video: videoUpdated,
71 user: undefined,
72 isRemote: true,
73 isNew: false,
74 transaction: undefined
75 })
76
57e4e1c1
C
77 await updateVideoRates(videoUpdated, this.videoObject)
78
08a47c75
C
79 // Notify our users?
80 if (this.wasPrivateVideo || this.wasUnlistedVideo) {
81 Notifier.Instance.notifyOnNewVideoIfNeeded(videoUpdated)
82 }
83
84 if (videoUpdated.isLive) {
85 PeerTubeSocket.Instance.sendVideoLiveNewState(videoUpdated)
08a47c75
C
86 }
87
908e6ead 88 logger.info('Remote video with uuid %s updated', this.videoObject.uuid, this.lTags())
08a47c75
C
89
90 return videoUpdated
91 } catch (err) {
92 this.catchUpdateError(err)
93 }
94 }
95
96 // Check we can update the channel: we trust the remote server
c56faf0d
C
97 private checkChannelUpdateOrThrow (newChannelActor: MActor) {
98 if (!this.oldVideoChannel.Actor.serverId || !newChannelActor.serverId) {
08a47c75
C
99 throw new Error('Cannot check old channel/new channel validity because `serverId` is null')
100 }
101
c56faf0d
C
102 if (this.oldVideoChannel.Actor.serverId !== newChannelActor.serverId) {
103 throw new Error(`New channel ${newChannelActor.url} is not on the same server than new channel ${this.oldVideoChannel.Actor.url}`)
08a47c75
C
104 }
105 }
106
28dfb44b 107 private updateVideo (channel: MChannelId, transaction?: Transaction, overrideTo?: string[]) {
c56faf0d
C
108 const to = overrideTo || this.videoObject.to
109 const videoData = getVideoAttributesFromObject(channel, this.videoObject, to)
08a47c75
C
110 this.video.name = videoData.name
111 this.video.uuid = videoData.uuid
112 this.video.url = videoData.url
113 this.video.category = videoData.category
114 this.video.licence = videoData.licence
115 this.video.language = videoData.language
116 this.video.description = videoData.description
117 this.video.support = videoData.support
118 this.video.nsfw = videoData.nsfw
119 this.video.commentsEnabled = videoData.commentsEnabled
120 this.video.downloadEnabled = videoData.downloadEnabled
121 this.video.waitTranscoding = videoData.waitTranscoding
122 this.video.state = videoData.state
123 this.video.duration = videoData.duration
124 this.video.createdAt = videoData.createdAt
125 this.video.publishedAt = videoData.publishedAt
126 this.video.originallyPublishedAt = videoData.originallyPublishedAt
127 this.video.privacy = videoData.privacy
128 this.video.channelId = videoData.channelId
129 this.video.views = videoData.views
130 this.video.isLive = videoData.isLive
131
136d7efd 132 // Ensures we update the updatedAt attribute, even if main attributes did not change
08a47c75
C
133 this.video.changed('updatedAt', true)
134
135 return this.video.save({ transaction }) as Promise<MVideoFullLight>
136 }
137
138 private async setCaptions (videoUpdated: MVideoFullLight, t: Transaction) {
08a47c75
C
139 await this.insertOrReplaceCaptions(videoUpdated, t)
140 }
141
28dfb44b
C
142 private async setOrDeleteLive (videoUpdated: MVideoFullLight, transaction?: Transaction) {
143 if (!this.video.isLive) return
144
08a47c75
C
145 if (this.video.isLive) return this.insertOrReplaceLive(videoUpdated, transaction)
146
147 // Delete existing live if it exists
148 await VideoLiveModel.destroy({
149 where: {
150 videoId: this.video.id
151 },
152 transaction
153 })
154
155 videoUpdated.VideoLive = null
156 }
157
158 private catchUpdateError (err: Error) {
159 if (this.video !== undefined && this.videoFieldsSave !== undefined) {
160 resetSequelizeInstance(this.video, this.videoFieldsSave)
161 }
162
163 // This is just a debug because we will retry the insert
908e6ead 164 logger.debug('Cannot update the remote video.', { err, ...this.lTags() })
08a47c75
C
165 throw err
166 }
167}