@hyperframes/producer 0.8.149 → 0.8.151
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 +128 -44
- package/dist/hyperframe.manifest.json +1 -1
- package/dist/hyperframe.runtime.iife.js +65 -58
- package/dist/index.js +134 -47
- package/dist/public-server.js +132 -46
- package/dist/services/htmlCompiler.d.ts +2 -0
- package/dist/services/render/captureImageFormat.d.ts +6 -1
- package/dist/services/render/segmentManifest.d.ts +1 -0
- package/dist/services/render/shared.d.ts +1 -0
- package/dist/services/render/stages/audioStage.d.ts +1 -0
- package/dist/services/renderMediaCollector.d.ts +5 -11
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -4304,7 +4304,8 @@ function computeSegmentPlanHash(input) {
|
|
|
4304
4304
|
input.imageFormat,
|
|
4305
4305
|
input.useGpu ? "gpu" : "cpu",
|
|
4306
4306
|
`${input.outputWidth}x${input.outputHeight}`,
|
|
4307
|
-
input.motionBlur
|
|
4307
|
+
input.motionBlur,
|
|
4308
|
+
input.captureQuality === void 0 ? "" : String(input.captureQuality)
|
|
4308
4309
|
].join(" ");
|
|
4309
4310
|
return createHash2("sha256").update(ordered).digest("hex").slice(0, 16);
|
|
4310
4311
|
}
|
|
@@ -5220,9 +5221,18 @@ import {
|
|
|
5220
5221
|
|
|
5221
5222
|
// src/services/renderMediaCollector.ts
|
|
5222
5223
|
import { parseHTML as parseHTML2 } from "linkedom";
|
|
5223
|
-
import {
|
|
5224
|
+
import {
|
|
5225
|
+
MEDIA_RENDER_ID_ATTR,
|
|
5226
|
+
normalizeRateSpec as normalizeRateSpec2,
|
|
5227
|
+
resolveAuthoredTimingWindow,
|
|
5228
|
+
shiftRateLane,
|
|
5229
|
+
sourceTimeAt as sourceTimeAt2
|
|
5230
|
+
} from "@hyperframes/core";
|
|
5224
5231
|
import {
|
|
5225
5232
|
MEDIA_START_BASIS_ATTR,
|
|
5233
|
+
compositionOriginSeconds,
|
|
5234
|
+
cutToHostSlots,
|
|
5235
|
+
hostInPointSeconds,
|
|
5226
5236
|
readMediaStartBasis,
|
|
5227
5237
|
resolveAbsoluteMediaStartSeconds
|
|
5228
5238
|
} from "@hyperframes/core/media-timing";
|
|
@@ -5232,7 +5242,6 @@ import {
|
|
|
5232
5242
|
parseAudioElements,
|
|
5233
5243
|
resolveReferencedStart
|
|
5234
5244
|
} from "@hyperframes/engine";
|
|
5235
|
-
var COMPOSITION_HOST_ATTR = "data-composition-file";
|
|
5236
5245
|
function resolveHostEnd(host, hostStart) {
|
|
5237
5246
|
return resolveAuthoredTimingWindow({
|
|
5238
5247
|
start: hostStart,
|
|
@@ -5240,39 +5249,52 @@ function resolveHostEnd(host, hostStart) {
|
|
|
5240
5249
|
end: host.getAttribute("data-end")
|
|
5241
5250
|
})?.end ?? null;
|
|
5242
5251
|
}
|
|
5243
|
-
var ROOT_WINDOW = { offset: 0, limit: Infinity, basis: "local" };
|
|
5244
|
-
|
|
5252
|
+
var ROOT_WINDOW = { offset: 0, limit: Infinity, basis: "local", slots: [] };
|
|
5253
|
+
var COMPOSITION_HOST_SELECTOR = "[data-composition-id], [data-composition-src], [data-composition-file]";
|
|
5254
|
+
function enclosingHosts(element) {
|
|
5245
5255
|
const hosts = [];
|
|
5246
5256
|
for (let ancestor = element.parentElement; ancestor; ancestor = ancestor.parentElement) {
|
|
5247
|
-
if (ancestor.
|
|
5257
|
+
if (ancestor.matches(COMPOSITION_HOST_SELECTOR)) {
|
|
5258
|
+
hosts.unshift(ancestor);
|
|
5259
|
+
}
|
|
5248
5260
|
}
|
|
5261
|
+
return hosts;
|
|
5262
|
+
}
|
|
5263
|
+
function resolveHostWindow(element, document2, startCache, visiting) {
|
|
5264
|
+
const hosts = enclosingHosts(element);
|
|
5249
5265
|
if (hosts.length === 0) return ROOT_WINDOW;
|
|
5250
5266
|
let offset = 0;
|
|
5251
5267
|
let limit = Infinity;
|
|
5252
|
-
|
|
5268
|
+
const slots = [];
|
|
5269
|
+
for (const [index, host] of hosts.entries()) {
|
|
5253
5270
|
const hostStart = resolveReferencedStart(document2, host, startCache, visiting);
|
|
5254
5271
|
const hostEnd = resolveHostEnd(host, hostStart);
|
|
5255
|
-
if (hostEnd != null
|
|
5256
|
-
|
|
5272
|
+
if (hostEnd != null && !isRootComposition(host, index)) {
|
|
5273
|
+
limit = Math.min(limit, offset + hostEnd);
|
|
5274
|
+
}
|
|
5275
|
+
if (hostInPointSeconds(host) > 0) {
|
|
5276
|
+
slots.push({ start: offset + hostStart, end: offset + (hostEnd ?? Infinity) });
|
|
5277
|
+
}
|
|
5278
|
+
offset += compositionOriginSeconds(hostStart, host);
|
|
5257
5279
|
}
|
|
5280
|
+
return { offset, limit, basis: mediaStartBasis(element), slots };
|
|
5281
|
+
}
|
|
5282
|
+
var isRootComposition = (host, depth) => depth === 0 && !host.matches("[data-composition-src], [data-composition-file]");
|
|
5283
|
+
function mediaStartBasis(element) {
|
|
5258
5284
|
const tag = element.tagName.toLowerCase();
|
|
5259
|
-
|
|
5260
|
-
return {
|
|
5261
|
-
offset,
|
|
5262
|
-
limit,
|
|
5263
|
-
basis
|
|
5264
|
-
};
|
|
5285
|
+
return tag === "video" || tag === "audio" ? readMediaStartBasis(element.getAttribute(MEDIA_START_BASIS_ATTR)) : "local";
|
|
5265
5286
|
}
|
|
5266
5287
|
function collectHostWindows(html) {
|
|
5267
5288
|
const { document: document2 } = parseHTML2(html);
|
|
5268
5289
|
const windows = /* @__PURE__ */ new Map();
|
|
5269
5290
|
const startCache = /* @__PURE__ */ new Map();
|
|
5270
5291
|
const visiting = /* @__PURE__ */ new Set();
|
|
5271
|
-
for (const element of document2.querySelectorAll(
|
|
5292
|
+
for (const element of document2.querySelectorAll("video, audio, img[src]")) {
|
|
5272
5293
|
const renderId = element.getAttribute(MEDIA_RENDER_ID_ATTR);
|
|
5273
|
-
|
|
5294
|
+
const id = renderId || element.getAttribute("id");
|
|
5295
|
+
if (!id || !renderId && windows.has(id)) continue;
|
|
5274
5296
|
windows.set(
|
|
5275
|
-
|
|
5297
|
+
id,
|
|
5276
5298
|
resolveHostWindow(element, document2, startCache, visiting)
|
|
5277
5299
|
);
|
|
5278
5300
|
}
|
|
@@ -5290,34 +5312,59 @@ function toAbsoluteWindow(start, end, window2) {
|
|
|
5290
5312
|
hostStart: window2.offset,
|
|
5291
5313
|
basis: window2.basis
|
|
5292
5314
|
});
|
|
5293
|
-
|
|
5315
|
+
const absolute = { start: absoluteStart, end: Math.min(absoluteEnd, window2.limit) };
|
|
5316
|
+
const played = cutToHostSlots(absolute, window2.slots);
|
|
5317
|
+
if (played.end === played.start && absolute.end > absolute.start) return null;
|
|
5318
|
+
return { start: absolute.start, end: played.end, played };
|
|
5319
|
+
}
|
|
5320
|
+
function startAtPlayed(clip, played) {
|
|
5321
|
+
const head = played.start - clip.start;
|
|
5322
|
+
if (head <= 0) return clip;
|
|
5323
|
+
if ("loop" in clip && clip.loop) return { ...clip, start: played.start };
|
|
5324
|
+
const rate = normalizeRateSpec2(clip.playbackRate);
|
|
5325
|
+
return {
|
|
5326
|
+
...clip,
|
|
5327
|
+
start: played.start,
|
|
5328
|
+
mediaStart: clip.mediaStart + sourceTimeAt2(rate, head),
|
|
5329
|
+
playbackRate: shiftRateLane(rate, head)
|
|
5330
|
+
};
|
|
5294
5331
|
}
|
|
5295
5332
|
function collectRenderMedia(html) {
|
|
5296
5333
|
const windows = collectHostWindows(html);
|
|
5297
5334
|
const windowFor = (id) => windows.get(id) ?? ROOT_WINDOW;
|
|
5335
|
+
const droppedMediaIds = /* @__PURE__ */ new Set();
|
|
5298
5336
|
const videos = [];
|
|
5299
5337
|
for (const video of parseVideoElements(html)) {
|
|
5300
5338
|
const absolute = toAbsoluteWindow(video.start, video.end, windowFor(video.id));
|
|
5301
|
-
if (absolute)
|
|
5339
|
+
if (!absolute) {
|
|
5340
|
+
droppedMediaIds.add(video.id);
|
|
5341
|
+
continue;
|
|
5342
|
+
}
|
|
5343
|
+
videos.push(
|
|
5344
|
+
startAtPlayed({ ...video, start: absolute.start, end: absolute.end }, absolute.played)
|
|
5345
|
+
);
|
|
5302
5346
|
}
|
|
5303
5347
|
const images = [];
|
|
5304
5348
|
for (const image of parseImageElements(html)) {
|
|
5305
5349
|
const absolute = toAbsoluteWindow(image.start, image.end, windowFor(image.id));
|
|
5306
|
-
if (absolute) images.push({ ...image,
|
|
5350
|
+
if (absolute) images.push({ ...image, start: absolute.start, end: absolute.end });
|
|
5351
|
+
else droppedMediaIds.add(image.id);
|
|
5307
5352
|
}
|
|
5308
5353
|
const audios = [];
|
|
5309
5354
|
for (const audio of parseAudioElements(html)) {
|
|
5310
5355
|
const elementId = audio.type === "video" ? audio.id.replace(/-audio$/, "") : audio.id;
|
|
5311
|
-
const
|
|
5312
|
-
|
|
5313
|
-
|
|
5314
|
-
audios.push({
|
|
5315
|
-
...audio,
|
|
5316
|
-
start: absolute.start,
|
|
5317
|
-
end: Number.isFinite(absolute.end) ? absolute.end : 0
|
|
5318
|
-
});
|
|
5356
|
+
const placed = placeAudio(audio, windowFor(elementId));
|
|
5357
|
+
if (placed) audios.push(placed);
|
|
5358
|
+
else droppedMediaIds.add(elementId);
|
|
5319
5359
|
}
|
|
5320
|
-
return { videos, audios, images };
|
|
5360
|
+
return { videos, audios, images, droppedMediaIds };
|
|
5361
|
+
}
|
|
5362
|
+
function placeAudio(audio, window2) {
|
|
5363
|
+
const authoredEnd = audio.end > 0 ? audio.end : Infinity;
|
|
5364
|
+
const absolute = toAbsoluteWindow(audio.start, authoredEnd, window2);
|
|
5365
|
+
if (!absolute) return null;
|
|
5366
|
+
const end = Number.isFinite(absolute.end) ? absolute.end : 0;
|
|
5367
|
+
return startAtPlayed({ ...audio, start: absolute.start, end }, absolute.played);
|
|
5321
5368
|
}
|
|
5322
5369
|
|
|
5323
5370
|
// src/services/htmlCompiler.ts
|
|
@@ -5325,6 +5372,7 @@ import {
|
|
|
5325
5372
|
analyzeKeyframeIntervals,
|
|
5326
5373
|
assertMediaPayload,
|
|
5327
5374
|
NotMediaPayloadError as NotMediaPayloadError2,
|
|
5375
|
+
parseMasterFxChain,
|
|
5328
5376
|
probeMediaProfile as probeMediaProfile2
|
|
5329
5377
|
} from "@hyperframes/engine";
|
|
5330
5378
|
|
|
@@ -7024,7 +7072,8 @@ async function compileForRender(projectDir, htmlPath, downloadDir, options = {})
|
|
|
7024
7072
|
for (const [relPath, absPath] of preparedGifAssets) {
|
|
7025
7073
|
externalAssets.set(relPath, absPath);
|
|
7026
7074
|
}
|
|
7027
|
-
const { videos, audios, images } = collectRenderMedia(html);
|
|
7075
|
+
const { videos, audios, images, droppedMediaIds } = collectRenderMedia(html);
|
|
7076
|
+
const masterFxChain = parseMasterFxChain(html);
|
|
7028
7077
|
for (const video of videos) {
|
|
7029
7078
|
if (isHttpUrl(video.src)) continue;
|
|
7030
7079
|
const videoPath = resolve6(projectDir, video.src);
|
|
@@ -7058,6 +7107,8 @@ async function compileForRender(projectDir, htmlPath, downloadDir, options = {})
|
|
|
7058
7107
|
videos,
|
|
7059
7108
|
audios,
|
|
7060
7109
|
images,
|
|
7110
|
+
droppedMediaIds,
|
|
7111
|
+
...masterFxChain ? { masterFxChain } : {},
|
|
7061
7112
|
unresolvedCompositions,
|
|
7062
7113
|
externalAssets,
|
|
7063
7114
|
width,
|
|
@@ -7490,6 +7541,7 @@ async function runCompileStage(input) {
|
|
|
7490
7541
|
videos: compiled.videos,
|
|
7491
7542
|
audios: compiled.audios,
|
|
7492
7543
|
images: compiled.images,
|
|
7544
|
+
masterFxChain: compiled.masterFxChain,
|
|
7493
7545
|
width: compiled.width,
|
|
7494
7546
|
height: compiled.height
|
|
7495
7547
|
};
|
|
@@ -7552,7 +7604,7 @@ import {
|
|
|
7552
7604
|
isTransientBrowserError,
|
|
7553
7605
|
probeBeginFrameLiveness
|
|
7554
7606
|
} from "@hyperframes/engine";
|
|
7555
|
-
import { fpsToNumber as fpsToNumber7 } from "@hyperframes/core";
|
|
7607
|
+
import { MEDIA_RENDER_ID_ATTR as MEDIA_RENDER_ID_ATTR2, fpsToNumber as fpsToNumber7, readMediaStart as readMediaStart2 } from "@hyperframes/core";
|
|
7556
7608
|
import { extractMediaSrcMutations } from "@hyperframes/parsers";
|
|
7557
7609
|
|
|
7558
7610
|
// src/services/render/stages/probeFailures.ts
|
|
@@ -7566,6 +7618,9 @@ function isActionableProbeFailure(line) {
|
|
|
7566
7618
|
function resolveCaptureImageFormat(input) {
|
|
7567
7619
|
return input.needsAlpha || input.motionBlur ? "png" : "jpeg";
|
|
7568
7620
|
}
|
|
7621
|
+
function resolveCaptureJpegQuality(quality) {
|
|
7622
|
+
return quality === "draft" ? 80 : 100;
|
|
7623
|
+
}
|
|
7569
7624
|
|
|
7570
7625
|
// src/services/render/stages/probeStage.ts
|
|
7571
7626
|
var FRAME_BOUNDARY_EPSILON = 1e-3;
|
|
@@ -7599,6 +7654,26 @@ function hasVariableBoundMedia(html, variables) {
|
|
|
7599
7654
|
return Boolean(variableId && Object.hasOwn(variables, variableId));
|
|
7600
7655
|
});
|
|
7601
7656
|
}
|
|
7657
|
+
function authoredMediaStarts(html) {
|
|
7658
|
+
const { document: document2 } = parseHTML5(html);
|
|
7659
|
+
const starts = /* @__PURE__ */ new Map();
|
|
7660
|
+
for (const media of document2.querySelectorAll("video, audio")) {
|
|
7661
|
+
const id = media.getAttribute(MEDIA_RENDER_ID_ATTR2) || media.getAttribute("id");
|
|
7662
|
+
if (id && !starts.has(id)) starts.set(id, readMediaStart2(media));
|
|
7663
|
+
}
|
|
7664
|
+
return starts;
|
|
7665
|
+
}
|
|
7666
|
+
function adoptRuntimeMediaStart(composition, el, authored) {
|
|
7667
|
+
const shift = el.mediaStart - authored;
|
|
7668
|
+
if (el.mediaStart <= 0 || Math.abs(shift) <= BROWSER_MEDIA_EPSILON) return;
|
|
7669
|
+
const entries = el.tagName === "video" ? [
|
|
7670
|
+
...composition.videos.filter((video) => video.id === el.id),
|
|
7671
|
+
...composition.audios.filter(
|
|
7672
|
+
(audio) => audio.type === "video" && audio.id === `${el.id}-audio`
|
|
7673
|
+
)
|
|
7674
|
+
] : composition.audios.filter((audio) => audio.type !== "video" && audio.id === el.id);
|
|
7675
|
+
for (const entry of entries) entry.mediaStart += shift;
|
|
7676
|
+
}
|
|
7602
7677
|
function reconcileBrowserMediaEnd(existingEnd, projectedEnd, sourceChanged, durationInferred) {
|
|
7603
7678
|
if (projectedEnd <= 0) return existingEnd;
|
|
7604
7679
|
if (sourceChanged && durationInferred) return projectedEnd;
|
|
@@ -7701,7 +7776,7 @@ async function runProbeStage(input) {
|
|
|
7701
7776
|
height,
|
|
7702
7777
|
fps: job.config.fps,
|
|
7703
7778
|
format: captureFormat,
|
|
7704
|
-
quality: captureFormat === "png" ? void 0 :
|
|
7779
|
+
quality: captureFormat === "png" ? void 0 : resolveCaptureJpegQuality(job.config.quality),
|
|
7705
7780
|
variables: job.config.variables,
|
|
7706
7781
|
deviceScaleFactor,
|
|
7707
7782
|
motionBlur,
|
|
@@ -7852,6 +7927,7 @@ async function runProbeStage(input) {
|
|
|
7852
7927
|
const existingVideoIds = new Set(composition.videos.map((v) => v.id));
|
|
7853
7928
|
const existingAudioIds = new Set(composition.audios.map((a) => a.id));
|
|
7854
7929
|
const existingImageIds = new Set(composition.images.map((image) => image.id));
|
|
7930
|
+
const authoredStarts = authoredMediaStarts(compiled.html);
|
|
7855
7931
|
pruneMutedBrowserMedia(composition, browserMedia, existingAudioIds);
|
|
7856
7932
|
for (const el of browserMedia) {
|
|
7857
7933
|
if (!el.src || el.src === "about:blank") continue;
|
|
@@ -7879,9 +7955,11 @@ async function runProbeStage(input) {
|
|
|
7879
7955
|
sourceChanged,
|
|
7880
7956
|
el.durationInferred
|
|
7881
7957
|
);
|
|
7882
|
-
|
|
7883
|
-
|
|
7884
|
-
|
|
7958
|
+
adoptRuntimeMediaStart(
|
|
7959
|
+
composition,
|
|
7960
|
+
el,
|
|
7961
|
+
authoredStarts.get(el.id) ?? existing.mediaStart
|
|
7962
|
+
);
|
|
7885
7963
|
if (el.hasAudio && !el.muted && !existing.hasAudio) {
|
|
7886
7964
|
existing.hasAudio = true;
|
|
7887
7965
|
}
|
|
@@ -7889,7 +7967,7 @@ async function runProbeStage(input) {
|
|
|
7889
7967
|
existing.loop = true;
|
|
7890
7968
|
}
|
|
7891
7969
|
}
|
|
7892
|
-
} else {
|
|
7970
|
+
} else if (!compiled.droppedMediaIds.has(el.id)) {
|
|
7893
7971
|
composition.videos.push({
|
|
7894
7972
|
id: el.id,
|
|
7895
7973
|
src,
|
|
@@ -7920,14 +7998,16 @@ async function runProbeStage(input) {
|
|
|
7920
7998
|
sourceChanged,
|
|
7921
7999
|
el.durationInferred
|
|
7922
8000
|
);
|
|
7923
|
-
|
|
7924
|
-
|
|
7925
|
-
|
|
8001
|
+
adoptRuntimeMediaStart(
|
|
8002
|
+
composition,
|
|
8003
|
+
el,
|
|
8004
|
+
authoredStarts.get(el.id) ?? existing.mediaStart
|
|
8005
|
+
);
|
|
7926
8006
|
if (el.volume > 0 && Math.abs((existing.volume ?? 1) - el.volume) > BROWSER_MEDIA_EPSILON) {
|
|
7927
8007
|
existing.volume = el.volume;
|
|
7928
8008
|
}
|
|
7929
8009
|
}
|
|
7930
|
-
} else {
|
|
8010
|
+
} else if (!compiled.droppedMediaIds.has(el.id)) {
|
|
7931
8011
|
composition.audios.push({
|
|
7932
8012
|
id: el.id,
|
|
7933
8013
|
src,
|
|
@@ -7955,7 +8035,7 @@ async function runProbeStage(input) {
|
|
|
7955
8035
|
existing.end = projectedEnd;
|
|
7956
8036
|
}
|
|
7957
8037
|
}
|
|
7958
|
-
} else {
|
|
8038
|
+
} else if (!compiled.droppedMediaIds.has(el.id)) {
|
|
7959
8039
|
composition.images.push({
|
|
7960
8040
|
id: el.id,
|
|
7961
8041
|
src,
|
|
@@ -8574,6 +8654,7 @@ async function runAudioStage(input) {
|
|
|
8574
8654
|
ffmpegProcessTimeout,
|
|
8575
8655
|
audioGain,
|
|
8576
8656
|
audios,
|
|
8657
|
+
masterFxChain,
|
|
8577
8658
|
abortSignal,
|
|
8578
8659
|
assertNotAborted,
|
|
8579
8660
|
log
|
|
@@ -8593,7 +8674,7 @@ async function runAudioStage(input) {
|
|
|
8593
8674
|
audioOutputPath,
|
|
8594
8675
|
duration,
|
|
8595
8676
|
abortSignal,
|
|
8596
|
-
{ ffmpegProcessTimeout, audioGain },
|
|
8677
|
+
{ ffmpegProcessTimeout, audioGain, masterFxChain },
|
|
8597
8678
|
compiledDir
|
|
8598
8679
|
);
|
|
8599
8680
|
} catch (err) {
|
|
@@ -8679,13 +8760,16 @@ function shouldAllowAdaptiveCaptureRetry(workerCount, _explicitlyConfigured) {
|
|
|
8679
8760
|
return workerCount > 1;
|
|
8680
8761
|
}
|
|
8681
8762
|
var JPEG_RAW_RGBA_DIVISOR = 8;
|
|
8763
|
+
var HIGH_QUALITY_JPEG_RAW_RGBA_DIVISOR = 4;
|
|
8682
8764
|
var DISK_PROJECTION_SAMPLE_FRAMES = 10;
|
|
8683
8765
|
function estimateDiskCaptureBytes(totalFrames, captureOptions) {
|
|
8684
8766
|
const scale = captureOptions.deviceScaleFactor ?? 1;
|
|
8685
8767
|
const outputWidth = Math.ceil(captureOptions.width * scale);
|
|
8686
8768
|
const outputHeight = Math.ceil(captureOptions.height * scale);
|
|
8687
8769
|
const rawBytes = Math.ceil(totalFrames) * outputWidth * outputHeight * 4;
|
|
8688
|
-
return frameFileExtension(captureOptions.format) === "jpg" ? Math.ceil(
|
|
8770
|
+
return frameFileExtension(captureOptions.format) === "jpg" ? Math.ceil(
|
|
8771
|
+
rawBytes / ((captureOptions.quality ?? 80) > 80 ? HIGH_QUALITY_JPEG_RAW_RGBA_DIVISOR : JPEG_RAW_RGBA_DIVISOR)
|
|
8772
|
+
) : rawBytes;
|
|
8689
8773
|
}
|
|
8690
8774
|
function freeDiskBytesAt(path) {
|
|
8691
8775
|
try {
|
|
@@ -12062,6 +12146,7 @@ async function executeRenderPipeline(input) {
|
|
|
12062
12146
|
ffmpegProcessTimeout: cfg.ffmpegProcessTimeout,
|
|
12063
12147
|
audioGain: cfg.audioGain,
|
|
12064
12148
|
audios: composition.audios,
|
|
12149
|
+
masterFxChain: composition.masterFxChain,
|
|
12065
12150
|
abortSignal: executionSignal,
|
|
12066
12151
|
assertNotAborted,
|
|
12067
12152
|
log
|
|
@@ -12146,7 +12231,7 @@ async function executeRenderPipeline(input) {
|
|
|
12146
12231
|
height,
|
|
12147
12232
|
fps: job.config.fps,
|
|
12148
12233
|
format: captureImageFormat,
|
|
12149
|
-
quality: captureImageFormat === "png" ? void 0 : job.config.quality
|
|
12234
|
+
quality: captureImageFormat === "png" ? void 0 : resolveCaptureJpegQuality(job.config.quality),
|
|
12150
12235
|
variables: job.config.variables,
|
|
12151
12236
|
deviceScaleFactor,
|
|
12152
12237
|
motionBlur: job.config.motionBlur,
|
|
@@ -12774,7 +12859,8 @@ async function executeRenderPipeline(input) {
|
|
|
12774
12859
|
// Device-scaled: the capture buffer, not the CSS composition size.
|
|
12775
12860
|
outputWidth: captureCompositionWidth ?? width,
|
|
12776
12861
|
outputHeight: captureCompositionHeight ?? height,
|
|
12777
|
-
motionBlur: job.config.motionBlur ? JSON.stringify(job.config.motionBlur) : ""
|
|
12862
|
+
motionBlur: job.config.motionBlur ? JSON.stringify(job.config.motionBlur) : "",
|
|
12863
|
+
captureQuality: captureOptions.quality
|
|
12778
12864
|
});
|
|
12779
12865
|
const segmentDir = segmentDirFor(join26(projectDir, "renders"), segmentPlanHash);
|
|
12780
12866
|
let completedSegments = /* @__PURE__ */ new Set();
|
|
@@ -16012,6 +16098,7 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
|
|
|
16012
16098
|
ffmpegProcessTimeout: cfg.ffmpegProcessTimeout,
|
|
16013
16099
|
audioGain: cfg.audioGain,
|
|
16014
16100
|
audios: composition.audios,
|
|
16101
|
+
masterFxChain: composition.masterFxChain,
|
|
16015
16102
|
abortSignal,
|
|
16016
16103
|
assertNotAborted
|
|
16017
16104
|
});
|
|
@@ -17645,7 +17732,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
|
|
|
17645
17732
|
height: plan2.dimensions.height,
|
|
17646
17733
|
fps: { num: plan2.dimensions.fpsNum, den: plan2.dimensions.fpsDen },
|
|
17647
17734
|
format: plan2.dimensions.format === "mp4" ? "jpeg" : "png",
|
|
17648
|
-
quality: plan2.dimensions.format === "mp4" ?
|
|
17735
|
+
quality: plan2.dimensions.format === "mp4" ? resolveCaptureJpegQuality(job.config.quality) : void 0,
|
|
17649
17736
|
deviceScaleFactor: encoder.deviceScaleFactor,
|
|
17650
17737
|
// Re-inject the controller's snapshotted variables so the chunk's
|
|
17651
17738
|
// first capture sees the same `window.__hfVariables` the in-process
|