diff options
Diffstat (limited to 'server/lib/live/live-manager.ts')
-rw-r--r-- | server/lib/live/live-manager.ts | 69 |
1 files changed, 45 insertions, 24 deletions
diff --git a/server/lib/live/live-manager.ts b/server/lib/live/live-manager.ts index 920d3a5ec..5ffe41ee3 100644 --- a/server/lib/live/live-manager.ts +++ b/server/lib/live/live-manager.ts | |||
@@ -1,6 +1,7 @@ | |||
1 | 1 | ||
2 | import { readFile } from 'fs-extra' | 2 | import { readdir, readFile } from 'fs-extra' |
3 | import { createServer, Server } from 'net' | 3 | import { createServer, Server } from 'net' |
4 | import { join } from 'path' | ||
4 | import { createServer as createServerTLS, Server as ServerTLS } from 'tls' | 5 | import { createServer as createServerTLS, Server as ServerTLS } from 'tls' |
5 | import { | 6 | import { |
6 | computeLowerResolutionsToTranscode, | 7 | computeLowerResolutionsToTranscode, |
@@ -18,10 +19,11 @@ import { VideoModel } from '@server/models/video/video' | |||
18 | import { VideoLiveModel } from '@server/models/video/video-live' | 19 | import { VideoLiveModel } from '@server/models/video/video-live' |
19 | import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist' | 20 | import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist' |
20 | import { MStreamingPlaylistVideo, MVideo, MVideoLiveVideo } from '@server/types/models' | 21 | import { MStreamingPlaylistVideo, MVideo, MVideoLiveVideo } from '@server/types/models' |
22 | import { wait } from '@shared/core-utils' | ||
21 | import { VideoState, VideoStreamingPlaylistType } from '@shared/models' | 23 | import { VideoState, VideoStreamingPlaylistType } from '@shared/models' |
22 | import { federateVideoIfNeeded } from '../activitypub/videos' | 24 | import { federateVideoIfNeeded } from '../activitypub/videos' |
23 | import { JobQueue } from '../job-queue' | 25 | import { JobQueue } from '../job-queue' |
24 | import { generateHLSMasterPlaylistFilename, generateHlsSha256SegmentsFilename } from '../paths' | 26 | import { generateHLSMasterPlaylistFilename, generateHlsSha256SegmentsFilename, getLiveReplayBaseDirectory } from '../paths' |
25 | import { PeerTubeSocket } from '../peertube-socket' | 27 | import { PeerTubeSocket } from '../peertube-socket' |
26 | import { LiveQuotaStore } from './live-quota-store' | 28 | import { LiveQuotaStore } from './live-quota-store' |
27 | import { LiveSegmentShaStore } from './live-segment-sha-store' | 29 | import { LiveSegmentShaStore } from './live-segment-sha-store' |
@@ -322,7 +324,7 @@ class LiveManager { | |||
322 | 324 | ||
323 | muxingSession.destroy() | 325 | muxingSession.destroy() |
324 | 326 | ||
325 | return this.onAfterMuxingCleanup(videoId) | 327 | return this.onAfterMuxingCleanup({ videoId }) |
326 | .catch(err => logger.error('Error in end transmuxing.', { err, ...localLTags })) | 328 | .catch(err => logger.error('Error in end transmuxing.', { err, ...localLTags })) |
327 | }) | 329 | }) |
328 | 330 | ||
@@ -349,12 +351,15 @@ class LiveManager { | |||
349 | 351 | ||
350 | live.Video = video | 352 | live.Video = video |
351 | 353 | ||
352 | setTimeout(() => { | 354 | await wait(getLiveSegmentTime(live.latencyMode) * 1000 * VIDEO_LIVE.EDGE_LIVE_DELAY_SEGMENTS_NOTIFICATION) |
353 | federateVideoIfNeeded(video, false) | ||
354 | .catch(err => logger.error('Cannot federate live video %s.', video.url, { err, ...localLTags })) | ||
355 | 355 | ||
356 | PeerTubeSocket.Instance.sendVideoLiveNewState(video) | 356 | try { |
357 | }, getLiveSegmentTime(live.latencyMode) * 1000 * VIDEO_LIVE.EDGE_LIVE_DELAY_SEGMENTS_NOTIFICATION) | 357 | await federateVideoIfNeeded(video, false) |
358 | } catch (err) { | ||
359 | logger.error('Cannot federate live video %s.', video.url, { err, ...localLTags }) | ||
360 | } | ||
361 | |||
362 | PeerTubeSocket.Instance.sendVideoLiveNewState(video) | ||
358 | } catch (err) { | 363 | } catch (err) { |
359 | logger.error('Cannot save/federate live video %d.', videoId, { err, ...localLTags }) | 364 | logger.error('Cannot save/federate live video %d.', videoId, { err, ...localLTags }) |
360 | } | 365 | } |
@@ -364,25 +369,32 @@ class LiveManager { | |||
364 | this.videoSessions.delete(videoId) | 369 | this.videoSessions.delete(videoId) |
365 | } | 370 | } |
366 | 371 | ||
367 | private async onAfterMuxingCleanup (videoUUID: string, cleanupNow = false) { | 372 | private async onAfterMuxingCleanup (options: { |
373 | videoId: number | string | ||
374 | cleanupNow?: boolean // Default false | ||
375 | }) { | ||
376 | const { videoId, cleanupNow = false } = options | ||
377 | |||
368 | try { | 378 | try { |
369 | const fullVideo = await VideoModel.loadAndPopulateAccountAndServerAndTags(videoUUID) | 379 | const fullVideo = await VideoModel.loadAndPopulateAccountAndServerAndTags(videoId) |
370 | if (!fullVideo) return | 380 | if (!fullVideo) return |
371 | 381 | ||
372 | const live = await VideoLiveModel.loadByVideoId(fullVideo.id) | 382 | const live = await VideoLiveModel.loadByVideoId(fullVideo.id) |
373 | 383 | ||
374 | if (!live.permanentLive) { | 384 | JobQueue.Instance.createJob({ |
375 | JobQueue.Instance.createJob({ | 385 | type: 'video-live-ending', |
376 | type: 'video-live-ending', | 386 | payload: { |
377 | payload: { | 387 | videoId: fullVideo.id, |
378 | videoId: fullVideo.id | 388 | replayDirectory: live.saveReplay |
379 | } | 389 | ? await this.findReplayDirectory(fullVideo) |
380 | }, { delay: cleanupNow ? 0 : VIDEO_LIVE.CLEANUP_DELAY }) | 390 | : undefined, |
381 | 391 | publishedAt: fullVideo.publishedAt.toISOString() | |
382 | fullVideo.state = VideoState.LIVE_ENDED | 392 | } |
383 | } else { | 393 | }, { delay: cleanupNow ? 0 : VIDEO_LIVE.CLEANUP_DELAY }) |
384 | fullVideo.state = VideoState.WAITING_FOR_LIVE | 394 | |
385 | } | 395 | fullVideo.state = live.permanentLive |
396 | ? VideoState.WAITING_FOR_LIVE | ||
397 | : VideoState.LIVE_ENDED | ||
386 | 398 | ||
387 | await fullVideo.save() | 399 | await fullVideo.save() |
388 | 400 | ||
@@ -390,7 +402,7 @@ class LiveManager { | |||
390 | 402 | ||
391 | await federateVideoIfNeeded(fullVideo, false) | 403 | await federateVideoIfNeeded(fullVideo, false) |
392 | } catch (err) { | 404 | } catch (err) { |
393 | logger.error('Cannot save/federate new video state of live streaming of video %d.', videoUUID, { err, ...lTags(videoUUID) }) | 405 | logger.error('Cannot save/federate new video state of live streaming of video %d.', videoId, { err, ...lTags(videoId + '') }) |
394 | } | 406 | } |
395 | } | 407 | } |
396 | 408 | ||
@@ -398,10 +410,19 @@ class LiveManager { | |||
398 | const videoUUIDs = await VideoModel.listPublishedLiveUUIDs() | 410 | const videoUUIDs = await VideoModel.listPublishedLiveUUIDs() |
399 | 411 | ||
400 | for (const uuid of videoUUIDs) { | 412 | for (const uuid of videoUUIDs) { |
401 | await this.onAfterMuxingCleanup(uuid, true) | 413 | await this.onAfterMuxingCleanup({ videoId: uuid, cleanupNow: true }) |
402 | } | 414 | } |
403 | } | 415 | } |
404 | 416 | ||
417 | private async findReplayDirectory (video: MVideo) { | ||
418 | const directory = getLiveReplayBaseDirectory(video) | ||
419 | const files = await readdir(directory) | ||
420 | |||
421 | if (files.length === 0) return undefined | ||
422 | |||
423 | return join(directory, files.sort().reverse()[0]) | ||
424 | } | ||
425 | |||
405 | private buildAllResolutionsToTranscode (originResolution: number) { | 426 | private buildAllResolutionsToTranscode (originResolution: number) { |
406 | const resolutionsEnabled = CONFIG.LIVE.TRANSCODING.ENABLED | 427 | const resolutionsEnabled = CONFIG.LIVE.TRANSCODING.ENABLED |
407 | ? computeLowerResolutionsToTranscode(originResolution, 'live') | 428 | ? computeLowerResolutionsToTranscode(originResolution, 'live') |