storyforge 0.7.5 → 0.8.1
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.
|
@@ -135,11 +135,11 @@ var require_p_limit = __commonJS({
|
|
|
135
135
|
// src/bridge-poller.ts
|
|
136
136
|
import { spawn as spawn2, spawnSync } from "child_process";
|
|
137
137
|
import * as crypto from "crypto";
|
|
138
|
-
import * as
|
|
138
|
+
import * as fs11 from "fs";
|
|
139
139
|
|
|
140
140
|
// ../pipeline/src/clip-render/render-chunk.ts
|
|
141
|
-
import * as
|
|
142
|
-
import * as
|
|
141
|
+
import * as fs10 from "fs";
|
|
142
|
+
import * as path11 from "path";
|
|
143
143
|
|
|
144
144
|
// ../../node_modules/zod/v3/external.js
|
|
145
145
|
var external_exports = {};
|
|
@@ -619,8 +619,8 @@ function getErrorMap() {
|
|
|
619
619
|
|
|
620
620
|
// ../../node_modules/zod/v3/helpers/parseUtil.js
|
|
621
621
|
var makeIssue = (params) => {
|
|
622
|
-
const { data, path:
|
|
623
|
-
const fullPath = [...
|
|
622
|
+
const { data, path: path12, errorMaps, issueData } = params;
|
|
623
|
+
const fullPath = [...path12, ...issueData.path || []];
|
|
624
624
|
const fullIssue = {
|
|
625
625
|
...issueData,
|
|
626
626
|
path: fullPath
|
|
@@ -736,11 +736,11 @@ var errorUtil;
|
|
|
736
736
|
|
|
737
737
|
// ../../node_modules/zod/v3/types.js
|
|
738
738
|
var ParseInputLazyPath = class {
|
|
739
|
-
constructor(parent, value,
|
|
739
|
+
constructor(parent, value, path12, key) {
|
|
740
740
|
this._cachedPath = [];
|
|
741
741
|
this.parent = parent;
|
|
742
742
|
this.data = value;
|
|
743
|
-
this._path =
|
|
743
|
+
this._path = path12;
|
|
744
744
|
this._key = key;
|
|
745
745
|
}
|
|
746
746
|
get path() {
|
|
@@ -5345,7 +5345,8 @@ var ENGINE_FALLBACK_CHAIN = {
|
|
|
5345
5345
|
hyperframes: "remotion",
|
|
5346
5346
|
"stock+remotion": "remotion",
|
|
5347
5347
|
manim: "remotion",
|
|
5348
|
-
remotion: null
|
|
5348
|
+
remotion: null,
|
|
5349
|
+
"documentary-v2": "gemini+remotion"
|
|
5349
5350
|
};
|
|
5350
5351
|
function nextFallback(engine) {
|
|
5351
5352
|
return ENGINE_FALLBACK_CHAIN[engine] ?? null;
|
|
@@ -6082,6 +6083,81 @@ var renderStockRemotion = async (shot, ctx) => {
|
|
|
6082
6083
|
}
|
|
6083
6084
|
};
|
|
6084
6085
|
|
|
6086
|
+
// ../pipeline/src/clip-render/engines/documentary-v2.ts
|
|
6087
|
+
import * as fs8 from "fs";
|
|
6088
|
+
import * as path9 from "path";
|
|
6089
|
+
var renderDocumentaryV2 = async (shot, ctx) => {
|
|
6090
|
+
if (ctx.signal?.aborted) throw new Error("aborted");
|
|
6091
|
+
if (!shot.prompt) {
|
|
6092
|
+
throw new Error(
|
|
6093
|
+
`documentary-v2: shot.prompt must be the path to a CompositionV2Data JSON file (shot ${shot.id})`
|
|
6094
|
+
);
|
|
6095
|
+
}
|
|
6096
|
+
ctx.progress.emit({ phase: "preparing", shotId: shot.id });
|
|
6097
|
+
let data;
|
|
6098
|
+
if (shot.prompt.trimStart().startsWith("{")) {
|
|
6099
|
+
data = JSON.parse(shot.prompt);
|
|
6100
|
+
} else {
|
|
6101
|
+
if (!fs8.existsSync(shot.prompt)) {
|
|
6102
|
+
throw new Error(`documentary-v2: CompositionV2Data file not found: ${shot.prompt}`);
|
|
6103
|
+
}
|
|
6104
|
+
data = JSON.parse(fs8.readFileSync(shot.prompt, "utf8"));
|
|
6105
|
+
}
|
|
6106
|
+
const durationFrames = data.totalDurationFrames > 0 ? data.totalDurationFrames : Math.max(1, Math.round(shot.durationSec * ctx.fps));
|
|
6107
|
+
const remotion = requireRemotionRoot();
|
|
6108
|
+
const cacheRel = path9.posix.join("clip-render-cache", ctx.chunkId);
|
|
6109
|
+
const cacheAbs = path9.join(remotion.cwd, "public", cacheRel);
|
|
6110
|
+
fs8.mkdirSync(cacheAbs, { recursive: true });
|
|
6111
|
+
for (const scene of data.scenes) {
|
|
6112
|
+
for (const layer of scene.layers) {
|
|
6113
|
+
if (!layer.assetPath) continue;
|
|
6114
|
+
if (path9.isAbsolute(layer.assetPath) && fs8.existsSync(layer.assetPath)) {
|
|
6115
|
+
const filename = path9.basename(layer.assetPath);
|
|
6116
|
+
const destAbs = path9.join(cacheAbs, filename);
|
|
6117
|
+
if (!fs8.existsSync(destAbs)) {
|
|
6118
|
+
fs8.copyFileSync(layer.assetPath, destAbs);
|
|
6119
|
+
}
|
|
6120
|
+
layer.assetPath = path9.posix.join(cacheRel, filename);
|
|
6121
|
+
}
|
|
6122
|
+
}
|
|
6123
|
+
}
|
|
6124
|
+
const propsPath = path9.join(ctx.tmpDir, "remotion", `${shot.id}_dv2_props.json`);
|
|
6125
|
+
fs8.mkdirSync(path9.dirname(propsPath), { recursive: true });
|
|
6126
|
+
fs8.writeFileSync(propsPath, JSON.stringify({ compositionData: data }, null, 2));
|
|
6127
|
+
ctx.progress.emit({
|
|
6128
|
+
phase: "rendering",
|
|
6129
|
+
shotId: shot.id,
|
|
6130
|
+
framesRendered: 0,
|
|
6131
|
+
totalFrames: durationFrames
|
|
6132
|
+
});
|
|
6133
|
+
const r = await runCmd(
|
|
6134
|
+
"npx",
|
|
6135
|
+
[
|
|
6136
|
+
"remotion",
|
|
6137
|
+
"render",
|
|
6138
|
+
remotion.entry,
|
|
6139
|
+
"DocumentaryV2",
|
|
6140
|
+
"--props",
|
|
6141
|
+
propsPath,
|
|
6142
|
+
"--output",
|
|
6143
|
+
ctx.sceneOutPath,
|
|
6144
|
+
"--concurrency",
|
|
6145
|
+
"4"
|
|
6146
|
+
],
|
|
6147
|
+
{ timeoutMs: 6e5, signal: ctx.signal, cwd: remotion.cwd }
|
|
6148
|
+
);
|
|
6149
|
+
if (r.code !== 0) {
|
|
6150
|
+
const tail = r.stderr.split("\n").slice(-8).join(" ").slice(0, 400);
|
|
6151
|
+
throw new Error(`documentary-v2 render failed (code ${r.code}): ${tail}`);
|
|
6152
|
+
}
|
|
6153
|
+
ctx.progress.emit({
|
|
6154
|
+
phase: "rendering",
|
|
6155
|
+
shotId: shot.id,
|
|
6156
|
+
framesRendered: durationFrames,
|
|
6157
|
+
totalFrames: durationFrames
|
|
6158
|
+
});
|
|
6159
|
+
};
|
|
6160
|
+
|
|
6085
6161
|
// ../pipeline/src/clip-render/engines/index.ts
|
|
6086
6162
|
var ENGINE_ADAPTERS = {
|
|
6087
6163
|
"gemini+remotion": renderGeminiRemotion,
|
|
@@ -6091,7 +6167,8 @@ var ENGINE_ADAPTERS = {
|
|
|
6091
6167
|
"remotion": renderRemotion,
|
|
6092
6168
|
"stock+remotion": renderStockRemotion,
|
|
6093
6169
|
"hyperframes": renderHyperframes,
|
|
6094
|
-
"remotion+htmlcanvas": renderRemotionHtmlCanvas
|
|
6170
|
+
"remotion+htmlcanvas": renderRemotionHtmlCanvas,
|
|
6171
|
+
"documentary-v2": renderDocumentaryV2
|
|
6095
6172
|
};
|
|
6096
6173
|
var MANIM_ENGINES = /* @__PURE__ */ new Set([
|
|
6097
6174
|
"manim",
|
|
@@ -6103,11 +6180,11 @@ function isManimEngine(engine) {
|
|
|
6103
6180
|
}
|
|
6104
6181
|
|
|
6105
6182
|
// ../pipeline/src/clip-render/fs-layout.ts
|
|
6106
|
-
import * as
|
|
6107
|
-
import * as
|
|
6183
|
+
import * as fs9 from "fs";
|
|
6184
|
+
import * as path10 from "path";
|
|
6108
6185
|
function resolveProjectRoot(projectSlug, callerOutputDir) {
|
|
6109
6186
|
if (callerOutputDir && callerOutputDir.length > 0) {
|
|
6110
|
-
if (!
|
|
6187
|
+
if (!path10.isAbsolute(callerOutputDir)) {
|
|
6111
6188
|
throw new Error(
|
|
6112
6189
|
`clip-render: spec.outputDir must be absolute, got "${callerOutputDir}"`
|
|
6113
6190
|
);
|
|
@@ -6117,41 +6194,41 @@ function resolveProjectRoot(projectSlug, callerOutputDir) {
|
|
|
6117
6194
|
if (!projectSlug || /[/\\]/.test(projectSlug)) {
|
|
6118
6195
|
throw new Error(`clip-render: invalid projectSlug "${projectSlug}"`);
|
|
6119
6196
|
}
|
|
6120
|
-
return
|
|
6197
|
+
return path10.resolve(process.cwd(), "forge-renders", projectSlug);
|
|
6121
6198
|
}
|
|
6122
6199
|
function chunkPaths(projectSlug, chunkId, callerOutputDir) {
|
|
6123
6200
|
if (!chunkId || /[/\\]/.test(chunkId)) {
|
|
6124
6201
|
throw new Error(`clip-render: invalid chunkId "${chunkId}"`);
|
|
6125
6202
|
}
|
|
6126
6203
|
const root = resolveProjectRoot(projectSlug, callerOutputDir);
|
|
6127
|
-
const tmpDir =
|
|
6204
|
+
const tmpDir = path10.join(root, ".tmp", chunkId);
|
|
6128
6205
|
return {
|
|
6129
6206
|
root,
|
|
6130
|
-
finalClipPath:
|
|
6207
|
+
finalClipPath: path10.join(root, "clips", `${chunkId}.mp4`),
|
|
6131
6208
|
tmpDir,
|
|
6132
|
-
scenesDir:
|
|
6133
|
-
manimDir:
|
|
6134
|
-
remotionDir:
|
|
6135
|
-
hyperframesDir:
|
|
6136
|
-
progressLogPath:
|
|
6209
|
+
scenesDir: path10.join(tmpDir, "scenes"),
|
|
6210
|
+
manimDir: path10.join(tmpDir, "manim"),
|
|
6211
|
+
remotionDir: path10.join(tmpDir, "remotion"),
|
|
6212
|
+
hyperframesDir: path10.join(tmpDir, "hyperframes"),
|
|
6213
|
+
progressLogPath: path10.join(root, "progress.json")
|
|
6137
6214
|
};
|
|
6138
6215
|
}
|
|
6139
6216
|
function ensureChunkDirs(paths) {
|
|
6140
6217
|
for (const dir of [
|
|
6141
|
-
|
|
6218
|
+
path10.dirname(paths.finalClipPath),
|
|
6142
6219
|
paths.tmpDir,
|
|
6143
6220
|
paths.scenesDir,
|
|
6144
6221
|
paths.manimDir,
|
|
6145
6222
|
paths.remotionDir,
|
|
6146
6223
|
paths.hyperframesDir
|
|
6147
6224
|
]) {
|
|
6148
|
-
|
|
6225
|
+
fs9.mkdirSync(dir, { recursive: true });
|
|
6149
6226
|
}
|
|
6150
6227
|
}
|
|
6151
6228
|
function appendProgress(progressLogPath, event) {
|
|
6152
6229
|
try {
|
|
6153
|
-
|
|
6154
|
-
|
|
6230
|
+
fs9.mkdirSync(path10.dirname(progressLogPath), { recursive: true });
|
|
6231
|
+
fs9.appendFileSync(progressLogPath, JSON.stringify(event) + "\n");
|
|
6155
6232
|
} catch {
|
|
6156
6233
|
}
|
|
6157
6234
|
}
|
|
@@ -6254,10 +6331,10 @@ async function renderChunk(spec, opts = {}) {
|
|
|
6254
6331
|
await renderWithEngine(spec, engine, paths, fps, tracker, opts);
|
|
6255
6332
|
tracker.emit({ phase: "compositing", percent: 85 });
|
|
6256
6333
|
const scenePaths = spec.shots.map((s) => sceneOutPath(paths, s));
|
|
6257
|
-
const concatTarget =
|
|
6334
|
+
const concatTarget = path11.join(paths.tmpDir, `${spec.chunkId}_video.mp4`);
|
|
6258
6335
|
await concatScenes(scenePaths, concatTarget, { signal: opts.signal });
|
|
6259
6336
|
if (opts.signal?.aborted) throw new Error("aborted");
|
|
6260
|
-
const audioAvailable = !!spec.audioPath &&
|
|
6337
|
+
const audioAvailable = !!spec.audioPath && fs10.existsSync(spec.audioPath);
|
|
6261
6338
|
if (audioAvailable) {
|
|
6262
6339
|
tracker.emit({ phase: "muxing", percent: 95 });
|
|
6263
6340
|
await muxAudio(concatTarget, spec.audioPath, paths.finalClipPath, {
|
|
@@ -6269,7 +6346,7 @@ async function renderChunk(spec, opts = {}) {
|
|
|
6269
6346
|
} else {
|
|
6270
6347
|
warnings.push("no audioPath in spec \u2014 wrote silent clip");
|
|
6271
6348
|
}
|
|
6272
|
-
|
|
6349
|
+
fs10.copyFileSync(concatTarget, paths.finalClipPath);
|
|
6273
6350
|
}
|
|
6274
6351
|
tracker.emit({ phase: "done", percent: 100 });
|
|
6275
6352
|
const renderTimeMs2 = (opts.now ?? Date.now)() - startedAt;
|
|
@@ -6308,7 +6385,7 @@ async function renderChunk(spec, opts = {}) {
|
|
|
6308
6385
|
};
|
|
6309
6386
|
}
|
|
6310
6387
|
function sceneOutPath(paths, shot) {
|
|
6311
|
-
return
|
|
6388
|
+
return path11.join(paths.scenesDir, `${shot.id}.mp4`);
|
|
6312
6389
|
}
|
|
6313
6390
|
function abortedResult(spec, engine, fallbackDepth, startedAt, now) {
|
|
6314
6391
|
return {
|
|
@@ -6341,7 +6418,7 @@ async function renderWithEngine(spec, engine, paths, fps, tracker, opts) {
|
|
|
6341
6418
|
progress: tracker,
|
|
6342
6419
|
resolvedEngine: engine
|
|
6343
6420
|
};
|
|
6344
|
-
|
|
6421
|
+
fs10.mkdirSync(path11.dirname(ctx.sceneOutPath), { recursive: true });
|
|
6345
6422
|
const release = opts.acquireManimSlot ? await opts.acquireManimSlot() : void 0;
|
|
6346
6423
|
try {
|
|
6347
6424
|
tracker.emit({
|
|
@@ -6353,7 +6430,7 @@ async function renderWithEngine(spec, engine, paths, fps, tracker, opts) {
|
|
|
6353
6430
|
} finally {
|
|
6354
6431
|
release?.();
|
|
6355
6432
|
}
|
|
6356
|
-
if (!
|
|
6433
|
+
if (!fs10.existsSync(ctx.sceneOutPath)) {
|
|
6357
6434
|
throw new Error(
|
|
6358
6435
|
`engine ${engine} produced no output at ${ctx.sceneOutPath} for shot ${shot.id}`
|
|
6359
6436
|
);
|
|
@@ -6537,8 +6614,8 @@ var BridgePoller = class {
|
|
|
6537
6614
|
if (!/^[a-z][a-z0-9-]*$/i.test(name)) return { available: false, path: null };
|
|
6538
6615
|
const r = spawnSync("which", [name], { encoding: "utf-8", timeout: 2e3 });
|
|
6539
6616
|
if (r.status !== 0) return { available: false, path: null };
|
|
6540
|
-
const
|
|
6541
|
-
return { available: !!
|
|
6617
|
+
const path12 = (r.stdout ?? "").trim();
|
|
6618
|
+
return { available: !!path12, path: path12 || null };
|
|
6542
6619
|
}
|
|
6543
6620
|
async heartbeat() {
|
|
6544
6621
|
if (this.stopped) return;
|
|
@@ -6808,12 +6885,12 @@ var BridgePoller = class {
|
|
|
6808
6885
|
*/
|
|
6809
6886
|
async uploadClipToStorj(bridgeJobId, projectId, spec, result) {
|
|
6810
6887
|
try {
|
|
6811
|
-
const
|
|
6812
|
-
const stat = await
|
|
6888
|
+
const fs12 = await import("fs/promises");
|
|
6889
|
+
const stat = await fs12.stat(result.outputPath);
|
|
6813
6890
|
if (!stat.isFile() || stat.size === 0) {
|
|
6814
6891
|
return { ok: false, error: `local clip empty or missing at ${result.outputPath}` };
|
|
6815
6892
|
}
|
|
6816
|
-
const buf = await
|
|
6893
|
+
const buf = await fs12.readFile(result.outputPath);
|
|
6817
6894
|
const durationFrames = Math.max(1, Math.round(result.durationSec * 30));
|
|
6818
6895
|
const form = new FormData();
|
|
6819
6896
|
form.append("bridgeJobId", bridgeJobId);
|
|
@@ -6878,9 +6955,9 @@ var BridgePoller = class {
|
|
|
6878
6955
|
error: `stitch-final job requires @forge/pipeline/stitch (not installed): ${err.message}`
|
|
6879
6956
|
};
|
|
6880
6957
|
}
|
|
6881
|
-
const
|
|
6882
|
-
const projectRoot =
|
|
6883
|
-
const clipsDir =
|
|
6958
|
+
const path12 = await import("path");
|
|
6959
|
+
const projectRoot = path12.resolve(process.cwd(), "forge-renders", payload.projectSlug);
|
|
6960
|
+
const clipsDir = path12.join(projectRoot, "clips");
|
|
6884
6961
|
const publish = async (phase, eventPayload) => {
|
|
6885
6962
|
try {
|
|
6886
6963
|
await fetch(`${this.baseUrl}/api/cli-bridge/stitch-event`, {
|
|
@@ -6906,7 +6983,7 @@ var BridgePoller = class {
|
|
|
6906
6983
|
projectSlug: payload.projectSlug,
|
|
6907
6984
|
clipsDir,
|
|
6908
6985
|
chunkOrder: payload.chunkOrder ?? [],
|
|
6909
|
-
masterAudioPath:
|
|
6986
|
+
masterAudioPath: path12.isAbsolute(masterAudioRel) ? masterAudioRel : path12.join(projectRoot, masterAudioRel),
|
|
6910
6987
|
outputDir: projectRoot,
|
|
6911
6988
|
aspect: payload.aspect,
|
|
6912
6989
|
skipTransitions: payload.skipTransitions,
|
|
@@ -7090,10 +7167,10 @@ function collectImagesFromCodexStdout(stdout, maxCount) {
|
|
|
7090
7167
|
for (const p of paths) {
|
|
7091
7168
|
if (images.length >= maxCount) break;
|
|
7092
7169
|
try {
|
|
7093
|
-
const stat =
|
|
7170
|
+
const stat = fs11.statSync(p);
|
|
7094
7171
|
if (!stat.isFile() || stat.size <= 0) continue;
|
|
7095
7172
|
images.push({
|
|
7096
|
-
base64:
|
|
7173
|
+
base64: fs11.readFileSync(p).toString("base64"),
|
|
7097
7174
|
mimeType: mimeTypeForPath(p),
|
|
7098
7175
|
model: "codex-cli:imagegen"
|
|
7099
7176
|
});
|
package/dist/index.js
CHANGED
|
@@ -1615,7 +1615,7 @@ Return ONLY the complete updated TSX. No markdown fences, no explanation.`;
|
|
|
1615
1615
|
return "0.0.0";
|
|
1616
1616
|
})();
|
|
1617
1617
|
void (async () => {
|
|
1618
|
-
const { BridgePoller } = await import("./bridge-poller-
|
|
1618
|
+
const { BridgePoller } = await import("./bridge-poller-HA7G7ILD.js");
|
|
1619
1619
|
const poller = new BridgePoller({ baseUrl: bridgeUrl, token: bridgeToken, clientVersion: `storyforge ${pkgVersion}` });
|
|
1620
1620
|
poller.start();
|
|
1621
1621
|
})();
|
|
@@ -1992,7 +1992,7 @@ async function installRenderersCommand(opts = {}) {
|
|
|
1992
1992
|
}
|
|
1993
1993
|
|
|
1994
1994
|
// src/index.ts
|
|
1995
|
-
var VERSION = "0.
|
|
1995
|
+
var VERSION = "0.8.1";
|
|
1996
1996
|
var HELP = `
|
|
1997
1997
|
storyforge \u2014 local bridge for the Forge video production web app
|
|
1998
1998
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "storyforge",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.1",
|
|
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": {
|