aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/lib
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2022-05-03 11:38:07 +0200
committerChocobozzz <me@florianbigard.com>2022-05-03 14:49:15 +0200
commit26e3e98ff0e222a9fb9226938ac6902af77921bd (patch)
tree73d1c6f2524e380862d3365f12043fc319d40841 /server/lib
parent86c5229b4d726202378ef46854383bcafca22310 (diff)
downloadPeerTube-26e3e98ff0e222a9fb9226938ac6902af77921bd.tar.gz
PeerTube-26e3e98ff0e222a9fb9226938ac6902af77921bd.tar.zst
PeerTube-26e3e98ff0e222a9fb9226938ac6902af77921bd.zip
Support live session in server
Diffstat (limited to 'server/lib')
-rw-r--r--server/lib/job-queue/handlers/video-live-ending.ts87
-rw-r--r--server/lib/live/live-manager.ts64
-rw-r--r--server/lib/live/shared/muxing-session.ts10
-rw-r--r--server/lib/video-blacklist.ts4
-rw-r--r--server/lib/video-state.ts4
5 files changed, 123 insertions, 46 deletions
diff --git a/server/lib/job-queue/handlers/video-live-ending.ts b/server/lib/job-queue/handlers/video-live-ending.ts
index 1e290338c..55fd09344 100644
--- a/server/lib/job-queue/handlers/video-live-ending.ts
+++ b/server/lib/job-queue/handlers/video-live-ending.ts
@@ -15,13 +15,14 @@ import { generateVideoMiniature } from '@server/lib/thumbnail'
15import { generateHlsPlaylistResolutionFromTS } from '@server/lib/transcoding/transcoding' 15import { generateHlsPlaylistResolutionFromTS } from '@server/lib/transcoding/transcoding'
16import { moveToNextState } from '@server/lib/video-state' 16import { moveToNextState } from '@server/lib/video-state'
17import { VideoModel } from '@server/models/video/video' 17import { VideoModel } from '@server/models/video/video'
18import { VideoBlacklistModel } from '@server/models/video/video-blacklist'
18import { VideoFileModel } from '@server/models/video/video-file' 19import { VideoFileModel } from '@server/models/video/video-file'
19import { VideoLiveModel } from '@server/models/video/video-live' 20import { VideoLiveModel } from '@server/models/video/video-live'
21import { VideoLiveSessionModel } from '@server/models/video/video-live-session'
20import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist' 22import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist'
21import { MVideo, MVideoLive, MVideoWithAllFiles } from '@server/types/models' 23import { MVideo, MVideoLive, MVideoLiveSession, MVideoWithAllFiles } from '@server/types/models'
22import { ThumbnailType, VideoLiveEndingPayload, VideoState } from '@shared/models' 24import { ThumbnailType, VideoLiveEndingPayload, VideoState } from '@shared/models'
23import { logger } from '../../../helpers/logger' 25import { logger } from '../../../helpers/logger'
24import { VideoBlacklistModel } from '@server/models/video/video-blacklist'
25 26
26async function processVideoLiveEnding (job: Job) { 27async function processVideoLiveEnding (job: Job) {
27 const payload = job.data as VideoLiveEndingPayload 28 const payload = job.data as VideoLiveEndingPayload
@@ -32,27 +33,28 @@ async function processVideoLiveEnding (job: Job) {
32 logger.warn('Video live %d does not exist anymore. Cannot process live ending.', payload.videoId) 33 logger.warn('Video live %d does not exist anymore. Cannot process live ending.', payload.videoId)
33 } 34 }
34 35
35 const video = await VideoModel.load(payload.videoId) 36 const liveVideo = await VideoModel.load(payload.videoId)
36 const live = await VideoLiveModel.loadByVideoId(payload.videoId) 37 const live = await VideoLiveModel.loadByVideoId(payload.videoId)
38 const liveSession = await VideoLiveSessionModel.load(payload.liveSessionId)
37 39
38 if (!video || !live) { 40 if (!liveVideo || !live || !liveSession) {
39 logError() 41 logError()
40 return 42 return
41 } 43 }
42 44
43 LiveSegmentShaStore.Instance.cleanupShaSegments(video.uuid) 45 LiveSegmentShaStore.Instance.cleanupShaSegments(liveVideo.uuid)
44 46
45 if (live.saveReplay !== true) { 47 if (live.saveReplay !== true) {
46 return cleanupLiveAndFederate(video) 48 return cleanupLiveAndFederate({ liveVideo })
47 } 49 }
48 50
49 if (live.permanentLive) { 51 if (live.permanentLive) {
50 await saveReplayToExternalVideo(video, payload.publishedAt, payload.replayDirectory) 52 await saveReplayToExternalVideo({ liveVideo, liveSession, publishedAt: payload.publishedAt, replayDirectory: payload.replayDirectory })
51 53
52 return cleanupLiveAndFederate(video) 54 return cleanupLiveAndFederate({ liveVideo })
53 } 55 }
54 56
55 return replaceLiveByReplay(video, live, payload.replayDirectory) 57 return replaceLiveByReplay({ liveVideo, live, liveSession, replayDirectory: payload.replayDirectory })
56} 58}
57 59
58// --------------------------------------------------------------------------- 60// ---------------------------------------------------------------------------
@@ -63,7 +65,14 @@ export {
63 65
64// --------------------------------------------------------------------------- 66// ---------------------------------------------------------------------------
65 67
66async function saveReplayToExternalVideo (liveVideo: MVideo, publishedAt: string, replayDirectory: string) { 68async function saveReplayToExternalVideo (options: {
69 liveVideo: MVideo
70 liveSession: MVideoLiveSession
71 publishedAt: string
72 replayDirectory: string
73}) {
74 const { liveVideo, liveSession, publishedAt, replayDirectory } = options
75
67 await cleanupTMPLiveFiles(getLiveDirectory(liveVideo)) 76 await cleanupTMPLiveFiles(getLiveDirectory(liveVideo))
68 77
69 const video = new VideoModel({ 78 const video = new VideoModel({
@@ -78,7 +87,7 @@ async function saveReplayToExternalVideo (liveVideo: MVideo, publishedAt: string
78 language: liveVideo.language, 87 language: liveVideo.language,
79 commentsEnabled: liveVideo.commentsEnabled, 88 commentsEnabled: liveVideo.commentsEnabled,
80 downloadEnabled: liveVideo.downloadEnabled, 89 downloadEnabled: liveVideo.downloadEnabled,
81 waitTranscoding: liveVideo.waitTranscoding, 90 waitTranscoding: true,
82 nsfw: liveVideo.nsfw, 91 nsfw: liveVideo.nsfw,
83 description: liveVideo.description, 92 description: liveVideo.description,
84 support: liveVideo.support, 93 support: liveVideo.support,
@@ -94,6 +103,9 @@ async function saveReplayToExternalVideo (liveVideo: MVideo, publishedAt: string
94 103
95 await video.save() 104 await video.save()
96 105
106 liveSession.replayVideoId = video.id
107 await liveSession.save()
108
97 // If live is blacklisted, also blacklist the replay 109 // If live is blacklisted, also blacklist the replay
98 const blacklist = await VideoBlacklistModel.loadByVideoId(liveVideo.id) 110 const blacklist = await VideoBlacklistModel.loadByVideoId(liveVideo.id)
99 if (blacklist) { 111 if (blacklist) {
@@ -105,7 +117,7 @@ async function saveReplayToExternalVideo (liveVideo: MVideo, publishedAt: string
105 }) 117 })
106 } 118 }
107 119
108 await assignReplaysToVideo(video, replayDirectory) 120 await assignReplayFilesToVideo({ video, replayDirectory })
109 121
110 await remove(replayDirectory) 122 await remove(replayDirectory)
111 123
@@ -117,18 +129,29 @@ async function saveReplayToExternalVideo (liveVideo: MVideo, publishedAt: string
117 await moveToNextState({ video, isNewVideo: true }) 129 await moveToNextState({ video, isNewVideo: true })
118} 130}
119 131
120async function replaceLiveByReplay (video: MVideo, live: MVideoLive, replayDirectory: string) { 132async function replaceLiveByReplay (options: {
121 await cleanupTMPLiveFiles(getLiveDirectory(video)) 133 liveVideo: MVideo
134 liveSession: MVideoLiveSession
135 live: MVideoLive
136 replayDirectory: string
137}) {
138 const { liveVideo, liveSession, live, replayDirectory } = options
139
140 await cleanupTMPLiveFiles(getLiveDirectory(liveVideo))
122 141
123 await live.destroy() 142 await live.destroy()
124 143
125 video.isLive = false 144 liveVideo.isLive = false
126 video.state = VideoState.TO_TRANSCODE 145 liveVideo.waitTranscoding = true
146 liveVideo.state = VideoState.TO_TRANSCODE
127 147
128 await video.save() 148 await liveVideo.save()
149
150 liveSession.replayVideoId = liveVideo.id
151 await liveSession.save()
129 152
130 // Remove old HLS playlist video files 153 // Remove old HLS playlist video files
131 const videoWithFiles = await VideoModel.loadAndPopulateAccountAndServerAndTags(video.id) 154 const videoWithFiles = await VideoModel.loadAndPopulateAccountAndServerAndTags(liveVideo.id)
132 155
133 const hlsPlaylist = videoWithFiles.getHLSPlaylist() 156 const hlsPlaylist = videoWithFiles.getHLSPlaylist()
134 await VideoFileModel.removeHLSFilesOfVideoId(hlsPlaylist.id) 157 await VideoFileModel.removeHLSFilesOfVideoId(hlsPlaylist.id)
@@ -139,7 +162,7 @@ async function replaceLiveByReplay (video: MVideo, live: MVideoLive, replayDirec
139 hlsPlaylist.segmentsSha256Filename = generateHlsSha256SegmentsFilename() 162 hlsPlaylist.segmentsSha256Filename = generateHlsSha256SegmentsFilename()
140 await hlsPlaylist.save() 163 await hlsPlaylist.save()
141 164
142 await assignReplaysToVideo(videoWithFiles, replayDirectory) 165 await assignReplayFilesToVideo({ video: videoWithFiles, replayDirectory })
143 166
144 await remove(getLiveReplayBaseDirectory(videoWithFiles)) 167 await remove(getLiveReplayBaseDirectory(videoWithFiles))
145 168
@@ -150,7 +173,7 @@ async function replaceLiveByReplay (video: MVideo, live: MVideoLive, replayDirec
150 videoFile: videoWithFiles.getMaxQualityFile(), 173 videoFile: videoWithFiles.getMaxQualityFile(),
151 type: ThumbnailType.MINIATURE 174 type: ThumbnailType.MINIATURE
152 }) 175 })
153 await video.addAndSaveThumbnail(miniature) 176 await videoWithFiles.addAndSaveThumbnail(miniature)
154 } 177 }
155 178
156 if (videoWithFiles.getPreview().automaticallyGenerated === true) { 179 if (videoWithFiles.getPreview().automaticallyGenerated === true) {
@@ -159,13 +182,19 @@ async function replaceLiveByReplay (video: MVideo, live: MVideoLive, replayDirec
159 videoFile: videoWithFiles.getMaxQualityFile(), 182 videoFile: videoWithFiles.getMaxQualityFile(),
160 type: ThumbnailType.PREVIEW 183 type: ThumbnailType.PREVIEW
161 }) 184 })
162 await video.addAndSaveThumbnail(preview) 185 await videoWithFiles.addAndSaveThumbnail(preview)
163 } 186 }
164 187
165 await moveToNextState({ video: videoWithFiles, isNewVideo: false }) 188 // We consider this is a new video
189 await moveToNextState({ video: videoWithFiles, isNewVideo: true })
166} 190}
167 191
168async function assignReplaysToVideo (video: MVideo, replayDirectory: string) { 192async function assignReplayFilesToVideo (options: {
193 video: MVideo
194 replayDirectory: string
195}) {
196 const { video, replayDirectory } = options
197
169 let durationDone = false 198 let durationDone = false
170 199
171 const concatenatedTsFiles = await readdir(replayDirectory) 200 const concatenatedTsFiles = await readdir(replayDirectory)
@@ -197,11 +226,15 @@ async function assignReplaysToVideo (video: MVideo, replayDirectory: string) {
197 return video 226 return video
198} 227}
199 228
200async function cleanupLiveAndFederate (video: MVideo) { 229async function cleanupLiveAndFederate (options: {
201 const streamingPlaylist = await VideoStreamingPlaylistModel.loadHLSPlaylistByVideo(video.id) 230 liveVideo: MVideo
202 await cleanupLive(video, streamingPlaylist) 231}) {
232 const { liveVideo } = options
233
234 const streamingPlaylist = await VideoStreamingPlaylistModel.loadHLSPlaylistByVideo(liveVideo.id)
235 await cleanupLive(liveVideo, streamingPlaylist)
203 236
204 const fullVideo = await VideoModel.loadAndPopulateAccountAndServerAndTags(video.id) 237 const fullVideo = await VideoModel.loadAndPopulateAccountAndServerAndTags(liveVideo.id)
205 return federateVideoIfNeeded(fullVideo, false, undefined) 238 return federateVideoIfNeeded(fullVideo, false, undefined)
206} 239}
207 240
diff --git a/server/lib/live/live-manager.ts b/server/lib/live/live-manager.ts
index da09aa05c..df2804a0e 100644
--- a/server/lib/live/live-manager.ts
+++ b/server/lib/live/live-manager.ts
@@ -17,10 +17,11 @@ import { P2P_MEDIA_LOADER_PEER_VERSION, VIDEO_LIVE } from '@server/initializers/
17import { UserModel } from '@server/models/user/user' 17import { UserModel } from '@server/models/user/user'
18import { VideoModel } from '@server/models/video/video' 18import { VideoModel } from '@server/models/video/video'
19import { VideoLiveModel } from '@server/models/video/video-live' 19import { VideoLiveModel } from '@server/models/video/video-live'
20import { VideoLiveSessionModel } from '@server/models/video/video-live-session'
20import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist' 21import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist'
21import { MStreamingPlaylistVideo, MVideo, MVideoLiveVideo } from '@server/types/models' 22import { MStreamingPlaylistVideo, MVideo, MVideoLiveSession, MVideoLiveVideo } from '@server/types/models'
22import { wait } from '@shared/core-utils' 23import { wait } from '@shared/core-utils'
23import { VideoState, VideoStreamingPlaylistType } from '@shared/models' 24import { LiveVideoError, VideoState, VideoStreamingPlaylistType } from '@shared/models'
24import { federateVideoIfNeeded } from '../activitypub/videos' 25import { federateVideoIfNeeded } from '../activitypub/videos'
25import { JobQueue } from '../job-queue' 26import { JobQueue } from '../job-queue'
26import { generateHLSMasterPlaylistFilename, generateHlsSha256SegmentsFilename, getLiveReplayBaseDirectory } from '../paths' 27import { generateHLSMasterPlaylistFilename, generateHlsSha256SegmentsFilename, getLiveReplayBaseDirectory } from '../paths'
@@ -174,10 +175,13 @@ class LiveManager {
174 return !!this.rtmpServer 175 return !!this.rtmpServer
175 } 176 }
176 177
177 stopSessionOf (videoId: number) { 178 stopSessionOf (videoId: number, error: LiveVideoError | null) {
178 const sessionId = this.videoSessions.get(videoId) 179 const sessionId = this.videoSessions.get(videoId)
179 if (!sessionId) return 180 if (!sessionId) return
180 181
182 this.saveEndingSession(videoId, error)
183 .catch(err => logger.error('Cannot save ending session.', { err, ...lTags(sessionId) }))
184
181 this.videoSessions.delete(videoId) 185 this.videoSessions.delete(videoId)
182 this.abortSession(sessionId) 186 this.abortSession(sessionId)
183 } 187 }
@@ -274,6 +278,8 @@ class LiveManager {
274 const videoUUID = videoLive.Video.uuid 278 const videoUUID = videoLive.Video.uuid
275 const localLTags = lTags(sessionId, videoUUID) 279 const localLTags = lTags(sessionId, videoUUID)
276 280
281 const liveSession = await this.saveStartingSession(videoLive)
282
277 const user = await UserModel.loadByLiveId(videoLive.id) 283 const user = await UserModel.loadByLiveId(videoLive.id)
278 LiveQuotaStore.Instance.addNewLive(user.id, videoLive.id) 284 LiveQuotaStore.Instance.addNewLive(user.id, videoLive.id)
279 285
@@ -299,24 +305,27 @@ class LiveManager {
299 localLTags 305 localLTags
300 ) 306 )
301 307
302 this.stopSessionOf(videoId) 308 this.stopSessionOf(videoId, LiveVideoError.BAD_SOCKET_HEALTH)
303 }) 309 })
304 310
305 muxingSession.on('duration-exceeded', ({ videoId }) => { 311 muxingSession.on('duration-exceeded', ({ videoId }) => {
306 logger.info('Stopping session of %s: max duration exceeded.', videoUUID, localLTags) 312 logger.info('Stopping session of %s: max duration exceeded.', videoUUID, localLTags)
307 313
308 this.stopSessionOf(videoId) 314 this.stopSessionOf(videoId, LiveVideoError.DURATION_EXCEEDED)
309 }) 315 })
310 316
311 muxingSession.on('quota-exceeded', ({ videoId }) => { 317 muxingSession.on('quota-exceeded', ({ videoId }) => {
312 logger.info('Stopping session of %s: user quota exceeded.', videoUUID, localLTags) 318 logger.info('Stopping session of %s: user quota exceeded.', videoUUID, localLTags)
313 319
314 this.stopSessionOf(videoId) 320 this.stopSessionOf(videoId, LiveVideoError.QUOTA_EXCEEDED)
321 })
322
323 muxingSession.on('ffmpeg-error', ({ videoId }) => {
324 this.stopSessionOf(videoId, LiveVideoError.FFMPEG_ERROR)
315 }) 325 })
316 326
317 muxingSession.on('ffmpeg-error', ({ sessionId }) => this.abortSession(sessionId))
318 muxingSession.on('ffmpeg-end', ({ videoId }) => { 327 muxingSession.on('ffmpeg-end', ({ videoId }) => {
319 this.onMuxingFFmpegEnd(videoId) 328 this.onMuxingFFmpegEnd(videoId, sessionId)
320 }) 329 })
321 330
322 muxingSession.on('after-cleanup', ({ videoId }) => { 331 muxingSession.on('after-cleanup', ({ videoId }) => {
@@ -324,7 +333,7 @@ class LiveManager {
324 333
325 muxingSession.destroy() 334 muxingSession.destroy()
326 335
327 return this.onAfterMuxingCleanup({ videoId }) 336 return this.onAfterMuxingCleanup({ videoId, liveSession })
328 .catch(err => logger.error('Error in end transmuxing.', { err, ...localLTags })) 337 .catch(err => logger.error('Error in end transmuxing.', { err, ...localLTags }))
329 }) 338 })
330 339
@@ -365,15 +374,19 @@ class LiveManager {
365 } 374 }
366 } 375 }
367 376
368 private onMuxingFFmpegEnd (videoId: number) { 377 private onMuxingFFmpegEnd (videoId: number, sessionId: string) {
369 this.videoSessions.delete(videoId) 378 this.videoSessions.delete(videoId)
379
380 this.saveEndingSession(videoId, null)
381 .catch(err => logger.error('Cannot save ending session.', { err, ...lTags(sessionId) }))
370 } 382 }
371 383
372 private async onAfterMuxingCleanup (options: { 384 private async onAfterMuxingCleanup (options: {
373 videoId: number | string 385 videoId: number | string
386 liveSession?: MVideoLiveSession
374 cleanupNow?: boolean // Default false 387 cleanupNow?: boolean // Default false
375 }) { 388 }) {
376 const { videoId, cleanupNow = false } = options 389 const { videoId, liveSession: liveSessionArg, cleanupNow = false } = options
377 390
378 try { 391 try {
379 const fullVideo = await VideoModel.loadAndPopulateAccountAndServerAndTags(videoId) 392 const fullVideo = await VideoModel.loadAndPopulateAccountAndServerAndTags(videoId)
@@ -381,13 +394,25 @@ class LiveManager {
381 394
382 const live = await VideoLiveModel.loadByVideoId(fullVideo.id) 395 const live = await VideoLiveModel.loadByVideoId(fullVideo.id)
383 396
397 const liveSession = liveSessionArg ?? await VideoLiveSessionModel.findCurrentSessionOf(fullVideo.id)
398
399 // On server restart during a live
400 if (!liveSession.endDate) {
401 liveSession.endDate = new Date()
402 await liveSession.save()
403 }
404
384 JobQueue.Instance.createJob({ 405 JobQueue.Instance.createJob({
385 type: 'video-live-ending', 406 type: 'video-live-ending',
386 payload: { 407 payload: {
387 videoId: fullVideo.id, 408 videoId: fullVideo.id,
409
388 replayDirectory: live.saveReplay 410 replayDirectory: live.saveReplay
389 ? await this.findReplayDirectory(fullVideo) 411 ? await this.findReplayDirectory(fullVideo)
390 : undefined, 412 : undefined,
413
414 liveSessionId: liveSession.id,
415
391 publishedAt: fullVideo.publishedAt.toISOString() 416 publishedAt: fullVideo.publishedAt.toISOString()
392 } 417 }
393 }, { delay: cleanupNow ? 0 : VIDEO_LIVE.CLEANUP_DELAY }) 418 }, { delay: cleanupNow ? 0 : VIDEO_LIVE.CLEANUP_DELAY })
@@ -445,6 +470,23 @@ class LiveManager {
445 return playlist.save() 470 return playlist.save()
446 } 471 }
447 472
473 private saveStartingSession (videoLive: MVideoLiveVideo) {
474 const liveSession = new VideoLiveSessionModel({
475 startDate: new Date(),
476 liveVideoId: videoLive.videoId
477 })
478
479 return liveSession.save()
480 }
481
482 private async saveEndingSession (videoId: number, error: LiveVideoError | null) {
483 const liveSession = await VideoLiveSessionModel.findCurrentSessionOf(videoId)
484 liveSession.endDate = new Date()
485 liveSession.error = error
486
487 return liveSession.save()
488 }
489
448 static get Instance () { 490 static get Instance () {
449 return this.instance || (this.instance = new this()) 491 return this.instance || (this.instance = new this())
450 } 492 }
diff --git a/server/lib/live/shared/muxing-session.ts b/server/lib/live/shared/muxing-session.ts
index 588ee8749..1ee9b430f 100644
--- a/server/lib/live/shared/muxing-session.ts
+++ b/server/lib/live/shared/muxing-session.ts
@@ -28,7 +28,7 @@ interface MuxingSessionEvents {
28 'quota-exceeded': ({ videoId: number }) => void 28 'quota-exceeded': ({ videoId: number }) => void
29 29
30 'ffmpeg-end': ({ videoId: number }) => void 30 'ffmpeg-end': ({ videoId: number }) => void
31 'ffmpeg-error': ({ sessionId: string }) => void 31 'ffmpeg-error': ({ videoId: string }) => void
32 32
33 'after-cleanup': ({ videoId: number }) => void 33 'after-cleanup': ({ videoId: number }) => void
34} 34}
@@ -164,7 +164,11 @@ class MuxingSession extends EventEmitter {
164 this.onFFmpegError({ err, stdout, stderr, outPath: this.outDirectory, ffmpegShellCommand }) 164 this.onFFmpegError({ err, stdout, stderr, outPath: this.outDirectory, ffmpegShellCommand })
165 }) 165 })
166 166
167 this.ffmpegCommand.on('end', () => this.onFFmpegEnded(this.outDirectory)) 167 this.ffmpegCommand.on('end', () => {
168 this.emit('ffmpeg-end', ({ videoId: this.videoId }))
169
170 this.onFFmpegEnded(this.outDirectory)
171 })
168 172
169 this.ffmpegCommand.run() 173 this.ffmpegCommand.run()
170 } 174 }
@@ -197,7 +201,7 @@ class MuxingSession extends EventEmitter {
197 201
198 logger.error('Live transcoding error.', { err, stdout, stderr, ffmpegShellCommand, ...this.lTags() }) 202 logger.error('Live transcoding error.', { err, stdout, stderr, ffmpegShellCommand, ...this.lTags() })
199 203
200 this.emit('ffmpeg-error', ({ sessionId: this.sessionId })) 204 this.emit('ffmpeg-error', ({ videoId: this.videoId }))
201 } 205 }
202 206
203 private onFFmpegEnded (outPath: string) { 207 private onFFmpegEnded (outPath: string) {
diff --git a/server/lib/video-blacklist.ts b/server/lib/video-blacklist.ts
index 91f44cb11..fd5837a3a 100644
--- a/server/lib/video-blacklist.ts
+++ b/server/lib/video-blacklist.ts
@@ -9,7 +9,7 @@ import {
9 MVideoFullLight, 9 MVideoFullLight,
10 MVideoWithBlacklistLight 10 MVideoWithBlacklistLight
11} from '@server/types/models' 11} from '@server/types/models'
12import { UserRight, VideoBlacklistCreate, VideoBlacklistType } from '../../shared/models' 12import { LiveVideoError, UserRight, VideoBlacklistCreate, VideoBlacklistType } from '../../shared/models'
13import { UserAdminFlag } from '../../shared/models/users/user-flag.model' 13import { UserAdminFlag } from '../../shared/models/users/user-flag.model'
14import { logger, loggerTagsFactory } from '../helpers/logger' 14import { logger, loggerTagsFactory } from '../helpers/logger'
15import { CONFIG } from '../initializers/config' 15import { CONFIG } from '../initializers/config'
@@ -81,7 +81,7 @@ async function blacklistVideo (videoInstance: MVideoAccountLight, options: Video
81 } 81 }
82 82
83 if (videoInstance.isLive) { 83 if (videoInstance.isLive) {
84 LiveManager.Instance.stopSessionOf(videoInstance.id) 84 LiveManager.Instance.stopSessionOf(videoInstance.id, LiveVideoError.BLACKLISTED)
85 } 85 }
86 86
87 Notifier.Instance.notifyOnVideoBlacklist(blacklist) 87 Notifier.Instance.notifyOnVideoBlacklist(blacklist)
diff --git a/server/lib/video-state.ts b/server/lib/video-state.ts
index 7b207eb87..ae2725d65 100644
--- a/server/lib/video-state.ts
+++ b/server/lib/video-state.ts
@@ -126,12 +126,10 @@ async function moveToPublishedState (options: {
126 const { video, isNewVideo, transaction, previousVideoState } = options 126 const { video, isNewVideo, transaction, previousVideoState } = options
127 const previousState = previousVideoState ?? video.state 127 const previousState = previousVideoState ?? video.state
128 128
129 logger.info('Publishing video %s.', video.uuid, { previousState, tags: [ video.uuid ] }) 129 logger.info('Publishing video %s.', video.uuid, { isNewVideo, previousState, tags: [ video.uuid ] })
130 130
131 await video.setNewState(VideoState.PUBLISHED, isNewVideo, transaction) 131 await video.setNewState(VideoState.PUBLISHED, isNewVideo, transaction)
132 132
133 // If the video was not published, we consider it is a new one for other instances
134 // Live videos are always federated, so it's not a new video
135 await federateVideoIfNeeded(video, isNewVideo, transaction) 133 await federateVideoIfNeeded(video, isNewVideo, transaction)
136 134
137 if (previousState === VideoState.TO_EDIT) { 135 if (previousState === VideoState.TO_EDIT) {