]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blobdiff - server/lib/job-queue/handlers/video-live-ending.ts
Reload the video after waiting for the files lock
[github/Chocobozzz/PeerTube.git] / server / lib / job-queue / handlers / video-live-ending.ts
index 78d0b21923ea1e2541f8bd1913ba9f2c5aca2764..814f313a3bb8d946905bbbce3fb1ffba571e0d28 100644 (file)
@@ -1,21 +1,23 @@
-import { Job } from 'bull'
+import { Job } from 'bullmq'
 import { readdir, remove } from 'fs-extra'
 import { join } from 'path'
-import { ffprobePromise, getAudioStream, getVideoStreamDimensionsInfo, getVideoStreamDuration } from '@server/helpers/ffmpeg'
 import { getLocalVideoActivityPubUrl } from '@server/lib/activitypub/url'
 import { federateVideoIfNeeded } from '@server/lib/activitypub/videos'
-import { cleanupPermanentLive, cleanupTMPLiveFiles, cleanupUnsavedNormalLive } from '@server/lib/live'
+import { cleanupAndDestroyPermanentLive, cleanupTMPLiveFiles, cleanupUnsavedNormalLive } from '@server/lib/live'
 import { generateHLSMasterPlaylistFilename, generateHlsSha256SegmentsFilename, getLiveReplayBaseDirectory } from '@server/lib/paths'
 import { generateVideoMiniature } from '@server/lib/thumbnail'
-import { generateHlsPlaylistResolutionFromTS } from '@server/lib/transcoding/transcoding'
+import { generateHlsPlaylistResolutionFromTS } from '@server/lib/transcoding/hls-transcoding'
+import { VideoPathManager } from '@server/lib/video-path-manager'
 import { moveToNextState } from '@server/lib/video-state'
 import { VideoModel } from '@server/models/video/video'
 import { VideoBlacklistModel } from '@server/models/video/video-blacklist'
 import { VideoFileModel } from '@server/models/video/video-file'
 import { VideoLiveModel } from '@server/models/video/video-live'
+import { VideoLiveReplaySettingModel } from '@server/models/video/video-live-replay-setting'
 import { VideoLiveSessionModel } from '@server/models/video/video-live-session'
 import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist'
 import { MVideo, MVideoLive, MVideoLiveSession, MVideoWithAllFiles } from '@server/types/models'
+import { ffprobePromise, getAudioStream, getVideoStreamDimensionsInfo, getVideoStreamFPS } from '@shared/ffmpeg'
 import { ThumbnailType, VideoLiveEndingPayload, VideoState } from '@shared/models'
 import { logger, loggerTagsFactory } from '../../../helpers/logger'
 
@@ -34,13 +36,13 @@ async function processVideoLiveEnding (job: Job) {
   const live = await VideoLiveModel.loadByVideoId(payload.videoId)
   const liveSession = await VideoLiveSessionModel.load(payload.liveSessionId)
 
-  const permanentLive = live.permanentLive
-
   if (!video || !live || !liveSession) {
     logError()
     return
   }
 
+  const permanentLive = live.permanentLive
+
   liveSession.endingProcessed = true
   await liveSession.save()
 
@@ -59,7 +61,13 @@ async function processVideoLiveEnding (job: Job) {
     return cleanupLiveAndFederate({ permanentLive, video, streamingPlaylistId: payload.streamingPlaylistId })
   }
 
-  return replaceLiveByReplay({ video, liveSession, live, permanentLive, replayDirectory: payload.replayDirectory })
+  return replaceLiveByReplay({
+    video,
+    liveSession,
+    live,
+    permanentLive,
+    replayDirectory: payload.replayDirectory
+  })
 }
 
 // ---------------------------------------------------------------------------
