@hyperframes/producer 0.8.149 → 0.8.150
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 +119 -41
- package/dist/hyperframe.manifest.json +1 -1
- package/dist/hyperframe.runtime.iife.js +65 -58
- package/dist/index.js +120 -41
- package/dist/public-server.js +119 -41
- package/dist/services/htmlCompiler.d.ts +2 -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/distributed.js
CHANGED
|
@@ -1417,9 +1417,18 @@ import {
|
|
|
1417
1417
|
|
|
1418
1418
|
// src/services/renderMediaCollector.ts
|
|
1419
1419
|
import { parseHTML as parseHTML2 } from "linkedom";
|
|
1420
|
-
import {
|
|
1420
|
+
import {
|
|
1421
|
+
MEDIA_RENDER_ID_ATTR,
|
|
1422
|
+
normalizeRateSpec as normalizeRateSpec2,
|
|
1423
|
+
resolveAuthoredTimingWindow,
|
|
1424
|
+
shiftRateLane,
|
|
1425
|
+
sourceTimeAt as sourceTimeAt2
|
|
1426
|
+
} from "@hyperframes/core";
|
|
1421
1427
|
import {
|
|
1422
1428
|
MEDIA_START_BASIS_ATTR,
|
|
1429
|
+
compositionOriginSeconds,
|
|
1430
|
+
cutToHostSlots,
|
|
1431
|
+
hostInPointSeconds,
|
|
1423
1432
|
readMediaStartBasis,
|
|
1424
1433
|
resolveAbsoluteMediaStartSeconds
|
|
1425
1434
|
} from "@hyperframes/core/media-timing";
|
|
@@ -1429,7 +1438,6 @@ import {
|
|
|
1429
1438
|
parseAudioElements,
|
|
1430
1439
|
resolveReferencedStart
|
|
1431
1440
|
} from "@hyperframes/engine";
|
|
1432
|
-
var COMPOSITION_HOST_ATTR = "data-composition-file";
|
|
1433
1441
|
function resolveHostEnd(host, hostStart) {
|
|
1434
1442
|
return resolveAuthoredTimingWindow({
|
|
1435
1443
|
start: hostStart,
|
|
@@ -1437,39 +1445,52 @@ function resolveHostEnd(host, hostStart) {
|
|
|
1437
1445
|
end: host.getAttribute("data-end")
|
|
1438
1446
|
})?.end ?? null;
|
|
1439
1447
|
}
|
|
1440
|
-
var ROOT_WINDOW = { offset: 0, limit: Infinity, basis: "local" };
|
|
1441
|
-
|
|
1448
|
+
var ROOT_WINDOW = { offset: 0, limit: Infinity, basis: "local", slots: [] };
|
|
1449
|
+
var COMPOSITION_HOST_SELECTOR = "[data-composition-id], [data-composition-src], [data-composition-file]";
|
|
1450
|
+
function enclosingHosts(element) {
|
|
1442
1451
|
const hosts = [];
|
|
1443
1452
|
for (let ancestor = element.parentElement; ancestor; ancestor = ancestor.parentElement) {
|
|
1444
|
-
if (ancestor.
|
|
1453
|
+
if (ancestor.matches(COMPOSITION_HOST_SELECTOR)) {
|
|
1454
|
+
hosts.unshift(ancestor);
|
|
1455
|
+
}
|
|
1445
1456
|
}
|
|
1457
|
+
return hosts;
|
|
1458
|
+
}
|
|
1459
|
+
function resolveHostWindow(element, document2, startCache, visiting) {
|
|
1460
|
+
const hosts = enclosingHosts(element);
|
|
1446
1461
|
if (hosts.length === 0) return ROOT_WINDOW;
|
|
1447
1462
|
let offset = 0;
|
|
1448
1463
|
let limit = Infinity;
|
|
1449
|
-
|
|
1464
|
+
const slots = [];
|
|
1465
|
+
for (const [index, host] of hosts.entries()) {
|
|
1450
1466
|
const hostStart = resolveReferencedStart(document2, host, startCache, visiting);
|
|
1451
1467
|
const hostEnd = resolveHostEnd(host, hostStart);
|
|
1452
|
-
if (hostEnd != null
|
|
1453
|
-
|
|
1468
|
+
if (hostEnd != null && !isRootComposition(host, index)) {
|
|
1469
|
+
limit = Math.min(limit, offset + hostEnd);
|
|
1470
|
+
}
|
|
1471
|
+
if (hostInPointSeconds(host) > 0) {
|
|
1472
|
+
slots.push({ start: offset + hostStart, end: offset + (hostEnd ?? Infinity) });
|
|
1473
|
+
}
|
|
1474
|
+
offset += compositionOriginSeconds(hostStart, host);
|
|
1454
1475
|
}
|
|
1476
|
+
return { offset, limit, basis: mediaStartBasis(element), slots };
|
|
1477
|
+
}
|
|
1478
|
+
var isRootComposition = (host, depth) => depth === 0 && !host.matches("[data-composition-src], [data-composition-file]");
|
|
1479
|
+
function mediaStartBasis(element) {
|
|
1455
1480
|
const tag = element.tagName.toLowerCase();
|
|
1456
|
-
|
|
1457
|
-
return {
|
|
1458
|
-
offset,
|
|
1459
|
-
limit,
|
|
1460
|
-
basis
|
|
1461
|
-
};
|
|
1481
|
+
return tag === "video" || tag === "audio" ? readMediaStartBasis(element.getAttribute(MEDIA_START_BASIS_ATTR)) : "local";
|
|
1462
1482
|
}
|
|
1463
1483
|
function collectHostWindows(html) {
|
|
1464
1484
|
const { document: document2 } = parseHTML2(html);
|
|
1465
1485
|
const windows = /* @__PURE__ */ new Map();
|
|
1466
1486
|
const startCache = /* @__PURE__ */ new Map();
|
|
1467
1487
|
const visiting = /* @__PURE__ */ new Set();
|
|
1468
|
-
for (const element of document2.querySelectorAll(
|
|
1488
|
+
for (const element of document2.querySelectorAll("video, audio, img[src]")) {
|
|
1469
1489
|
const renderId = element.getAttribute(MEDIA_RENDER_ID_ATTR);
|
|
1470
|
-
|
|
1490
|
+
const id = renderId || element.getAttribute("id");
|
|
1491
|
+
if (!id || !renderId && windows.has(id)) continue;
|
|
1471
1492
|
windows.set(
|
|
1472
|
-
|
|
1493
|
+
id,
|
|
1473
1494
|
resolveHostWindow(element, document2, startCache, visiting)
|
|
1474
1495
|
);
|
|
1475
1496
|
}
|
|
@@ -1487,34 +1508,59 @@ function toAbsoluteWindow(start, end, window2) {
|
|
|
1487
1508
|
hostStart: window2.offset,
|
|
1488
1509
|
basis: window2.basis
|
|
1489
1510
|
});
|
|
1490
|
-
|
|
1511
|
+
const absolute = { start: absoluteStart, end: Math.min(absoluteEnd, window2.limit) };
|
|
1512
|
+
const played = cutToHostSlots(absolute, window2.slots);
|
|
1513
|
+
if (played.end === played.start && absolute.end > absolute.start) return null;
|
|
1514
|
+
return { start: absolute.start, end: played.end, played };
|
|
1515
|
+
}
|
|
1516
|
+
function startAtPlayed(clip, played) {
|
|
1517
|
+
const head = played.start - clip.start;
|
|
1518
|
+
if (head <= 0) return clip;
|
|
1519
|
+
if ("loop" in clip && clip.loop) return { ...clip, start: played.start };
|
|
1520
|
+
const rate = normalizeRateSpec2(clip.playbackRate);
|
|
1521
|
+
return {
|
|
1522
|
+
...clip,
|
|
1523
|
+
start: played.start,
|
|
1524
|
+
mediaStart: clip.mediaStart + sourceTimeAt2(rate, head),
|
|
1525
|
+
playbackRate: shiftRateLane(rate, head)
|
|
1526
|
+
};
|
|
1491
1527
|
}
|
|
1492
1528
|
function collectRenderMedia(html) {
|
|
1493
1529
|
const windows = collectHostWindows(html);
|
|
1494
1530
|
const windowFor = (id) => windows.get(id) ?? ROOT_WINDOW;
|
|
1531
|
+
const droppedMediaIds = /* @__PURE__ */ new Set();
|
|
1495
1532
|
const videos = [];
|
|
1496
1533
|
for (const video of parseVideoElements(html)) {
|
|
1497
1534
|
const absolute = toAbsoluteWindow(video.start, video.end, windowFor(video.id));
|
|
1498
|
-
if (absolute)
|
|
1535
|
+
if (!absolute) {
|
|
1536
|
+
droppedMediaIds.add(video.id);
|
|
1537
|
+
continue;
|
|
1538
|
+
}
|
|
1539
|
+
videos.push(
|
|
1540
|
+
startAtPlayed({ ...video, start: absolute.start, end: absolute.end }, absolute.played)
|
|
1541
|
+
);
|
|
1499
1542
|
}
|
|
1500
1543
|
const images = [];
|
|
1501
1544
|
for (const image of parseImageElements(html)) {
|
|
1502
1545
|
const absolute = toAbsoluteWindow(image.start, image.end, windowFor(image.id));
|
|
1503
|
-
if (absolute) images.push({ ...image,
|
|
1546
|
+
if (absolute) images.push({ ...image, start: absolute.start, end: absolute.end });
|
|
1547
|
+
else droppedMediaIds.add(image.id);
|
|
1504
1548
|
}
|
|
1505
1549
|
const audios = [];
|
|
1506
1550
|
for (const audio of parseAudioElements(html)) {
|
|
1507
1551
|
const elementId = audio.type === "video" ? audio.id.replace(/-audio$/, "") : audio.id;
|
|
1508
|
-
const
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
audios.push({
|
|
1512
|
-
...audio,
|
|
1513
|
-
start: absolute.start,
|
|
1514
|
-
end: Number.isFinite(absolute.end) ? absolute.end : 0
|
|
1515
|
-
});
|
|
1552
|
+
const placed = placeAudio(audio, windowFor(elementId));
|
|
1553
|
+
if (placed) audios.push(placed);
|
|
1554
|
+
else droppedMediaIds.add(elementId);
|
|
1516
1555
|
}
|
|
1517
|
-
return { videos, audios, images };
|
|
1556
|
+
return { videos, audios, images, droppedMediaIds };
|
|
1557
|
+
}
|
|
1558
|
+
function placeAudio(audio, window2) {
|
|
1559
|
+
const authoredEnd = audio.end > 0 ? audio.end : Infinity;
|
|
1560
|
+
const absolute = toAbsoluteWindow(audio.start, authoredEnd, window2);
|
|
1561
|
+
if (!absolute) return null;
|
|
1562
|
+
const end = Number.isFinite(absolute.end) ? absolute.end : 0;
|
|
1563
|
+
return startAtPlayed({ ...audio, start: absolute.start, end }, absolute.played);
|
|
1518
1564
|
}
|
|
1519
1565
|
|
|
1520
1566
|
// src/services/htmlCompiler.ts
|
|
@@ -1522,6 +1568,7 @@ import {
|
|
|
1522
1568
|
analyzeKeyframeIntervals,
|
|
1523
1569
|
assertMediaPayload,
|
|
1524
1570
|
NotMediaPayloadError as NotMediaPayloadError2,
|
|
1571
|
+
parseMasterFxChain,
|
|
1525
1572
|
probeMediaProfile as probeMediaProfile2
|
|
1526
1573
|
} from "@hyperframes/engine";
|
|
1527
1574
|
|
|
@@ -3221,7 +3268,8 @@ async function compileForRender(projectDir, htmlPath, downloadDir, options = {})
|
|
|
3221
3268
|
for (const [relPath, absPath] of preparedGifAssets) {
|
|
3222
3269
|
externalAssets.set(relPath, absPath);
|
|
3223
3270
|
}
|
|
3224
|
-
const { videos, audios, images } = collectRenderMedia(html);
|
|
3271
|
+
const { videos, audios, images, droppedMediaIds } = collectRenderMedia(html);
|
|
3272
|
+
const masterFxChain = parseMasterFxChain(html);
|
|
3225
3273
|
for (const video of videos) {
|
|
3226
3274
|
if (isHttpUrl(video.src)) continue;
|
|
3227
3275
|
const videoPath = resolve5(projectDir, video.src);
|
|
@@ -3255,6 +3303,8 @@ async function compileForRender(projectDir, htmlPath, downloadDir, options = {})
|
|
|
3255
3303
|
videos,
|
|
3256
3304
|
audios,
|
|
3257
3305
|
images,
|
|
3306
|
+
droppedMediaIds,
|
|
3307
|
+
...masterFxChain ? { masterFxChain } : {},
|
|
3258
3308
|
unresolvedCompositions,
|
|
3259
3309
|
externalAssets,
|
|
3260
3310
|
width,
|
|
@@ -3687,6 +3737,7 @@ async function runCompileStage(input) {
|
|
|
3687
3737
|
videos: compiled.videos,
|
|
3688
3738
|
audios: compiled.audios,
|
|
3689
3739
|
images: compiled.images,
|
|
3740
|
+
masterFxChain: compiled.masterFxChain,
|
|
3690
3741
|
width: compiled.width,
|
|
3691
3742
|
height: compiled.height
|
|
3692
3743
|
};
|
|
@@ -3749,7 +3800,7 @@ import {
|
|
|
3749
3800
|
isTransientBrowserError,
|
|
3750
3801
|
probeBeginFrameLiveness
|
|
3751
3802
|
} from "@hyperframes/engine";
|
|
3752
|
-
import { fpsToNumber as fpsToNumber7 } from "@hyperframes/core";
|
|
3803
|
+
import { MEDIA_RENDER_ID_ATTR as MEDIA_RENDER_ID_ATTR2, fpsToNumber as fpsToNumber7, readMediaStart as readMediaStart2 } from "@hyperframes/core";
|
|
3753
3804
|
import { extractMediaSrcMutations } from "@hyperframes/parsers";
|
|
3754
3805
|
|
|
3755
3806
|
// src/services/render/stages/probeFailures.ts
|
|
@@ -3796,6 +3847,26 @@ function hasVariableBoundMedia(html, variables) {
|
|
|
3796
3847
|
return Boolean(variableId && Object.hasOwn(variables, variableId));
|
|
3797
3848
|
});
|
|
3798
3849
|
}
|
|
3850
|
+
function authoredMediaStarts(html) {
|
|
3851
|
+
const { document: document2 } = parseHTML5(html);
|
|
3852
|
+
const starts = /* @__PURE__ */ new Map();
|
|
3853
|
+
for (const media of document2.querySelectorAll("video, audio")) {
|
|
3854
|
+
const id = media.getAttribute(MEDIA_RENDER_ID_ATTR2) || media.getAttribute("id");
|
|
3855
|
+
if (id && !starts.has(id)) starts.set(id, readMediaStart2(media));
|
|
3856
|
+
}
|
|
3857
|
+
return starts;
|
|
3858
|
+
}
|
|
3859
|
+
function adoptRuntimeMediaStart(composition, el, authored) {
|
|
3860
|
+
const shift = el.mediaStart - authored;
|
|
3861
|
+
if (el.mediaStart <= 0 || Math.abs(shift) <= BROWSER_MEDIA_EPSILON) return;
|
|
3862
|
+
const entries = el.tagName === "video" ? [
|
|
3863
|
+
...composition.videos.filter((video) => video.id === el.id),
|
|
3864
|
+
...composition.audios.filter(
|
|
3865
|
+
(audio) => audio.type === "video" && audio.id === `${el.id}-audio`
|
|
3866
|
+
)
|
|
3867
|
+
] : composition.audios.filter((audio) => audio.type !== "video" && audio.id === el.id);
|
|
3868
|
+
for (const entry of entries) entry.mediaStart += shift;
|
|
3869
|
+
}
|
|
3799
3870
|
function reconcileBrowserMediaEnd(existingEnd, projectedEnd, sourceChanged, durationInferred) {
|
|
3800
3871
|
if (projectedEnd <= 0) return existingEnd;
|
|
3801
3872
|
if (sourceChanged && durationInferred) return projectedEnd;
|
|
@@ -4049,6 +4120,7 @@ async function runProbeStage(input) {
|
|
|
4049
4120
|
const existingVideoIds = new Set(composition.videos.map((v) => v.id));
|
|
4050
4121
|
const existingAudioIds = new Set(composition.audios.map((a) => a.id));
|
|
4051
4122
|
const existingImageIds = new Set(composition.images.map((image) => image.id));
|
|
4123
|
+
const authoredStarts = authoredMediaStarts(compiled.html);
|
|
4052
4124
|
pruneMutedBrowserMedia(composition, browserMedia, existingAudioIds);
|
|
4053
4125
|
for (const el of browserMedia) {
|
|
4054
4126
|
if (!el.src || el.src === "about:blank") continue;
|
|
@@ -4076,9 +4148,11 @@ async function runProbeStage(input) {
|
|
|
4076
4148
|
sourceChanged,
|
|
4077
4149
|
el.durationInferred
|
|
4078
4150
|
);
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4151
|
+
adoptRuntimeMediaStart(
|
|
4152
|
+
composition,
|
|
4153
|
+
el,
|
|
4154
|
+
authoredStarts.get(el.id) ?? existing.mediaStart
|
|
4155
|
+
);
|
|
4082
4156
|
if (el.hasAudio && !el.muted && !existing.hasAudio) {
|
|
4083
4157
|
existing.hasAudio = true;
|
|
4084
4158
|
}
|
|
@@ -4086,7 +4160,7 @@ async function runProbeStage(input) {
|
|
|
4086
4160
|
existing.loop = true;
|
|
4087
4161
|
}
|
|
4088
4162
|
}
|
|
4089
|
-
} else {
|
|
4163
|
+
} else if (!compiled.droppedMediaIds.has(el.id)) {
|
|
4090
4164
|
composition.videos.push({
|
|
4091
4165
|
id: el.id,
|
|
4092
4166
|
src,
|
|
@@ -4117,14 +4191,16 @@ async function runProbeStage(input) {
|
|
|
4117
4191
|
sourceChanged,
|
|
4118
4192
|
el.durationInferred
|
|
4119
4193
|
);
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
|
|
4194
|
+
adoptRuntimeMediaStart(
|
|
4195
|
+
composition,
|
|
4196
|
+
el,
|
|
4197
|
+
authoredStarts.get(el.id) ?? existing.mediaStart
|
|
4198
|
+
);
|
|
4123
4199
|
if (el.volume > 0 && Math.abs((existing.volume ?? 1) - el.volume) > BROWSER_MEDIA_EPSILON) {
|
|
4124
4200
|
existing.volume = el.volume;
|
|
4125
4201
|
}
|
|
4126
4202
|
}
|
|
4127
|
-
} else {
|
|
4203
|
+
} else if (!compiled.droppedMediaIds.has(el.id)) {
|
|
4128
4204
|
composition.audios.push({
|
|
4129
4205
|
id: el.id,
|
|
4130
4206
|
src,
|
|
@@ -4152,7 +4228,7 @@ async function runProbeStage(input) {
|
|
|
4152
4228
|
existing.end = projectedEnd;
|
|
4153
4229
|
}
|
|
4154
4230
|
}
|
|
4155
|
-
} else {
|
|
4231
|
+
} else if (!compiled.droppedMediaIds.has(el.id)) {
|
|
4156
4232
|
composition.images.push({
|
|
4157
4233
|
id: el.id,
|
|
4158
4234
|
src,
|
|
@@ -4771,6 +4847,7 @@ async function runAudioStage(input) {
|
|
|
4771
4847
|
ffmpegProcessTimeout,
|
|
4772
4848
|
audioGain,
|
|
4773
4849
|
audios,
|
|
4850
|
+
masterFxChain,
|
|
4774
4851
|
abortSignal,
|
|
4775
4852
|
assertNotAborted,
|
|
4776
4853
|
log
|
|
@@ -4790,7 +4867,7 @@ async function runAudioStage(input) {
|
|
|
4790
4867
|
audioOutputPath,
|
|
4791
4868
|
duration,
|
|
4792
4869
|
abortSignal,
|
|
4793
|
-
{ ffmpegProcessTimeout, audioGain },
|
|
4870
|
+
{ ffmpegProcessTimeout, audioGain, masterFxChain },
|
|
4794
4871
|
compiledDir
|
|
4795
4872
|
);
|
|
4796
4873
|
} catch (err) {
|
|
@@ -7449,6 +7526,7 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
|
|
|
7449
7526
|
ffmpegProcessTimeout: cfg.ffmpegProcessTimeout,
|
|
7450
7527
|
audioGain: cfg.audioGain,
|
|
7451
7528
|
audios: composition.audios,
|
|
7529
|
+
masterFxChain: composition.masterFxChain,
|
|
7452
7530
|
abortSignal,
|
|
7453
7531
|
assertNotAborted
|
|
7454
7532
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": "0.1.0",
|
|
3
3
|
"buildId": "dev",
|
|
4
|
-
"sha256": "
|
|
4
|
+
"sha256": "5e5acecab6120687d5061738fa55e78cbfc0fe48f7dcbc81eb494e36ba6bcc50",
|
|
5
5
|
"artifacts": {
|
|
6
6
|
"iife": "hyperframe.runtime.iife.js",
|
|
7
7
|
"esm": "hyperframe.runtime.mjs"
|