aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/lib
diff options
context:
space:
mode:
Diffstat (limited to 'server/lib')
-rw-r--r--server/lib/activitypub/videos.ts2
-rw-r--r--server/lib/job-queue/handlers/video-live-ending.ts27
-rw-r--r--server/lib/live-manager.ts40
3 files changed, 51 insertions, 18 deletions
diff --git a/server/lib/activitypub/videos.ts b/server/lib/activitypub/videos.ts
index 4053f487c..04f0bfc23 100644
--- a/server/lib/activitypub/videos.ts
+++ b/server/lib/activitypub/videos.ts
@@ -429,6 +429,7 @@ async function updateVideoFromAP (options: {
429 if (video.isLive) { 429 if (video.isLive) {
430 const [ videoLive ] = await VideoLiveModel.upsert({ 430 const [ videoLive ] = await VideoLiveModel.upsert({
431 saveReplay: videoObject.liveSaveReplay, 431 saveReplay: videoObject.liveSaveReplay,
432 permanentLive: videoObject.permanentLive,
432 videoId: video.id 433 videoId: video.id
433 }, { transaction: t, returning: true }) 434 }, { transaction: t, returning: true })
434 435
@@ -631,6 +632,7 @@ async function createVideo (videoObject: VideoObject, channel: MChannelAccountLi
631 const videoLive = new VideoLiveModel({ 632 const videoLive = new VideoLiveModel({
632 streamKey: null, 633 streamKey: null,
633 saveReplay: videoObject.liveSaveReplay, 634 saveReplay: videoObject.liveSaveReplay,
635 permanentLive: videoObject.permanentLive,
634 videoId: videoCreated.id 636 videoId: videoCreated.id
635 }) 637 })
636 638
diff --git a/server/lib/job-queue/handlers/video-live-ending.ts b/server/lib/job-queue/handlers/video-live-ending.ts
index d3c84ce75..e3c11caa2 100644
--- a/server/lib/job-queue/handlers/video-live-ending.ts
+++ b/server/lib/job-queue/handlers/video-live-ending.ts
@@ -1,5 +1,5 @@
1import * as Bull from 'bull' 1import * as Bull from 'bull'
2import { copy, readdir, remove } from 'fs-extra' 2import { copy, pathExists, readdir, remove } from 'fs-extra'
3import { join } from 'path' 3import { join } from 'path'
4import { getDurationFromVideoFile, getVideoFileResolution } from '@server/helpers/ffprobe-utils' 4import { getDurationFromVideoFile, getVideoFileResolution } from '@server/helpers/ffprobe-utils'
5import { VIDEO_LIVE } from '@server/initializers/constants' 5import { VIDEO_LIVE } from '@server/initializers/constants'
@@ -14,6 +14,7 @@ import { VideoStreamingPlaylistModel } from '@server/models/video/video-streamin
14import { MStreamingPlaylist, MVideo, MVideoLive } from '@server/types/models' 14import { MStreamingPlaylist, MVideo, MVideoLive } from '@server/types/models'
15import { ThumbnailType, VideoLiveEndingPayload, VideoState } from '@shared/models' 15import { ThumbnailType, VideoLiveEndingPayload, VideoState } from '@shared/models'
16import { logger } from '../../../helpers/logger' 16import { logger } from '../../../helpers/logger'
17import { LiveManager } from '@server/lib/live-manager'
17 18
18async function processVideoLiveEnding (job: Bull.Job) { 19async function processVideoLiveEnding (job: Bull.Job) {
19 const payload = job.data as VideoLiveEndingPayload 20 const payload = job.data as VideoLiveEndingPayload
@@ -36,6 +37,8 @@ async function processVideoLiveEnding (job: Bull.Job) {
36 return 37 return
37 } 38 }
38 39
40 LiveManager.Instance.cleanupShaSegments(video.uuid)
41
39 if (live.saveReplay !== true) { 42 if (live.saveReplay !== true) {
40 return cleanupLive(video, streamingPlaylist) 43 return cleanupLive(video, streamingPlaylist)
41 } 44 }
@@ -43,10 +46,19 @@ async function processVideoLiveEnding (job: Bull.Job) {
43 return saveLive(video, live) 46 return saveLive(video, live)
44} 47}
45 48
49async function cleanupLive (video: MVideo, streamingPlaylist: MStreamingPlaylist) {
50 const hlsDirectory = getHLSDirectory(video)
51
52 await remove(hlsDirectory)
53
54 await streamingPlaylist.destroy()
55}
56
46// --------------------------------------------------------------------------- 57// ---------------------------------------------------------------------------
47 58
48export { 59export {
49 processVideoLiveEnding 60 processVideoLiveEnding,
61 cleanupLive
50} 62}
51 63
52// --------------------------------------------------------------------------- 64// ---------------------------------------------------------------------------
@@ -131,16 +143,9 @@ async function saveLive (video: MVideo, live: MVideoLive) {
131 await publishAndFederateIfNeeded(videoWithFiles, true) 143 await publishAndFederateIfNeeded(videoWithFiles, true)
132} 144}
133 145
134async function cleanupLive (video: MVideo, streamingPlaylist: MStreamingPlaylist) {
135 const hlsDirectory = getHLSDirectory(video)
136
137 await remove(hlsDirectory)
138
139 streamingPlaylist.destroy()
140 .catch(err => logger.error('Cannot remove live streaming playlist.', { err }))
141}
142
143async function cleanupLiveFiles (hlsDirectory: string) { 146async function cleanupLiveFiles (hlsDirectory: string) {
147 if (!await pathExists(hlsDirectory)) return
148
144 const files = await readdir(hlsDirectory) 149 const files = await readdir(hlsDirectory)
145 150
146 for (const filename of files) { 151 for (const filename of files) {
diff --git a/server/lib/live-manager.ts b/server/lib/live-manager.ts
index 4f45ce530..dcf016169 100644
--- a/server/lib/live-manager.ts
+++ b/server/lib/live-manager.ts
@@ -19,6 +19,7 @@ import { VideoState, VideoStreamingPlaylistType } from '@shared/models'
19import { federateVideoIfNeeded } from './activitypub/videos' 19import { federateVideoIfNeeded } from './activitypub/videos'
20import { buildSha256Segment } from './hls' 20import { buildSha256Segment } from './hls'
21import { JobQueue } from './job-queue' 21import { JobQueue } from './job-queue'
22import { cleanupLive } from './job-queue/handlers/video-live-ending'
22import { PeerTubeSocket } from './peertube-socket' 23import { PeerTubeSocket } from './peertube-socket'
23import { isAbleToUploadVideo } from './user' 24import { isAbleToUploadVideo } from './user'
24import { getHLSDirectory } from './video-paths' 25import { getHLSDirectory } from './video-paths'
@@ -153,6 +154,10 @@ class LiveManager {
153 watchers.push(new Date().getTime()) 154 watchers.push(new Date().getTime())
154 } 155 }
155 156
157 cleanupShaSegments (videoUUID: string) {
158 this.segmentsSha256.delete(videoUUID)
159 }
160
156 private getContext () { 161 private getContext () {
157 return context 162 return context
158 } 163 }
@@ -184,6 +189,14 @@ class LiveManager {
184 return this.abortSession(sessionId) 189 return this.abortSession(sessionId)
185 } 190 }
186 191
192 // Cleanup old potential live files (could happen with a permanent live)
193 this.cleanupShaSegments(video.uuid)
194
195 const oldStreamingPlaylist = await VideoStreamingPlaylistModel.loadHLSPlaylistByVideo(video.id)
196 if (oldStreamingPlaylist) {
197 await cleanupLive(video, oldStreamingPlaylist)
198 }
199
187 this.videoSessions.set(video.id, sessionId) 200 this.videoSessions.set(video.id, sessionId)
188 201
189 const playlistUrl = WEBSERVER.URL + VideoStreamingPlaylistModel.getHlsMasterPlaylistStaticPath(video.uuid) 202 const playlistUrl = WEBSERVER.URL + VideoStreamingPlaylistModel.getHlsMasterPlaylistStaticPath(video.uuid)
@@ -372,7 +385,13 @@ class LiveManager {
372 logger.info('RTMP transmuxing for video %s ended. Scheduling cleanup', rtmpUrl) 385 logger.info('RTMP transmuxing for video %s ended. Scheduling cleanup', rtmpUrl)
373 386
374 this.transSessions.delete(sessionId) 387 this.transSessions.delete(sessionId)
388
375 this.watchersPerVideo.delete(videoLive.videoId) 389 this.watchersPerVideo.delete(videoLive.videoId)
390 this.videoSessions.delete(videoLive.videoId)
391
392 const newLivesPerUser = this.livesPerUser.get(user.id)
393 .filter(o => o.liveId !== videoLive.id)
394 this.livesPerUser.set(user.id, newLivesPerUser)
376 395
377 setTimeout(() => { 396 setTimeout(() => {
378 // Wait latest segments generation, and close watchers 397 // Wait latest segments generation, and close watchers
@@ -412,14 +431,21 @@ class LiveManager {
412 const fullVideo = await VideoModel.loadAndPopulateAccountAndServerAndTags(videoId) 431 const fullVideo = await VideoModel.loadAndPopulateAccountAndServerAndTags(videoId)
413 if (!fullVideo) return 432 if (!fullVideo) return
414 433
415 JobQueue.Instance.createJob({ 434 const live = await VideoLiveModel.loadByVideoId(videoId)
416 type: 'video-live-ending', 435
417 payload: { 436 if (!live.permanentLive) {
418 videoId: fullVideo.id 437 JobQueue.Instance.createJob({
419 } 438 type: 'video-live-ending',
420 }, { delay: cleanupNow ? 0 : VIDEO_LIVE.CLEANUP_DELAY }) 439 payload: {
440 videoId: fullVideo.id
441 }
442 }, { delay: cleanupNow ? 0 : VIDEO_LIVE.CLEANUP_DELAY })
443
444 fullVideo.state = VideoState.LIVE_ENDED
445 } else {
446 fullVideo.state = VideoState.WAITING_FOR_LIVE
447 }
421 448
422 fullVideo.state = VideoState.LIVE_ENDED
423 await fullVideo.save() 449 await fullVideo.save()
424 450
425 PeerTubeSocket.Instance.sendVideoLiveNewState(fullVideo) 451 PeerTubeSocket.Instance.sendVideoLiveNewState(fullVideo)