constructor (private readonly options: ProcessOptions<RunnerJobLiveRTMPHLSTranscodingPayload>) {
this.outputPath = join(ConfigManager.Instance.getTranscodingDirectory(), buildUUID())
+
+ logger.debug(`Using ${this.outputPath} to process live rtmp hls transcoding job ${options.job.uuid}`)
}
process () {
})
} catch (err) {
if (currentTry >= 3) throw err
+ if ((err.res?.body as PeerTubeProblemDocument)?.code === ServerErrorCode.RUNNER_JOB_NOT_IN_PROCESSING_STATE) throw err
logger.warn({ err }, 'Will retry update after error')
await wait(250)
// ---------------------------------------------------------------------------
private cleanup () {
+ logger.debug(`Cleaning up job ${this.options.job.uuid}`)
+
for (const fsWatcher of this.fsWatchers) {
fsWatcher.close()
.catch(err => logger.error({ err }, 'Cannot close watcher'))
return !!this.rtmpServer
}
+ hasSession (sessionId: string) {
+ return this.getContext().sessions.has(sessionId)
+ }
+
stopSessionOf (videoUUID: string, error: LiveVideoError | null) {
const sessionId = this.videoSessions.get(videoUUID)
if (!sessionId) {
lTags: this.lTags,
+ sessionId: this.sessionId,
inputLocalUrl: this.inputLocalUrl,
inputPublicUrl: this.inputPublicUrl,
lTags: LoggerTagsFn
+ sessionId: string
inputLocalUrl: string
inputPublicUrl: string
fps: number
}[]
+ protected readonly sessionId: string
protected readonly inputLocalUrl: string
protected readonly inputPublicUrl: string
this.videoUUID = options.videoLive.Video.uuid
this.streamingPlaylist = options.streamingPlaylist
+ this.sessionId = options.sessionId
this.inputLocalUrl = options.inputLocalUrl
this.inputPublicUrl = options.inputPublicUrl
async run () {
await new LiveRTMPHLSTranscodingJobHandler().create({
rtmpUrl: this.inputPublicUrl,
+ sessionId: this.sessionId,
toTranscode: this.toTranscode,
video: this.videoLive.Video,
outputDirectory: this.outDirectory,
video: MVideo
playlist: MStreamingPlaylist
+ sessionId: string
rtmpUrl: string
toTranscode: {
export class LiveRTMPHLSTranscodingJobHandler extends AbstractJobHandler<CreateOptions, LiveRTMPHLSTranscodingUpdatePayload, LiveRTMPHLSTranscodingSuccess> {
async create (options: CreateOptions) {
- const { video, rtmpUrl, toTranscode, playlist, segmentDuration, segmentListSize, outputDirectory } = options
+ const { video, rtmpUrl, toTranscode, playlist, segmentDuration, segmentListSize, outputDirectory, sessionId } = options
const jobUUID = buildUUID()
const payload: RunnerJobLiveRTMPHLSTranscodingPayload = {
const privatePayload: RunnerJobLiveRTMPHLSTranscodingPrivatePayload = {
videoUUID: video.uuid,
masterPlaylistName: playlist.playlistFilename,
+ sessionId,
outputDirectory
}
} from '@server/helpers/custom-validators/runners/jobs'
import { isRunnerTokenValid } from '@server/helpers/custom-validators/runners/runners'
import { cleanUpReqFiles } from '@server/helpers/express-utils'
+import { LiveManager } from '@server/lib/live'
import { RunnerJobModel } from '@server/models/runner/runner-job'
-import { HttpStatusCode, RunnerJobState, RunnerJobSuccessBody, RunnerJobUpdateBody, ServerErrorCode } from '@shared/models'
+import {
+ HttpStatusCode,
+ RunnerJobLiveRTMPHLSTranscodingPrivatePayload,
+ RunnerJobState,
+ RunnerJobSuccessBody,
+ RunnerJobUpdateBody,
+ ServerErrorCode
+} from '@shared/models'
import { areValidationErrors } from '../shared'
const tags = [ 'runner' ]
if (areValidationErrors(req, res, { tags })) return cleanUpReqFiles(req)
const body = req.body as RunnerJobUpdateBody
+ const job = res.locals.runnerJob
- if (isRunnerJobUpdatePayloadValid(body.payload, res.locals.runnerJob.type, req.files) !== true) {
+ if (isRunnerJobUpdatePayloadValid(body.payload, job.type, req.files) !== true) {
cleanUpReqFiles(req)
return res.fail({
})
}
+ if (res.locals.runnerJob.type === 'live-rtmp-hls-transcoding') {
+ const privatePayload = job.privatePayload as RunnerJobLiveRTMPHLSTranscodingPrivatePayload
+
+ if (!LiveManager.Instance.hasSession(privatePayload.sessionId)) {
+ cleanUpReqFiles(req)
+
+ return res.fail({
+ status: HttpStatusCode.BAD_REQUEST_400,
+ message: 'Session of this live ended',
+ tags
+ })
+ }
+ }
+
return next()
}
]
live: {
enabled: true,
allowReplay: true,
- maxDuration: 1,
+ maxDuration: 3,
transcoding: {
enabled: true,
resolutions: ConfigCommand.getCustomConfigResolutions(true)
})
it('Should search by live', async function () {
- this.timeout(60000)
+ this.timeout(120000)
{
const newConfig = {
videoUUID: string
masterPlaylistName: string
outputDirectory: string
+ sessionId: string
}
// ---------------------------------------------------------------------------