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