]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/lib/job-queue/handlers/video-live-ending.ts
Fix replay saving
[github/Chocobozzz/PeerTube.git] / server / lib / job-queue / handlers / video-live-ending.ts
CommitLineData
a5cf76af
C
1import * as Bull from 'bull'
2import { readdir, remove } from 'fs-extra'
3import { join } from 'path'
b5b68755 4import { getVideoFileResolution, hlsPlaylistToFragmentedMP4 } from '@server/helpers/ffmpeg-utils'
a5cf76af 5import { getHLSDirectory } from '@server/lib/video-paths'
b5b68755 6import { generateHlsPlaylist } from '@server/lib/video-transcoding'
a5cf76af 7import { VideoModel } from '@server/models/video/video'
b5b68755 8import { VideoLiveModel } from '@server/models/video/video-live'
a5cf76af 9import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist'
31c82cd9 10import { MStreamingPlaylist, MVideo, MVideoLive } from '@server/types/models'
b5b68755 11import { VideoLiveEndingPayload, VideoState } from '@shared/models'
a5cf76af
C
12import { logger } from '../../../helpers/logger'
13
14async function processVideoLiveEnding (job: Bull.Job) {
15 const payload = job.data as VideoLiveEndingPayload
16
b5b68755
C
17 const video = await VideoModel.load(payload.videoId)
18 const live = await VideoLiveModel.loadByVideoId(payload.videoId)
19
20 const streamingPlaylist = await VideoStreamingPlaylistModel.loadHLSPlaylistByVideo(video.id)
21 if (!video || !streamingPlaylist || !live) {
22 logger.warn('Video live %d does not exist anymore. Cannot process live ending.', payload.videoId)
a5cf76af
C
23 return
24 }
25
b5b68755
C
26 if (live.saveReplay !== true) {
27 return cleanupLive(video, streamingPlaylist)
28 }
29
31c82cd9 30 return saveLive(video, live)
b5b68755
C
31}
32
33// ---------------------------------------------------------------------------
34
35export {
36 processVideoLiveEnding
37}
38
39// ---------------------------------------------------------------------------
40
31c82cd9 41async function saveLive (video: MVideo, live: MVideoLive) {
b5b68755 42 const hlsDirectory = getHLSDirectory(video, false)
31c82cd9
C
43 const files = await readdir(hlsDirectory)
44
45 const playlistFiles = files.filter(f => f.endsWith('.m3u8') && f !== 'master.m3u8')
46 const resolutions: number[] = []
47
48 for (const playlistFile of playlistFiles) {
49 const playlistPath = join(hlsDirectory, playlistFile)
50 const { videoFileResolution } = await getVideoFileResolution(playlistPath)
b5b68755 51
31c82cd9 52 const mp4TmpName = buildMP4TmpName(videoFileResolution)
b5b68755 53
31c82cd9
C
54 // Playlist name is for example 3.m3u8
55 // Segments names are 3-0.ts 3-1.ts etc
56 const shouldStartWith = playlistFile.replace(/\.m3u8$/, '') + '-'
57
58 const segmentFiles = files.filter(f => f.startsWith(shouldStartWith) && f.endsWith('.ts'))
59 await hlsPlaylistToFragmentedMP4(hlsDirectory, segmentFiles, mp4TmpName)
60
61 resolutions.push(videoFileResolution)
b5b68755
C
62 }
63
64 await cleanupLiveFiles(hlsDirectory)
65
31c82cd9
C
66 await live.destroy()
67
b5b68755
C
68 video.isLive = false
69 video.state = VideoState.TO_TRANSCODE
70 await video.save()
71
72 const videoWithFiles = await VideoModel.loadWithFiles(video.id)
73
31c82cd9
C
74 for (const resolution of resolutions) {
75 const videoInputPath = buildMP4TmpName(resolution)
b5b68755
C
76 const { isPortraitMode } = await getVideoFileResolution(videoInputPath)
77
78 await generateHlsPlaylist({
79 video: videoWithFiles,
80 videoInputPath,
31c82cd9 81 resolution: resolution,
b5b68755
C
82 copyCodecs: true,
83 isPortraitMode
84 })
85 }
86
87 video.state = VideoState.PUBLISHED
88 await video.save()
89}
90
91async function cleanupLive (video: MVideo, streamingPlaylist: MStreamingPlaylist) {
a5cf76af
C
92 const hlsDirectory = getHLSDirectory(video, false)
93
b5b68755
C
94 await cleanupLiveFiles(hlsDirectory)
95
96 streamingPlaylist.destroy()
97 .catch(err => logger.error('Cannot remove live streaming playlist.', { err }))
98}
99
100async function cleanupLiveFiles (hlsDirectory: string) {
a5cf76af
C
101 const files = await readdir(hlsDirectory)
102
103 for (const filename of files) {
104 if (
105 filename.endsWith('.ts') ||
106 filename.endsWith('.m3u8') ||
107 filename.endsWith('.mpd') ||
108 filename.endsWith('.m4s') ||
109 filename.endsWith('.tmp')
110 ) {
111 const p = join(hlsDirectory, filename)
112
113 remove(p)
114 .catch(err => logger.error('Cannot remove %s.', p, { err }))
115 }
116 }
a5cf76af
C
117}
118
b5b68755 119function buildMP4TmpName (resolution: number) {
31c82cd9 120 return resolution + '-tmp.mp4'
a5cf76af 121}