@@ -78,6 +86,8 @@ async function saveReplayToExternalVideo (options: {
 }) {
   const { liveVideo, liveSession, publishedAt, replayDirectory } = options
 
+  const replaySettings = await VideoLiveReplaySettingModel.load(liveSession.replaySettingId)
+
   const replayVideo = new VideoModel({
     name: `${liveVideo.name} - ${new Date(publishedAt).toLocaleString()}`,
     isLive: false,
@@ -94,7 +104,7 @@ async function saveReplayToExternalVideo (options: {
     nsfw: liveVideo.nsfw,
     description: liveVideo.description,
     support: liveVideo.support,
-    privacy: liveVideo.privacy,
+    privacy: replaySettings.privacy,
     channelId: liveVideo.channelId
   }) as MVideoWithAllFiles
 
@@ -141,23 +151,24 @@ async function replaceLiveByReplay (options: {
 }) {
   const { video, liveSession, live, permanentLive, replayDirectory } = options
 
-  await cleanupTMPLiveFiles(video)
+  const replaySettings = await VideoLiveReplaySettingModel.load(liveSession.replaySettingId)
+  const videoWithFiles = await VideoModel.loadFull(video.id)
+  const hlsPlaylist = videoWithFiles.getHLSPlaylist()
+
+  await cleanupTMPLiveFiles(videoWithFiles, hlsPlaylist)
 
   await live.destroy()
 
-  video.isLive = false
-  video.waitTranscoding = true
-  video.state = VideoState.TO_TRANSCODE
+  videoWithFiles.isLive = false
+  videoWithFiles.privacy = replaySettings.privacy
+  videoWithFiles.waitTranscoding = true
+  videoWithFiles.state = VideoState.TO_TRANSCODE
 
-  await video.save()
+  await videoWithFiles.save()
 
-  liveSession.replayVideoId = video.id
+  liveSession.replayVideoId = videoWithFiles.id
   await liveSession.save()
 
-  // Remove old HLS playlist video files
-  const videoWithFiles = await VideoModel.loadFull(video.id)
-
-  const hlsPlaylist = videoWithFiles.getHLSPlaylist()
   await VideoFileModel.removeHLSFilesOfVideoId(hlsPlaylist.id)
 
   // Reset playlist
@@ -203,31 +214,33 @@ async function assignReplayFilesToVideo (options: {
 }) {
   const { video, replayDirectory } = options
 
-  let durationDone = false
-
   const concatenatedTsFiles = await readdir(replayDirectory)
 
   for (const concatenatedTsFile of concatenatedTsFiles) {
+    const inputFileMutexReleaser = await VideoPathManager.Instance.lockFiles(video.uuid)
+    await video.reload()
+
     const concatenatedTsFilePath = join(replayDirectory, concatenatedTsFile)
 
     const probe = await ffprobePromise(concatenatedTsFilePath)
     const { audioStream } = await getAudioStream(concatenatedTsFilePath, probe)
-
     const { resolution } = await getVideoStreamDimensionsInfo(concatenatedTsFilePath, probe)
-
-    const { resolutionPlaylistPath: outputPath } = await generateHlsPlaylistResolutionFromTS({
-      video,
-      concatenatedTsFilePath,
-      resolution,
-      isAAC: audioStream?.codec_name === 'aac'
-    })
-
-    if (!durationDone) {
-      video.duration = await getVideoStreamDuration(outputPath)
-      await video.save()
-
-      durationDone = true
+    const fps = await getVideoStreamFPS(concatenatedTsFilePath, probe)
+
+    try {
+      await generateHlsPlaylistResolutionFromTS({
+        video,
+        inputFileMutexReleaser,
+        concatenatedTsFilePath,
+        resolution,
+        fps,
+        isAAC: audioStream?.codec_name === 'aac'
+      })
+    } catch (err) {
+      logger.error('Cannot generate HLS playlist resolution from TS files.', { err })
     }
+
+    inputFileMutexReleaser()
   }
 
   return video
@@ -244,7 +257,7 @@ async function cleanupLiveAndFederate (options: {
 
   if (streamingPlaylist) {
     if (permanentLive) {
-      await cleanupPermanentLive(video, streamingPlaylist)
+      await cleanupAndDestroyPermanentLive(video, streamingPlaylist)
     } else {
       await cleanupUnsavedNormalLive(video, streamingPlaylist)
     }