+ const tsWatcher = chokidar.watch(outPath + '/*.ts')
+
+ const segmentsToProcessPerPlaylist: { [playlistId: string]: string[] } = {}
+ const playlistIdMatcher = /^([\d+])-/
+
+ const addHandler = segmentPath => {
+ logger.debug('Live add handler of %s.', segmentPath)
+
+ const playlistId = basename(segmentPath).match(playlistIdMatcher)[0]
+
+ const segmentsToProcess = segmentsToProcessPerPlaylist[playlistId] || []
+ this.processSegments(outPath, videoUUID, videoLive, segmentsToProcess)
+
+ segmentsToProcessPerPlaylist[playlistId] = [ segmentPath ]
+
+ if (this.hasClientSocketsInBadHealthWithCache(sessionId)) {
+ logger.error(
+ 'Too much data in client socket stream (ffmpeg is too slow to transcode the video).' +
+ ' Stopping session of video %s.', videoUUID)
+
+ this.stopSessionOf(videoLive.videoId)
+ return
+ }
+
+ // Duration constraint check
+ if (this.isDurationConstraintValid(startStreamDateTime) !== true) {
+ logger.info('Stopping session of %s: max duration exceeded.', videoUUID)
+
+ this.stopSessionOf(videoLive.videoId)
+ return
+ }
+
+ // Check user quota if the user enabled replay saving
+ if (videoLive.saveReplay === true) {
+ stat(segmentPath)
+ .then(segmentStat => {
+ currentUserLive.size += segmentStat.size
+ })
+ .then(() => this.isQuotaConstraintValid(user, videoLive))
+ .then(quotaValid => {
+ if (quotaValid !== true) {
+ logger.info('Stopping session of %s: user quota exceeded.', videoUUID)
+
+ this.stopSessionOf(videoLive.videoId)
+ }
+ })
+ .catch(err => logger.error('Cannot stat %s or check quota of %d.', segmentPath, user.id, { err }))
+ }
+ }
+
+ const deleteHandler = segmentPath => this.removeSegmentSha(videoUUID, segmentPath)
+
+ tsWatcher.on('add', p => addHandler(p))
+ tsWatcher.on('unlink', p => deleteHandler(p))
+
+ const masterWatcher = chokidar.watch(outPath + '/master.m3u8')
+ masterWatcher.on('add', async () => {
+ try {
+ const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(videoLive.videoId)
+
+ video.state = VideoState.PUBLISHED
+ await video.save()
+ videoLive.Video = video
+
+ setTimeout(() => {
+ federateVideoIfNeeded(video, false)
+ .catch(err => logger.error('Cannot federate live video %s.', video.url, { err }))
+
+ PeerTubeSocket.Instance.sendVideoLiveNewState(video)
+ }, VIDEO_LIVE.SEGMENT_TIME_SECONDS * 1000 * VIDEO_LIVE.EDGE_LIVE_DELAY_SEGMENTS_NOTIFICATION)
+
+ } catch (err) {
+ logger.error('Cannot save/federate live video %d.', videoLive.videoId, { err })
+ } finally {
+ masterWatcher.close()
+ .catch(err => logger.error('Cannot close master watcher of %s.', outPath, { err }))
+ }
+ })
+