X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=server%2Flib%2Fjob-queue%2Fhandlers%2Fvideo-live-ending.ts;h=d3c84ce75d6c170c6ada2cec89b79f2b4b2624e0;hb=4a54a93941d1c095bf249331df799c51e39c3774;hp=1e964726ef7df331fea2f9ed5ce6e92f6ea83e88;hpb=97969c4edf51b37eee691adba43368bb0fbb729b;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 1e964726e..d3c84ce75 100644 --- a/server/lib/job-queue/handlers/video-live-ending.ts +++ b/server/lib/job-queue/handlers/video-live-ending.ts @@ -1,27 +1,38 @@ import * as Bull from 'bull' -import { readdir, remove } from 'fs-extra' +import { copy, readdir, remove } from 'fs-extra' import { join } from 'path' -import { getDurationFromVideoFile, getVideoFileResolution, hlsPlaylistToFragmentedMP4 } from '@server/helpers/ffmpeg-utils' +import { getDurationFromVideoFile, getVideoFileResolution } from '@server/helpers/ffprobe-utils' +import { VIDEO_LIVE } from '@server/initializers/constants' +import { generateVideoMiniature } from '@server/lib/thumbnail' import { publishAndFederateIfNeeded } from '@server/lib/video' import { getHLSDirectory } from '@server/lib/video-paths' -import { generateHlsPlaylist } from '@server/lib/video-transcoding' +import { generateHlsPlaylistFromTS } from '@server/lib/video-transcoding' 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 { MStreamingPlaylist, MVideo, MVideoLive, MVideoWithFile } from '@server/types/models' -import { VideoLiveEndingPayload, VideoState } from '@shared/models' +import { MStreamingPlaylist, MVideo, MVideoLive } from '@server/types/models' +import { ThumbnailType, VideoLiveEndingPayload, VideoState } from '@shared/models' import { logger } from '../../../helpers/logger' -import { VideoFileModel } from '@server/models/video/video-file' async function processVideoLiveEnding (job: Bull.Job) { const payload = job.data as VideoLiveEndingPayload + 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) - if (!video || !streamingPlaylist || !live) { - logger.warn('Video live %d does not exist anymore. Cannot process live ending.', payload.videoId) + if (!streamingPlaylist) { + logError() return } @@ -42,34 +53,21 @@ export { async function saveLive (video: MVideo, live: MVideoLive) { const hlsDirectory = getHLSDirectory(video, false) - const files = await readdir(hlsDirectory) - - const playlistFiles = files.filter(f => f.endsWith('.m3u8') && f !== 'master.m3u8') - const resolutions: number[] = [] - let duration: number + const replayDirectory = join(hlsDirectory, VIDEO_LIVE.REPLAY_DIRECTORY) - for (const playlistFile of playlistFiles) { - const playlistPath = join(hlsDirectory, playlistFile) - const { videoFileResolution } = await getVideoFileResolution(playlistPath) + const rootFiles = await readdir(hlsDirectory) - const mp4TmpName = buildMP4TmpName(videoFileResolution) + const playlistFiles: string[] = [] - // Playlist name is for example 3.m3u8 - // Segments names are 3-0.ts 3-1.ts etc - const shouldStartWith = playlistFile.replace(/\.m3u8$/, '') + '-' - - const segmentFiles = files.filter(f => f.startsWith(shouldStartWith) && f.endsWith('.ts')) - await hlsPlaylistToFragmentedMP4(hlsDirectory, segmentFiles, mp4TmpName) - - for (const file of segmentFiles) { - await remove(join(hlsDirectory, file)) + for (const file of rootFiles) { + // Move remaining files in the replay directory + if (file.endsWith('.ts') || file.endsWith('.m3u8')) { + await copy(join(hlsDirectory, file), join(replayDirectory, file)) } - if (!duration) { - duration = await getDurationFromVideoFile(mp4TmpName) + if (file.endsWith('.m3u8') && file !== 'master.m3u8') { + playlistFiles.push(file) } - - resolutions.push(videoFileResolution) } await cleanupLiveFiles(hlsDirectory) @@ -77,8 +75,9 @@ async function saveLive (video: MVideo, live: MVideoLive) { await live.destroy() video.isLive = false + // Reinit views + video.views = 0 video.state = VideoState.TO_TRANSCODE - video.duration = duration await video.save() @@ -89,28 +88,53 @@ async function saveLive (video: MVideo, live: MVideoLive) { await VideoFileModel.removeHLSFilesOfVideoId(hlsPlaylist.id) hlsPlaylist.VideoFiles = [] - for (const resolution of resolutions) { - const videoInputPath = buildMP4TmpName(resolution) - const { isPortraitMode } = await getVideoFileResolution(videoInputPath) + const replayFiles = await readdir(replayDirectory) + let durationDone: boolean - await generateHlsPlaylist({ + for (const playlistFile of playlistFiles) { + const playlistPath = join(replayDirectory, playlistFile) + const { videoFileResolution, isPortraitMode } = await getVideoFileResolution(playlistPath) + + // Playlist name is for example 3.m3u8 + // Segments names are 3-0.ts 3-1.ts etc + const shouldStartWith = playlistFile.replace(/\.m3u8$/, '') + '-' + + const segmentFiles = replayFiles.filter(f => f.startsWith(shouldStartWith) && f.endsWith('.ts')) + + const outputPath = await generateHlsPlaylistFromTS({ video: videoWithFiles, - videoInputPath, - resolution: resolution, - copyCodecs: true, + replayDirectory, + segmentFiles, + resolution: videoFileResolution, isPortraitMode }) - await remove(join(hlsDirectory, videoInputPath)) + 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(videoWithFiles, videoWithFiles.getMaxQualityFile(), ThumbnailType.MINIATURE) + } + + if (videoWithFiles.getPreview().automaticallyGenerated === true) { + await generateVideoMiniature(videoWithFiles, videoWithFiles.getMaxQualityFile(), ThumbnailType.PREVIEW) } - await publishAndFederateIfNeeded(video, true) + await publishAndFederateIfNeeded(videoWithFiles, true) } async function cleanupLive (video: MVideo, streamingPlaylist: MStreamingPlaylist) { - const hlsDirectory = getHLSDirectory(video, false) + const hlsDirectory = getHLSDirectory(video) - await cleanupLiveFiles(hlsDirectory) + await remove(hlsDirectory) streamingPlaylist.destroy() .catch(err => logger.error('Cannot remove live streaming playlist.', { err })) @@ -134,7 +158,3 @@ async function cleanupLiveFiles (hlsDirectory: string) { } } } - -function buildMP4TmpName (resolution: number) { - return resolution + '-tmp.mp4' -}