diff options
Diffstat (limited to 'server')
-rw-r--r-- | server/server/lib/job-queue/handlers/video-live-ending.ts | 59 | ||||
-rw-r--r-- | server/server/lib/transcoding/hls-transcoding.ts | 17 |
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' | |||
8 | import { getLocalVideoActivityPubUrl } from '@server/lib/activitypub/url.js' | 8 | import { getLocalVideoActivityPubUrl } from '@server/lib/activitypub/url.js' |
9 | import { federateVideoIfNeeded } from '@server/lib/activitypub/videos/index.js' | 9 | import { federateVideoIfNeeded } from '@server/lib/activitypub/videos/index.js' |
10 | import { cleanupAndDestroyPermanentLive, cleanupTMPLiveFiles, cleanupUnsavedNormalLive } from '@server/lib/live/index.js' | 10 | import { cleanupAndDestroyPermanentLive, cleanupTMPLiveFiles, cleanupUnsavedNormalLive } from '@server/lib/live/index.js' |
11 | import { generateHLSMasterPlaylistFilename, generateHlsSha256SegmentsFilename, getLiveReplayBaseDirectory } from '@server/lib/paths.js' | 11 | import { |
12 | generateHLSMasterPlaylistFilename, | ||
13 | generateHlsSha256SegmentsFilename, | ||
14 | getHLSDirectory, | ||
15 | getLiveReplayBaseDirectory | ||
16 | } from '@server/lib/paths.js' | ||
12 | import { generateLocalVideoMiniature, regenerateMiniaturesIfNeeded } from '@server/lib/thumbnail.js' | 17 | import { generateLocalVideoMiniature, regenerateMiniaturesIfNeeded } from '@server/lib/thumbnail.js' |
13 | import { generateHlsPlaylistResolutionFromTS } from '@server/lib/transcoding/hls-transcoding.js' | 18 | import { generateHlsPlaylistResolutionFromTS } from '@server/lib/transcoding/hls-transcoding.js' |
14 | import { VideoPathManager } from '@server/lib/video-path-manager.js' | 19 | import { VideoPathManager } from '@server/lib/video-path-manager.js' |
@@ -24,6 +29,7 @@ import { MVideo, MVideoLive, MVideoLiveSession, MVideoWithAllFiles } from '@serv | |||
24 | import { ffprobePromise, getAudioStream, getVideoStreamDimensionsInfo, getVideoStreamFPS } from '@peertube/peertube-ffmpeg' | 29 | import { ffprobePromise, getAudioStream, getVideoStreamDimensionsInfo, getVideoStreamFPS } from '@peertube/peertube-ffmpeg' |
25 | import { logger, loggerTagsFactory } from '../../../helpers/logger.js' | 30 | import { logger, loggerTagsFactory } from '../../../helpers/logger.js' |
26 | import { JobQueue } from '../job-queue.js' | 31 | import { JobQueue } from '../job-queue.js' |
32 | import { isVideoInPublicDirectory } from '@server/lib/video-privacy.js' | ||
27 | 33 | ||
28 | const lTags = loggerTagsFactory('live', 'job') | 34 | const 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 | } |