aboutsummaryrefslogtreecommitdiffhomepage
path: root/server
diff options
context:
space:
mode:
Diffstat (limited to 'server')
-rw-r--r--server/server/lib/job-queue/handlers/video-live-ending.ts59
-rw-r--r--server/server/lib/transcoding/hls-transcoding.ts17
2 files changed, 57 insertions, 19 deletions
diff --git a/server/server/lib/job-queue/handlers/video-live-ending.ts b/server/server/lib/job-queue/handlers/video-live-ending.ts
index 0b4a4fd8b..f10cc763c 100644
--- a/server/server/lib/job-queue/handlers/video-live-ending.ts
+++ b/server/server/lib/job-queue/handlers/video-live-ending.ts
@@ -8,7 +8,12 @@ import { CONSTRAINTS_FIELDS } from '@server/initializers/constants.js'
8import { getLocalVideoActivityPubUrl } from '@server/lib/activitypub/url.js' 8import { getLocalVideoActivityPubUrl } from '@server/lib/activitypub/url.js'
9import { federateVideoIfNeeded } from '@server/lib/activitypub/videos/index.js' 9import { federateVideoIfNeeded } from '@server/lib/activitypub/videos/index.js'
10import { cleanupAndDestroyPermanentLive, cleanupTMPLiveFiles, cleanupUnsavedNormalLive } from '@server/lib/live/index.js' 10import { cleanupAndDestroyPermanentLive, cleanupTMPLiveFiles, cleanupUnsavedNormalLive } from '@server/lib/live/index.js'
11import { generateHLSMasterPlaylistFilename, generateHlsSha256SegmentsFilename, getLiveReplayBaseDirectory } from '@server/lib/paths.js' 11import {
12 generateHLSMasterPlaylistFilename,
13 generateHlsSha256SegmentsFilename,
14 getHLSDirectory,
15 getLiveReplayBaseDirectory
16} from '@server/lib/paths.js'
12import { generateLocalVideoMiniature, regenerateMiniaturesIfNeeded } from '@server/lib/thumbnail.js' 17import { generateLocalVideoMiniature, regenerateMiniaturesIfNeeded } from '@server/lib/thumbnail.js'
13import { generateHlsPlaylistResolutionFromTS } from '@server/lib/transcoding/hls-transcoding.js' 18import { generateHlsPlaylistResolutionFromTS } from '@server/lib/transcoding/hls-transcoding.js'
14import { VideoPathManager } from '@server/lib/video-path-manager.js' 19import { VideoPathManager } from '@server/lib/video-path-manager.js'
@@ -24,6 +29,7 @@ import { MVideo, MVideoLive, MVideoLiveSession, MVideoWithAllFiles } from '@serv
24import { ffprobePromise, getAudioStream, getVideoStreamDimensionsInfo, getVideoStreamFPS } from '@peertube/peertube-ffmpeg' 29import { ffprobePromise, getAudioStream, getVideoStreamDimensionsInfo, getVideoStreamFPS } from '@peertube/peertube-ffmpeg'
25import { logger, loggerTagsFactory } from '../../../helpers/logger.js' 30import { logger, loggerTagsFactory } from '../../../helpers/logger.js'
26import { JobQueue } from '../job-queue.js' 31import { JobQueue } from '../job-queue.js'
32import { isVideoInPublicDirectory } from '@server/lib/video-privacy.js'
27 33
28const lTags = loggerTagsFactory('live', 'job') 34const lTags = loggerTagsFactory('live', 'job')
29 35
@@ -139,9 +145,15 @@ async function saveReplayToExternalVideo (options: {
139 }) 145 })
140 } 146 }
141 147
142 await assignReplayFilesToVideo({ video: replayVideo, replayDirectory }) 148 const inputFileMutexReleaser = await VideoPathManager.Instance.lockFiles(liveVideo.uuid)
143 149
144 await remove(replayDirectory) 150 try {
151 await assignReplayFilesToVideo({ video: replayVideo, replayDirectory })
152
153 await remove(replayDirectory)
154 } finally {
155 inputFileMutexReleaser()
156 }
145 157
146 for (const type of [ ThumbnailType.MINIATURE, ThumbnailType.PREVIEW ]) { 158 for (const type of [ ThumbnailType.MINIATURE, ThumbnailType.PREVIEW ]) {
147 const image = await generateLocalVideoMiniature({ video: replayVideo, videoFile: replayVideo.getMaxQualityFile(), type }) 159 const image = await generateLocalVideoMiniature({ video: replayVideo, videoFile: replayVideo.getMaxQualityFile(), type })
@@ -160,11 +172,14 @@ async function replaceLiveByReplay (options: {
160 permanentLive: boolean 172 permanentLive: boolean
161 replayDirectory: string 173 replayDirectory: string
162}) { 174}) {
163 const { video, liveSession, live, permanentLive, replayDirectory } = options 175 const { video: liveVideo, liveSession, live, permanentLive, replayDirectory } = options
164 176
165 const replaySettings = await VideoLiveReplaySettingModel.load(liveSession.replaySettingId) 177 const replaySettings = await VideoLiveReplaySettingModel.load(liveSession.replaySettingId)
166 const videoWithFiles = await VideoModel.loadFull(video.id) 178 const videoWithFiles = await VideoModel.loadFull(liveVideo.id)
167 const hlsPlaylist = videoWithFiles.getHLSPlaylist() 179 const hlsPlaylist = videoWithFiles.getHLSPlaylist()
180 const replayInAnotherDirectory = isVideoInPublicDirectory(liveVideo.privacy) !== isVideoInPublicDirectory(replaySettings.privacy)
181
182 logger.info(`Replacing live ${liveVideo.uuid} by replay ${replayDirectory}.`, { replayInAnotherDirectory, ...lTags(liveVideo.uuid) })
168 183
169 await cleanupTMPLiveFiles(videoWithFiles, hlsPlaylist) 184 await cleanupTMPLiveFiles(videoWithFiles, hlsPlaylist)
170 185
@@ -188,13 +203,25 @@ async function replaceLiveByReplay (options: {
188 hlsPlaylist.segmentsSha256Filename = generateHlsSha256SegmentsFilename() 203 hlsPlaylist.segmentsSha256Filename = generateHlsSha256SegmentsFilename()
189 await hlsPlaylist.save() 204 await hlsPlaylist.save()
190 205
191 await assignReplayFilesToVideo({ video: videoWithFiles, replayDirectory }) 206 const inputFileMutexReleaser = await VideoPathManager.Instance.lockFiles(videoWithFiles.uuid)
192 207
193 // Should not happen in this function, but we keep the code if in the future we can replace the permanent live by a replay 208 try {
194 if (permanentLive) { // Remove session replay 209 await assignReplayFilesToVideo({ video: videoWithFiles, replayDirectory })
195 await remove(replayDirectory) 210
196 } else { // We won't stream again in this live, we can delete the base replay directory 211 // Should not happen in this function, but we keep the code if in the future we can replace the permanent live by a replay
197 await remove(getLiveReplayBaseDirectory(videoWithFiles)) 212 if (permanentLive) { // Remove session replay
213 await remove(replayDirectory)
214 } else {
215 // We won't stream again in this live, we can delete the base replay directory
216 await remove(getLiveReplayBaseDirectory(liveVideo))
217
218 // If the live was in another base directory, also delete it
219 if (replayInAnotherDirectory) {
220 await remove(getHLSDirectory(liveVideo))
221 }
222 }
223 } finally {
224 inputFileMutexReleaser()
198 } 225 }
199 226
200 // Regenerate the thumbnail & preview? 227 // Regenerate the thumbnail & preview?
@@ -214,8 +241,10 @@ async function assignReplayFilesToVideo (options: {
214 241
215 const concatenatedTsFiles = await readdir(replayDirectory) 242 const concatenatedTsFiles = await readdir(replayDirectory)
216 243
244 logger.info(`Assigning replays ${replayDirectory} to video ${video.uuid}.`, { concatenatedTsFiles, ...lTags(video.uuid) })
245
217 for (const concatenatedTsFile of concatenatedTsFiles) { 246 for (const concatenatedTsFile of concatenatedTsFiles) {
218 const inputFileMutexReleaser = await VideoPathManager.Instance.lockFiles(video.uuid) 247 // Generating hls playlist can be long, reload the video in this case
219 await video.reload() 248 await video.reload()
220 249
221 const concatenatedTsFilePath = join(replayDirectory, concatenatedTsFile) 250 const concatenatedTsFilePath = join(replayDirectory, concatenatedTsFile)
@@ -228,17 +257,17 @@ async function assignReplayFilesToVideo (options: {
228 try { 257 try {
229 await generateHlsPlaylistResolutionFromTS({ 258 await generateHlsPlaylistResolutionFromTS({
230 video, 259 video,
231 inputFileMutexReleaser, 260 inputFileMutexReleaser: null, // Already locked in parent
232 concatenatedTsFilePath, 261 concatenatedTsFilePath,
233 resolution, 262 resolution,
234 fps, 263 fps,
235 isAAC: audioStream?.codec_name === 'aac' 264 isAAC: audioStream?.codec_name === 'aac'
236 }) 265 })
266
267 logger.error('coucou')
237 } catch (err) { 268 } catch (err) {
238 logger.error('Cannot generate HLS playlist resolution from TS files.', { err }) 269 logger.error('Cannot generate HLS playlist resolution from TS files.', { err })
239 } 270 }
240
241 inputFileMutexReleaser()
242 } 271 }
243 272
244 return video 273 return video
diff --git a/server/server/lib/transcoding/hls-transcoding.ts b/server/server/lib/transcoding/hls-transcoding.ts
index 5f07f112a..15182f5e6 100644
--- a/server/server/lib/transcoding/hls-transcoding.ts
+++ b/server/server/lib/transcoding/hls-transcoding.ts
@@ -58,8 +58,9 @@ export async function onHLSVideoFileTranscoding (options: {
58 videoFile: MVideoFile 58 videoFile: MVideoFile
59 videoOutputPath: string 59 videoOutputPath: string
60 m3u8OutputPath: string 60 m3u8OutputPath: string
61 filesLockedInParent?: boolean // default false
61}) { 62}) {
62 const { video, videoFile, videoOutputPath, m3u8OutputPath } = options 63 const { video, videoFile, videoOutputPath, m3u8OutputPath, filesLockedInParent = false } = options
63 64
64 // Create or update the playlist 65 // Create or update the playlist
65 const playlist = await retryTransactionWrapper(() => { 66 const playlist = await retryTransactionWrapper(() => {
@@ -69,7 +70,9 @@ export async function onHLSVideoFileTranscoding (options: {
69 }) 70 })
70 videoFile.videoStreamingPlaylistId = playlist.id 71 videoFile.videoStreamingPlaylistId = playlist.id
71 72
72 const mutexReleaser = await VideoPathManager.Instance.lockFiles(video.uuid) 73 const mutexReleaser = !filesLockedInParent
74 ? await VideoPathManager.Instance.lockFiles(video.uuid)
75 : null
73 76
74 try { 77 try {
75 await video.reload() 78 await video.reload()
@@ -114,7 +117,7 @@ export async function onHLSVideoFileTranscoding (options: {
114 117
115 return { resolutionPlaylistPath, videoFile: savedVideoFile } 118 return { resolutionPlaylistPath, videoFile: savedVideoFile }
116 } finally { 119 } finally {
117 mutexReleaser() 120 if (mutexReleaser) mutexReleaser()
118 } 121 }
119} 122}
120 123
@@ -176,5 +179,11 @@ async function generateHlsPlaylistCommon (options: {
176 fps: -1 179 fps: -1
177 }) 180 })
178 181
179 await onHLSVideoFileTranscoding({ video, videoFile: newVideoFile, videoOutputPath, m3u8OutputPath }) 182 await onHLSVideoFileTranscoding({
183 video,
184 videoFile: newVideoFile,
185 videoOutputPath,
186 m3u8OutputPath,
187 filesLockedInParent: !inputFileMutexReleaser
188 })
180} 189}