mirror of
https://github.com/Chocobozzz/PeerTube.git
synced 2025-02-20 11:48:31 -06:00
Prefer video studio instead of video edition
Clearer and easier to find in the project
This commit is contained in:
parent
5e47f6ab98
commit
ab14f0e0dc
@ -1,7 +1,7 @@
|
|||||||
import { logger } from 'packages/peertube-runner/shared/logger'
|
import { logger } from 'packages/peertube-runner/shared/logger'
|
||||||
import {
|
import {
|
||||||
RunnerJobLiveRTMPHLSTranscodingPayload,
|
RunnerJobLiveRTMPHLSTranscodingPayload,
|
||||||
RunnerJobVideoEditionTranscodingPayload,
|
RunnerJobStudioTranscodingPayload,
|
||||||
RunnerJobVODAudioMergeTranscodingPayload,
|
RunnerJobVODAudioMergeTranscodingPayload,
|
||||||
RunnerJobVODHLSTranscodingPayload,
|
RunnerJobVODHLSTranscodingPayload,
|
||||||
RunnerJobVODWebVideoTranscodingPayload
|
RunnerJobVODWebVideoTranscodingPayload
|
||||||
@ -23,8 +23,8 @@ export async function processJob (options: ProcessOptions) {
|
|||||||
await processHLSTranscoding(options as ProcessOptions<RunnerJobVODHLSTranscodingPayload>)
|
await processHLSTranscoding(options as ProcessOptions<RunnerJobVODHLSTranscodingPayload>)
|
||||||
} else if (job.type === 'live-rtmp-hls-transcoding') {
|
} else if (job.type === 'live-rtmp-hls-transcoding') {
|
||||||
await new ProcessLiveRTMPHLSTranscoding(options as ProcessOptions<RunnerJobLiveRTMPHLSTranscodingPayload>).process()
|
await new ProcessLiveRTMPHLSTranscoding(options as ProcessOptions<RunnerJobLiveRTMPHLSTranscodingPayload>).process()
|
||||||
} else if (job.type === 'video-edition-transcoding') {
|
} else if (job.type === 'video-studio-transcoding') {
|
||||||
await processStudioTranscoding(options as ProcessOptions<RunnerJobVideoEditionTranscodingPayload>)
|
await processStudioTranscoding(options as ProcessOptions<RunnerJobStudioTranscodingPayload>)
|
||||||
} else {
|
} else {
|
||||||
logger.error(`Unknown job ${job.type} to process`)
|
logger.error(`Unknown job ${job.type} to process`)
|
||||||
return
|
return
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
|
import { remove } from 'fs-extra'
|
||||||
import { throttle } from 'lodash'
|
import { throttle } from 'lodash'
|
||||||
import { ConfigManager, downloadFile, logger } from 'packages/peertube-runner/shared'
|
import { ConfigManager, downloadFile, logger } from 'packages/peertube-runner/shared'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import { buildUUID } from '@shared/extra-utils'
|
import { buildUUID } from '@shared/extra-utils'
|
||||||
import { FFmpegEdition, FFmpegLive, FFmpegVOD } from '@shared/ffmpeg'
|
import { FFmpegEdition, FFmpegLive, FFmpegVOD, getDefaultAvailableEncoders, getDefaultEncodersToTry } from '@shared/ffmpeg'
|
||||||
import { RunnerJob, RunnerJobPayload } from '@shared/models'
|
import { RunnerJob, RunnerJobPayload } from '@shared/models'
|
||||||
import { PeerTubeServer } from '@shared/server-commands'
|
import { PeerTubeServer } from '@shared/server-commands'
|
||||||
import { getTranscodingLogger } from './transcoding-logger'
|
import { getTranscodingLogger } from './transcoding-logger'
|
||||||
import { getAvailableEncoders, getEncodersToTry } from './transcoding-profiles'
|
|
||||||
import { remove } from 'fs-extra'
|
|
||||||
|
|
||||||
export type JobWithToken <T extends RunnerJobPayload = RunnerJobPayload> = RunnerJob<T> & { jobToken: string }
|
export type JobWithToken <T extends RunnerJobPayload = RunnerJobPayload> = RunnerJob<T> & { jobToken: string }
|
||||||
|
|
||||||
@ -92,8 +91,8 @@ function getCommonFFmpegOptions () {
|
|||||||
tmpDirectory: ConfigManager.Instance.getTranscodingDirectory(),
|
tmpDirectory: ConfigManager.Instance.getTranscodingDirectory(),
|
||||||
profile: 'default',
|
profile: 'default',
|
||||||
availableEncoders: {
|
availableEncoders: {
|
||||||
available: getAvailableEncoders(),
|
available: getDefaultAvailableEncoders(),
|
||||||
encodersToTry: getEncodersToTry()
|
encodersToTry: getDefaultEncodersToTry()
|
||||||
},
|
},
|
||||||
logger: getTranscodingLogger()
|
logger: getTranscodingLogger()
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
export * from './common'
|
export * from './common'
|
||||||
export * from './process-vod'
|
export * from './process-vod'
|
||||||
export * from './transcoding-logger'
|
export * from './transcoding-logger'
|
||||||
export * from './transcoding-profiles'
|
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { remove } from 'fs-extra'
|
import { remove } from 'fs-extra'
|
||||||
import { pick } from 'lodash'
|
import { pick } from 'lodash'
|
||||||
import { logger } from 'packages/peertube-runner/shared'
|
import { logger } from 'packages/peertube-runner/shared'
|
||||||
import { extname, join } from 'path'
|
import { join } from 'path'
|
||||||
import { buildUUID } from '@shared/extra-utils'
|
import { buildUUID } from '@shared/extra-utils'
|
||||||
import {
|
import {
|
||||||
RunnerJobVideoEditionTranscodingPayload,
|
RunnerJobStudioTranscodingPayload,
|
||||||
VideoEditionTranscodingSuccess,
|
VideoStudioTranscodingSuccess,
|
||||||
VideoStudioTask,
|
VideoStudioTask,
|
||||||
VideoStudioTaskCutPayload,
|
VideoStudioTaskCutPayload,
|
||||||
VideoStudioTaskIntroPayload,
|
VideoStudioTaskIntroPayload,
|
||||||
@ -16,7 +16,7 @@ import {
|
|||||||
import { ConfigManager } from '../../../shared/config-manager'
|
import { ConfigManager } from '../../../shared/config-manager'
|
||||||
import { buildFFmpegEdition, downloadInputFile, JobWithToken, ProcessOptions } from './common'
|
import { buildFFmpegEdition, downloadInputFile, JobWithToken, ProcessOptions } from './common'
|
||||||
|
|
||||||
export async function processStudioTranscoding (options: ProcessOptions<RunnerJobVideoEditionTranscodingPayload>) {
|
export async function processStudioTranscoding (options: ProcessOptions<RunnerJobStudioTranscodingPayload>) {
|
||||||
const { server, job, runnerToken } = options
|
const { server, job, runnerToken } = options
|
||||||
const payload = job.payload
|
const payload = job.payload
|
||||||
|
|
||||||
@ -43,7 +43,7 @@ export async function processStudioTranscoding (options: ProcessOptions<RunnerJo
|
|||||||
tmpInputFilePath = outputPath
|
tmpInputFilePath = outputPath
|
||||||
}
|
}
|
||||||
|
|
||||||
const successBody: VideoEditionTranscodingSuccess = {
|
const successBody: VideoStudioTranscodingSuccess = {
|
||||||
videoFile: outputPath
|
videoFile: outputPath
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,14 +94,18 @@ async function processAddIntroOutro (options: TaskProcessorOptions<VideoStudioTa
|
|||||||
|
|
||||||
const introOutroPath = await downloadInputFile({ url: task.options.file, runnerToken, job })
|
const introOutroPath = await downloadInputFile({ url: task.options.file, runnerToken, job })
|
||||||
|
|
||||||
return buildFFmpegEdition().addIntroOutro({
|
try {
|
||||||
...pick(options, [ 'inputPath', 'outputPath' ]),
|
await buildFFmpegEdition().addIntroOutro({
|
||||||
|
...pick(options, [ 'inputPath', 'outputPath' ]),
|
||||||
|
|
||||||
introOutroPath,
|
introOutroPath,
|
||||||
type: task.name === 'add-intro'
|
type: task.name === 'add-intro'
|
||||||
? 'intro'
|
? 'intro'
|
||||||
: 'outro'
|
: 'outro'
|
||||||
})
|
})
|
||||||
|
} finally {
|
||||||
|
await remove(introOutroPath)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function processCut (options: TaskProcessorOptions<VideoStudioTaskCutPayload>) {
|
function processCut (options: TaskProcessorOptions<VideoStudioTaskCutPayload>) {
|
||||||
@ -124,15 +128,19 @@ async function processAddWatermark (options: TaskProcessorOptions<VideoStudioTas
|
|||||||
|
|
||||||
const watermarkPath = await downloadInputFile({ url: task.options.file, runnerToken, job })
|
const watermarkPath = await downloadInputFile({ url: task.options.file, runnerToken, job })
|
||||||
|
|
||||||
return buildFFmpegEdition().addWatermark({
|
try {
|
||||||
...pick(options, [ 'inputPath', 'outputPath' ]),
|
await buildFFmpegEdition().addWatermark({
|
||||||
|
...pick(options, [ 'inputPath', 'outputPath' ]),
|
||||||
|
|
||||||
watermarkPath,
|
watermarkPath,
|
||||||
|
|
||||||
videoFilters: {
|
videoFilters: {
|
||||||
watermarkSizeRatio: task.options.watermarkSizeRatio,
|
watermarkSizeRatio: task.options.watermarkSizeRatio,
|
||||||
horitonzalMarginRatio: task.options.horitonzalMarginRatio,
|
horitonzalMarginRatio: task.options.horitonzalMarginRatio,
|
||||||
verticalMarginRatio: task.options.verticalMarginRatio
|
verticalMarginRatio: task.options.verticalMarginRatio
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
} finally {
|
||||||
|
await remove(watermarkPath)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,31 +22,34 @@ export async function processWebVideoTranscoding (options: ProcessOptions<Runner
|
|||||||
|
|
||||||
const outputPath = join(ConfigManager.Instance.getTranscodingDirectory(), `output-${buildUUID()}.mp4`)
|
const outputPath = join(ConfigManager.Instance.getTranscodingDirectory(), `output-${buildUUID()}.mp4`)
|
||||||
|
|
||||||
await ffmpegVod.transcode({
|
try {
|
||||||
type: 'video',
|
await ffmpegVod.transcode({
|
||||||
|
type: 'video',
|
||||||
|
|
||||||
inputPath,
|
inputPath,
|
||||||
|
|
||||||
outputPath,
|
outputPath,
|
||||||
|
|
||||||
inputFileMutexReleaser: () => {},
|
inputFileMutexReleaser: () => {},
|
||||||
|
|
||||||
resolution: payload.output.resolution,
|
resolution: payload.output.resolution,
|
||||||
fps: payload.output.fps
|
fps: payload.output.fps
|
||||||
})
|
})
|
||||||
|
|
||||||
const successBody: VODWebVideoTranscodingSuccess = {
|
const successBody: VODWebVideoTranscodingSuccess = {
|
||||||
videoFile: outputPath
|
videoFile: outputPath
|
||||||
|
}
|
||||||
|
|
||||||
|
await server.runnerJobs.success({
|
||||||
|
jobToken: job.jobToken,
|
||||||
|
jobUUID: job.uuid,
|
||||||
|
runnerToken,
|
||||||
|
payload: successBody
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
await remove(inputPath)
|
||||||
|
await remove(outputPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
await server.runnerJobs.success({
|
|
||||||
jobToken: job.jobToken,
|
|
||||||
jobUUID: job.uuid,
|
|
||||||
runnerToken,
|
|
||||||
payload: successBody
|
|
||||||
})
|
|
||||||
|
|
||||||
await remove(outputPath)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function processHLSTranscoding (options: ProcessOptions<RunnerJobVODHLSTranscodingPayload>) {
|
export async function processHLSTranscoding (options: ProcessOptions<RunnerJobVODHLSTranscodingPayload>) {
|
||||||
@ -105,30 +108,34 @@ export async function processAudioMergeTranscoding (options: ProcessOptions<Runn
|
|||||||
|
|
||||||
const ffmpegVod = buildFFmpegVOD({ job, server, runnerToken })
|
const ffmpegVod = buildFFmpegVOD({ job, server, runnerToken })
|
||||||
|
|
||||||
await ffmpegVod.transcode({
|
try {
|
||||||
type: 'merge-audio',
|
await ffmpegVod.transcode({
|
||||||
|
type: 'merge-audio',
|
||||||
|
|
||||||
audioPath,
|
audioPath,
|
||||||
inputPath,
|
inputPath,
|
||||||
|
|
||||||
outputPath,
|
outputPath,
|
||||||
|
|
||||||
inputFileMutexReleaser: () => {},
|
inputFileMutexReleaser: () => {},
|
||||||
|
|
||||||
resolution: payload.output.resolution,
|
resolution: payload.output.resolution,
|
||||||
fps: payload.output.fps
|
fps: payload.output.fps
|
||||||
})
|
})
|
||||||
|
|
||||||
const successBody: VODAudioMergeTranscodingSuccess = {
|
const successBody: VODAudioMergeTranscodingSuccess = {
|
||||||
videoFile: outputPath
|
videoFile: outputPath
|
||||||
|
}
|
||||||
|
|
||||||
|
await server.runnerJobs.success({
|
||||||
|
jobToken: job.jobToken,
|
||||||
|
jobUUID: job.uuid,
|
||||||
|
runnerToken,
|
||||||
|
payload: successBody
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
await remove(audioPath)
|
||||||
|
await remove(inputPath)
|
||||||
|
await remove(outputPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
await server.runnerJobs.success({
|
|
||||||
jobToken: job.jobToken,
|
|
||||||
jobUUID: job.uuid,
|
|
||||||
runnerToken,
|
|
||||||
payload: successBody
|
|
||||||
})
|
|
||||||
|
|
||||||
await remove(outputPath)
|
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ import {
|
|||||||
RunnerJobLiveRTMPHLSTranscodingPayload,
|
RunnerJobLiveRTMPHLSTranscodingPayload,
|
||||||
RunnerJobPayload,
|
RunnerJobPayload,
|
||||||
RunnerJobType,
|
RunnerJobType,
|
||||||
RunnerJobVideoEditionTranscodingPayload,
|
RunnerJobStudioTranscodingPayload,
|
||||||
RunnerJobVODAudioMergeTranscodingPayload,
|
RunnerJobVODAudioMergeTranscodingPayload,
|
||||||
RunnerJobVODHLSTranscodingPayload,
|
RunnerJobVODHLSTranscodingPayload,
|
||||||
RunnerJobVODWebVideoTranscodingPayload,
|
RunnerJobVODWebVideoTranscodingPayload,
|
||||||
@ -22,7 +22,7 @@ const supportedMatrix = {
|
|||||||
'live-rtmp-hls-transcoding': (_payload: RunnerJobLiveRTMPHLSTranscodingPayload) => {
|
'live-rtmp-hls-transcoding': (_payload: RunnerJobLiveRTMPHLSTranscodingPayload) => {
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
'video-edition-transcoding': (payload: RunnerJobVideoEditionTranscodingPayload) => {
|
'video-studio-transcoding': (payload: RunnerJobStudioTranscodingPayload) => {
|
||||||
const tasks = payload?.tasks
|
const tasks = payload?.tasks
|
||||||
const supported = new Set<VideoStudioTaskPayload['name']>([ 'add-intro', 'add-outro', 'add-watermark', 'cut' ])
|
const supported = new Set<VideoStudioTaskPayload['name']>([ 'add-intro', 'add-outro', 'add-watermark', 'cut' ])
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ runnerJobFilesRouter.post('/jobs/:jobUUID/files/videos/:videoId/studio/task-file
|
|||||||
asyncMiddleware(jobOfRunnerGetValidator),
|
asyncMiddleware(jobOfRunnerGetValidator),
|
||||||
asyncMiddleware(runnerJobGetVideoTranscodingFileValidator),
|
asyncMiddleware(runnerJobGetVideoTranscodingFileValidator),
|
||||||
runnerJobGetVideoStudioTaskFileValidator,
|
runnerJobGetVideoStudioTaskFileValidator,
|
||||||
getVideoEditionTaskFile
|
getVideoStudioTaskFile
|
||||||
)
|
)
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@ -94,14 +94,14 @@ function getMaxQualityVideoPreview (req: express.Request, res: express.Response)
|
|||||||
return res.sendFile(file.getPath())
|
return res.sendFile(file.getPath())
|
||||||
}
|
}
|
||||||
|
|
||||||
function getVideoEditionTaskFile (req: express.Request, res: express.Response) {
|
function getVideoStudioTaskFile (req: express.Request, res: express.Response) {
|
||||||
const runnerJob = res.locals.runnerJob
|
const runnerJob = res.locals.runnerJob
|
||||||
const runner = runnerJob.Runner
|
const runner = runnerJob.Runner
|
||||||
const video = res.locals.videoAll
|
const video = res.locals.videoAll
|
||||||
const filename = req.params.filename
|
const filename = req.params.filename
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
'Get video edition task file %s of video %s of job %s for runner %s', filename, video.uuid, runnerJob.uuid, runner.name,
|
'Get video studio task file %s of video %s of job %s for runner %s', filename, video.uuid, runnerJob.uuid, runner.name,
|
||||||
lTags(runner.name, runnerJob.id, runnerJob.type)
|
lTags(runner.name, runnerJob.id, runnerJob.type)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ import {
|
|||||||
RunnerJobUpdateBody,
|
RunnerJobUpdateBody,
|
||||||
RunnerJobUpdatePayload,
|
RunnerJobUpdatePayload,
|
||||||
UserRight,
|
UserRight,
|
||||||
VideoEditionTranscodingSuccess,
|
VideoStudioTranscodingSuccess,
|
||||||
VODAudioMergeTranscodingSuccess,
|
VODAudioMergeTranscodingSuccess,
|
||||||
VODHLSTranscodingSuccess,
|
VODHLSTranscodingSuccess,
|
||||||
VODWebVideoTranscodingSuccess
|
VODWebVideoTranscodingSuccess
|
||||||
@ -300,7 +300,7 @@ const jobSuccessPayloadBuilders: {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
'video-edition-transcoding': (payload: VideoEditionTranscodingSuccess, files) => {
|
'video-studio-transcoding': (payload: VideoStudioTranscodingSuccess, files) => {
|
||||||
return {
|
return {
|
||||||
...payload,
|
...payload,
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ import {
|
|||||||
RunnerJobSuccessPayload,
|
RunnerJobSuccessPayload,
|
||||||
RunnerJobType,
|
RunnerJobType,
|
||||||
RunnerJobUpdatePayload,
|
RunnerJobUpdatePayload,
|
||||||
VideoEditionTranscodingSuccess,
|
VideoStudioTranscodingSuccess,
|
||||||
VODAudioMergeTranscodingSuccess,
|
VODAudioMergeTranscodingSuccess,
|
||||||
VODHLSTranscodingSuccess,
|
VODHLSTranscodingSuccess,
|
||||||
VODWebVideoTranscodingSuccess
|
VODWebVideoTranscodingSuccess
|
||||||
@ -25,7 +25,7 @@ function isRunnerJobSuccessPayloadValid (value: RunnerJobSuccessPayload, type: R
|
|||||||
isRunnerJobVODHLSResultPayloadValid(value as VODHLSTranscodingSuccess, type, files) ||
|
isRunnerJobVODHLSResultPayloadValid(value as VODHLSTranscodingSuccess, type, files) ||
|
||||||
isRunnerJobVODAudioMergeResultPayloadValid(value as VODHLSTranscodingSuccess, type, files) ||
|
isRunnerJobVODAudioMergeResultPayloadValid(value as VODHLSTranscodingSuccess, type, files) ||
|
||||||
isRunnerJobLiveRTMPHLSResultPayloadValid(value as LiveRTMPHLSTranscodingSuccess, type) ||
|
isRunnerJobLiveRTMPHLSResultPayloadValid(value as LiveRTMPHLSTranscodingSuccess, type) ||
|
||||||
isRunnerJobVideoEditionResultPayloadValid(value as VideoEditionTranscodingSuccess, type, files)
|
isRunnerJobVideoStudioResultPayloadValid(value as VideoStudioTranscodingSuccess, type, files)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@ -37,7 +37,7 @@ function isRunnerJobProgressValid (value: string) {
|
|||||||
function isRunnerJobUpdatePayloadValid (value: RunnerJobUpdatePayload, type: RunnerJobType, files: UploadFilesForCheck) {
|
function isRunnerJobUpdatePayloadValid (value: RunnerJobUpdatePayload, type: RunnerJobType, files: UploadFilesForCheck) {
|
||||||
return isRunnerJobVODWebVideoUpdatePayloadValid(value, type, files) ||
|
return isRunnerJobVODWebVideoUpdatePayloadValid(value, type, files) ||
|
||||||
isRunnerJobVODHLSUpdatePayloadValid(value, type, files) ||
|
isRunnerJobVODHLSUpdatePayloadValid(value, type, files) ||
|
||||||
isRunnerJobVideoEditionUpdatePayloadValid(value, type, files) ||
|
isRunnerJobVideoStudioUpdatePayloadValid(value, type, files) ||
|
||||||
isRunnerJobVODAudioMergeUpdatePayloadValid(value, type, files) ||
|
isRunnerJobVODAudioMergeUpdatePayloadValid(value, type, files) ||
|
||||||
isRunnerJobLiveRTMPHLSUpdatePayloadValid(value, type, files)
|
isRunnerJobLiveRTMPHLSUpdatePayloadValid(value, type, files)
|
||||||
}
|
}
|
||||||
@ -105,12 +105,12 @@ function isRunnerJobLiveRTMPHLSResultPayloadValid (
|
|||||||
return type === 'live-rtmp-hls-transcoding' && (!value || (typeof value === 'object' && Object.keys(value).length === 0))
|
return type === 'live-rtmp-hls-transcoding' && (!value || (typeof value === 'object' && Object.keys(value).length === 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
function isRunnerJobVideoEditionResultPayloadValid (
|
function isRunnerJobVideoStudioResultPayloadValid (
|
||||||
_value: VideoEditionTranscodingSuccess,
|
_value: VideoStudioTranscodingSuccess,
|
||||||
type: RunnerJobType,
|
type: RunnerJobType,
|
||||||
files: UploadFilesForCheck
|
files: UploadFilesForCheck
|
||||||
) {
|
) {
|
||||||
return type === 'video-edition-transcoding' &&
|
return type === 'video-studio-transcoding' &&
|
||||||
isFileValid({ files, field: 'payload[videoFile]', mimeTypeRegex: null, maxSize: null })
|
isFileValid({ files, field: 'payload[videoFile]', mimeTypeRegex: null, maxSize: null })
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,11 +177,11 @@ function isRunnerJobLiveRTMPHLSUpdatePayloadValid (
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function isRunnerJobVideoEditionUpdatePayloadValid (
|
function isRunnerJobVideoStudioUpdatePayloadValid (
|
||||||
value: RunnerJobUpdatePayload,
|
value: RunnerJobUpdatePayload,
|
||||||
type: RunnerJobType,
|
type: RunnerJobType,
|
||||||
_files: UploadFilesForCheck
|
_files: UploadFilesForCheck
|
||||||
) {
|
) {
|
||||||
return type === 'video-edition-transcoding' &&
|
return type === 'video-studio-transcoding' &&
|
||||||
(!value || (typeof value === 'object' && Object.keys(value).length === 0))
|
(!value || (typeof value === 'object' && Object.keys(value).length === 0))
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ import { CONFIG } from '@server/initializers/config'
|
|||||||
import { VideoTranscodingProfilesManager } from '@server/lib/transcoding/default-transcoding-profiles'
|
import { VideoTranscodingProfilesManager } from '@server/lib/transcoding/default-transcoding-profiles'
|
||||||
import { isAbleToUploadVideo } from '@server/lib/user'
|
import { isAbleToUploadVideo } from '@server/lib/user'
|
||||||
import { VideoPathManager } from '@server/lib/video-path-manager'
|
import { VideoPathManager } from '@server/lib/video-path-manager'
|
||||||
import { approximateIntroOutroAdditionalSize, onVideoEditionEnded, safeCleanupStudioTMPFiles } from '@server/lib/video-studio'
|
import { approximateIntroOutroAdditionalSize, onVideoStudioEnded, safeCleanupStudioTMPFiles } from '@server/lib/video-studio'
|
||||||
import { UserModel } from '@server/models/user/user'
|
import { UserModel } from '@server/models/user/user'
|
||||||
import { VideoModel } from '@server/models/video/video'
|
import { VideoModel } from '@server/models/video/video'
|
||||||
import { MVideo, MVideoFullLight } from '@server/types/models'
|
import { MVideo, MVideoFullLight } from '@server/types/models'
|
||||||
@ -24,7 +24,7 @@ import {
|
|||||||
} from '@shared/models'
|
} from '@shared/models'
|
||||||
import { logger, loggerTagsFactory } from '../../../helpers/logger'
|
import { logger, loggerTagsFactory } from '../../../helpers/logger'
|
||||||
|
|
||||||
const lTagsBase = loggerTagsFactory('video-edition')
|
const lTagsBase = loggerTagsFactory('video-studio')
|
||||||
|
|
||||||
async function processVideoStudioEdition (job: Job) {
|
async function processVideoStudioEdition (job: Job) {
|
||||||
const payload = job.data as VideoStudioEditionPayload
|
const payload = job.data as VideoStudioEditionPayload
|
||||||
@ -74,7 +74,7 @@ async function processVideoStudioEdition (job: Job) {
|
|||||||
|
|
||||||
logger.info('Video edition ended for video %s.', video.uuid, lTags)
|
logger.info('Video edition ended for video %s.', video.uuid, lTags)
|
||||||
|
|
||||||
await onVideoEditionEnded({ video, editionResultPath, tasks: payload.tasks })
|
await onVideoStudioEnded({ video, editionResultPath, tasks: payload.tasks })
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
await safeCleanupStudioTMPFiles(payload.tasks)
|
await safeCleanupStudioTMPFiles(payload.tasks)
|
||||||
|
|
||||||
|
@ -15,8 +15,8 @@ import {
|
|||||||
RunnerJobSuccessPayload,
|
RunnerJobSuccessPayload,
|
||||||
RunnerJobType,
|
RunnerJobType,
|
||||||
RunnerJobUpdatePayload,
|
RunnerJobUpdatePayload,
|
||||||
RunnerJobVideoEditionTranscodingPayload,
|
RunnerJobStudioTranscodingPayload,
|
||||||
RunnerJobVideoEditionTranscodingPrivatePayload,
|
RunnerJobVideoStudioTranscodingPrivatePayload,
|
||||||
RunnerJobVODAudioMergeTranscodingPayload,
|
RunnerJobVODAudioMergeTranscodingPayload,
|
||||||
RunnerJobVODAudioMergeTranscodingPrivatePayload,
|
RunnerJobVODAudioMergeTranscodingPrivatePayload,
|
||||||
RunnerJobVODHLSTranscodingPayload,
|
RunnerJobVODHLSTranscodingPayload,
|
||||||
@ -47,9 +47,9 @@ type CreateRunnerJobArg =
|
|||||||
privatePayload: RunnerJobLiveRTMPHLSTranscodingPrivatePayload
|
privatePayload: RunnerJobLiveRTMPHLSTranscodingPrivatePayload
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
type: Extract<RunnerJobType, 'video-edition-transcoding'>
|
type: Extract<RunnerJobType, 'video-studio-transcoding'>
|
||||||
payload: RunnerJobVideoEditionTranscodingPayload
|
payload: RunnerJobStudioTranscodingPayload
|
||||||
privatePayload: RunnerJobVideoEditionTranscodingPrivatePayload
|
privatePayload: RunnerJobVideoStudioTranscodingPrivatePayload
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class AbstractJobHandler <C, U extends RunnerJobUpdatePayload, S extends RunnerJobSuccessPayload> {
|
export abstract class AbstractJobHandler <C, U extends RunnerJobUpdatePayload, S extends RunnerJobSuccessPayload> {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
export * from './abstract-job-handler'
|
export * from './abstract-job-handler'
|
||||||
export * from './live-rtmp-hls-transcoding-job-handler'
|
export * from './live-rtmp-hls-transcoding-job-handler'
|
||||||
export * from './runner-job-handlers'
|
export * from './runner-job-handlers'
|
||||||
export * from './video-edition-transcoding-job-handler'
|
export * from './video-studio-transcoding-job-handler'
|
||||||
export * from './vod-audio-merge-transcoding-job-handler'
|
export * from './vod-audio-merge-transcoding-job-handler'
|
||||||
export * from './vod-hls-transcoding-job-handler'
|
export * from './vod-hls-transcoding-job-handler'
|
||||||
export * from './vod-web-video-transcoding-job-handler'
|
export * from './vod-web-video-transcoding-job-handler'
|
||||||
|
@ -2,7 +2,7 @@ import { MRunnerJob } from '@server/types/models/runners'
|
|||||||
import { RunnerJobSuccessPayload, RunnerJobType, RunnerJobUpdatePayload } from '@shared/models'
|
import { RunnerJobSuccessPayload, RunnerJobType, RunnerJobUpdatePayload } from '@shared/models'
|
||||||
import { AbstractJobHandler } from './abstract-job-handler'
|
import { AbstractJobHandler } from './abstract-job-handler'
|
||||||
import { LiveRTMPHLSTranscodingJobHandler } from './live-rtmp-hls-transcoding-job-handler'
|
import { LiveRTMPHLSTranscodingJobHandler } from './live-rtmp-hls-transcoding-job-handler'
|
||||||
import { VideoEditionTranscodingJobHandler } from './video-edition-transcoding-job-handler'
|
import { VideoStudioTranscodingJobHandler } from './video-studio-transcoding-job-handler'
|
||||||
import { VODAudioMergeTranscodingJobHandler } from './vod-audio-merge-transcoding-job-handler'
|
import { VODAudioMergeTranscodingJobHandler } from './vod-audio-merge-transcoding-job-handler'
|
||||||
import { VODHLSTranscodingJobHandler } from './vod-hls-transcoding-job-handler'
|
import { VODHLSTranscodingJobHandler } from './vod-hls-transcoding-job-handler'
|
||||||
import { VODWebVideoTranscodingJobHandler } from './vod-web-video-transcoding-job-handler'
|
import { VODWebVideoTranscodingJobHandler } from './vod-web-video-transcoding-job-handler'
|
||||||
@ -12,7 +12,7 @@ const processors: Record<RunnerJobType, new() => AbstractJobHandler<unknown, Run
|
|||||||
'vod-hls-transcoding': VODHLSTranscodingJobHandler,
|
'vod-hls-transcoding': VODHLSTranscodingJobHandler,
|
||||||
'vod-audio-merge-transcoding': VODAudioMergeTranscodingJobHandler,
|
'vod-audio-merge-transcoding': VODAudioMergeTranscodingJobHandler,
|
||||||
'live-rtmp-hls-transcoding': LiveRTMPHLSTranscodingJobHandler,
|
'live-rtmp-hls-transcoding': LiveRTMPHLSTranscodingJobHandler,
|
||||||
'video-edition-transcoding': VideoEditionTranscodingJobHandler
|
'video-studio-transcoding': VideoStudioTranscodingJobHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getRunnerJobHandlerClass (job: MRunnerJob) {
|
export function getRunnerJobHandlerClass (job: MRunnerJob) {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
import { basename } from 'path'
|
import { basename } from 'path'
|
||||||
import { logger } from '@server/helpers/logger'
|
import { logger } from '@server/helpers/logger'
|
||||||
import { onVideoEditionEnded, safeCleanupStudioTMPFiles } from '@server/lib/video-studio'
|
import { onVideoStudioEnded, safeCleanupStudioTMPFiles } from '@server/lib/video-studio'
|
||||||
import { MVideo } from '@server/types/models'
|
import { MVideo } from '@server/types/models'
|
||||||
import { MRunnerJob } from '@server/types/models/runners'
|
import { MRunnerJob } from '@server/types/models/runners'
|
||||||
import { buildUUID } from '@shared/extra-utils'
|
import { buildUUID } from '@shared/extra-utils'
|
||||||
@ -11,9 +11,9 @@ import {
|
|||||||
isVideoStudioTaskWatermark,
|
isVideoStudioTaskWatermark,
|
||||||
RunnerJobState,
|
RunnerJobState,
|
||||||
RunnerJobUpdatePayload,
|
RunnerJobUpdatePayload,
|
||||||
RunnerJobVideoEditionTranscodingPayload,
|
RunnerJobStudioTranscodingPayload,
|
||||||
RunnerJobVideoEditionTranscodingPrivatePayload,
|
RunnerJobVideoStudioTranscodingPrivatePayload,
|
||||||
VideoEditionTranscodingSuccess,
|
VideoStudioTranscodingSuccess,
|
||||||
VideoState,
|
VideoState,
|
||||||
VideoStudioTaskPayload
|
VideoStudioTaskPayload
|
||||||
} from '@shared/models'
|
} from '@shared/models'
|
||||||
@ -28,13 +28,13 @@ type CreateOptions = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line max-len
|
// eslint-disable-next-line max-len
|
||||||
export class VideoEditionTranscodingJobHandler extends AbstractJobHandler<CreateOptions, RunnerJobUpdatePayload, VideoEditionTranscodingSuccess> {
|
export class VideoStudioTranscodingJobHandler extends AbstractJobHandler<CreateOptions, RunnerJobUpdatePayload, VideoStudioTranscodingSuccess> {
|
||||||
|
|
||||||
async create (options: CreateOptions) {
|
async create (options: CreateOptions) {
|
||||||
const { video, priority, tasks } = options
|
const { video, priority, tasks } = options
|
||||||
|
|
||||||
const jobUUID = buildUUID()
|
const jobUUID = buildUUID()
|
||||||
const payload: RunnerJobVideoEditionTranscodingPayload = {
|
const payload: RunnerJobStudioTranscodingPayload = {
|
||||||
input: {
|
input: {
|
||||||
videoFileUrl: generateRunnerTranscodingVideoInputFileUrl(jobUUID, video.uuid)
|
videoFileUrl: generateRunnerTranscodingVideoInputFileUrl(jobUUID, video.uuid)
|
||||||
},
|
},
|
||||||
@ -67,13 +67,13 @@ export class VideoEditionTranscodingJobHandler extends AbstractJobHandler<Create
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const privatePayload: RunnerJobVideoEditionTranscodingPrivatePayload = {
|
const privatePayload: RunnerJobVideoStudioTranscodingPrivatePayload = {
|
||||||
videoUUID: video.uuid,
|
videoUUID: video.uuid,
|
||||||
originalTasks: tasks
|
originalTasks: tasks
|
||||||
}
|
}
|
||||||
|
|
||||||
const job = await this.createRunnerJob({
|
const job = await this.createRunnerJob({
|
||||||
type: 'video-edition-transcoding',
|
type: 'video-studio-transcoding',
|
||||||
jobUUID,
|
jobUUID,
|
||||||
payload,
|
payload,
|
||||||
privatePayload,
|
privatePayload,
|
||||||
@ -103,10 +103,10 @@ export class VideoEditionTranscodingJobHandler extends AbstractJobHandler<Create
|
|||||||
|
|
||||||
protected async specificComplete (options: {
|
protected async specificComplete (options: {
|
||||||
runnerJob: MRunnerJob
|
runnerJob: MRunnerJob
|
||||||
resultPayload: VideoEditionTranscodingSuccess
|
resultPayload: VideoStudioTranscodingSuccess
|
||||||
}) {
|
}) {
|
||||||
const { runnerJob, resultPayload } = options
|
const { runnerJob, resultPayload } = options
|
||||||
const privatePayload = runnerJob.privatePayload as RunnerJobVideoEditionTranscodingPrivatePayload
|
const privatePayload = runnerJob.privatePayload as RunnerJobVideoStudioTranscodingPrivatePayload
|
||||||
|
|
||||||
const video = await loadTranscodingRunnerVideo(runnerJob, this.lTags)
|
const video = await loadTranscodingRunnerVideo(runnerJob, this.lTags)
|
||||||
if (!video) {
|
if (!video) {
|
||||||
@ -116,7 +116,7 @@ export class VideoEditionTranscodingJobHandler extends AbstractJobHandler<Create
|
|||||||
|
|
||||||
const videoFilePath = resultPayload.videoFile as string
|
const videoFilePath = resultPayload.videoFile as string
|
||||||
|
|
||||||
await onVideoEditionEnded({ video, editionResultPath: videoFilePath, tasks: privatePayload.originalTasks })
|
await onVideoStudioEnded({ video, editionResultPath: videoFilePath, tasks: privatePayload.originalTasks })
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
'Runner video edition transcoding job %s for %s ended.',
|
'Runner video edition transcoding job %s for %s ended.',
|
||||||
@ -146,7 +146,7 @@ export class VideoEditionTranscodingJobHandler extends AbstractJobHandler<Create
|
|||||||
}) {
|
}) {
|
||||||
const { runnerJob } = options
|
const { runnerJob } = options
|
||||||
|
|
||||||
const payload = runnerJob.privatePayload as RunnerJobVideoEditionTranscodingPrivatePayload
|
const payload = runnerJob.privatePayload as RunnerJobVideoStudioTranscodingPrivatePayload
|
||||||
await safeCleanupStudioTMPFiles(payload.originalTasks)
|
await safeCleanupStudioTMPFiles(payload.originalTasks)
|
||||||
|
|
||||||
const video = await loadTranscodingRunnerVideo(options.runnerJob, this.lTags)
|
const video = await loadTranscodingRunnerVideo(options.runnerJob, this.lTags)
|
@ -1,88 +1,7 @@
|
|||||||
|
|
||||||
import { logger } from '@server/helpers/logger'
|
import { logger } from '@server/helpers/logger'
|
||||||
import { getAverageBitrate, getMinLimitBitrate } from '@shared/core-utils'
|
import { FFmpegCommandWrapper, getDefaultAvailableEncoders } from '@shared/ffmpeg'
|
||||||
import { buildStreamSuffix, FFmpegCommandWrapper, ffprobePromise, getAudioStream, getMaxAudioBitrate } from '@shared/ffmpeg'
|
import { AvailableEncoders, EncoderOptionsBuilder } from '@shared/models'
|
||||||
import { AvailableEncoders, EncoderOptionsBuilder, EncoderOptionsBuilderParams, VideoResolution } from '@shared/models'
|
|
||||||
import { canDoQuickAudioTranscode } from './transcoding-quick-transcode'
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Available encoders and profiles for the transcoding jobs
|
|
||||||
* These functions are used by ffmpeg-utils that will get the encoders and options depending on the chosen profile
|
|
||||||
*
|
|
||||||
* Resources:
|
|
||||||
* * https://slhck.info/video/2017/03/01/rate-control.html
|
|
||||||
* * https://trac.ffmpeg.org/wiki/Limiting%20the%20output%20bitrate
|
|
||||||
*/
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
// Default builders
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
const defaultX264VODOptionsBuilder: EncoderOptionsBuilder = (options: EncoderOptionsBuilderParams) => {
|
|
||||||
const { fps, inputRatio, inputBitrate, resolution } = options
|
|
||||||
|
|
||||||
// TODO: remove in 4.2, fps is not optional anymore
|
|
||||||
if (!fps) return { outputOptions: [ ] }
|
|
||||||
|
|
||||||
const targetBitrate = getTargetBitrate({ inputBitrate, ratio: inputRatio, fps, resolution })
|
|
||||||
|
|
||||||
return {
|
|
||||||
outputOptions: [
|
|
||||||
...getCommonOutputOptions(targetBitrate),
|
|
||||||
|
|
||||||
`-r ${fps}`
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const defaultX264LiveOptionsBuilder: EncoderOptionsBuilder = (options: EncoderOptionsBuilderParams) => {
|
|
||||||
const { streamNum, fps, inputBitrate, inputRatio, resolution } = options
|
|
||||||
|
|
||||||
const targetBitrate = getTargetBitrate({ inputBitrate, ratio: inputRatio, fps, resolution })
|
|
||||||
|
|
||||||
return {
|
|
||||||
outputOptions: [
|
|
||||||
...getCommonOutputOptions(targetBitrate, streamNum),
|
|
||||||
|
|
||||||
`${buildStreamSuffix('-r:v', streamNum)} ${fps}`,
|
|
||||||
`${buildStreamSuffix('-b:v', streamNum)} ${targetBitrate}`
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const defaultAACOptionsBuilder: EncoderOptionsBuilder = async ({ input, streamNum, canCopyAudio }) => {
|
|
||||||
const probe = await ffprobePromise(input)
|
|
||||||
|
|
||||||
if (canCopyAudio && await canDoQuickAudioTranscode(input, probe)) {
|
|
||||||
logger.debug('Copy audio stream %s by AAC encoder.', input)
|
|
||||||
return { copy: true, outputOptions: [ ] }
|
|
||||||
}
|
|
||||||
|
|
||||||
const parsedAudio = await getAudioStream(input, probe)
|
|
||||||
|
|
||||||
// We try to reduce the ceiling bitrate by making rough matches of bitrates
|
|
||||||
// Of course this is far from perfect, but it might save some space in the end
|
|
||||||
|
|
||||||
const audioCodecName = parsedAudio.audioStream['codec_name']
|
|
||||||
|
|
||||||
const bitrate = getMaxAudioBitrate(audioCodecName, parsedAudio.bitrate)
|
|
||||||
|
|
||||||
logger.debug('Calculating audio bitrate of %s by AAC encoder.', input, { bitrate: parsedAudio.bitrate, audioCodecName })
|
|
||||||
|
|
||||||
// Force stereo as it causes some issues with HLS playback in Chrome
|
|
||||||
const base = [ '-channel_layout', 'stereo' ]
|
|
||||||
|
|
||||||
if (bitrate !== -1) {
|
|
||||||
return { outputOptions: base.concat([ buildStreamSuffix('-b:a', streamNum), bitrate + 'k' ]) }
|
|
||||||
}
|
|
||||||
|
|
||||||
return { outputOptions: base }
|
|
||||||
}
|
|
||||||
|
|
||||||
const defaultLibFDKAACVODOptionsBuilder: EncoderOptionsBuilder = ({ streamNum }) => {
|
|
||||||
return { outputOptions: [ buildStreamSuffix('-q:a', streamNum), '5' ] }
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Profile manager to get and change default profiles
|
// Profile manager to get and change default profiles
|
||||||
@ -97,27 +16,7 @@ class VideoTranscodingProfilesManager {
|
|||||||
live: this.buildDefaultEncodersPriorities()
|
live: this.buildDefaultEncodersPriorities()
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly availableEncoders = {
|
private readonly availableEncoders = getDefaultAvailableEncoders()
|
||||||
vod: {
|
|
||||||
libx264: {
|
|
||||||
default: defaultX264VODOptionsBuilder
|
|
||||||
},
|
|
||||||
aac: {
|
|
||||||
default: defaultAACOptionsBuilder
|
|
||||||
},
|
|
||||||
libfdk_aac: {
|
|
||||||
default: defaultLibFDKAACVODOptionsBuilder
|
|
||||||
}
|
|
||||||
},
|
|
||||||
live: {
|
|
||||||
libx264: {
|
|
||||||
default: defaultX264LiveOptionsBuilder
|
|
||||||
},
|
|
||||||
aac: {
|
|
||||||
default: defaultAACOptionsBuilder
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private availableProfiles = {
|
private availableProfiles = {
|
||||||
vod: [] as string[],
|
vod: [] as string[],
|
||||||
@ -242,41 +141,3 @@ class VideoTranscodingProfilesManager {
|
|||||||
export {
|
export {
|
||||||
VideoTranscodingProfilesManager
|
VideoTranscodingProfilesManager
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
function getTargetBitrate (options: {
|
|
||||||
inputBitrate: number
|
|
||||||
resolution: VideoResolution
|
|
||||||
ratio: number
|
|
||||||
fps: number
|
|
||||||
}) {
|
|
||||||
const { inputBitrate, resolution, ratio, fps } = options
|
|
||||||
|
|
||||||
const capped = capBitrate(inputBitrate, getAverageBitrate({ resolution, fps, ratio }))
|
|
||||||
const limit = getMinLimitBitrate({ resolution, fps, ratio })
|
|
||||||
|
|
||||||
return Math.max(limit, capped)
|
|
||||||
}
|
|
||||||
|
|
||||||
function capBitrate (inputBitrate: number, targetBitrate: number) {
|
|
||||||
if (!inputBitrate) return targetBitrate
|
|
||||||
|
|
||||||
// Add 30% margin to input bitrate
|
|
||||||
const inputBitrateWithMargin = inputBitrate + (inputBitrate * 0.3)
|
|
||||||
|
|
||||||
return Math.min(targetBitrate, inputBitrateWithMargin)
|
|
||||||
}
|
|
||||||
|
|
||||||
function getCommonOutputOptions (targetBitrate: number, streamNum?: number) {
|
|
||||||
return [
|
|
||||||
`-preset veryfast`,
|
|
||||||
`${buildStreamSuffix('-maxrate:v', streamNum)} ${targetBitrate}`,
|
|
||||||
`${buildStreamSuffix('-bufsize:v', streamNum)} ${targetBitrate * 2}`,
|
|
||||||
|
|
||||||
// NOTE: b-strategy 1 - heuristic algorithm, 16 is optimal B-frames for it
|
|
||||||
`-b_strategy 1`,
|
|
||||||
// NOTE: Why 16: https://github.com/Chocobozzz/PeerTube/pull/774. b-strategy 2 -> B-frames<16
|
|
||||||
`-bf 16`
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
@ -9,13 +9,13 @@ import { getVideoStreamDuration } from '@shared/ffmpeg'
|
|||||||
import { VideoStudioEditionPayload, VideoStudioTask, VideoStudioTaskPayload } from '@shared/models'
|
import { VideoStudioEditionPayload, VideoStudioTask, VideoStudioTaskPayload } from '@shared/models'
|
||||||
import { federateVideoIfNeeded } from './activitypub/videos'
|
import { federateVideoIfNeeded } from './activitypub/videos'
|
||||||
import { JobQueue } from './job-queue'
|
import { JobQueue } from './job-queue'
|
||||||
import { VideoEditionTranscodingJobHandler } from './runners'
|
import { VideoStudioTranscodingJobHandler } from './runners'
|
||||||
import { createOptimizeOrMergeAudioJobs } from './transcoding/create-transcoding-job'
|
import { createOptimizeOrMergeAudioJobs } from './transcoding/create-transcoding-job'
|
||||||
import { getTranscodingJobPriority } from './transcoding/transcoding-priority'
|
import { getTranscodingJobPriority } from './transcoding/transcoding-priority'
|
||||||
import { buildNewFile, removeHLSPlaylist, removeWebTorrentFile } from './video-file'
|
import { buildNewFile, removeHLSPlaylist, removeWebTorrentFile } from './video-file'
|
||||||
import { VideoPathManager } from './video-path-manager'
|
import { VideoPathManager } from './video-path-manager'
|
||||||
|
|
||||||
const lTags = loggerTagsFactory('video-edition')
|
const lTags = loggerTagsFactory('video-studio')
|
||||||
|
|
||||||
export function buildTaskFileFieldname (indice: number, fieldName = 'file') {
|
export function buildTaskFileFieldname (indice: number, fieldName = 'file') {
|
||||||
return `tasks[${indice}][options][${fieldName}]`
|
return `tasks[${indice}][options][${fieldName}]`
|
||||||
@ -78,14 +78,14 @@ export async function createVideoStudioJob (options: {
|
|||||||
const priority = await getTranscodingJobPriority({ user, type: 'studio', fallback: 0 })
|
const priority = await getTranscodingJobPriority({ user, type: 'studio', fallback: 0 })
|
||||||
|
|
||||||
if (CONFIG.VIDEO_STUDIO.REMOTE_RUNNERS.ENABLED) {
|
if (CONFIG.VIDEO_STUDIO.REMOTE_RUNNERS.ENABLED) {
|
||||||
await new VideoEditionTranscodingJobHandler().create({ video, tasks: payload.tasks, priority })
|
await new VideoStudioTranscodingJobHandler().create({ video, tasks: payload.tasks, priority })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
await JobQueue.Instance.createJob({ type: 'video-studio-edition', payload, priority })
|
await JobQueue.Instance.createJob({ type: 'video-studio-edition', payload, priority })
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function onVideoEditionEnded (options: {
|
export async function onVideoStudioEnded (options: {
|
||||||
editionResultPath: string
|
editionResultPath: string
|
||||||
tasks: VideoStudioTaskPayload[]
|
tasks: VideoStudioTaskPayload[]
|
||||||
video: MVideoFullLight
|
video: MVideoFullLight
|
||||||
|
@ -2,7 +2,7 @@ import express from 'express'
|
|||||||
import { param } from 'express-validator'
|
import { param } from 'express-validator'
|
||||||
import { basename } from 'path'
|
import { basename } from 'path'
|
||||||
import { isSafeFilename } from '@server/helpers/custom-validators/misc'
|
import { isSafeFilename } from '@server/helpers/custom-validators/misc'
|
||||||
import { hasVideoStudioTaskFile, HttpStatusCode, RunnerJobVideoEditionTranscodingPayload } from '@shared/models'
|
import { hasVideoStudioTaskFile, HttpStatusCode, RunnerJobStudioTranscodingPayload } from '@shared/models'
|
||||||
import { areValidationErrors, doesVideoExist, isValidVideoIdParam } from '../shared'
|
import { areValidationErrors, doesVideoExist, isValidVideoIdParam } from '../shared'
|
||||||
|
|
||||||
const tags = [ 'runner' ]
|
const tags = [ 'runner' ]
|
||||||
@ -37,7 +37,7 @@ export const runnerJobGetVideoStudioTaskFileValidator = [
|
|||||||
|
|
||||||
const filename = req.params.filename
|
const filename = req.params.filename
|
||||||
|
|
||||||
const payload = res.locals.runnerJob.payload as RunnerJobVideoEditionTranscodingPayload
|
const payload = res.locals.runnerJob.payload as RunnerJobStudioTranscodingPayload
|
||||||
|
|
||||||
const found = Array.isArray(payload?.tasks) && payload.tasks.some(t => {
|
const found = Array.isArray(payload?.tasks) && payload.tasks.some(t => {
|
||||||
if (hasVideoStudioTaskFile(t)) {
|
if (hasVideoStudioTaskFile(t)) {
|
||||||
|
@ -8,7 +8,7 @@ import {
|
|||||||
RunnerJobState,
|
RunnerJobState,
|
||||||
RunnerJobSuccessPayload,
|
RunnerJobSuccessPayload,
|
||||||
RunnerJobUpdatePayload,
|
RunnerJobUpdatePayload,
|
||||||
RunnerJobVideoEditionTranscodingPayload,
|
RunnerJobStudioTranscodingPayload,
|
||||||
VideoPrivacy,
|
VideoPrivacy,
|
||||||
VideoStudioTaskIntro
|
VideoStudioTaskIntro
|
||||||
} from '@shared/models'
|
} from '@shared/models'
|
||||||
@ -404,10 +404,10 @@ describe('Test managing runners', function () {
|
|||||||
tasks: VideoStudioCommand.getComplexTask()
|
tasks: VideoStudioCommand.getComplexTask()
|
||||||
})
|
})
|
||||||
|
|
||||||
const { job } = await server.runnerJobs.autoAccept({ runnerToken, type: 'video-edition-transcoding' })
|
const { job } = await server.runnerJobs.autoAccept({ runnerToken, type: 'video-studio-transcoding' })
|
||||||
studioAcceptedJob = job
|
studioAcceptedJob = job
|
||||||
|
|
||||||
const tasks = (job.payload as RunnerJobVideoEditionTranscodingPayload).tasks
|
const tasks = (job.payload as RunnerJobStudioTranscodingPayload).tasks
|
||||||
const fileUrl = (tasks.find(t => isVideoStudioTaskIntro(t)) as VideoStudioTaskIntro).options.file as string
|
const fileUrl = (tasks.find(t => isVideoStudioTaskIntro(t)) as VideoStudioTaskIntro).options.file as string
|
||||||
studioFile = basename(fileUrl)
|
studioFile = basename(fileUrl)
|
||||||
}
|
}
|
||||||
@ -787,7 +787,7 @@ describe('Test managing runners', function () {
|
|||||||
|
|
||||||
describe('Video studio', function () {
|
describe('Video studio', function () {
|
||||||
|
|
||||||
it('Should fail with an invalid video edition transcoding payload', async function () {
|
it('Should fail with an invalid video studio transcoding payload', async function () {
|
||||||
await server.runnerJobs.success({
|
await server.runnerJobs.success({
|
||||||
jobUUID: studioAcceptedJob.uuid,
|
jobUUID: studioAcceptedJob.uuid,
|
||||||
jobToken: studioAcceptedJob.jobToken,
|
jobToken: studioAcceptedJob.jobToken,
|
||||||
@ -849,7 +849,7 @@ describe('Test managing runners', function () {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('Video edition tasks file routes', function () {
|
describe('Video studio tasks file routes', function () {
|
||||||
|
|
||||||
it('Should fail with an invalid studio filename', async function () {
|
it('Should fail with an invalid studio filename', async function () {
|
||||||
await fetchStudioFiles({
|
await fetchStudioFiles({
|
||||||
|
@ -5,8 +5,8 @@ import { readFile } from 'fs-extra'
|
|||||||
import { checkPersistentTmpIsEmpty, checkVideoDuration } from '@server/tests/shared'
|
import { checkPersistentTmpIsEmpty, checkVideoDuration } from '@server/tests/shared'
|
||||||
import { buildAbsoluteFixturePath } from '@shared/core-utils'
|
import { buildAbsoluteFixturePath } from '@shared/core-utils'
|
||||||
import {
|
import {
|
||||||
RunnerJobVideoEditionTranscodingPayload,
|
RunnerJobStudioTranscodingPayload,
|
||||||
VideoEditionTranscodingSuccess,
|
VideoStudioTranscodingSuccess,
|
||||||
VideoState,
|
VideoState,
|
||||||
VideoStudioTask,
|
VideoStudioTask,
|
||||||
VideoStudioTaskIntro
|
VideoStudioTaskIntro
|
||||||
@ -121,10 +121,10 @@ describe('Test runner video studio transcoding', function () {
|
|||||||
await checkVideoDuration(server, videoUUID, 5)
|
await checkVideoDuration(server, videoUUID, 5)
|
||||||
}
|
}
|
||||||
|
|
||||||
const { job } = await servers[0].runnerJobs.accept<RunnerJobVideoEditionTranscodingPayload>({ runnerToken, jobUUID })
|
const { job } = await servers[0].runnerJobs.accept<RunnerJobStudioTranscodingPayload>({ runnerToken, jobUUID })
|
||||||
const jobToken = job.jobToken
|
const jobToken = job.jobToken
|
||||||
|
|
||||||
expect(job.type === 'video-edition-transcoding')
|
expect(job.type === 'video-studio-transcoding')
|
||||||
expect(job.payload.input.videoFileUrl).to.exist
|
expect(job.payload.input.videoFileUrl).to.exist
|
||||||
|
|
||||||
// Check video input file
|
// Check video input file
|
||||||
@ -150,7 +150,7 @@ describe('Test runner video studio transcoding', function () {
|
|||||||
expect(body).to.deep.equal(inputFile)
|
expect(body).to.deep.equal(inputFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
const payload: VideoEditionTranscodingSuccess = { videoFile: 'video_very_short_240p.mp4' }
|
const payload: VideoStudioTranscodingSuccess = { videoFile: 'video_very_short_240p.mp4' }
|
||||||
await servers[0].runnerJobs.success({ runnerToken, jobUUID, jobToken, payload })
|
await servers[0].runnerJobs.success({ runnerToken, jobUUID, jobToken, payload })
|
||||||
|
|
||||||
await waitJobs(servers)
|
await waitJobs(servers)
|
||||||
|
@ -270,7 +270,7 @@ describe('Test video studio', function () {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('HLS only video edition', function () {
|
describe('HLS only studio edition', function () {
|
||||||
|
|
||||||
before(async function () {
|
before(async function () {
|
||||||
// Disable webtorrent
|
// Disable webtorrent
|
||||||
@ -300,7 +300,7 @@ describe('Test video studio', function () {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('Object storage video edition', function () {
|
describe('Object storage studio edition', function () {
|
||||||
if (areMockObjectStorageTestsDisabled()) return
|
if (areMockObjectStorageTestsDisabled()) return
|
||||||
|
|
||||||
before(async function () {
|
before(async function () {
|
||||||
|
@ -57,7 +57,7 @@ const defaultLibFDKAACVODOptionsBuilder: EncoderOptionsBuilder = ({ streamNum })
|
|||||||
return { outputOptions: [ buildStreamSuffix('-q:a', streamNum), '5' ] }
|
return { outputOptions: [ buildStreamSuffix('-q:a', streamNum), '5' ] }
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getAvailableEncoders () {
|
export function getDefaultAvailableEncoders () {
|
||||||
return {
|
return {
|
||||||
vod: {
|
vod: {
|
||||||
libx264: {
|
libx264: {
|
||||||
@ -81,7 +81,7 @@ export function getAvailableEncoders () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getEncodersToTry () {
|
export function getDefaultEncodersToTry () {
|
||||||
return {
|
return {
|
||||||
vod: {
|
vod: {
|
||||||
video: [ 'libx264' ],
|
video: [ 'libx264' ],
|
@ -1,4 +1,5 @@
|
|||||||
export * from './ffmpeg-command-wrapper'
|
export * from './ffmpeg-command-wrapper'
|
||||||
|
export * from './ffmpeg-default-transcoding-profile'
|
||||||
export * from './ffmpeg-edition'
|
export * from './ffmpeg-edition'
|
||||||
export * from './ffmpeg-images'
|
export * from './ffmpeg-images'
|
||||||
export * from './ffmpeg-live'
|
export * from './ffmpeg-live'
|
||||||
|
@ -8,7 +8,7 @@ export type RunnerJobVODPayload =
|
|||||||
export type RunnerJobPayload =
|
export type RunnerJobPayload =
|
||||||
RunnerJobVODPayload |
|
RunnerJobVODPayload |
|
||||||
RunnerJobLiveRTMPHLSTranscodingPayload |
|
RunnerJobLiveRTMPHLSTranscodingPayload |
|
||||||
RunnerJobVideoEditionTranscodingPayload
|
RunnerJobStudioTranscodingPayload
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
@ -46,7 +46,7 @@ export interface RunnerJobVODAudioMergeTranscodingPayload {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RunnerJobVideoEditionTranscodingPayload {
|
export interface RunnerJobStudioTranscodingPayload {
|
||||||
input: {
|
input: {
|
||||||
videoFileUrl: string
|
videoFileUrl: string
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ export type RunnerJobVODPrivatePayload =
|
|||||||
export type RunnerJobPrivatePayload =
|
export type RunnerJobPrivatePayload =
|
||||||
RunnerJobVODPrivatePayload |
|
RunnerJobVODPrivatePayload |
|
||||||
RunnerJobLiveRTMPHLSTranscodingPrivatePayload |
|
RunnerJobLiveRTMPHLSTranscodingPrivatePayload |
|
||||||
RunnerJobVideoEditionTranscodingPrivatePayload
|
RunnerJobVideoStudioTranscodingPrivatePayload
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
@ -38,7 +38,7 @@ export interface RunnerJobLiveRTMPHLSTranscodingPrivatePayload {
|
|||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
export interface RunnerJobVideoEditionTranscodingPrivatePayload {
|
export interface RunnerJobVideoStudioTranscodingPrivatePayload {
|
||||||
videoUUID: string
|
videoUUID: string
|
||||||
originalTasks: VideoStudioTaskPayload[]
|
originalTasks: VideoStudioTaskPayload[]
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ export type RunnerJobSuccessPayload =
|
|||||||
VODHLSTranscodingSuccess |
|
VODHLSTranscodingSuccess |
|
||||||
VODAudioMergeTranscodingSuccess |
|
VODAudioMergeTranscodingSuccess |
|
||||||
LiveRTMPHLSTranscodingSuccess |
|
LiveRTMPHLSTranscodingSuccess |
|
||||||
VideoEditionTranscodingSuccess
|
VideoStudioTranscodingSuccess
|
||||||
|
|
||||||
export interface VODWebVideoTranscodingSuccess {
|
export interface VODWebVideoTranscodingSuccess {
|
||||||
videoFile: Blob | string
|
videoFile: Blob | string
|
||||||
@ -31,7 +31,7 @@ export interface LiveRTMPHLSTranscodingSuccess {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface VideoEditionTranscodingSuccess {
|
export interface VideoStudioTranscodingSuccess {
|
||||||
videoFile: Blob | string
|
videoFile: Blob | string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,4 +3,4 @@ export type RunnerJobType =
|
|||||||
'vod-hls-transcoding' |
|
'vod-hls-transcoding' |
|
||||||
'vod-audio-merge-transcoding' |
|
'vod-audio-merge-transcoding' |
|
||||||
'live-rtmp-hls-transcoding' |
|
'live-rtmp-hls-transcoding' |
|
||||||
'video-edition-transcoding'
|
'video-studio-transcoding'
|
||||||
|
Loading…
Reference in New Issue
Block a user