shiveshnavin commited on
Commit
51570c3
·
1 Parent(s): 2f9c678

Add split media render

Browse files
routes.js CHANGED
@@ -352,7 +352,7 @@ RenderRouter.post('/api/render-sync', async (req, res) => {
352
  });
353
  uploader.creds.url = uploader.creds.url;
354
  let outFiles = await listOutputFiles(jobId);
355
- await applyPluginsPostrender(plugins, originalManuscript, jobId, outFiles)
356
 
357
  console.log('Render complete, uploading... to target folder: ' + targetUrl + jobId + '/')
358
  if (zip) {
 
352
  });
353
  uploader.creds.url = uploader.creds.url;
354
  let outFiles = await listOutputFiles(jobId);
355
+ await applyPluginsPostrender(plugins, originalManuscript, originalManuscriptPath, jobId, outFiles)
356
 
357
  console.log('Render complete, uploading... to target folder: ' + targetUrl + jobId + '/')
358
  if (zip) {
server-plugins/apply.js CHANGED
@@ -26,10 +26,17 @@ export async function applyPluginsPrerender(plugins, originalManuscript, origina
26
  }
27
  }
28
 
29
- export async function applyPluginsPostrender(plugins, originalManuscript, jobId, outFiles) {
30
  for (let pluginInfo of plugins) {
31
  let pluginName = pluginInfo.name;
32
  let options = pluginInfo || {};
33
  console.log(`Applying post-render plugin: ${pluginName} with options:`, options)
 
 
 
 
 
 
34
  }
 
35
  }
 
26
  }
27
  }
28
 
29
+ export async function applyPluginsPostrender(plugins, originalManuscript, originalManuscriptPath, jobId, outFiles) {
30
  for (let pluginInfo of plugins) {
31
  let pluginName = pluginInfo.name;
32
  let options = pluginInfo || {};
33
  console.log(`Applying post-render plugin: ${pluginName} with options:`, options)
34
+ if (pluginRegistry[pluginName]) {
35
+ let pluginInstance = new pluginRegistry[pluginName](pluginName, options);
36
+ await pluginInstance.applyPostrender(originalManuscript, jobId, outFiles);
37
+ } else {
38
+ console.warn(`Plugin ${pluginName} not found in registry.`);
39
+ }
40
  }
41
+ fs.writeFileSync(originalManuscriptPath, JSON.stringify(originalManuscript, null, 2))
42
  }
server-plugins/split-render.js CHANGED
@@ -1,6 +1,8 @@
1
  import _ from 'lodash'
2
  import fs from 'fs'
3
  import { Plugin } from './plugin.js';
 
 
4
 
5
  export class SplitRenderPlugin extends Plugin {
6
  constructor(name, options) {
@@ -8,7 +10,81 @@ export class SplitRenderPlugin extends Plugin {
8
  }
9
  async applyPrerender(originalManuscript, jobId) {
10
  _.set(originalManuscript, 'meta.generationConfig.extras.buildParams', ` --codec=vp9 --transparent --pixel-format=yuva420p --image-format=png --crf=18`);
 
 
 
 
11
  }
12
  async applyPostrender(originalManuscript, jobId, outFiles) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  }
14
  }
 
1
  import _ from 'lodash'
2
  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) {
 
10
  }
11
  async applyPrerender(originalManuscript, jobId) {
12
  _.set(originalManuscript, 'meta.generationConfig.extras.buildParams', ` --codec=vp9 --transparent --pixel-format=yuva420p --image-format=png --crf=18`);
13
+ originalManuscript.transcript.forEach(element => {
14
+ element._mediaAbsPaths = _.cloneDeep(element.mediaAbsPaths)
15
+ element.mediaAbsPaths = []
16
+ });
17
  }
18
  async applyPostrender(originalManuscript, jobId, outFiles) {
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 = 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
+ }
88
+ });
89
  }
90
  }