Spaces:
Running
Running
Commit
·
8bdc503
1
Parent(s):
a97b6c8
Backup before bye bye to frame based resumable
Browse files- .gitignore +2 -1
- package.json +2 -0
- src/common/Error.tsx +4 -1
- src/common/PosterSingleTextWithBG.tsx +4 -1
- src/ig-reel/IG2LineMotivationalScene.tsx +4 -1
- src/ig-reel/IGReelComposition.tsx +3 -1
- src/linkedin-video/LinkedinFullSysDesignComposition.tsx +3 -1
- src/subtitles/general_Subtitles.tsx +2 -1
- src/subtitles/motivational_Subtitles.tsx +2 -1
- src/youtube/SectionTextWithBG.tsx +3 -1
- src/youtube/SingleTextWithBG.tsx +3 -1
- test-render.cjs +111 -8
.gitignore
CHANGED
|
@@ -18,4 +18,5 @@ functions/*.log
|
|
| 18 |
uploads/
|
| 19 |
uploads/**
|
| 20 |
audit_log_creds.json
|
| 21 |
-
build/
|
|
|
|
|
|
| 18 |
uploads/
|
| 19 |
uploads/**
|
| 20 |
audit_log_creds.json
|
| 21 |
+
build/
|
| 22 |
+
frames/
|
package.json
CHANGED
|
@@ -10,6 +10,8 @@
|
|
| 10 |
"submodule": "git submodule init && git submodule update",
|
| 11 |
"ci": "echo Runnin on CI",
|
| 12 |
"start": "node server.js",
|
|
|
|
|
|
|
| 13 |
"extract32": "set MODIFY_FILES=1 && node app.js",
|
| 14 |
"extract": "MODIFY_FILES=1 && node app.js",
|
| 15 |
"preview": "remotion studio --log=verbose",
|
|
|
|
| 10 |
"submodule": "git submodule init && git submodule update",
|
| 11 |
"ci": "echo Runnin on CI",
|
| 12 |
"start": "node server.js",
|
| 13 |
+
"bundle": "echo -n | remotion bundle",
|
| 14 |
+
"skip-font-warn": "sed -E -i \"s/\\bfontsLoaded[[:space:]]*>[[:space:]]*20\\b/fontsLoaded > 20000/g\" build/bundle.js",
|
| 15 |
"extract32": "set MODIFY_FILES=1 && node app.js",
|
| 16 |
"extract": "MODIFY_FILES=1 && node app.js",
|
| 17 |
"preview": "remotion studio --log=verbose",
|
src/common/Error.tsx
CHANGED
|
@@ -2,7 +2,10 @@ import * as Montserrat from "@remotion/google-fonts/Montserrat";
|
|
| 2 |
|
| 3 |
export function ErrorMsg(props: { error?: string, children?: any }) {
|
| 4 |
|
| 5 |
-
const { fontFamily } = Montserrat.loadFont("normal", {
|
|
|
|
|
|
|
|
|
|
| 6 |
const baseStyle = {
|
| 7 |
borderColor: "transparent",
|
| 8 |
borderRadius: "2rem",
|
|
|
|
| 2 |
|
| 3 |
export function ErrorMsg(props: { error?: string, children?: any }) {
|
| 4 |
|
| 5 |
+
const { fontFamily } = Montserrat.loadFont("normal", {
|
| 6 |
+
weights: ["500"],
|
| 7 |
+
ignoreTooManyRequestsWarning: true
|
| 8 |
+
});
|
| 9 |
const baseStyle = {
|
| 10 |
borderColor: "transparent",
|
| 11 |
borderRadius: "2rem",
|
src/common/PosterSingleTextWithBG.tsx
CHANGED
|
@@ -38,7 +38,10 @@ export const PosterSingleTextWithBG: React.FC<Transcript> = (item) => {
|
|
| 38 |
padding: "0.2rem 1.5rem",
|
| 39 |
};
|
| 40 |
|
| 41 |
-
const { fontFamily } = Montserrat.loadFont("normal", {
|
|
|
|
|
|
|
|
|
|
| 42 |
let curZoom = 1.5
|
| 43 |
let lines = audioCaption?.words || [{
|
| 44 |
word: text
|
|
|
|
| 38 |
padding: "0.2rem 1.5rem",
|
| 39 |
};
|
| 40 |
|
| 41 |
+
const { fontFamily } = Montserrat.loadFont("normal", {
|
| 42 |
+
weights: ["400"],
|
| 43 |
+
ignoreTooManyRequestsWarning: true
|
| 44 |
+
});
|
| 45 |
let curZoom = 1.5
|
| 46 |
let lines = audioCaption?.words || [{
|
| 47 |
word: text
|
src/ig-reel/IG2LineMotivationalScene.tsx
CHANGED
|
@@ -13,7 +13,10 @@ export default function IG2LineMotivationalScene(props: { meta: Meta; item: Tran
|
|
| 13 |
const media = mediaAbsPaths[0];
|
| 14 |
const watermark = mediaAbsPaths[1];
|
| 15 |
const bgMusicPath = RenderUtils.tryStaticFile(audioFullPath);
|
| 16 |
-
const { fontFamily } = Montserrat.loadFont("normal", {
|
|
|
|
|
|
|
|
|
|
| 17 |
const frame = useCurrentFrame();
|
| 18 |
|
| 19 |
|
|
|
|
| 13 |
const media = mediaAbsPaths[0];
|
| 14 |
const watermark = mediaAbsPaths[1];
|
| 15 |
const bgMusicPath = RenderUtils.tryStaticFile(audioFullPath);
|
| 16 |
+
const { fontFamily } = Montserrat.loadFont("normal", {
|
| 17 |
+
weights: ["400"],
|
| 18 |
+
ignoreTooManyRequestsWarning: true
|
| 19 |
+
});
|
| 20 |
const frame = useCurrentFrame();
|
| 21 |
|
| 22 |
|
src/ig-reel/IGReelComposition.tsx
CHANGED
|
@@ -21,7 +21,9 @@ import { wipe } from "@remotion/transitions/wipe";
|
|
| 21 |
import { TransitionAnims } from '../anims/transitions';
|
| 22 |
|
| 23 |
export const IGReelComposition: React.FC = (props: OriginalManuscript) => {
|
| 24 |
-
const { fontFamily } = loadFont(
|
|
|
|
|
|
|
| 25 |
let { meta, bgMusic, bgMusicDuration, bgMusicVolume } = props
|
| 26 |
let transcripts: Transcript[] = props.contents
|
| 27 |
let fps = meta.fps
|
|
|
|
| 21 |
import { TransitionAnims } from '../anims/transitions';
|
| 22 |
|
| 23 |
export const IGReelComposition: React.FC = (props: OriginalManuscript) => {
|
| 24 |
+
const { fontFamily } = loadFont("normal", {
|
| 25 |
+
ignoreTooManyRequestsWarning: true
|
| 26 |
+
});
|
| 27 |
let { meta, bgMusic, bgMusicDuration, bgMusicVolume } = props
|
| 28 |
let transcripts: Transcript[] = props.contents
|
| 29 |
let fps = meta.fps
|
src/linkedin-video/LinkedinFullSysDesignComposition.tsx
CHANGED
|
@@ -13,7 +13,9 @@ import { AnimGraphScene } from './AnimGraphScene';
|
|
| 13 |
import GeneralSubtitles from '../subtitles/general_Subtitles';
|
| 14 |
|
| 15 |
export const LinkedinFullSysDesignComposition: React.FC = (props: OriginalManuscript) => {
|
| 16 |
-
const { fontFamily } = loadFont(
|
|
|
|
|
|
|
| 17 |
let { meta, bgMusic, bgMusicDuration, bgMusicVolume } = props
|
| 18 |
let transcripts: Transcript[] = props.contents
|
| 19 |
let fps = meta.fps
|
|
|
|
| 13 |
import GeneralSubtitles from '../subtitles/general_Subtitles';
|
| 14 |
|
| 15 |
export const LinkedinFullSysDesignComposition: React.FC = (props: OriginalManuscript) => {
|
| 16 |
+
const { fontFamily } = loadFont("normal", {
|
| 17 |
+
ignoreTooManyRequestsWarning: true
|
| 18 |
+
});
|
| 19 |
let { meta, bgMusic, bgMusicDuration, bgMusicVolume } = props
|
| 20 |
let transcripts: Transcript[] = props.contents
|
| 21 |
let fps = meta.fps
|
src/subtitles/general_Subtitles.tsx
CHANGED
|
@@ -15,7 +15,8 @@ const GeneralSubtitles: React.FC<{ group: Group, word: Word, position: 'start' |
|
|
| 15 |
fontSize = '6em',
|
| 16 |
position = 'end' }) => {
|
| 17 |
|
| 18 |
-
let { fontFamily } = SubtitleFont.loadFont("normal", {
|
|
|
|
| 19 |
weights: ['900']
|
| 20 |
})
|
| 21 |
|
|
|
|
| 15 |
fontSize = '6em',
|
| 16 |
position = 'end' }) => {
|
| 17 |
|
| 18 |
+
let { fontFamily } = SubtitleFont.loadFont("normal", {
|
| 19 |
+
ignoreTooManyRequestsWarning: true,
|
| 20 |
weights: ['900']
|
| 21 |
})
|
| 22 |
|
src/subtitles/motivational_Subtitles.tsx
CHANGED
|
@@ -8,7 +8,8 @@ import _ from 'lodash'
|
|
| 8 |
const MotivationalSubtitles: React.FC<{ group: Group, word: Word, position: 'top' | 'center' | 'bottom' }> = ({ group, word: curWord, position = 'center' }) => {
|
| 9 |
|
| 10 |
let { fontFamily } = Montserrat.loadFont("normal", {
|
| 11 |
-
weights: ['900']
|
|
|
|
| 12 |
})
|
| 13 |
|
| 14 |
const frame = useCurrentFrame();
|
|
|
|
| 8 |
const MotivationalSubtitles: React.FC<{ group: Group, word: Word, position: 'top' | 'center' | 'bottom' }> = ({ group, word: curWord, position = 'center' }) => {
|
| 9 |
|
| 10 |
let { fontFamily } = Montserrat.loadFont("normal", {
|
| 11 |
+
weights: ['900'],
|
| 12 |
+
ignoreTooManyRequestsWarning: true
|
| 13 |
})
|
| 14 |
|
| 15 |
const frame = useCurrentFrame();
|
src/youtube/SectionTextWithBG.tsx
CHANGED
|
@@ -34,7 +34,9 @@ export const SectionTextWithBG: React.FC<SequentialSceneData> = (props: Transcri
|
|
| 34 |
|
| 35 |
const videoConfig = useVideoConfig();
|
| 36 |
const { fps, width, height } = useVideoConfig();
|
| 37 |
-
const { fontFamily } = loadFont(
|
|
|
|
|
|
|
| 38 |
const frame = useCurrentFrame();
|
| 39 |
|
| 40 |
let textBgColor = textColor;
|
|
|
|
| 34 |
|
| 35 |
const videoConfig = useVideoConfig();
|
| 36 |
const { fps, width, height } = useVideoConfig();
|
| 37 |
+
const { fontFamily } = loadFont("normal", {
|
| 38 |
+
ignoreTooManyRequestsWarning: true
|
| 39 |
+
});
|
| 40 |
const frame = useCurrentFrame();
|
| 41 |
|
| 42 |
let textBgColor = textColor;
|
src/youtube/SingleTextWithBG.tsx
CHANGED
|
@@ -28,7 +28,9 @@ function adjustFontSize(text: string, fullFont: number, minFontSizeDef): number
|
|
| 28 |
export const SingleTextWithBG: React.FC<SequentialSceneData> = ({ text, textColor = '#fff', duration, direction = 'left', bgImagePath, audioPath, emphasisOnImage }) => {
|
| 29 |
const videoConfig = useVideoConfig();
|
| 30 |
const { fps, width, height } = useVideoConfig();
|
| 31 |
-
const { fontFamily } = loadFont(
|
|
|
|
|
|
|
| 32 |
|
| 33 |
let textBgColor = textColor;
|
| 34 |
let textFontColor = "#fff";
|
|
|
|
| 28 |
export const SingleTextWithBG: React.FC<SequentialSceneData> = ({ text, textColor = '#fff', duration, direction = 'left', bgImagePath, audioPath, emphasisOnImage }) => {
|
| 29 |
const videoConfig = useVideoConfig();
|
| 30 |
const { fps, width, height } = useVideoConfig();
|
| 31 |
+
const { fontFamily } = loadFont("normal", {
|
| 32 |
+
ignoreTooManyRequestsWarning: true
|
| 33 |
+
});
|
| 34 |
|
| 35 |
let textBgColor = textColor;
|
| 36 |
let textFontColor = "#fff";
|
test-render.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
const { renderFrames, renderStill } = require('@remotion/renderer');
|
| 2 |
const { bundle } = require('@remotion/bundler');
|
| 3 |
const path = require('path');
|
| 4 |
const fs = require('fs');
|
|
@@ -10,12 +10,28 @@ const { GenerateScript } = require('./src/GenerateScript.cjs')
|
|
| 10 |
const entry = path.join(process.cwd(), 'src/index.ts'); // your Remotion entry file
|
| 11 |
const originalManuScriptPath = path.join(process.cwd(), 'public/original_manuscript.json');
|
| 12 |
const compositionId = 'IGReelComposition';
|
|
|
|
| 13 |
const outputDir = path.join(process.cwd(), 'out');
|
| 14 |
const bundleLocation = path.join(process.cwd(), 'build');
|
| 15 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
const ScriptStr = fs.readFileSync(originalManuScriptPath);
|
| 17 |
const ScriptInput = JSON.parse(ScriptStr);
|
| 18 |
-
|
| 19 |
duration,
|
| 20 |
Script,
|
| 21 |
contents,
|
|
@@ -29,7 +45,7 @@ const bundleLocation = path.join(process.cwd(), 'build');
|
|
| 29 |
fps: Script.meta.fps,
|
| 30 |
height: Script.meta?.resolution?.height,
|
| 31 |
width: Script.meta?.resolution?.width,
|
| 32 |
-
durationInFrames:
|
| 33 |
defaultProps: Object.assign(Script, {
|
| 34 |
bgMusic: RenderUtils.getFileName(Script.bgMusic),
|
| 35 |
contents: contents,
|
|
@@ -44,8 +60,95 @@ const bundleLocation = path.join(process.cwd(), 'build');
|
|
| 44 |
}),
|
| 45 |
},
|
| 46 |
serveUrl: bundleLocation,
|
| 47 |
-
output: path.join(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 48 |
}
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
const { renderFrames, renderStill, stitchFramesToVideo } = require('@remotion/renderer');
|
| 2 |
const { bundle } = require('@remotion/bundler');
|
| 3 |
const path = require('path');
|
| 4 |
const fs = require('fs');
|
|
|
|
| 10 |
const entry = path.join(process.cwd(), 'src/index.ts'); // your Remotion entry file
|
| 11 |
const originalManuScriptPath = path.join(process.cwd(), 'public/original_manuscript.json');
|
| 12 |
const compositionId = 'IGReelComposition';
|
| 13 |
+
const frameOutputDir = path.join(process.cwd(), 'frames');
|
| 14 |
const outputDir = path.join(process.cwd(), 'out');
|
| 15 |
const bundleLocation = path.join(process.cwd(), 'build');
|
| 16 |
+
const assetsInfoFile = path.join(process.cwd(), 'public/assetsInfo.json');
|
| 17 |
+
|
| 18 |
+
function readAssetInfo() {
|
| 19 |
+
if (!fs.existsSync(assetsInfoFile)) {
|
| 20 |
+
return undefined
|
| 21 |
+
}
|
| 22 |
+
let assetsInfoStr = fs.readFileSync(assetsInfoFile)
|
| 23 |
+
let assetsInfo = JSON.parse(assetsInfoStr);
|
| 24 |
+
return assetsInfo
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
function updateAssetInfo(assetInfoNew) {
|
| 28 |
+
fs.writeFileSync(assetsInfoFile, JSON.stringify(assetInfoNew))
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
export const renderSSR = async (frameRange) => {
|
| 32 |
const ScriptStr = fs.readFileSync(originalManuScriptPath);
|
| 33 |
const ScriptInput = JSON.parse(ScriptStr);
|
| 34 |
+
let {
|
| 35 |
duration,
|
| 36 |
Script,
|
| 37 |
contents,
|
|
|
|
| 45 |
fps: Script.meta.fps,
|
| 46 |
height: Script.meta?.resolution?.height,
|
| 47 |
width: Script.meta?.resolution?.width,
|
| 48 |
+
durationInFrames: duration,
|
| 49 |
defaultProps: Object.assign(Script, {
|
| 50 |
bgMusic: RenderUtils.getFileName(Script.bgMusic),
|
| 51 |
contents: contents,
|
|
|
|
| 60 |
}),
|
| 61 |
},
|
| 62 |
serveUrl: bundleLocation,
|
| 63 |
+
output: path.join(frameOutputDir, 'output.jpg'),
|
| 64 |
+
// additional renderer options
|
| 65 |
+
audioCodec: 'mp3',
|
| 66 |
+
imageFormat: 'jpeg',
|
| 67 |
+
enableMultiProcessOnLinux: true,
|
| 68 |
+
jpegQuality: 70,
|
| 69 |
+
timeoutInMilliseconds: 60000,
|
| 70 |
+
concurrency: 1,
|
| 71 |
+
gl: 'angle'
|
| 72 |
+
}
|
| 73 |
+
const fps = Script.meta?.fps;
|
| 74 |
+
if (!fs.existsSync(frameOutputDir)) {
|
| 75 |
+
fs.mkdirSync(frameOutputDir, { recursive: true });
|
| 76 |
}
|
| 77 |
+
|
| 78 |
+
const files = fs.readdirSync(frameOutputDir);
|
| 79 |
+
const frameNamePattern = 'frame-[frame].[ext]';
|
| 80 |
+
const frameNamedRe = /frame-(\d+)\.(png|jpg|jpeg)$/i;
|
| 81 |
+
let lastIndex = -1;
|
| 82 |
+
for (const f of files) {
|
| 83 |
+
const m = f.match(frameNamedRe);
|
| 84 |
+
if (m) {
|
| 85 |
+
const idx = Number(m[1]);
|
| 86 |
+
if (!Number.isNaN(idx) && idx > lastIndex) lastIndex = idx;
|
| 87 |
+
}
|
| 88 |
+
}
|
| 89 |
+
|
| 90 |
+
//-------DEBUG--------//
|
| 91 |
+
duration = 5 * fps;
|
| 92 |
+
let startFrame = Math.min(duration - 1, Math.max(0, lastIndex + 1));
|
| 93 |
+
startFrame = 0;
|
| 94 |
+
//-------DEBUG--------//
|
| 95 |
+
if (lastIndex >= 0) {
|
| 96 |
+
console.log(`Found last rendered frame index ${lastIndex}, resuming from ${startFrame}`);
|
| 97 |
+
} else {
|
| 98 |
+
console.log('No existing frame-*.png found, starting from frame 0');
|
| 99 |
+
}
|
| 100 |
+
|
| 101 |
+
const formatMsToETA = (ms) => {
|
| 102 |
+
const totalSeconds = Math.ceil(ms / 1000);
|
| 103 |
+
const hours = Math.floor(totalSeconds / 3600);
|
| 104 |
+
const minutes = Math.floor((totalSeconds % 3600) / 60);
|
| 105 |
+
const seconds = totalSeconds % 60;
|
| 106 |
+
const parts = [];
|
| 107 |
+
if (hours) parts.push(`${hours}h`);
|
| 108 |
+
if (minutes) parts.push(`${minutes}min`);
|
| 109 |
+
if (seconds || parts.length === 0) parts.push(`${seconds}sec`);
|
| 110 |
+
return parts.join(' ');
|
| 111 |
+
};
|
| 112 |
+
|
| 113 |
+
let result = await renderFrames({
|
| 114 |
+
...renderConfig,
|
| 115 |
+
onBrowserLog: (info) => {
|
| 116 |
+
console.log(`${info.type}: ${info.text}`);
|
| 117 |
+
console.log(
|
| 118 |
+
info.stackTrace
|
| 119 |
+
.map((stack) => {
|
| 120 |
+
return ` ${stack.url}:${stack.lineNumber}:${stack.columnNumber}`;
|
| 121 |
+
})
|
| 122 |
+
.join('\n'),
|
| 123 |
+
);
|
| 124 |
+
},
|
| 125 |
+
imageSequencePattern: frameNamePattern,
|
| 126 |
+
outputDir: frameOutputDir,
|
| 127 |
+
frameRange: frameRange ?? [0, duration - 1],
|
| 128 |
+
onFrameUpdate: (rendered, frameIndex, timeTakenPerFrameMs) => {
|
| 129 |
+
|
| 130 |
+
if (rendered % 10 !== 0 && frameIndex !== duration - 1) return;
|
| 131 |
+
const remainingFrames = Math.max(0, duration - frameIndex);
|
| 132 |
+
const estimatedMsLeft = remainingFrames * (timeTakenPerFrameMs || 0);
|
| 133 |
+
const timeLeftSeconds = formatMsToETA(estimatedMsLeft);
|
| 134 |
+
console.log(`Rendered ${frameIndex} of ${duration} in ${timeTakenPerFrameMs?.toFixed(0)}ms Time left ${timeLeftSeconds}`);
|
| 135 |
+
}
|
| 136 |
+
});
|
| 137 |
+
|
| 138 |
+
if (!fs.existsSync(outputDir)) {
|
| 139 |
+
fs.mkdirSync(outputDir, { recursive: true });
|
| 140 |
+
}
|
| 141 |
+
|
| 142 |
+
const resultVideo = await stitchFramesToVideo({
|
| 143 |
+
assetsInfo: result.assetsInfo,
|
| 144 |
+
...renderConfig,
|
| 145 |
+
...renderConfig.composition,
|
| 146 |
+
audioCodec: 'mp3',
|
| 147 |
+
outputLocation: 'D:\\code\\node_projects\\semibit-media-render-farm\\out\\video.mp4',
|
| 148 |
+
verbose: true
|
| 149 |
+
})
|
| 150 |
+
|
| 151 |
+
console.log('resultVideo', resultVideo)
|
| 152 |
+
|
| 153 |
+
|
| 154 |
+
}
|