@hyperframes/producer 0.8.224 → 0.8.226
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.
- package/dist/distributed.js +63 -45
- package/dist/hyperframe.manifest.json +1 -1
- package/dist/hyperframe.runtime.iife.js +67 -67
- package/dist/index.js +308 -140
- package/dist/public-server.js +223 -55
- package/dist/services/render/perfSummary.d.ts +5 -1
- package/dist/services/render/stages/extractVideosStage.d.ts +2 -1
- package/dist/services/render/videoLayerPassthroughPlan.d.ts +30 -0
- package/package.json +7 -7
package/dist/public-server.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/server.ts
|
|
4
4
|
import {
|
|
5
|
-
existsSync as
|
|
5
|
+
existsSync as existsSync20,
|
|
6
6
|
mkdirSync as mkdirSync11,
|
|
7
7
|
statSync as statSync10,
|
|
8
8
|
mkdtempSync as mkdtempSync4,
|
|
@@ -22,9 +22,8 @@ import { fetchPublicHttpsText as fetchPublicHttpsText2 } from "@hyperframes/engi
|
|
|
22
22
|
// src/services/renderOrchestrator.ts
|
|
23
23
|
import { audioFailureDiagnostics as audioFailureDiagnostics2 } from "@hyperframes/engine";
|
|
24
24
|
import {
|
|
25
|
-
existsSync as
|
|
25
|
+
existsSync as existsSync17,
|
|
26
26
|
mkdirSync as mkdirSync10,
|
|
27
|
-
readFileSync as readFileSync9,
|
|
28
27
|
readdirSync as readdirSync5,
|
|
29
28
|
rmSync as rmSync10,
|
|
30
29
|
statSync as statSync8,
|
|
@@ -45,6 +44,7 @@ import {
|
|
|
45
44
|
import { HF_AUDIO_GROUP_TAG } from "@hyperframes/core/audio-groups";
|
|
46
45
|
import { extractStandaloneEntryFromIndex, isTemplateEntry } from "@hyperframes/core/compiler";
|
|
47
46
|
import { HTML_BODY_CSS_HEIGHT_FIRST_RE, HTML_BODY_CSS_WIDTH_FIRST_RE } from "@hyperframes/parsers";
|
|
47
|
+
import { readTextFile as readTextFile2 } from "@hyperframes/parsers/asset-resolution";
|
|
48
48
|
import {
|
|
49
49
|
extractMediaMetadata as extractMediaMetadata4,
|
|
50
50
|
resolveConfig as resolveConfig2,
|
|
@@ -2151,6 +2151,14 @@ function pushWorkerDedupPerfs(results, sink) {
|
|
|
2151
2151
|
if (r.perf) sink.push(r.perf);
|
|
2152
2152
|
}
|
|
2153
2153
|
}
|
|
2154
|
+
function capturedBeyondViewport(perfs, fallback) {
|
|
2155
|
+
const used = perfs.flatMap((perf) => perf.captureBeyondViewport ?? []);
|
|
2156
|
+
return used.length > 0 ? used.includes(true) : fallback;
|
|
2157
|
+
}
|
|
2158
|
+
function withCapturedBeyondViewport(capture, perfs) {
|
|
2159
|
+
const used = capturedBeyondViewport(perfs, capture.captureBeyondViewport);
|
|
2160
|
+
return used === void 0 ? capture : { ...capture, captureBeyondViewport: used };
|
|
2161
|
+
}
|
|
2154
2162
|
function pushWorkerPageErrors(results, sink) {
|
|
2155
2163
|
for (const result of results) {
|
|
2156
2164
|
for (const line of result.pageErrorLines ?? []) if (!sink.includes(line)) sink.push(line);
|
|
@@ -5783,7 +5791,7 @@ import { suggestMatchingPreset } from "@hyperframes/core";
|
|
|
5783
5791
|
// src/services/htmlCompiler.ts
|
|
5784
5792
|
import { decodedUrlPath } from "@hyperframes/parsers/asset-paths";
|
|
5785
5793
|
import { createHash as createHash6 } from "node:crypto";
|
|
5786
|
-
import { createReadStream as createReadStream2, existsSync as existsSync10, mkdirSync as mkdirSync7
|
|
5794
|
+
import { createReadStream as createReadStream2, existsSync as existsSync10, mkdirSync as mkdirSync7 } from "fs";
|
|
5787
5795
|
import { join as join15, dirname as dirname7, resolve as resolve7, basename as basename4, relative as relative2 } from "path";
|
|
5788
5796
|
import { parseHTML as parseHTML5 } from "linkedom";
|
|
5789
5797
|
import {
|
|
@@ -5836,7 +5844,8 @@ import {
|
|
|
5836
5844
|
} from "@hyperframes/parsers/sub-composition-validity";
|
|
5837
5845
|
import {
|
|
5838
5846
|
isUnresolvedAssetPlaceholder,
|
|
5839
|
-
readProjectFile
|
|
5847
|
+
readProjectFile,
|
|
5848
|
+
readTextFile
|
|
5840
5849
|
} from "@hyperframes/parsers/asset-resolution";
|
|
5841
5850
|
|
|
5842
5851
|
// src/services/renderMediaCollector.ts
|
|
@@ -7925,11 +7934,7 @@ function rebaseDirectEntryAssetPaths(html, projectDir, htmlPath) {
|
|
|
7925
7934
|
return document2.toString();
|
|
7926
7935
|
}
|
|
7927
7936
|
async function compileForRender(projectDir, htmlPath, downloadDir, options = {}) {
|
|
7928
|
-
const entryHtml = rebaseDirectEntryAssetPaths(
|
|
7929
|
-
readFileSync7(htmlPath, "utf-8"),
|
|
7930
|
-
projectDir,
|
|
7931
|
-
htmlPath
|
|
7932
|
-
);
|
|
7937
|
+
const entryHtml = rebaseDirectEntryAssetPaths(readTextFile(htmlPath), projectDir, htmlPath);
|
|
7933
7938
|
const rawHtml = rewriteUnresolvableGsapToCdn(entryHtml, projectDir);
|
|
7934
7939
|
assertSubCompositionsUsable(rawHtml, projectDir);
|
|
7935
7940
|
const probes = /* @__PURE__ */ new Map();
|
|
@@ -8780,7 +8785,7 @@ async function runProbeStage(input) {
|
|
|
8780
8785
|
);
|
|
8781
8786
|
const hasVariableMedia = hasVariableBoundMedia(compiled.html, job.config.variables);
|
|
8782
8787
|
const hasInsertedMedia = hasRuntimeMediaChanges(compiled.html);
|
|
8783
|
-
const needsBrowser = probeRequiresBrowser({
|
|
8788
|
+
const needsBrowser = cfg.videoLayerPassthrough || probeRequiresBrowser({
|
|
8784
8789
|
durationSeconds: composition.duration,
|
|
8785
8790
|
unresolvedCompositionCount: compiled.unresolvedCompositions.length,
|
|
8786
8791
|
hasAutoStart,
|
|
@@ -9380,17 +9385,23 @@ function resolveVideoExtractionPolicy(env = process.env) {
|
|
|
9380
9385
|
const maxTransientRetries = failureMode !== "off" && env.HF_VIDEO_EXTRACTION_MAX_RETRIES?.trim() === "1" ? 1 : 0;
|
|
9381
9386
|
return { failureMode, maxTransientRetries };
|
|
9382
9387
|
}
|
|
9388
|
+
var MAX_NAMED_SOURCES = 5;
|
|
9383
9389
|
var VideoExtractionStageError = class extends Error {
|
|
9384
9390
|
constructor(code, retryable, failures, extractionFailure = buildExtractionFailureMetadata(
|
|
9385
9391
|
failures.map((failure) => ({
|
|
9386
9392
|
kind: failure.kind,
|
|
9387
9393
|
affectedElementCount: failure.count
|
|
9388
9394
|
}))
|
|
9389
|
-
), ffmpegReason) {
|
|
9395
|
+
), ffmpegReason, sources = []) {
|
|
9390
9396
|
const total = failures.reduce((sum, failure) => sum + failure.count, 0);
|
|
9391
9397
|
const breakdown = failures.map((failure) => `${failure.kind}=${failure.count}`).join(",");
|
|
9392
9398
|
const reason = ffmpegReason ? `: ffmpeg said: ${ffmpegReason}` : "";
|
|
9393
|
-
|
|
9399
|
+
const shown = sources.slice(0, MAX_NAMED_SOURCES).join(", ");
|
|
9400
|
+
const more = sources.length > MAX_NAMED_SOURCES ? ` and ${sources.length - MAX_NAMED_SOURCES} more` : "";
|
|
9401
|
+
const files = sources.length > 0 ? ` (${shown}${more})` : "";
|
|
9402
|
+
super(
|
|
9403
|
+
`Video extraction failed for ${total} source(s) [${code}; ${breakdown}]${files}${reason}`
|
|
9404
|
+
);
|
|
9394
9405
|
this.code = code;
|
|
9395
9406
|
this.retryable = retryable;
|
|
9396
9407
|
this.failures = failures;
|
|
@@ -9433,7 +9444,10 @@ function buildVideoExtractionStageError(result) {
|
|
|
9433
9444
|
retryable,
|
|
9434
9445
|
failures,
|
|
9435
9446
|
extractionFailureMetadataFromResult(result),
|
|
9436
|
-
result.errors.find((failure) => failure.ffmpegReason)?.ffmpegReason
|
|
9447
|
+
result.errors.find((failure) => failure.ffmpegReason)?.ffmpegReason,
|
|
9448
|
+
[
|
|
9449
|
+
...new Set(result.errors.flatMap((failure) => failure.source ? [failure.source] : []))
|
|
9450
|
+
].sort()
|
|
9437
9451
|
);
|
|
9438
9452
|
}
|
|
9439
9453
|
function buildHdrProbeStageError(failures) {
|
|
@@ -9512,7 +9526,8 @@ async function runExtractVideosStage(input) {
|
|
|
9512
9526
|
assertNotAborted,
|
|
9513
9527
|
materializeSymlinks,
|
|
9514
9528
|
deferRangeExtraction,
|
|
9515
|
-
urlByAbsentLocalPath
|
|
9529
|
+
urlByAbsentLocalPath,
|
|
9530
|
+
skipFrameExtractionIds = []
|
|
9516
9531
|
} = input;
|
|
9517
9532
|
const stage2Start = Date.now();
|
|
9518
9533
|
const extractionPolicy = resolveVideoExtractionPolicy();
|
|
@@ -9583,7 +9598,10 @@ async function runExtractVideosStage(input) {
|
|
|
9583
9598
|
);
|
|
9584
9599
|
imageColorSpaces.push(...probed);
|
|
9585
9600
|
}
|
|
9586
|
-
if (
|
|
9601
|
+
if (skipFrameExtractionIds.length > 0) {
|
|
9602
|
+
await appendVideoAudioFromProbe({ projectDir, compiledDir, composition, urlByAbsentLocalPath });
|
|
9603
|
+
videoReadinessSkipIds = [...skipFrameExtractionIds];
|
|
9604
|
+
} else if (composition.videos.length > 0) {
|
|
9587
9605
|
const totalVideos = composition.videos.length;
|
|
9588
9606
|
for (let i = 0; i < totalVideos; i++) {
|
|
9589
9607
|
const v = composition.videos[i];
|
|
@@ -9679,6 +9697,116 @@ function appendAutoDetectedVideoAudio(composition, extracted) {
|
|
|
9679
9697
|
existingAudioSrcs.add(video.src);
|
|
9680
9698
|
}
|
|
9681
9699
|
}
|
|
9700
|
+
async function appendVideoAudioFromProbe(input) {
|
|
9701
|
+
const { projectDir, compiledDir, composition, urlByAbsentLocalPath } = input;
|
|
9702
|
+
const videos = composition.videos.filter(
|
|
9703
|
+
(video) => !isVideoPastTimelineEnd(video, composition.duration)
|
|
9704
|
+
);
|
|
9705
|
+
const probed = await Promise.all(
|
|
9706
|
+
videos.map(async (video) => {
|
|
9707
|
+
const path = isHttpUrl2(video.src) ? video.src : resolveProjectRelativeSrc3(video.src, projectDir, compiledDir);
|
|
9708
|
+
const readFrom = urlByAbsentLocalPath?.get(path) ?? path;
|
|
9709
|
+
if (!isHttpUrl2(readFrom) && !existsSync11(readFrom)) return null;
|
|
9710
|
+
const metadata = await extractMediaMetadata3(readFrom);
|
|
9711
|
+
const window2 = await resolveFinalFrameExtractionWindow2(
|
|
9712
|
+
readFrom,
|
|
9713
|
+
video,
|
|
9714
|
+
metadata,
|
|
9715
|
+
resolveVideoExtractionWindow2(video, metadata, composition.duration)
|
|
9716
|
+
);
|
|
9717
|
+
if (window2.durationSeconds <= 0) return null;
|
|
9718
|
+
rebaseVideoToWindow(video, window2);
|
|
9719
|
+
return { videoId: video.id, metadata };
|
|
9720
|
+
})
|
|
9721
|
+
);
|
|
9722
|
+
appendAutoDetectedVideoAudio(
|
|
9723
|
+
composition,
|
|
9724
|
+
probed.filter((entry) => entry !== null)
|
|
9725
|
+
);
|
|
9726
|
+
}
|
|
9727
|
+
|
|
9728
|
+
// src/services/render/videoLayerPassthroughPlan.ts
|
|
9729
|
+
import { existsSync as existsSync12 } from "node:fs";
|
|
9730
|
+
import {
|
|
9731
|
+
buildVideoLayerInput,
|
|
9732
|
+
isHttpUrl as isHttpUrl3,
|
|
9733
|
+
qualifyVideoLayer,
|
|
9734
|
+
resolveProjectRelativeSrc as resolveProjectRelativeSrc4
|
|
9735
|
+
} from "@hyperframes/engine";
|
|
9736
|
+
var SAMPLES = 9;
|
|
9737
|
+
function inspectVideoLayout(id, w, h, time) {
|
|
9738
|
+
window.__hf?.seek?.(time);
|
|
9739
|
+
const video = document.getElementById(id);
|
|
9740
|
+
if (!video) return { reason: "video_missing" };
|
|
9741
|
+
const r = video.getBoundingClientRect();
|
|
9742
|
+
if (Math.abs(r.left) > 0.5 || Math.abs(r.top) > 0.5) return { reason: "not_full_canvas" };
|
|
9743
|
+
if (Math.abs(r.width - w) > 0.5 || Math.abs(r.height - h) > 0.5) {
|
|
9744
|
+
return { reason: "not_full_canvas" };
|
|
9745
|
+
}
|
|
9746
|
+
const painted = (s) => s.backgroundColor !== "" && s.backgroundColor !== "transparent" && s.backgroundColor !== "rgba(0, 0, 0, 0)" || s.backgroundImage !== "" && s.backgroundImage !== "none";
|
|
9747
|
+
for (let e = video; e && e !== document.documentElement; ) {
|
|
9748
|
+
const s = getComputedStyle(e);
|
|
9749
|
+
if (s.opacity !== "1") return { reason: "opacity" };
|
|
9750
|
+
if (s.transform !== "none" || s.filter !== "none") return { reason: "transform_or_filter" };
|
|
9751
|
+
if (s.clipPath !== "none" || s.maskImage !== "none") return { reason: "clip_or_mask" };
|
|
9752
|
+
if (s.mixBlendMode !== "normal") return { reason: "blend_mode" };
|
|
9753
|
+
if (s.visibility !== "visible" || s.display === "none") return { reason: "not_shown" };
|
|
9754
|
+
const cleared = e === document.body || e.hasAttribute("data-composition-id");
|
|
9755
|
+
if (e !== video && painted(s) && !cleared) return { reason: "ancestor_background" };
|
|
9756
|
+
e = e.parentElement;
|
|
9757
|
+
}
|
|
9758
|
+
for (const root of Array.from(document.querySelectorAll("[data-composition-id]"))) {
|
|
9759
|
+
if (root.parentElement?.closest("[data-composition-id]") && painted(getComputedStyle(root))) {
|
|
9760
|
+
return { reason: "subcomposition_background" };
|
|
9761
|
+
}
|
|
9762
|
+
}
|
|
9763
|
+
const fitStyle = getComputedStyle(video).objectFit;
|
|
9764
|
+
if (fitStyle !== "fill" && fitStyle !== "cover") return { reason: "object_fit" };
|
|
9765
|
+
const own = Number.parseInt(getComputedStyle(video).zIndex, 10) || 0;
|
|
9766
|
+
for (const other of Array.from(document.body.querySelectorAll("*"))) {
|
|
9767
|
+
if (other === video || other.contains(video) || video.contains(other)) continue;
|
|
9768
|
+
if (other.tagName === "SCRIPT" || other.tagName === "STYLE") continue;
|
|
9769
|
+
const z = Number.parseInt(getComputedStyle(other).zIndex, 10) || 0;
|
|
9770
|
+
const before = video.compareDocumentPosition(other) & Node.DOCUMENT_POSITION_PRECEDING;
|
|
9771
|
+
if (z < own || z === own && before) {
|
|
9772
|
+
const box = other.getBoundingClientRect();
|
|
9773
|
+
if (box.width > 0 && box.height > 0) return { reason: "paints_under" };
|
|
9774
|
+
}
|
|
9775
|
+
}
|
|
9776
|
+
return { fit: fitStyle === "cover" ? "cover" : "fill" };
|
|
9777
|
+
}
|
|
9778
|
+
async function probeVideoLayout(page, videoId, canvas, duration) {
|
|
9779
|
+
let fit = "fill";
|
|
9780
|
+
for (let i = 0; i < SAMPLES; i++) {
|
|
9781
|
+
const t = duration * i / SAMPLES;
|
|
9782
|
+
const seen = await page.evaluate(inspectVideoLayout, videoId, canvas.width, canvas.height, t);
|
|
9783
|
+
if ("reason" in seen) return seen;
|
|
9784
|
+
if (seen.fit === "cover") fit = "cover";
|
|
9785
|
+
}
|
|
9786
|
+
await page.evaluate(
|
|
9787
|
+
() => window.__hf?.seek?.(0)
|
|
9788
|
+
);
|
|
9789
|
+
return { fit };
|
|
9790
|
+
}
|
|
9791
|
+
async function planVideoLayerPassthrough(input) {
|
|
9792
|
+
const q = qualifyVideoLayer(input.videos, input.duration);
|
|
9793
|
+
if (!q.qualifies) return { reason: q.reason };
|
|
9794
|
+
const { video } = q;
|
|
9795
|
+
if (isHttpUrl3(video.src)) return { reason: "remote_source" };
|
|
9796
|
+
const path = resolveProjectRelativeSrc4(video.src, input.projectDir, input.compiledDir);
|
|
9797
|
+
if (!existsSync12(path)) return { reason: "source_missing" };
|
|
9798
|
+
const layout = await probeVideoLayout(input.page, video.id, input.canvas, input.duration);
|
|
9799
|
+
if ("reason" in layout) return layout;
|
|
9800
|
+
const layer = await buildVideoLayerInput(
|
|
9801
|
+
path,
|
|
9802
|
+
video.mediaStart,
|
|
9803
|
+
input.duration,
|
|
9804
|
+
input.fps,
|
|
9805
|
+
input.canvas,
|
|
9806
|
+
layout.fit
|
|
9807
|
+
);
|
|
9808
|
+
return layer ? { videoId: video.id, layer } : { reason: "source_unsupported" };
|
|
9809
|
+
}
|
|
9682
9810
|
|
|
9683
9811
|
// src/services/render/stages/audioStage.ts
|
|
9684
9812
|
import { join as join20 } from "node:path";
|
|
@@ -10290,7 +10418,7 @@ async function mixAudioForDelivery(input, duration, pcmSourcePath) {
|
|
|
10290
10418
|
}
|
|
10291
10419
|
|
|
10292
10420
|
// src/services/render/stages/captureStage.ts
|
|
10293
|
-
import { existsSync as
|
|
10421
|
+
import { existsSync as existsSync13, readdirSync as readdirSync3, statfsSync as statfsSync2 } from "node:fs";
|
|
10294
10422
|
import { join as join21 } from "node:path";
|
|
10295
10423
|
import {
|
|
10296
10424
|
classifyCaptureFailure as classifyCaptureFailure2,
|
|
@@ -10361,7 +10489,7 @@ function diskCaptureShortfallError(needBytes, freeBytes, framesDir, basis, remed
|
|
|
10361
10489
|
}
|
|
10362
10490
|
function measureCaptureFrameBytes(workDir, framesDir) {
|
|
10363
10491
|
let total = sampleDirectoryBytes(framesDir);
|
|
10364
|
-
if (!
|
|
10492
|
+
if (!existsSync13(workDir)) return total;
|
|
10365
10493
|
for (const attempt of readdirSync3(workDir)) {
|
|
10366
10494
|
if (/^retry-\d+-batch-\d+-worker-\d+$/.test(attempt)) {
|
|
10367
10495
|
total += sampleDirectoryBytes(join21(workDir, attempt));
|
|
@@ -10664,7 +10792,7 @@ async function captureSessionFrames(session, { input, rangeStart, rangeEnd, resu
|
|
|
10664
10792
|
}
|
|
10665
10793
|
|
|
10666
10794
|
// src/services/render/stages/captureHdrStage.ts
|
|
10667
|
-
import { existsSync as
|
|
10795
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync8 } from "node:fs";
|
|
10668
10796
|
import { join as join25 } from "node:path";
|
|
10669
10797
|
import {
|
|
10670
10798
|
closeCaptureSession as closeCaptureSession7,
|
|
@@ -10910,7 +11038,7 @@ import { Worker } from "node:worker_threads";
|
|
|
10910
11038
|
import { fileURLToPath as fileURLToPath3, pathToFileURL } from "node:url";
|
|
10911
11039
|
import { dirname as dirname9, join as join23 } from "node:path";
|
|
10912
11040
|
import { createRequire } from "node:module";
|
|
10913
|
-
import { existsSync as
|
|
11041
|
+
import { existsSync as existsSync14 } from "node:fs";
|
|
10914
11042
|
import { cpus as cpus2 } from "node:os";
|
|
10915
11043
|
function resolveWorkerEntry(explicit) {
|
|
10916
11044
|
if (explicit && explicit.length > 0) {
|
|
@@ -10923,7 +11051,7 @@ function resolveWorkerEntry(explicit) {
|
|
|
10923
11051
|
}
|
|
10924
11052
|
const moduleDir = dirname9(fileURLToPath3(import.meta.url));
|
|
10925
11053
|
const jsPath = join23(moduleDir, "shaderTransitionWorker.js");
|
|
10926
|
-
if (
|
|
11054
|
+
if (existsSync14(jsPath)) return { path: jsPath, isTs: false };
|
|
10927
11055
|
const tsPath = join23(moduleDir, "shaderTransitionWorker.ts");
|
|
10928
11056
|
return { path: tsPath, isTs: true };
|
|
10929
11057
|
}
|
|
@@ -11484,7 +11612,7 @@ async function runCaptureHdrStage(input) {
|
|
|
11484
11612
|
}
|
|
11485
11613
|
const debugDumpEnabled = process.env.KEEP_TEMP === "1";
|
|
11486
11614
|
const debugDumpDir = debugDumpEnabled ? join25(framesDir, "debug-composite") : null;
|
|
11487
|
-
if (debugDumpDir && !
|
|
11615
|
+
if (debugDumpDir && !existsSync15(debugDumpDir)) {
|
|
11488
11616
|
mkdirSync8(debugDumpDir, { recursive: true });
|
|
11489
11617
|
}
|
|
11490
11618
|
const compositeTransfer = resolveCompositeTransfer(hasHdrContent, effectiveHdr);
|
|
@@ -11641,10 +11769,10 @@ async function runCaptureHdrStage(input) {
|
|
|
11641
11769
|
// src/services/render/stages/encodeStage.ts
|
|
11642
11770
|
import {
|
|
11643
11771
|
copyFileSync as copyFileSync4,
|
|
11644
|
-
existsSync as
|
|
11772
|
+
existsSync as existsSync16,
|
|
11645
11773
|
mkdirSync as mkdirSync9,
|
|
11646
11774
|
readdirSync as readdirSync4,
|
|
11647
|
-
readFileSync as
|
|
11775
|
+
readFileSync as readFileSync7,
|
|
11648
11776
|
rmSync as rmSync9,
|
|
11649
11777
|
statSync as statSync7,
|
|
11650
11778
|
writeFileSync as writeFileSync5
|
|
@@ -11738,7 +11866,7 @@ async function reencodeWithClearedWholeFrames(argsInput, run, startTime) {
|
|
|
11738
11866
|
const error = formatFfmpegError(result.exitCode, result.stderr);
|
|
11739
11867
|
return gifEncodeFailure(startTime, outputPath, error, result.failureReason);
|
|
11740
11868
|
}
|
|
11741
|
-
const gif =
|
|
11869
|
+
const gif = readFileSync7(outputPath);
|
|
11742
11870
|
if (!clearGifFramesBeforeNext(gif)) {
|
|
11743
11871
|
const error = "[FFmpeg] GIF output could not be parsed to clear its frames";
|
|
11744
11872
|
return gifEncodeFailure(startTime, outputPath, error);
|
|
@@ -11771,11 +11899,11 @@ async function encodeGifFromDir(framesDir, framePattern, outputPath, input) {
|
|
|
11771
11899
|
return gifEncodeFailure(startTime, outputPath, error, result.failureReason);
|
|
11772
11900
|
}
|
|
11773
11901
|
}
|
|
11774
|
-
if (input.preserveAlpha && gifClearsAfterLeavingFrameInPlace(
|
|
11902
|
+
if (input.preserveAlpha && gifClearsAfterLeavingFrameInPlace(readFileSync7(outputPath)) !== false) {
|
|
11775
11903
|
const failure = await reencodeWithClearedWholeFrames(argsInput, run, startTime);
|
|
11776
11904
|
if (failure) return failure;
|
|
11777
11905
|
}
|
|
11778
|
-
const fileSize =
|
|
11906
|
+
const fileSize = existsSync16(outputPath) ? statSync7(outputPath).size : 0;
|
|
11779
11907
|
return {
|
|
11780
11908
|
success: true,
|
|
11781
11909
|
outputPath,
|
|
@@ -11936,7 +12064,7 @@ async function runEncodeStage(input) {
|
|
|
11936
12064
|
const stage5Start = Date.now();
|
|
11937
12065
|
if (isPngSequence) {
|
|
11938
12066
|
const encoded = startEncodeProgress(job, "Writing PNG sequence", onProgress);
|
|
11939
|
-
if (!
|
|
12067
|
+
if (!existsSync16(outputPath)) mkdirSync9(outputPath, { recursive: true });
|
|
11940
12068
|
const captured = readdirSync4(framesDir).filter((name) => name.endsWith(".png")).sort();
|
|
11941
12069
|
if (captured.length === 0) {
|
|
11942
12070
|
throw new Error(
|
|
@@ -11947,7 +12075,7 @@ async function runEncodeStage(input) {
|
|
|
11947
12075
|
const dst = join26(outputPath, formatExportFrameName(i, "png"));
|
|
11948
12076
|
copyFileSync4(join26(framesDir, name), dst);
|
|
11949
12077
|
});
|
|
11950
|
-
if (hasAudio && audioOutputPath &&
|
|
12078
|
+
if (hasAudio && audioOutputPath && existsSync16(audioOutputPath)) {
|
|
11951
12079
|
copyFileSync4(audioOutputPath, join26(outputPath, MIXED_AUDIO_FILENAME2));
|
|
11952
12080
|
log.info(
|
|
11953
12081
|
`[Render] png-sequence: ${MIXED_AUDIO_FILENAME2} sidecar written to ${outputPath}/${MIXED_AUDIO_FILENAME2}`
|
|
@@ -12328,7 +12456,7 @@ function findMissingFrameRanges(totalFrames, framesDir, frameExt) {
|
|
|
12328
12456
|
let rangeStart = null;
|
|
12329
12457
|
for (let frameIndex = 0; frameIndex < totalFrames; frameIndex++) {
|
|
12330
12458
|
const framePath = join27(framesDir, formatCaptureFrameName(frameIndex, frameExt));
|
|
12331
|
-
const missing = !
|
|
12459
|
+
const missing = !existsSync17(framePath) || statSync8(framePath).size <= 8;
|
|
12332
12460
|
if (missing && rangeStart === null) {
|
|
12333
12461
|
rangeStart = frameIndex;
|
|
12334
12462
|
} else if (!missing && rangeStart !== null) {
|
|
@@ -12453,7 +12581,7 @@ function countCapturedFrames(totalFrames, framesDir, frameExt) {
|
|
|
12453
12581
|
let captured = 0;
|
|
12454
12582
|
for (let frameIndex = 0; frameIndex < totalFrames; frameIndex++) {
|
|
12455
12583
|
const framePath = join27(framesDir, formatCaptureFrameName(frameIndex, frameExt));
|
|
12456
|
-
if (
|
|
12584
|
+
if (existsSync17(framePath)) captured++;
|
|
12457
12585
|
}
|
|
12458
12586
|
return captured;
|
|
12459
12587
|
}
|
|
@@ -13116,7 +13244,7 @@ async function executeRenderJob(job, projectDir, outputPath, progressSink, abort
|
|
|
13116
13244
|
validateDeliveryEncoderConfig(job.config);
|
|
13117
13245
|
const debugDir = resolveRenderDebugDir();
|
|
13118
13246
|
const outputDir = dirname11(outputPath);
|
|
13119
|
-
if (!
|
|
13247
|
+
if (!existsSync17(outputDir)) mkdirSync10(outputDir, { recursive: true });
|
|
13120
13248
|
const workDir = job.config.debug ? join27(debugDir, job.id) : createRenderWorkDir(resolveRenderWorkDirPrefix(outputPath, job.id), outputDir);
|
|
13121
13249
|
const pipelineStart = Date.now();
|
|
13122
13250
|
const baseLog = job.config.logger ?? defaultLogger;
|
|
@@ -13257,7 +13385,7 @@ async function executeRenderPipeline(input) {
|
|
|
13257
13385
|
job.startedAt = /* @__PURE__ */ new Date();
|
|
13258
13386
|
assertNotAborted();
|
|
13259
13387
|
assertConfiguredFfmpegBinariesExist();
|
|
13260
|
-
if (!
|
|
13388
|
+
if (!existsSync17(workDir)) mkdirSync10(workDir, { recursive: true });
|
|
13261
13389
|
if (job.config.debug) {
|
|
13262
13390
|
log.info("[Render] Debug artifacts enabled", { workDir, logPath });
|
|
13263
13391
|
}
|
|
@@ -13287,21 +13415,21 @@ async function executeRenderPipeline(input) {
|
|
|
13287
13415
|
});
|
|
13288
13416
|
const entryFile = job.config.entryFile || "index.html";
|
|
13289
13417
|
let htmlPath = join27(projectDir, entryFile);
|
|
13290
|
-
if (!
|
|
13418
|
+
if (!existsSync17(htmlPath)) {
|
|
13291
13419
|
throw new Error(`Entry file not found: ${htmlPath}`);
|
|
13292
13420
|
}
|
|
13293
13421
|
assertNotAborted();
|
|
13294
|
-
const rawEntry =
|
|
13422
|
+
const rawEntry = readTextFile2(htmlPath);
|
|
13295
13423
|
if (entryFile !== "index.html" && isTemplateEntry(rawEntry)) {
|
|
13296
13424
|
const wrapperPath = join27(workDir, "standalone-entry.html");
|
|
13297
13425
|
const projectIndexPath = join27(projectDir, "index.html");
|
|
13298
|
-
if (!
|
|
13426
|
+
if (!existsSync17(projectIndexPath)) {
|
|
13299
13427
|
throw new Error(
|
|
13300
13428
|
`Template entry file "${entryFile}" requires a project index.html to extract its render shell.`
|
|
13301
13429
|
);
|
|
13302
13430
|
}
|
|
13303
13431
|
const standaloneHtml = extractStandaloneEntryFromIndex(
|
|
13304
|
-
|
|
13432
|
+
readTextFile2(projectIndexPath),
|
|
13305
13433
|
entryFile,
|
|
13306
13434
|
rawEntry
|
|
13307
13435
|
);
|
|
@@ -13464,6 +13592,42 @@ async function executeRenderPipeline(input) {
|
|
|
13464
13592
|
compositionHash,
|
|
13465
13593
|
beginFrameStalled: probeResult.beginFrameStalled
|
|
13466
13594
|
});
|
|
13595
|
+
let videoLayerPlan;
|
|
13596
|
+
if (cfg.videoLayerPassthrough) {
|
|
13597
|
+
const gates = {
|
|
13598
|
+
mp4: outputFormat === "mp4",
|
|
13599
|
+
software_encode: !job.config.useGpu,
|
|
13600
|
+
opaque_output: !needsAlpha,
|
|
13601
|
+
sdr: rendersSdrOnly(job.config, composition) || (job.config.hdrMode === void 0 || job.config.hdrMode === "auto") && composition.images.length === 0,
|
|
13602
|
+
streaming: shouldUseStreamingEncode(cfg, outputFormat, 1, probeResult.duration),
|
|
13603
|
+
probe_page: probeSession?.isInitialized === true
|
|
13604
|
+
};
|
|
13605
|
+
const failed = Object.entries(gates).find(([, ok]) => !ok)?.[0];
|
|
13606
|
+
if (failed || !probeSession) {
|
|
13607
|
+
log.info(`[Render] Video layer passthrough off: ${failed ?? "probe_page"}`);
|
|
13608
|
+
} else {
|
|
13609
|
+
videoLayerPlan = await planVideoLayerPassthrough({
|
|
13610
|
+
videos: composition.videos,
|
|
13611
|
+
duration: probeResult.duration,
|
|
13612
|
+
fps: job.config.fps,
|
|
13613
|
+
canvas: { width, height },
|
|
13614
|
+
page: probeSession.page,
|
|
13615
|
+
projectDir,
|
|
13616
|
+
compiledDir: join27(workDir, "compiled")
|
|
13617
|
+
});
|
|
13618
|
+
if (videoLayerPlan.videoId === void 0) {
|
|
13619
|
+
log.info(`[Render] Video layer passthrough off: ${videoLayerPlan.reason}`);
|
|
13620
|
+
} else {
|
|
13621
|
+
cfg.useDrawElement = false;
|
|
13622
|
+
cfg.forceScreenshot = true;
|
|
13623
|
+
captureForceScreenshot = true;
|
|
13624
|
+
const opened = probeSession;
|
|
13625
|
+
probeSession = null;
|
|
13626
|
+
await closeCaptureSession8(opened);
|
|
13627
|
+
}
|
|
13628
|
+
}
|
|
13629
|
+
}
|
|
13630
|
+
const videoLayer = videoLayerPlan?.videoId === void 0 ? void 0 : videoLayerPlan;
|
|
13467
13631
|
updateJobStatus(job, "preprocessing", "Extracting video frames", 10, onProgress, {
|
|
13468
13632
|
code: "extract_video"
|
|
13469
13633
|
});
|
|
@@ -13479,6 +13643,7 @@ async function executeRenderPipeline(input) {
|
|
|
13479
13643
|
cfg,
|
|
13480
13644
|
log,
|
|
13481
13645
|
composition,
|
|
13646
|
+
skipFrameExtractionIds: videoLayer ? [videoLayer.videoId] : [],
|
|
13482
13647
|
abortSignal: executionSignal,
|
|
13483
13648
|
assertNotAborted,
|
|
13484
13649
|
// Local staging can use links; distributed plan() alone requires real copies.
|
|
@@ -13597,7 +13762,7 @@ async function executeRenderPipeline(input) {
|
|
|
13597
13762
|
throw new Error("File server failed to initialize before frame capture");
|
|
13598
13763
|
}
|
|
13599
13764
|
const framesDir = join27(workDir, "captured-frames");
|
|
13600
|
-
if (!
|
|
13765
|
+
if (!existsSync17(framesDir)) mkdirSync10(framesDir, { recursive: true });
|
|
13601
13766
|
updateJobStatus(job, "rendering", "Checking browser GPU", 25, onProgress, {
|
|
13602
13767
|
code: "start_browsers"
|
|
13603
13768
|
});
|
|
@@ -13623,8 +13788,9 @@ async function executeRenderPipeline(input) {
|
|
|
13623
13788
|
width,
|
|
13624
13789
|
height,
|
|
13625
13790
|
fps: job.config.fps,
|
|
13626
|
-
format: captureImageFormat,
|
|
13627
|
-
transparent: needsAlpha,
|
|
13791
|
+
format: videoLayer ? "png" : captureImageFormat,
|
|
13792
|
+
transparent: needsAlpha || videoLayer !== void 0,
|
|
13793
|
+
...videoLayer ? { hiddenElementIds: [videoLayer.videoId] } : {},
|
|
13628
13794
|
quality: captureImageFormat === "png" ? void 0 : resolveCaptureJpegQuality(job.config.quality),
|
|
13629
13795
|
variables: job.config.variables,
|
|
13630
13796
|
deviceScaleFactor,
|
|
@@ -13921,6 +14087,7 @@ async function executeRenderPipeline(input) {
|
|
|
13921
14087
|
}
|
|
13922
14088
|
}
|
|
13923
14089
|
);
|
|
14090
|
+
if (videoLayer) workerCount = 1;
|
|
13924
14091
|
const preRoutingWorkerCount = workerCount;
|
|
13925
14092
|
if (deParallelRouterEligible && workerCount > 1) {
|
|
13926
14093
|
deParallelRouter = "routed";
|
|
@@ -14586,6 +14753,7 @@ async function executeRenderPipeline(input) {
|
|
|
14586
14753
|
vp9CpuUsed: cfg.vp9CpuUsed,
|
|
14587
14754
|
useGpu: job.config.useGpu,
|
|
14588
14755
|
imageFormat: captureOptions.format || "jpeg",
|
|
14756
|
+
...videoLayer ? { videoLayer: videoLayer.layer } : {},
|
|
14589
14757
|
hdr: preset.hdr,
|
|
14590
14758
|
// HLS only: force an IDR every `gopSize` frames so the
|
|
14591
14759
|
// assemble stage's `-c copy` segmentation cuts exactly on
|
|
@@ -14916,12 +15084,12 @@ async function executeRenderPipeline(input) {
|
|
|
14916
15084
|
})
|
|
14917
15085
|
);
|
|
14918
15086
|
const totalElapsed = Date.now() - pipelineStart;
|
|
14919
|
-
const tmpPeakBytes =
|
|
15087
|
+
const tmpPeakBytes = existsSync17(workDir) ? sampleDirectoryBytes(workDir) : 0;
|
|
14920
15088
|
recordTransientRetryObservability();
|
|
14921
15089
|
observability.checkpoint("pipeline", "artifact validated", { totalElapsedMs: totalElapsed });
|
|
14922
15090
|
const observabilitySummary = observability.summary({
|
|
14923
15091
|
lastBrowserConsole,
|
|
14924
|
-
capture: captureObservability,
|
|
15092
|
+
capture: withCapturedBeyondViewport(captureObservability, dedupPerfs),
|
|
14925
15093
|
initFallback: mergeWorkerInitObservability(dedupPerfs),
|
|
14926
15094
|
extraction: extractionObservability,
|
|
14927
15095
|
compositionHash
|
|
@@ -14997,7 +15165,7 @@ async function executeRenderPipeline(input) {
|
|
|
14997
15165
|
}
|
|
14998
15166
|
}
|
|
14999
15167
|
if (job.config.debug) {
|
|
15000
|
-
if (!isPngSequence && !isHls &&
|
|
15168
|
+
if (!isPngSequence && !isHls && existsSync17(stagedOutputPath)) {
|
|
15001
15169
|
const debugOutput = join27(workDir, `output${videoExt}`);
|
|
15002
15170
|
copyFileSync5(stagedOutputPath, debugOutput);
|
|
15003
15171
|
}
|
|
@@ -15057,7 +15225,7 @@ async function executeRenderPipeline(input) {
|
|
|
15057
15225
|
const failedStage = job.currentStage || "pipeline";
|
|
15058
15226
|
const observabilitySummary = observability.summary({
|
|
15059
15227
|
lastBrowserConsole,
|
|
15060
|
-
capture: captureObservability,
|
|
15228
|
+
capture: withCapturedBeyondViewport(captureObservability, dedupPerfs),
|
|
15061
15229
|
initFallback: mergeWorkerInitObservability(dedupPerfs),
|
|
15062
15230
|
extraction: extractionObservability,
|
|
15063
15231
|
compositionHash
|
|
@@ -15104,8 +15272,8 @@ async function executeRenderPipeline(input) {
|
|
|
15104
15272
|
|
|
15105
15273
|
// src/services/hyperframeLint.ts
|
|
15106
15274
|
import {
|
|
15107
|
-
existsSync as
|
|
15108
|
-
readFileSync as
|
|
15275
|
+
existsSync as existsSync18,
|
|
15276
|
+
readFileSync as readFileSync8,
|
|
15109
15277
|
statSync as statSync9,
|
|
15110
15278
|
openSync as openSync5,
|
|
15111
15279
|
fstatSync as fstatSync3,
|
|
@@ -15151,14 +15319,14 @@ function readEntryHtml(filePath) {
|
|
|
15151
15319
|
}
|
|
15152
15320
|
try {
|
|
15153
15321
|
if (!fstatSync3(fd).isFile()) return null;
|
|
15154
|
-
return
|
|
15322
|
+
return readFileSync8(fd, "utf-8");
|
|
15155
15323
|
} finally {
|
|
15156
15324
|
closeSync5(fd);
|
|
15157
15325
|
}
|
|
15158
15326
|
}
|
|
15159
15327
|
function readProjectEntryFile(projectDir, preferredEntryFile) {
|
|
15160
15328
|
const absProjectDir = resolve9(projectDir);
|
|
15161
|
-
if (!
|
|
15329
|
+
if (!existsSync18(absProjectDir) || !statSync9(absProjectDir).isDirectory()) {
|
|
15162
15330
|
return { error: `Project directory not found: ${absProjectDir}` };
|
|
15163
15331
|
}
|
|
15164
15332
|
const entryCandidates = [preferredEntryFile, "index.html", "src/index.html"].filter(
|
|
@@ -15227,7 +15395,7 @@ async function runHyperframeLint(prepared) {
|
|
|
15227
15395
|
import { Worker as Worker2 } from "node:worker_threads";
|
|
15228
15396
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
15229
15397
|
import { dirname as dirname12, join as join29 } from "node:path";
|
|
15230
|
-
import { existsSync as
|
|
15398
|
+
import { existsSync as existsSync19 } from "node:fs";
|
|
15231
15399
|
var DEFAULT_HEALTH_PORT = 9848;
|
|
15232
15400
|
async function startHealthWorker(options = {}) {
|
|
15233
15401
|
const log = options.logger ?? defaultLogger2();
|
|
@@ -15304,7 +15472,7 @@ function resolveWorkerEntry2() {
|
|
|
15304
15472
|
const here = dirname12(fileURLToPath5(import.meta.url));
|
|
15305
15473
|
const candidates = [join29(here, "healthWorkerThread.js"), join29(here, "healthWorkerThread.ts")];
|
|
15306
15474
|
for (const candidate of candidates) {
|
|
15307
|
-
if (
|
|
15475
|
+
if (existsSync19(candidate)) return candidate;
|
|
15308
15476
|
}
|
|
15309
15477
|
return void 0;
|
|
15310
15478
|
}
|
|
@@ -15831,7 +15999,7 @@ function resolvePreparedRenderOutput(prepared, rendersDir, log) {
|
|
|
15831
15999
|
const { input, cleanupProjectDir } = prepared;
|
|
15832
16000
|
const absoluteOutputPath = resolveOutputPath(input.projectDir, input.outputPath, rendersDir, log);
|
|
15833
16001
|
const outputDir = dirname13(absoluteOutputPath);
|
|
15834
|
-
if (!
|
|
16002
|
+
if (!existsSync20(outputDir)) mkdirSync11(outputDir, { recursive: true });
|
|
15835
16003
|
return { input, cleanupProjectDir, absoluteOutputPath };
|
|
15836
16004
|
}
|
|
15837
16005
|
function validateRenderOverrides(body) {
|
|
@@ -15872,11 +16040,11 @@ function prepareProjectDirectory(projectDir, options) {
|
|
|
15872
16040
|
const candidate = nonEmptyString(projectDir);
|
|
15873
16041
|
if (!candidate) return null;
|
|
15874
16042
|
const absProjectDir = resolve10(candidate);
|
|
15875
|
-
if (!
|
|
16043
|
+
if (!existsSync20(absProjectDir) || !statSync10(absProjectDir).isDirectory()) {
|
|
15876
16044
|
return { error: `Project directory not found: ${absProjectDir}` };
|
|
15877
16045
|
}
|
|
15878
16046
|
const entry = options.entryFile || "index.html";
|
|
15879
|
-
if (!
|
|
16047
|
+
if (!existsSync20(resolve10(absProjectDir, entry))) {
|
|
15880
16048
|
return { error: `Entry file "${entry}" not found in project directory: ${absProjectDir}` };
|
|
15881
16049
|
}
|
|
15882
16050
|
return { prepared: { input: { projectDir: absProjectDir, ...options } } };
|
|
@@ -15968,7 +16136,7 @@ function cleanupTempDir(dir, log) {
|
|
|
15968
16136
|
}
|
|
15969
16137
|
}
|
|
15970
16138
|
function outputFileSize(path) {
|
|
15971
|
-
return
|
|
16139
|
+
return existsSync20(path) ? statSync10(path).size : 0;
|
|
15972
16140
|
}
|
|
15973
16141
|
function createBlockingProgressReporter(log, requestId) {
|
|
15974
16142
|
let lastLoggedPct = -10;
|
|
@@ -16270,7 +16438,7 @@ function createRenderHandlers(options = {}) {
|
|
|
16270
16438
|
if (!artifact) {
|
|
16271
16439
|
return c.json({ success: false, error: "Output artifact not found or expired" }, 404);
|
|
16272
16440
|
}
|
|
16273
|
-
if (!
|
|
16441
|
+
if (!existsSync20(artifact.path)) {
|
|
16274
16442
|
store.delete(token);
|
|
16275
16443
|
return c.json({ success: false, error: "Output artifact file missing" }, 404);
|
|
16276
16444
|
}
|
|
@@ -7,7 +7,7 @@ import type { CapturePerfSummary, SubTimelineWaitOutcome, WorkerSizing } from "@
|
|
|
7
7
|
import type { CaptureCalibrationSample, CaptureCostEstimate } from "./captureCost.js";
|
|
8
8
|
import type { CaptureAttemptSummary, HdrDiagnostics, RenderJob, RenderPerfSummary } from "../renderOrchestrator.js";
|
|
9
9
|
import { type HdrPerfCollector } from "./hdrPerf.js";
|
|
10
|
-
import type { RenderObservabilitySummary } from "./observability.js";
|
|
10
|
+
import type { RenderCaptureObservability, RenderObservabilitySummary } from "./observability.js";
|
|
11
11
|
/**
|
|
12
12
|
* Worst sub-composition timeline wait outcome across sessions: script_failure
|
|
13
13
|
* > timeout > ready. Shared by the success-path perf summary and the error
|
|
@@ -24,6 +24,10 @@ export declare function worstSubTimelineWaitOutcome(perfs: CapturePerfSummary[])
|
|
|
24
24
|
export declare function pushWorkerDedupPerfs(results: ReadonlyArray<{
|
|
25
25
|
perf?: CapturePerfSummary;
|
|
26
26
|
}>, sink: CapturePerfSummary[]): void;
|
|
27
|
+
/** Beyond-viewport capture as the workers' sessions used it: on if any kept it, `fallback` when none reported. */
|
|
28
|
+
export declare function capturedBeyondViewport(perfs: ReadonlyArray<CapturePerfSummary>, fallback: boolean | undefined): boolean | undefined;
|
|
29
|
+
/** The capture facts with beyond-viewport capture as the sessions used it, on every capture path. */
|
|
30
|
+
export declare function withCapturedBeyondViewport(capture: RenderCaptureObservability, perfs: ReadonlyArray<CapturePerfSummary>): RenderCaptureObservability;
|
|
27
31
|
/** Adds each worker's page-error console lines to `sink`, once per distinct line. */
|
|
28
32
|
export declare function pushWorkerPageErrors(results: ReadonlyArray<{
|
|
29
33
|
pageErrorLines?: string[];
|
|
@@ -52,6 +52,7 @@ export interface ExtractVideosStageInput {
|
|
|
52
52
|
*/
|
|
53
53
|
materializeSymlinks?: boolean;
|
|
54
54
|
deferRangeExtraction?: boolean;
|
|
55
|
+
skipFrameExtractionIds?: readonly string[];
|
|
55
56
|
urlByAbsentLocalPath?: ReadonlyMap<string, string>;
|
|
56
57
|
onProgress?: ProgressCallback;
|
|
57
58
|
}
|
|
@@ -119,7 +120,7 @@ export declare class VideoExtractionStageError extends Error {
|
|
|
119
120
|
* validating and applying policy to it.
|
|
120
121
|
*/
|
|
121
122
|
readonly publicMetadata: Readonly<Record<string, unknown>>;
|
|
122
|
-
constructor(code: VideoExtractionStageErrorCode, retryable: boolean, failures: readonly VideoExtractionStageFailureSummary[], extractionFailure?: ExtractionFailureMetadataV1, ffmpegReason?: string);
|
|
123
|
+
constructor(code: VideoExtractionStageErrorCode, retryable: boolean, failures: readonly VideoExtractionStageFailureSummary[], extractionFailure?: ExtractionFailureMetadataV1, ffmpegReason?: string, sources?: readonly string[]);
|
|
123
124
|
}
|
|
124
125
|
export declare function assertVideoExtractionSucceeded(result: ExtractionResult): void;
|
|
125
126
|
export declare function buildHdrProbeStageError(failures: readonly Pick<ReturnType<typeof classifyVideoExtractionError>, "kind" | "retryable">[]): VideoExtractionStageError;
|