shiveshnavin commited on
Commit
0f9c090
·
1 Parent(s): bbbffa3

Add file out

Browse files
Files changed (1) hide show
  1. server-plugins/split-render.js +100 -59
server-plugins/split-render.js CHANGED
@@ -3,6 +3,7 @@ import fs from 'fs'
3
  import { Plugin } from './plugin.js';
4
  import { exec } from 'child_process'
5
  import _path from 'path';
 
6
 
7
  export class SplitRenderPlugin extends Plugin {
8
  constructor(name, options) {
@@ -19,69 +20,109 @@ export class SplitRenderPlugin extends Plugin {
19
  return new Promise((resolve, reject) => {
20
  let ffmpegCommand = ''
21
  let outFile = outFiles.find(f => f.includes('.webm')) || outFiles.find(f => f.includes('.mp4')) || outFiles.find(f => f.includes('.mov'));
22
- if (outFile) {
23
- let inputFiles = [];
24
- let sceneIndex = 0;
25
-
26
- originalManuscript.transcript.forEach(scene => {
27
- let durationInSeconds = scene.durationInSeconds
28
- scene.mediaAbsPaths = _.cloneDeep(scene._mediaAbsPaths)
29
- delete scene._mediaAbsPaths
30
-
31
- for (let i = 0; i < scene.mediaAbsPaths.length; i++) {
32
- const { path, type, dimensions, durationSec } = scene.mediaAbsPaths[i];
33
- const { width, height } = dimensions;
34
- if (type === 'video') {
35
- // Clip the video to scene duration if it's longer
36
- const clipDuration = Math.min(durationSec, durationInSeconds);
37
- let fileName = _path.basename(path);
38
- let publicFilePath = `public/${fileName}`;
39
- inputFiles.push(`-ss 0 -t ${clipDuration} -i "${publicFilePath}"`);
40
- }
41
  }
42
- sceneIndex++;
43
- });
44
 
45
- // Add the front overlay video (transparent webm) - this controls the final duration
46
- inputFiles.push(`-i "${outFile}"`);
47
-
48
- if (inputFiles.length > 1) {
49
- // Build the ffmpeg command
50
- const inputPart = inputFiles.join(' ');
51
- const frontVideoIndex = inputFiles.length - 1; // Last input is the front video (master duration)
52
- const backVideoIndex = 0; // First input is the back video
53
-
54
- // Create filter complex for overlay with transparency
55
- const filterComplex = `"[${frontVideoIndex}:v]colorkey=0x000000:0.08:0.02[fg];[${backVideoIndex}:v][fg]overlay=0:0:format=auto"`;
56
-
57
- // Remove -shortest so the output duration matches the overlay video (outFile)
58
- ffmpegCommand = `ffmpeg ${inputPart} -filter_complex ${filterComplex} -c:v libx264 -pix_fmt yuv420p final_${jobId}.mp4`;
59
- }
60
- else {
61
- resolve('No input files to process. Skipping split-render post process');
62
- return
63
- }
64
-
65
- //run ffmpeg command
66
- if (ffmpegCommand) {
67
- console.log('Running ffmpeg command:', ffmpegCommand);
68
- exec(ffmpegCommand, (error, stdout, stderr) => {
69
- if (error) {
70
- console.error(`Error: ${error.message}`);
71
- reject(error);
72
- return;
73
- }
74
- if (stderr) {
75
- console.error(`Error: ${stderr}`);
76
- reject(new Error(stderr));
77
- return;
78
  }
79
- console.log(`Output: ${stdout}`);
80
- resolve(stdout);
81
  });
82
- } else {
83
- resolve('No ffmpeg command generated');
84
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
  } else {
86
  resolve('No output file found');
87
  }
 
3
  import { Plugin } from './plugin.js';
4
  import { exec } from 'child_process'
5
  import _path from 'path';
6
+ import { PerformanceRecorder } from 'common-utils';
7
 
8
  export class SplitRenderPlugin extends Plugin {
9
  constructor(name, options) {
 
20
  return new Promise((resolve, reject) => {
21
  let ffmpegCommand = ''
22
  let outFile = outFiles.find(f => f.includes('.webm')) || outFiles.find(f => f.includes('.mp4')) || outFiles.find(f => f.includes('.mov'));
23
+ if (outFile && fs.existsSync(outFile)) {
24
+ let perf = new PerformanceRecorder()
25
+ // First, get the overlay video duration to use as master duration
26
+ const getVideoDuration = `ffprobe -v quiet -show_entries format=duration -of csv=p=0 "${outFile}"`;
27
+ exec(getVideoDuration, (error, stdout, stderr) => {
28
+ if (error) {
29
+ console.error(`Error getting overlay duration: ${error.message}`);
30
+ reject(error);
31
+ return;
 
 
 
 
 
 
 
 
 
 
32
  }
 
 
33
 
34
+ const overlayDuration = parseFloat(stdout.trim());
35
+ console.log(`Overlay duration: ${overlayDuration} seconds`);
36
+
37
+ let inputFiles = [];
38
+ let sceneIndex = 0;
39
+
40
+ originalManuscript.transcript.forEach(scene => {
41
+ let durationInSeconds = scene.durationInSeconds
42
+ scene.mediaAbsPaths = _.cloneDeep(scene._mediaAbsPaths)
43
+ delete scene._mediaAbsPaths
44
+
45
+ for (let i = 0; i < scene.mediaAbsPaths.length; i++) {
46
+ const { path, type, dimensions, durationSec } = scene.mediaAbsPaths[i];
47
+ const { width, height } = dimensions;
48
+ if (type === 'video') {
49
+ // Clip the video to the minimum of scene duration, file duration, or overlay duration
50
+ const clipDuration = Math.min(durationSec, durationInSeconds, overlayDuration);
51
+ let fileName = _path.basename(path);
52
+ let publicFilePath = `public/${fileName}`;
53
+ inputFiles.push(`-ss 0 -t ${clipDuration} -i "${publicFilePath}"`);
54
+ }
 
 
 
 
 
 
 
 
 
 
 
 
55
  }
56
+ sceneIndex++;
 
57
  });
58
+
59
+ // Add the front overlay video (transparent webm) - this controls the final duration
60
+ inputFiles.push(`-ss 0 -t ${overlayDuration} -i "${outFile}"`);
61
+ const finalOutFile = `out/final_${jobId}.mp4`
62
+
63
+ if (inputFiles.length > 1) {
64
+ // Build the ffmpeg command
65
+ const inputPart = inputFiles.join(' ');
66
+ const frontVideoIndex = inputFiles.length - 1; // Last input is the front video (master duration)
67
+ const backVideoIndex = 0; // First input is the back video
68
+
69
+ // Create filter complex for overlay with transparency
70
+ const filterComplex = `"[${frontVideoIndex}:v]colorkey=0x000000:0.08:0.02[fg];[${backVideoIndex}:v][fg]overlay=0:0:format=auto"`;
71
+
72
+ // Remove -shortest so the output duration matches the overlay video (outFile)
73
+ ffmpegCommand = `ffmpeg ${inputPart} -filter_complex ${filterComplex} -c:v libx264 -pix_fmt yuv420p ${finalOutFile}`;
74
+ }
75
+ else {
76
+ resolve('No input files to process. Skipping split-render post process');
77
+ return
78
+ }
79
+
80
+ //run ffmpeg command
81
+ if (ffmpegCommand) {
82
+ console.log('Running ffmpeg command:', ffmpegCommand);
83
+
84
+ const { spawn } = require('child_process');
85
+ const ffmpegProcess = spawn('ffmpeg', ffmpegCommand.split(' ').slice(1), { stdio: 'pipe' });
86
+
87
+ let stdoutData = '';
88
+ let stderrData = '';
89
+
90
+ // Stream stdout in real-time
91
+ ffmpegProcess.stdout.on('data', (data) => {
92
+ const output = data.toString();
93
+ console.log('[FFmpeg stdout]:', output);
94
+ stdoutData += output;
95
+ });
96
+
97
+ // Stream stderr in real-time (FFmpeg outputs progress info to stderr)
98
+ ffmpegProcess.stderr.on('data', (data) => {
99
+ const output = data.toString();
100
+ console.log('[FFmpeg stderr]:', output);
101
+ stderrData += output;
102
+ });
103
+
104
+ ffmpegProcess.on('close', (code) => {
105
+ if (code === 0) {
106
+ fs.renameSync(outFile, 'raw_' + outFile)
107
+ fs.renameSync(finalOutFile, outFile)
108
+ console.log('FFmpeg process completed successfully, took ', perf.elapsedString());
109
+ resolve(stdoutData || 'FFmpeg completed successfully');
110
+ } else {
111
+ const fullErrorLog = `FFmpeg process exited with code ${code}\n\nSTDOUT:\n${stdoutData}\n\nSTDERR:\n${stderrData}`;
112
+ console.error('FFmpeg failed:', fullErrorLog);
113
+ reject(new Error(fullErrorLog));
114
+ }
115
+ });
116
+
117
+ ffmpegProcess.on('error', (error) => {
118
+ const fullErrorLog = `FFmpeg spawn error: ${error.message}\n\nSTDOUT:\n${stdoutData}\n\nSTDERR:\n${stderrData}`;
119
+ console.error('FFmpeg spawn error:', fullErrorLog);
120
+ reject(new Error(fullErrorLog));
121
+ });
122
+ } else {
123
+ resolve('No ffmpeg command generated');
124
+ }
125
+ });
126
  } else {
127
  resolve('No output file found');
128
  }