storyforge 0.18.0 → 0.18.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.
@@ -1,12 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ FINAL_LOUDNORM_FILTER,
3
4
  buildCrossfadeArgs,
4
5
  buildFinalConcatArgs,
6
+ buildFinalEncodeArgs,
5
7
  buildNormalizeClipArgs,
6
8
  probeDuration,
7
9
  runCmd
8
- } from "./chunk-NSB32ZRA.js";
9
- import "./chunk-NSPRIPOP.js";
10
+ } from "./chunk-ZHECC3YI.js";
10
11
 
11
12
  // ../pipeline/src/stitch/stitch-final.ts
12
13
  import * as fs3 from "fs";
@@ -293,7 +294,8 @@ async function stitchFinalVideo(args) {
293
294
  }
294
295
  }
295
296
  const segmentPaths = [];
296
- if (args.skipTransitions) {
297
+ const skipTransitions = args.skipTransitions ?? true;
298
+ if (skipTransitions) {
297
299
  segmentPaths.push(...normalizedPaths);
298
300
  } else {
299
301
  emit("compositing", { percent: 65 });
@@ -433,14 +435,85 @@ async function stitchFinalVideo(args) {
433
435
  function sanitizeId(id) {
434
436
  return id.replace(/[^a-zA-Z0-9_.-]/g, "_").slice(0, 64);
435
437
  }
438
+
439
+ // ../pipeline/src/stitch/final-encode-contract.ts
440
+ function buildFinalEncodeContract() {
441
+ const tail = buildFinalEncodeArgs("");
442
+ const encodeTail = tail.slice(0, -1);
443
+ return {
444
+ crf: 8,
445
+ preset: "slow",
446
+ pixFmt: "yuv420p",
447
+ colorspace: "bt709",
448
+ colorRange: "tv",
449
+ profile: "high",
450
+ level: "4.1",
451
+ gop: 30,
452
+ audioCodec: "aac",
453
+ audioBitrate: "192k",
454
+ loudnormFilter: FINAL_LOUDNORM_FILTER,
455
+ encodeTail
456
+ };
457
+ }
458
+
459
+ // ../pipeline/src/stitch/resolve-channel-package.ts
460
+ var AGNOSTIC = "__any__";
461
+ function normAspect(raw) {
462
+ if (!raw) return "any";
463
+ const s = String(raw).trim().toLowerCase();
464
+ if (s === "16:9" || s === "horizontal" || s === "h") return "16:9";
465
+ if (s === "9:16" || s === "vertical" || s === "v") return "9:16";
466
+ return "any";
467
+ }
468
+ function pick(assets, type, wantAspect) {
469
+ const ofType = assets.filter((a) => a.type === type && a.storj_key);
470
+ if (ofType.length === 0) return void 0;
471
+ let chosen;
472
+ if (wantAspect === AGNOSTIC) {
473
+ chosen = ofType.find((a) => normAspect(a.aspect) === "any") ?? ofType[0];
474
+ } else {
475
+ chosen = ofType.find((a) => normAspect(a.aspect) === wantAspect);
476
+ }
477
+ if (!chosen) return void 0;
478
+ return { assetId: chosen.id, storjKey: chosen.storj_key, aspect: normAspect(chosen.aspect) };
479
+ }
480
+ function resolveChannelPackage(assets, aspect) {
481
+ const rows = Array.isArray(assets) ? assets : [];
482
+ return {
483
+ aspect,
484
+ watermark: pick(rows, "watermark", AGNOSTIC),
485
+ introMusic: pick(rows, "intro_music", AGNOSTIC),
486
+ outroMusic: pick(rows, "outro_music", AGNOSTIC),
487
+ introVideo: pick(rows, "intro_video", aspect),
488
+ outroVideo: pick(rows, "outro_video", aspect),
489
+ endCard: pick(rows, "end_card", aspect)
490
+ };
491
+ }
492
+ function channelPackageProvenance(pkg) {
493
+ const slots = {};
494
+ const add = (name, slot) => {
495
+ if (slot) slots[name] = { assetId: slot.assetId, storjKey: slot.storjKey, aspect: slot.aspect };
496
+ };
497
+ add("watermark", pkg.watermark);
498
+ add("intro_music", pkg.introMusic);
499
+ add("outro_music", pkg.outroMusic);
500
+ add("intro_video", pkg.introVideo);
501
+ add("outro_video", pkg.outroVideo);
502
+ add("end_card", pkg.endCard);
503
+ return { aspect: pkg.aspect, slots };
504
+ }
505
+
436
506
  export {
507
+ resolutionFor,
508
+ normalizeClip,
437
509
  LEGACY_SECTION_TRANSITION_ROOT,
510
+ renderSectionTransition,
511
+ renderCrossfadeFallback,
438
512
  concatWithExtras,
439
513
  formatSrtTime,
440
- normalizeClip,
441
- renderCrossfadeFallback,
442
- renderSectionTransition,
443
- resolutionFor,
444
514
  srtFromTimings,
445
- stitchFinalVideo
515
+ stitchFinalVideo,
516
+ buildFinalEncodeContract,
517
+ resolveChannelPackage,
518
+ channelPackageProvenance
446
519
  };
@@ -155,16 +155,10 @@ function buildNormalizeClipArgs(videoPath, outPath, opts) {
155
155
  );
156
156
  return args;
157
157
  }
158
- function buildFinalConcatArgs(opts) {
159
- const {
160
- manifestPath,
161
- outPath,
162
- watermarkPath,
163
- brandAudioPath,
164
- brandAudioVolume = 0.1
165
- } = opts;
166
- const vol = brandAudioVolume.toFixed(3);
167
- const encoderTail = [
158
+ var FINAL_LOUDNORM_FILTER = "loudnorm=I=-14:TP=-1.5:LRA=11";
159
+ function buildFinalEncodeArgs(outPath) {
160
+ return [
161
+ // ── video ──────────────────────────────────────────────────────────
168
162
  "-c:v",
169
163
  "libx264",
170
164
  "-preset",
@@ -179,12 +173,40 @@ function buildFinalConcatArgs(opts) {
179
173
  "bt709",
180
174
  "-color_trc",
181
175
  "bt709",
176
+ "-color_range",
177
+ "tv",
178
+ // x264-level VUI atoms — required so color_primaries/color_transfer land
179
+ // in the container on full-range yuvj/bt470bg (Remotion) sources.
180
+ "-x264-params",
181
+ "colorprim=bt709:transfer=bt709:colormatrix=bt709",
182
+ "-profile:v",
183
+ "high",
184
+ "-level",
185
+ "4.1",
186
+ "-g",
187
+ "30",
188
+ "-keyint_min",
189
+ "30",
190
+ "-movflags",
191
+ "+faststart",
192
+ // ── audio ──────────────────────────────────────────────────────────
182
193
  "-c:a",
183
194
  "aac",
184
195
  "-b:a",
185
196
  "192k",
186
197
  outPath
187
198
  ];
199
+ }
200
+ function buildFinalConcatArgs(opts) {
201
+ const {
202
+ manifestPath,
203
+ outPath,
204
+ watermarkPath,
205
+ brandAudioPath,
206
+ brandAudioVolume = 0.1
207
+ } = opts;
208
+ const vol = brandAudioVolume.toFixed(3);
209
+ const encoderTail = buildFinalEncodeArgs(outPath);
188
210
  if (watermarkPath && brandAudioPath) {
189
211
  return [
190
212
  "-y",
@@ -199,7 +221,7 @@ function buildFinalConcatArgs(opts) {
199
221
  "-i",
200
222
  brandAudioPath,
201
223
  "-filter_complex",
202
- `[0:v][1:v]overlay=W-w-20:H-h-20,format=yuv420p[vout];[2:a]volume=${vol}[bgm];[0:a][bgm]amix=inputs=2:duration=first:dropout_transition=2[aout]`,
224
+ `[0:v][1:v]overlay=W-w-20:H-h-20,format=yuv420p[vout];[2:a]volume=${vol}[bgm];[0:a][bgm]amix=inputs=2:duration=first:dropout_transition=2,${FINAL_LOUDNORM_FILTER}[aout]`,
203
225
  "-map",
204
226
  "[vout]",
205
227
  "-map",
@@ -219,11 +241,11 @@ function buildFinalConcatArgs(opts) {
219
241
  "-i",
220
242
  watermarkPath,
221
243
  "-filter_complex",
222
- `[0:v][1:v]overlay=W-w-20:H-h-20,format=yuv420p[vout]`,
244
+ `[0:v][1:v]overlay=W-w-20:H-h-20,format=yuv420p[vout];[0:a]${FINAL_LOUDNORM_FILTER}[aout]`,
223
245
  "-map",
224
246
  "[vout]",
225
247
  "-map",
226
- "0:a",
248
+ "[aout]",
227
249
  ...encoderTail
228
250
  ];
229
251
  }
@@ -239,7 +261,7 @@ function buildFinalConcatArgs(opts) {
239
261
  "-i",
240
262
  brandAudioPath,
241
263
  "-filter_complex",
242
- `[1:a]volume=${vol}[bgm];[0:a][bgm]amix=inputs=2:duration=first:dropout_transition=2[aout]`,
264
+ `[1:a]volume=${vol}[bgm];[0:a][bgm]amix=inputs=2:duration=first:dropout_transition=2,${FINAL_LOUDNORM_FILTER}[aout]`,
243
265
  "-map",
244
266
  "0:v",
245
267
  "-map",
@@ -255,6 +277,8 @@ function buildFinalConcatArgs(opts) {
255
277
  "0",
256
278
  "-i",
257
279
  manifestPath,
280
+ "-af",
281
+ FINAL_LOUDNORM_FILTER,
258
282
  ...encoderTail
259
283
  ];
260
284
  }
@@ -337,6 +361,8 @@ export {
337
361
  muxAudio,
338
362
  concatScenes,
339
363
  buildNormalizeClipArgs,
364
+ FINAL_LOUDNORM_FILTER,
365
+ buildFinalEncodeArgs,
340
366
  buildFinalConcatArgs,
341
367
  buildCrossfadeArgs,
342
368
  probeDuration,