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-SJ6APEPZ.js";
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
- fs5.copyFileSync(manimSceneOut, ctx.sceneOutPath);
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 status = await runJob(
7018
- endpointId,
7019
- {
7020
- // Flash unpacks `input` via **kwargs into the handler. Our handler
7021
- // signature is `async def render_manim_scene(input_data: dict)`, so
7022
- // the payload must be nested under `input_data`.
7023
- input: {
7024
- input_data: {
7025
- python_source: shot.manimCode,
7026
- scene_class: "GeneratedScene",
7027
- fps: ctx.fps,
7028
- quality: "high"
7029
- // manim --quality h => 1080p60; we'll downsample at concat
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
- executionTimeout: 600
7033
- },
7034
- {
7035
- timeoutMs: 12 * 6e4,
7036
- pollIntervalMs: 3e3,
7037
- signal: ctx.signal,
7038
- onPoll: (s) => {
7039
- const phase = s.status === "IN_QUEUE" ? "preparing" : "rendering";
7040
- ctx.progress.emit({
7041
- phase,
7042
- shotId: shot.id,
7043
- warnings: s.delayTime ? [`runpod queue: ${(s.delayTime / 1e3).toFixed(1)}s`] : void 0
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
- const buf = decodeBase64Mp4(status.output);
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-MRZJ34KA.js");
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(manifestPath, outPath) {
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
- "-f",
88
- "concat",
89
- "-safe",
90
- "0",
91
- "-i",
92
- manifestPath,
93
- "-c",
94
- "copy",
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 manifestPath = path.join(path.dirname(outPath), `concat_${path.basename(outPath)}.txt`);
107
- const manifest = scenePaths.map((p) => `file '${p.replace(/'/g, "'\\''")}'`).join("\n") + "\n";
108
- fs.writeFileSync(manifestPath, manifest);
109
- try {
110
- const args = buildConcatArgs(manifestPath, outPath);
111
- const r = await runCmd("ffmpeg", args, opts);
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-6AWMIF3V.js");
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.0",
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: {
@@ -5,7 +5,7 @@ import {
5
5
  buildNormalizeClipArgs,
6
6
  probeDuration,
7
7
  runCmd
8
- } from "./chunk-SJ6APEPZ.js";
8
+ } from "./chunk-NSB32ZRA.js";
9
9
  import "./chunk-NSPRIPOP.js";
10
10
 
11
11
  // ../pipeline/src/stitch/stitch-final.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "storyforge",
3
- "version": "0.12.0",
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": {