storyforge 0.12.0 → 0.12.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
probeDuration,
|
|
6
6
|
renderPlaceholderShot,
|
|
7
7
|
runCmd
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-NSB32ZRA.js";
|
|
9
9
|
import {
|
|
10
10
|
log
|
|
11
11
|
} from "./chunk-GJQ45C5W.js";
|
|
@@ -5876,6 +5876,8 @@ var renderManimRemotion = async (shot, ctx) => {
|
|
|
5876
5876
|
propsPath,
|
|
5877
5877
|
"--output",
|
|
5878
5878
|
ctx.sceneOutPath,
|
|
5879
|
+
"--frames",
|
|
5880
|
+
`0-${totalFrames - 1}`,
|
|
5879
5881
|
"--concurrency",
|
|
5880
5882
|
"4"
|
|
5881
5883
|
],
|
|
@@ -5883,11 +5885,33 @@ var renderManimRemotion = async (shot, ctx) => {
|
|
|
5883
5885
|
);
|
|
5884
5886
|
if (r.code !== 0) {
|
|
5885
5887
|
const tail = r.stderr.split("\n").slice(-5).join(" ").slice(0, 300);
|
|
5886
|
-
|
|
5888
|
+
const clampRes = await runCmd(
|
|
5889
|
+
"ffmpeg",
|
|
5890
|
+
[
|
|
5891
|
+
"-y",
|
|
5892
|
+
"-i",
|
|
5893
|
+
manimSceneOut,
|
|
5894
|
+
"-t",
|
|
5895
|
+
String(shot.durationSec),
|
|
5896
|
+
"-c:v",
|
|
5897
|
+
"libx264",
|
|
5898
|
+
"-preset",
|
|
5899
|
+
"medium",
|
|
5900
|
+
"-crf",
|
|
5901
|
+
"18",
|
|
5902
|
+
"-pix_fmt",
|
|
5903
|
+
"yuv420p",
|
|
5904
|
+
ctx.sceneOutPath
|
|
5905
|
+
],
|
|
5906
|
+
{ timeoutMs: 6e4, signal: ctx.signal }
|
|
5907
|
+
);
|
|
5908
|
+
if (clampRes.code !== 0) {
|
|
5909
|
+
fs5.copyFileSync(manimSceneOut, ctx.sceneOutPath);
|
|
5910
|
+
}
|
|
5887
5911
|
ctx.progress.emit({
|
|
5888
5912
|
phase: "compositing",
|
|
5889
5913
|
shotId: shot.id,
|
|
5890
|
-
warnings: [`remotion overlay failed, used raw manim: ${tail}`]
|
|
5914
|
+
warnings: [`remotion overlay failed, used raw manim clamped to ${shot.durationSec}s: ${tail}`]
|
|
5891
5915
|
});
|
|
5892
5916
|
return;
|
|
5893
5917
|
}
|
|
@@ -7014,39 +7038,62 @@ var renderManimRunPod = async (shot, ctx) => {
|
|
|
7014
7038
|
return localAdapter(shot, ctx);
|
|
7015
7039
|
}
|
|
7016
7040
|
ctx.progress.emit({ phase: "preparing", shotId: shot.id });
|
|
7017
|
-
const
|
|
7018
|
-
|
|
7019
|
-
|
|
7020
|
-
|
|
7021
|
-
|
|
7022
|
-
|
|
7023
|
-
|
|
7024
|
-
input_data:
|
|
7025
|
-
|
|
7026
|
-
|
|
7027
|
-
|
|
7028
|
-
|
|
7029
|
-
|
|
7030
|
-
|
|
7041
|
+
const localFallback = ctx.resolvedEngine === "manim+remotion" ? renderManimRemotion : ctx.resolvedEngine === "gemini+manim+remotion" ? renderGeminiManimRemotion : renderManim;
|
|
7042
|
+
let status;
|
|
7043
|
+
try {
|
|
7044
|
+
status = await runJob(
|
|
7045
|
+
endpointId,
|
|
7046
|
+
{
|
|
7047
|
+
// Flash unpacks `input` via **kwargs into the handler. Our handler
|
|
7048
|
+
// signature is `async def render_manim_scene(input_data: dict)`, so
|
|
7049
|
+
// the payload must be nested under `input_data`.
|
|
7050
|
+
input: {
|
|
7051
|
+
input_data: {
|
|
7052
|
+
python_source: shot.manimCode,
|
|
7053
|
+
scene_class: "GeneratedScene",
|
|
7054
|
+
fps: ctx.fps,
|
|
7055
|
+
quality: "high"
|
|
7056
|
+
// manim --quality h => 1080p60; we'll downsample at concat
|
|
7057
|
+
}
|
|
7058
|
+
},
|
|
7059
|
+
executionTimeout: 600
|
|
7031
7060
|
},
|
|
7032
|
-
|
|
7033
|
-
|
|
7034
|
-
|
|
7035
|
-
|
|
7036
|
-
|
|
7037
|
-
|
|
7038
|
-
|
|
7039
|
-
|
|
7040
|
-
|
|
7041
|
-
|
|
7042
|
-
|
|
7043
|
-
|
|
7044
|
-
});
|
|
7061
|
+
{
|
|
7062
|
+
timeoutMs: 12 * 6e4,
|
|
7063
|
+
pollIntervalMs: 3e3,
|
|
7064
|
+
signal: ctx.signal,
|
|
7065
|
+
onPoll: (s) => {
|
|
7066
|
+
const phase = s.status === "IN_QUEUE" ? "preparing" : "rendering";
|
|
7067
|
+
ctx.progress.emit({
|
|
7068
|
+
phase,
|
|
7069
|
+
shotId: shot.id,
|
|
7070
|
+
warnings: s.delayTime ? [`runpod queue: ${(s.delayTime / 1e3).toFixed(1)}s`] : void 0
|
|
7071
|
+
});
|
|
7072
|
+
}
|
|
7045
7073
|
}
|
|
7046
|
-
|
|
7047
|
-
)
|
|
7074
|
+
);
|
|
7075
|
+
} catch (err) {
|
|
7076
|
+
const reason = err.message.slice(0, 200);
|
|
7077
|
+
ctx.progress.emit({
|
|
7078
|
+
phase: "preparing",
|
|
7079
|
+
shotId: shot.id,
|
|
7080
|
+
warnings: [`runpod-manim runtime failure \u2192 falling back to local manim: ${reason}`]
|
|
7081
|
+
});
|
|
7082
|
+
return localFallback(shot, ctx);
|
|
7083
|
+
}
|
|
7048
7084
|
ctx.progress.emit({ phase: "compositing", shotId: shot.id, percent: 90 });
|
|
7049
|
-
|
|
7085
|
+
let buf;
|
|
7086
|
+
try {
|
|
7087
|
+
buf = decodeBase64Mp4(status.output);
|
|
7088
|
+
} catch (err) {
|
|
7089
|
+
const reason = err.message.slice(0, 200);
|
|
7090
|
+
ctx.progress.emit({
|
|
7091
|
+
phase: "preparing",
|
|
7092
|
+
shotId: shot.id,
|
|
7093
|
+
warnings: [`runpod-manim decode failure \u2192 falling back to local manim: ${reason}`]
|
|
7094
|
+
});
|
|
7095
|
+
return localFallback(shot, ctx);
|
|
7096
|
+
}
|
|
7050
7097
|
fs13.writeFileSync(ctx.sceneOutPath, buf);
|
|
7051
7098
|
if (status.executionTime) {
|
|
7052
7099
|
const cost = status.executionTime / 1e3 * 19e-5;
|
|
@@ -8117,7 +8164,7 @@ var BridgePoller = class {
|
|
|
8117
8164
|
async invokeStitchFinal(jobId, payload) {
|
|
8118
8165
|
let stitchFinalVideo;
|
|
8119
8166
|
try {
|
|
8120
|
-
const mod = await import("./stitch-
|
|
8167
|
+
const mod = await import("./stitch-P3P6SK6M.js");
|
|
8121
8168
|
stitchFinalVideo = mod.stitchFinalVideo;
|
|
8122
8169
|
} catch (err) {
|
|
8123
8170
|
return {
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
// ../pipeline/src/clip-render/ffmpeg.ts
|
|
4
4
|
import { spawn } from "child_process";
|
|
5
5
|
import * as fs from "fs";
|
|
6
|
-
import * as path from "path";
|
|
7
6
|
var defaultRunner = (cmd, args, { timeoutMs = 3e5, signal, cwd }) => new Promise((resolve, reject) => {
|
|
8
7
|
const child = spawn(cmd, args, { stdio: ["pipe", "pipe", "pipe"], cwd });
|
|
9
8
|
let stdout = "";
|
|
@@ -81,17 +80,28 @@ async function muxAudio(videoPath, audioPath, outPath, opts = {}) {
|
|
|
81
80
|
);
|
|
82
81
|
}
|
|
83
82
|
}
|
|
84
|
-
function buildConcatArgs(
|
|
83
|
+
function buildConcatArgs(scenePaths, outPath) {
|
|
84
|
+
if (scenePaths.length === 0) {
|
|
85
|
+
throw new Error("buildConcatArgs: at least one scene required");
|
|
86
|
+
}
|
|
87
|
+
const inputs = scenePaths.flatMap((p) => ["-i", p]);
|
|
88
|
+
const filterChain = scenePaths.map((_, i) => `[${i}:v]`).join("");
|
|
89
|
+
const filter = `${filterChain}concat=n=${scenePaths.length}:v=1:a=0[v]`;
|
|
85
90
|
return [
|
|
86
91
|
"-y",
|
|
87
|
-
|
|
88
|
-
"
|
|
89
|
-
|
|
90
|
-
"
|
|
91
|
-
"
|
|
92
|
-
|
|
93
|
-
"
|
|
94
|
-
"
|
|
92
|
+
...inputs,
|
|
93
|
+
"-filter_complex",
|
|
94
|
+
filter,
|
|
95
|
+
"-map",
|
|
96
|
+
"[v]",
|
|
97
|
+
"-c:v",
|
|
98
|
+
"libx264",
|
|
99
|
+
"-preset",
|
|
100
|
+
"medium",
|
|
101
|
+
"-crf",
|
|
102
|
+
"18",
|
|
103
|
+
"-pix_fmt",
|
|
104
|
+
"yuv420p",
|
|
95
105
|
outPath
|
|
96
106
|
];
|
|
97
107
|
}
|
|
@@ -103,22 +113,12 @@ async function concatScenes(scenePaths, outPath, opts = {}) {
|
|
|
103
113
|
fs.copyFileSync(scenePaths[0], outPath);
|
|
104
114
|
return outPath;
|
|
105
115
|
}
|
|
106
|
-
const
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
if (r.code !== 0) {
|
|
113
|
-
throw new Error(
|
|
114
|
-
`ffmpeg concat failed (code ${r.code}): ${r.stderr.split("\n").slice(-5).join(" ")}`
|
|
115
|
-
);
|
|
116
|
-
}
|
|
117
|
-
} finally {
|
|
118
|
-
try {
|
|
119
|
-
fs.unlinkSync(manifestPath);
|
|
120
|
-
} catch {
|
|
121
|
-
}
|
|
116
|
+
const args = buildConcatArgs(scenePaths, outPath);
|
|
117
|
+
const r = await runCmd("ffmpeg", args, opts);
|
|
118
|
+
if (r.code !== 0) {
|
|
119
|
+
throw new Error(
|
|
120
|
+
`ffmpeg concat failed (code ${r.code}): ${r.stderr.split("\n").slice(-5).join(" ")}`
|
|
121
|
+
);
|
|
122
122
|
}
|
|
123
123
|
return outPath;
|
|
124
124
|
}
|
package/dist/index.js
CHANGED
|
@@ -1689,7 +1689,7 @@ Return ONLY the complete updated TSX. No markdown fences, no explanation.`;
|
|
|
1689
1689
|
return "0.0.0";
|
|
1690
1690
|
})();
|
|
1691
1691
|
void (async () => {
|
|
1692
|
-
const { BridgePoller } = await import("./bridge-poller-
|
|
1692
|
+
const { BridgePoller } = await import("./bridge-poller-TZM5T7CN.js");
|
|
1693
1693
|
const poller = new BridgePoller({ baseUrl: bridgeUrl, token: bridgeToken2, clientVersion: `storyforge ${pkgVersion}` });
|
|
1694
1694
|
poller.start();
|
|
1695
1695
|
})();
|
|
@@ -2512,7 +2512,7 @@ function resolveBridgeToken2(explicit) {
|
|
|
2512
2512
|
// package.json
|
|
2513
2513
|
var package_default = {
|
|
2514
2514
|
name: "storyforge",
|
|
2515
|
-
version: "0.12.
|
|
2515
|
+
version: "0.12.2",
|
|
2516
2516
|
description: "StoryForge \u2014 local bridge for the Forge video production web app. Parallel clip-render orchestrator (Remotion 4 + Manim + HyperFrames + ffmpeg) + final video stitcher + dependency doctor.",
|
|
2517
2517
|
type: "module",
|
|
2518
2518
|
bin: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "storyforge",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.2",
|
|
4
4
|
"description": "StoryForge — local bridge for the Forge video production web app. Parallel clip-render orchestrator (Remotion 4 + Manim + HyperFrames + ffmpeg) + final video stitcher + dependency doctor.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|