1 import { remove } from 'fs-extra'
2 import { pick } from 'lodash'
3 import { logger } from 'packages/peertube-runner/shared'
4 import { join } from 'path'
5 import { buildUUID } from '@shared/extra-utils'
7 RunnerJobStudioTranscodingPayload,
8 VideoStudioTranscodingSuccess,
10 VideoStudioTaskCutPayload,
11 VideoStudioTaskIntroPayload,
12 VideoStudioTaskOutroPayload,
13 VideoStudioTaskPayload,
14 VideoStudioTaskWatermarkPayload
15 } from '@shared/models'
16 import { ConfigManager } from '../../../shared/config-manager'
17 import { buildFFmpegEdition, downloadInputFile, JobWithToken, ProcessOptions } from './common'
19 export async function processStudioTranscoding (options: ProcessOptions<RunnerJobStudioTranscodingPayload>) {
20 const { server, job, runnerToken } = options
21 const payload = job.payload
23 let outputPath: string
24 const inputPath = await downloadInputFile({ url: payload.input.videoFileUrl, runnerToken, job })
25 let tmpInputFilePath = inputPath
28 for (const task of payload.tasks) {
29 const outputFilename = 'output-edition-' + buildUUID() + '.mp4'
30 outputPath = join(ConfigManager.Instance.getTranscodingDirectory(), outputFilename)
33 inputPath: tmpInputFilePath,
40 if (tmpInputFilePath) await remove(tmpInputFilePath)
42 // For the next iteration
43 tmpInputFilePath = outputPath
46 const successBody: VideoStudioTranscodingSuccess = {
50 await server.runnerJobs.success({
51 jobToken: job.jobToken,
57 await remove(tmpInputFilePath)
58 await remove(outputPath)
62 // ---------------------------------------------------------------------------
64 // ---------------------------------------------------------------------------
66 type TaskProcessorOptions <T extends VideoStudioTaskPayload = VideoStudioTaskPayload> = {
74 const taskProcessors: { [id in VideoStudioTask['name']]: (options: TaskProcessorOptions) => Promise<any> } = {
75 'add-intro': processAddIntroOutro,
76 'add-outro': processAddIntroOutro,
78 'add-watermark': processAddWatermark
81 async function processTask (options: TaskProcessorOptions) {
82 const { task } = options
84 const processor = taskProcessors[options.task.name]
85 if (!process) throw new Error('Unknown task ' + task.name)
87 return processor(options)
90 async function processAddIntroOutro (options: TaskProcessorOptions<VideoStudioTaskIntroPayload | VideoStudioTaskOutroPayload>) {
91 const { inputPath, task, runnerToken, job } = options
93 logger.debug('Adding intro/outro to ' + inputPath)
95 const introOutroPath = await downloadInputFile({ url: task.options.file, runnerToken, job })
98 await buildFFmpegEdition().addIntroOutro({
99 ...pick(options, [ 'inputPath', 'outputPath' ]),
102 type: task.name === 'add-intro'
107 await remove(introOutroPath)
111 function processCut (options: TaskProcessorOptions<VideoStudioTaskCutPayload>) {
112 const { inputPath, task } = options
114 logger.debug(`Cutting ${inputPath}`)
116 return buildFFmpegEdition().cutVideo({
117 ...pick(options, [ 'inputPath', 'outputPath' ]),
119 start: task.options.start,
120 end: task.options.end
124 async function processAddWatermark (options: TaskProcessorOptions<VideoStudioTaskWatermarkPayload>) {
125 const { inputPath, task, runnerToken, job } = options
127 logger.debug('Adding watermark to ' + inputPath)
129 const watermarkPath = await downloadInputFile({ url: task.options.file, runnerToken, job })
132 await buildFFmpegEdition().addWatermark({
133 ...pick(options, [ 'inputPath', 'outputPath' ]),
138 watermarkSizeRatio: task.options.watermarkSizeRatio,
139 horitonzalMarginRatio: task.options.horitonzalMarginRatio,
140 verticalMarginRatio: task.options.verticalMarginRatio
144 await remove(watermarkPath)