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