]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/lib/job-queue/handlers/video-live-ending.ts
Add pixel size to tooltip and gif support with FFmpeg for avatar upload (#3329)
[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'
d846d99c
C
4import { getDurationFromVideoFile, getVideoFileResolution, hlsPlaylistToFragmentedMP4 } from '@server/helpers/ffmpeg-utils'
5import { publishAndFederateIfNeeded } from '@server/lib/video'
a5cf76af 6import { getHLSDirectory } from '@server/lib/video-paths'
b5b68755 7import { generateHlsPlaylist } from '@server/lib/video-transcoding'
a5cf76af 8import { VideoModel } from '@server/models/video/video'
68e70a74 9import { VideoFileModel } from '@server/models/video/video-file'
b5b68755 10import { VideoLiveModel } from '@server/models/video/video-live'
a5cf76af 11import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist'
68e70a74 12import { MStreamingPlaylist, MVideo, MVideoLive } from '@server/types/models'
053aed43 13import { ThumbnailType, VideoLiveEndingPayload, VideoState } from '@shared/models'
a5cf76af 14import { logger } from '../../../helpers/logger'
053aed43 15import { generateVideoMiniature } from '@server/lib/thumbnail'
a5cf76af
C
16
17async function processVideoLiveEnding (job: Bull.Job) {
18 const payload = job.data as VideoLiveEndingPayload
19
68e70a74
C
20 function logError () {
21 logger.warn('Video live %d does not exist anymore. Cannot process live ending.', payload.videoId)
22 }
23
b5b68755
C
24 const video = await VideoModel.load(payload.videoId)
25 const live = await VideoLiveModel.loadByVideoId(payload.videoId)
26
68e70a74
C
27 if (!video || !live) {
28 logError()
29 return
30 }
31
b5b68755 32 const streamingPlaylist = await VideoStreamingPlaylistModel.loadHLSPlaylistByVideo(video.id)
68e70a74
C
33 if (!streamingPlaylist) {
34 logError()
a5cf76af
C
35 return
36 }
37
b5b68755
C
38 if (live.saveReplay !== true) {
39 return cleanupLive(video, streamingPlaylist)
40 }
41
31c82cd9 42 return saveLive(video, live)
b5b68755
C
43}
44
45// ---------------------------------------------------------------------------
46
47export {
48 processVideoLiveEnding
49}
50
51// ---------------------------------------------------------------------------
52
31c82cd9 53async function saveLive (video: MVideo, live: MVideoLive) {
b5b68755 54 const hlsDirectory = getHLSDirectory(video, false)
31c82cd9
C
55 const files = await readdir(hlsDirectory)
56
57 const playlistFiles = files.filter(f => f.endsWith('.m3u8') && f !== 'master.m3u8')
58 const resolutions: number[] = []
d846d99c 59 let duration: number
31c82cd9
C
60
61 for (const playlistFile of playlistFiles) {
62 const playlistPath = join(hlsDirectory, playlistFile)
63 const { videoFileResolution } = await getVideoFileResolution(playlistPath)
b5b68755 64
68e70a74 65 const mp4TmpPath = buildMP4TmpPath(hlsDirectory, videoFileResolution)
b5b68755 66
31c82cd9
C
67 // Playlist name is for example 3.m3u8
68 // Segments names are 3-0.ts 3-1.ts etc
69 const shouldStartWith = playlistFile.replace(/\.m3u8$/, '') + '-'
70
71 const segmentFiles = files.filter(f => f.startsWith(shouldStartWith) && f.endsWith('.ts'))
68e70a74 72 await hlsPlaylistToFragmentedMP4(hlsDirectory, segmentFiles, mp4TmpPath)
31c82cd9 73
d846d99c 74 if (!duration) {
68e70a74 75 duration = await getDurationFromVideoFile(mp4TmpPath)
d846d99c
C
76 }
77
31c82cd9 78 resolutions.push(videoFileResolution)
b5b68755
C
79 }
80
81 await cleanupLiveFiles(hlsDirectory)
82
31c82cd9
C
83 await live.destroy()
84
b5b68755 85 video.isLive = false
e4bf7856
C
86 // Reinit views
87 video.views = 0
b5b68755 88 video.state = VideoState.TO_TRANSCODE
d846d99c
C
89 video.duration = duration
90
b5b68755
C
91 await video.save()
92
97969c4e 93 // Remove old HLS playlist video files
b5b68755
C
94 const videoWithFiles = await VideoModel.loadWithFiles(video.id)
95
97969c4e
C
96 const hlsPlaylist = videoWithFiles.getHLSPlaylist()
97 await VideoFileModel.removeHLSFilesOfVideoId(hlsPlaylist.id)
98 hlsPlaylist.VideoFiles = []
99
31c82cd9 100 for (const resolution of resolutions) {
68e70a74 101 const videoInputPath = buildMP4TmpPath(hlsDirectory, resolution)
b5b68755
C
102 const { isPortraitMode } = await getVideoFileResolution(videoInputPath)
103
104 await generateHlsPlaylist({
105 video: videoWithFiles,
106 videoInputPath,
31c82cd9 107 resolution: resolution,
b5b68755
C
108 copyCodecs: true,
109 isPortraitMode
110 })
b5b68755 111
68e70a74 112 await remove(videoInputPath)
97969c4e 113 }
d846d99c 114
053aed43
C
115 // Regenerate the thumbnail & preview?
116 if (videoWithFiles.getMiniature().automaticallyGenerated === true) {
117 await generateVideoMiniature(videoWithFiles, videoWithFiles.getMaxQualityFile(), ThumbnailType.MINIATURE)
118 }
119
120 if (videoWithFiles.getPreview().automaticallyGenerated === true) {
121 await generateVideoMiniature(videoWithFiles, videoWithFiles.getMaxQualityFile(), ThumbnailType.PREVIEW)
122 }
123
97969c4e 124 await publishAndFederateIfNeeded(video, true)
b5b68755
C
125}
126
127async function cleanupLive (video: MVideo, streamingPlaylist: MStreamingPlaylist) {
9e2b2e76 128 const hlsDirectory = getHLSDirectory(video)
a5cf76af 129
68e70a74 130 await remove(hlsDirectory)
b5b68755
C
131
132 streamingPlaylist.destroy()
133 .catch(err => logger.error('Cannot remove live streaming playlist.', { err }))
134}
135
136async function cleanupLiveFiles (hlsDirectory: string) {
a5cf76af
C
137 const files = await readdir(hlsDirectory)
138
139 for (const filename of files) {
140 if (
141 filename.endsWith('.ts') ||
142 filename.endsWith('.m3u8') ||
143 filename.endsWith('.mpd') ||
144 filename.endsWith('.m4s') ||
145 filename.endsWith('.tmp')
146 ) {
147 const p = join(hlsDirectory, filename)
148
149 remove(p)
150 .catch(err => logger.error('Cannot remove %s.', p, { err }))
151 }
152 }
a5cf76af
C
153}
154
68e70a74
C
155function buildMP4TmpPath (basePath: string, resolution: number) {
156 return join(basePath, resolution + '-tmp.mp4')
a5cf76af 157}