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