X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=server%2Flib%2Fjob-queue%2Fhandlers%2Fvideo-live-ending.ts;h=a04cfa2c906a10e0ddb934d0def181eb02e1f232;hb=0882c8e6509b2a4ea48f6c48ecb2aa4aa371500a;hp=1a58a9f7ee2c34d38ca91528ade36d252dd632e8;hpb=a5cf76afa378aae81af2a9b0ce548e5d2582f832;p=github%2FChocobozzz%2FPeerTube.git diff --git a/server/lib/job-queue/handlers/video-live-ending.ts b/server/lib/job-queue/handlers/video-live-ending.ts index 1a58a9f7e..a04cfa2c9 100644 --- a/server/lib/job-queue/handlers/video-live-ending.ts +++ b/server/lib/job-queue/handlers/video-live-ending.ts @@ -1,23 +1,143 @@ -import * as Bull from 'bull' -import { readdir, remove } from 'fs-extra' +import { Job } from 'bull' +import { pathExists, readdir, remove } from 'fs-extra' import { join } from 'path' -import { getHLSDirectory } from '@server/lib/video-paths' +import { ffprobePromise, getAudioStream, getDurationFromVideoFile, getVideoFileResolution } from '@server/helpers/ffprobe-utils' +import { VIDEO_LIVE } from '@server/initializers/constants' +import { buildConcatenatedName, cleanupLive, LiveSegmentShaStore } from '@server/lib/live' +import { generateHLSMasterPlaylistFilename, generateHlsSha256SegmentsFilename, getLiveDirectory } from '@server/lib/paths' +import { generateVideoMiniature } from '@server/lib/thumbnail' +import { generateHlsPlaylistResolutionFromTS } from '@server/lib/transcoding/video-transcoding' +import { VideoPathManager } from '@server/lib/video-path-manager' +import { moveToNextState } from '@server/lib/video-state' import { VideoModel } from '@server/models/video/video' +import { VideoFileModel } from '@server/models/video/video-file' +import { VideoLiveModel } from '@server/models/video/video-live' import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist' -import { VideoLiveEndingPayload } from '@shared/models' +import { MStreamingPlaylist, MVideo, MVideoLive } from '@server/types/models' +import { ThumbnailType, VideoLiveEndingPayload, VideoState } from '@shared/models' import { logger } from '../../../helpers/logger' -async function processVideoLiveEnding (job: Bull.Job) { +async function processVideoLiveEnding (job: Job) { const payload = job.data as VideoLiveEndingPayload - const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(payload.videoId) - if (!video) { - logger.warn('Video live %d does not exist anymore. Cannot cleanup.', payload.videoId) + function logError () { + logger.warn('Video live %d does not exist anymore. Cannot process live ending.', payload.videoId) + } + + const video = await VideoModel.load(payload.videoId) + const live = await VideoLiveModel.loadByVideoId(payload.videoId) + + if (!video || !live) { + logError() return } const streamingPlaylist = await VideoStreamingPlaylistModel.loadHLSPlaylistByVideo(video.id) - const hlsDirectory = getHLSDirectory(video, false) + if (!streamingPlaylist) { + logError() + return + } + + LiveSegmentShaStore.Instance.cleanupShaSegments(video.uuid) + + if (live.saveReplay !== true) { + return cleanupLive(video, streamingPlaylist) + } + + return saveLive(video, live, streamingPlaylist) +} + +// --------------------------------------------------------------------------- + +export { + processVideoLiveEnding +} + +// --------------------------------------------------------------------------- + +async function saveLive (video: MVideo, live: MVideoLive, streamingPlaylist: MStreamingPlaylist) { + const replayDirectory = VideoPathManager.Instance.getFSHLSOutputPath(video, VIDEO_LIVE.REPLAY_DIRECTORY) + + const rootFiles = await readdir(getLiveDirectory(video)) + + const playlistFiles = rootFiles.filter(file => { + return file.endsWith('.m3u8') && file !== streamingPlaylist.playlistFilename + }) + + await cleanupTMPLiveFiles(getLiveDirectory(video)) + + await live.destroy() + + video.isLive = false + // Reinit views + video.views = 0 + video.state = VideoState.TO_TRANSCODE + + await video.save() + + // Remove old HLS playlist video files + const videoWithFiles = await VideoModel.loadAndPopulateAccountAndServerAndTags(video.id) + + const hlsPlaylist = videoWithFiles.getHLSPlaylist() + await VideoFileModel.removeHLSFilesOfVideoId(hlsPlaylist.id) + + // Reset playlist + hlsPlaylist.VideoFiles = [] + hlsPlaylist.playlistFilename = generateHLSMasterPlaylistFilename() + hlsPlaylist.segmentsSha256Filename = generateHlsSha256SegmentsFilename() + await hlsPlaylist.save() + + let durationDone = false + + for (const playlistFile of playlistFiles) { + const concatenatedTsFile = buildConcatenatedName(playlistFile) + const concatenatedTsFilePath = join(replayDirectory, concatenatedTsFile) + + const probe = await ffprobePromise(concatenatedTsFilePath) + const { audioStream } = await getAudioStream(concatenatedTsFilePath, probe) + + const { resolution, isPortraitMode } = await getVideoFileResolution(concatenatedTsFilePath, probe) + + const { resolutionPlaylistPath: outputPath } = await generateHlsPlaylistResolutionFromTS({ + video: videoWithFiles, + concatenatedTsFilePath, + resolution, + isPortraitMode, + isAAC: audioStream?.codec_name === 'aac' + }) + + if (!durationDone) { + videoWithFiles.duration = await getDurationFromVideoFile(outputPath) + await videoWithFiles.save() + + durationDone = true + } + } + + await remove(replayDirectory) + + // Regenerate the thumbnail & preview? + if (videoWithFiles.getMiniature().automaticallyGenerated === true) { + await generateVideoMiniature({ + video: videoWithFiles, + videoFile: videoWithFiles.getMaxQualityFile(), + type: ThumbnailType.MINIATURE + }) + } + + if (videoWithFiles.getPreview().automaticallyGenerated === true) { + await generateVideoMiniature({ + video: videoWithFiles, + videoFile: videoWithFiles.getMaxQualityFile(), + type: ThumbnailType.PREVIEW + }) + } + + await moveToNextState(videoWithFiles, false) +} + +async function cleanupTMPLiveFiles (hlsDirectory: string) { + if (!await pathExists(hlsDirectory)) return const files = await readdir(hlsDirectory) @@ -35,13 +155,4 @@ async function processVideoLiveEnding (job: Bull.Job) { .catch(err => logger.error('Cannot remove %s.', p, { err })) } } - - streamingPlaylist.destroy() - .catch(err => logger.error('Cannot remove live streaming playlist.', { err })) -} - -// --------------------------------------------------------------------------- - -export { - processVideoLiveEnding }