hyperframes 0.8.61 → 0.8.63
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/cli.js +422 -299
- package/dist/fontLocalizeCli.js +101 -32
- package/dist/hyperframes-player.global.js +1 -1
- package/dist/renderSetupWorker.js +4 -4
- package/dist/skills/media-use/scripts/lib/transcriptCutFade.mjs +21 -0
- package/dist/skills/media-use/scripts/lib/transcriptCutFade.test.mjs +33 -0
- package/dist/skills/media-use/scripts/transcript-cut.mjs +39 -8
- package/dist/skills/media-use/scripts/transcript-cut.test.mjs +117 -0
- package/dist/studio/assets/{hyperframes-player-nmVkPQza.js → hyperframes-player-FmXtbvEE.js} +1 -1
- package/dist/studio/assets/{index-DS0kLbuM.css → index-BnsYeuxA.css} +1 -1
- package/dist/studio/assets/{index-COG0K-CQ.js → index-CISQydDK.js} +1 -1
- package/dist/studio/assets/{index-BK36b0XY.js → index-CjueZL58.js} +176 -176
- package/dist/studio/assets/{index-2-HsQB2C.js → index-DCqmNhmi.js} +1 -1
- package/dist/studio/index.d.ts +44 -1
- package/dist/studio/index.html +2 -2
- package/dist/studio/index.js +4977 -5001
- package/dist/studio/index.js.map +1 -1
- package/dist/studio/styles.css +1 -1
- package/package.json +1 -1
package/dist/fontLocalizeCli.js
CHANGED
|
@@ -55119,17 +55119,22 @@ var BrowserLeasePool;
|
|
|
55119
55119
|
var init_browserLeasePool = __esm({
|
|
55120
55120
|
"../engine/src/services/browserLeasePool.ts"() {
|
|
55121
55121
|
"use strict";
|
|
55122
|
-
BrowserLeasePool = class {
|
|
55122
|
+
BrowserLeasePool = class _BrowserLeasePool {
|
|
55123
55123
|
constructor(options) {
|
|
55124
55124
|
this.options = options;
|
|
55125
55125
|
}
|
|
55126
55126
|
options;
|
|
55127
|
+
/** Under the CLI's 3s exit watchdog, alongside the render-wait bound it races. */
|
|
55128
|
+
static DRAIN_FORCE_CLOSE_MS = 1e3;
|
|
55127
55129
|
available = /* @__PURE__ */ new Map();
|
|
55128
55130
|
entries = /* @__PURE__ */ new Set();
|
|
55129
55131
|
leasesByBrowser = /* @__PURE__ */ new Map();
|
|
55130
55132
|
drainPromise = null;
|
|
55133
|
+
closed = false;
|
|
55131
55134
|
async acquire(fingerprintInput, pooled) {
|
|
55135
|
+
if (this.closed) throw new Error("Browser pool is closed; no new browsers can be acquired");
|
|
55132
55136
|
if (this.drainPromise) await this.drainPromise;
|
|
55137
|
+
if (this.closed) throw new Error("Browser pool is closed; no new browsers can be acquired");
|
|
55133
55138
|
const fingerprint = freezeFingerprint(fingerprintInput);
|
|
55134
55139
|
const entry = this.reserveEntry(fingerprint, pooled);
|
|
55135
55140
|
return this.issueLease(entry);
|
|
@@ -55188,17 +55193,30 @@ var init_browserLeasePool = __esm({
|
|
|
55188
55193
|
if (this.drainPromise) return this.drainPromise;
|
|
55189
55194
|
const entries2 = [...this.entries];
|
|
55190
55195
|
for (const entry of entries2) this.requestClose(entry, false);
|
|
55191
|
-
|
|
55196
|
+
const settle = Promise.all(entries2.map((entry) => entry.closePromise));
|
|
55197
|
+
const bound = new Promise(
|
|
55198
|
+
(resolve18) => setTimeout(resolve18, _BrowserLeasePool.DRAIN_FORCE_CLOSE_MS).unref()
|
|
55199
|
+
);
|
|
55200
|
+
this.drainPromise = Promise.race([settle, bound]).then(() => {
|
|
55201
|
+
for (const entry of entries2) if (this.entries.has(entry)) this.requestClose(entry, true);
|
|
55202
|
+
return settle;
|
|
55203
|
+
}).then(() => {
|
|
55192
55204
|
this.drainPromise = null;
|
|
55193
55205
|
});
|
|
55194
55206
|
return this.drainPromise;
|
|
55195
55207
|
}
|
|
55208
|
+
/** Terminal: drains every entry and refuses every `acquire()` after. */
|
|
55209
|
+
close() {
|
|
55210
|
+
this.closed = true;
|
|
55211
|
+
return this.drain();
|
|
55212
|
+
}
|
|
55196
55213
|
/** Test-only state reset. Call `drain()` first when entries own real browsers. */
|
|
55197
55214
|
reset() {
|
|
55198
55215
|
this.available.clear();
|
|
55199
55216
|
this.entries.clear();
|
|
55200
55217
|
this.leasesByBrowser.clear();
|
|
55201
55218
|
this.drainPromise = null;
|
|
55219
|
+
this.closed = false;
|
|
55202
55220
|
}
|
|
55203
55221
|
createEntry(key2, fingerprint, pooled) {
|
|
55204
55222
|
const entry = {
|
|
@@ -55732,6 +55750,9 @@ function forceReleaseBrowser(browser) {
|
|
|
55732
55750
|
async function drainBrowserPool() {
|
|
55733
55751
|
await browserLeasePool.drain();
|
|
55734
55752
|
}
|
|
55753
|
+
async function closeBrowserPool() {
|
|
55754
|
+
await browserLeasePool.close();
|
|
55755
|
+
}
|
|
55735
55756
|
function probeNvidiaVramMb() {
|
|
55736
55757
|
if (_cachedVramMb !== null) return _cachedVramMb;
|
|
55737
55758
|
try {
|
|
@@ -62755,11 +62776,19 @@ async function extractMediaMetadata(filePath) {
|
|
|
62755
62776
|
});
|
|
62756
62777
|
return probePromise;
|
|
62757
62778
|
}
|
|
62779
|
+
function resolveStreamWindow(filePath, metadata) {
|
|
62780
|
+
const videoStreamDurationSeconds = metadata.videoStreamDurationSeconds;
|
|
62781
|
+
const candidateStart = metadata.videoStreamStartSeconds ?? 0;
|
|
62782
|
+
const videoStreamStartSeconds = Number.isFinite(candidateStart) ? candidateStart : 0;
|
|
62783
|
+
const cacheKey = `${filePath}\0${String(videoStreamStartSeconds)}\0${String(videoStreamDurationSeconds)}`;
|
|
62784
|
+
return { videoStreamStartSeconds, videoStreamDurationSeconds, cacheKey };
|
|
62785
|
+
}
|
|
62758
62786
|
async function extractFinalVideoFrameTimestamp(filePath, metadata, signal) {
|
|
62759
|
-
const
|
|
62760
|
-
|
|
62761
|
-
|
|
62762
|
-
|
|
62787
|
+
const {
|
|
62788
|
+
videoStreamStartSeconds,
|
|
62789
|
+
videoStreamDurationSeconds: videoDurationSeconds,
|
|
62790
|
+
cacheKey
|
|
62791
|
+
} = resolveStreamWindow(filePath, metadata);
|
|
62763
62792
|
let probeCache = finalVideoFrameTimestampCache;
|
|
62764
62793
|
if (signal) {
|
|
62765
62794
|
probeCache = finalVideoFrameTimestampSignalCaches.get(signal) ?? /* @__PURE__ */ new Map();
|
|
@@ -62862,19 +62891,27 @@ async function extractAudioMetadata(filePath, options) {
|
|
|
62862
62891
|
});
|
|
62863
62892
|
return probePromise;
|
|
62864
62893
|
}
|
|
62865
|
-
async function analyzeKeyframeIntervals(filePath) {
|
|
62866
|
-
const
|
|
62894
|
+
async function analyzeKeyframeIntervals(filePath, metadata) {
|
|
62895
|
+
const { videoStreamStartSeconds, videoStreamDurationSeconds, cacheKey } = resolveStreamWindow(
|
|
62896
|
+
filePath,
|
|
62897
|
+
metadata
|
|
62898
|
+
);
|
|
62899
|
+
const cached2 = keyframeCache.get(cacheKey);
|
|
62867
62900
|
if (cached2) return cached2;
|
|
62868
|
-
const promise = analyzeKeyframeIntervalsUncached(
|
|
62869
|
-
|
|
62901
|
+
const promise = analyzeKeyframeIntervalsUncached(
|
|
62902
|
+
filePath,
|
|
62903
|
+
videoStreamStartSeconds,
|
|
62904
|
+
videoStreamDurationSeconds
|
|
62905
|
+
);
|
|
62906
|
+
keyframeCache.set(cacheKey, promise);
|
|
62870
62907
|
promise.catch(() => {
|
|
62871
|
-
if (keyframeCache.get(
|
|
62872
|
-
keyframeCache.delete(
|
|
62908
|
+
if (keyframeCache.get(cacheKey) === promise) {
|
|
62909
|
+
keyframeCache.delete(cacheKey);
|
|
62873
62910
|
}
|
|
62874
62911
|
});
|
|
62875
62912
|
return promise;
|
|
62876
62913
|
}
|
|
62877
|
-
async function analyzeKeyframeIntervalsUncached(filePath) {
|
|
62914
|
+
async function analyzeKeyframeIntervalsUncached(filePath, videoStreamStartSeconds, videoStreamDurationSeconds) {
|
|
62878
62915
|
const stdout = await runFfprobe(filePath, [
|
|
62879
62916
|
"-select_streams",
|
|
62880
62917
|
"v:0",
|
|
@@ -62886,14 +62923,26 @@ async function analyzeKeyframeIntervalsUncached(filePath) {
|
|
|
62886
62923
|
"csv=p=0"
|
|
62887
62924
|
]);
|
|
62888
62925
|
const timestamps = stdout.split("\n").map((line) => parseFloat(line.trim())).filter((t) => Number.isFinite(t));
|
|
62889
|
-
if (timestamps.length
|
|
62926
|
+
if (timestamps.length === 0) {
|
|
62890
62927
|
return {
|
|
62891
62928
|
avgIntervalSeconds: 0,
|
|
62892
62929
|
maxIntervalSeconds: 0,
|
|
62893
|
-
keyframeCount:
|
|
62930
|
+
keyframeCount: 0,
|
|
62894
62931
|
isProblematic: false
|
|
62895
62932
|
};
|
|
62896
62933
|
}
|
|
62934
|
+
if (timestamps.length === 1) {
|
|
62935
|
+
const streamEnd = videoStreamStartSeconds + videoStreamDurationSeconds;
|
|
62936
|
+
const rawInterval = streamEnd - (timestamps[0] ?? videoStreamStartSeconds);
|
|
62937
|
+
const singleGopInterval = Number.isFinite(rawInterval) ? Math.max(rawInterval, 0) : 0;
|
|
62938
|
+
const roundedInterval = Math.round(singleGopInterval * 100) / 100;
|
|
62939
|
+
return {
|
|
62940
|
+
avgIntervalSeconds: roundedInterval,
|
|
62941
|
+
maxIntervalSeconds: roundedInterval,
|
|
62942
|
+
keyframeCount: 1,
|
|
62943
|
+
isProblematic: singleGopInterval > PROBLEMATIC_KEYFRAME_INTERVAL_SECONDS
|
|
62944
|
+
};
|
|
62945
|
+
}
|
|
62897
62946
|
let maxInterval = 0;
|
|
62898
62947
|
let totalInterval = 0;
|
|
62899
62948
|
for (let i = 1; i < timestamps.length; i++) {
|
|
@@ -62906,10 +62955,10 @@ async function analyzeKeyframeIntervalsUncached(filePath) {
|
|
|
62906
62955
|
avgIntervalSeconds: Math.round(avgInterval * 100) / 100,
|
|
62907
62956
|
maxIntervalSeconds: Math.round(maxInterval * 100) / 100,
|
|
62908
62957
|
keyframeCount: timestamps.length,
|
|
62909
|
-
isProblematic: maxInterval >
|
|
62958
|
+
isProblematic: maxInterval > PROBLEMATIC_KEYFRAME_INTERVAL_SECONDS
|
|
62910
62959
|
};
|
|
62911
62960
|
}
|
|
62912
|
-
var FFPROBE_STDERR_MAX_BYTES, FFPROBE_STDOUT_MAX_CHARS, FFPROBE_ERROR_MAX_CHARS, videoMetadataCache, finalVideoFrameTimestampCache, finalVideoFrameTimestampSignalCaches, audioMetadataCache, mediaProbeOutputCache, mediaProbeOutputSignalCaches, MEDIA_PROBE_OUTPUT_CACHE_MAX_ENTRIES, AAC_LC_SAMPLES_PER_PACKET, STILL_IMAGE_DEMUXERS, StructurallyIncompletePngError, extractVideoMetadata, keyframeCache;
|
|
62961
|
+
var FFPROBE_STDERR_MAX_BYTES, FFPROBE_STDOUT_MAX_CHARS, FFPROBE_ERROR_MAX_CHARS, videoMetadataCache, finalVideoFrameTimestampCache, finalVideoFrameTimestampSignalCaches, audioMetadataCache, mediaProbeOutputCache, mediaProbeOutputSignalCaches, MEDIA_PROBE_OUTPUT_CACHE_MAX_ENTRIES, AAC_LC_SAMPLES_PER_PACKET, STILL_IMAGE_DEMUXERS, StructurallyIncompletePngError, extractVideoMetadata, keyframeCache, PROBLEMATIC_KEYFRAME_INTERVAL_SECONDS;
|
|
62913
62962
|
var init_ffprobe = __esm({
|
|
62914
62963
|
"../engine/src/utils/ffprobe.ts"() {
|
|
62915
62964
|
"use strict";
|
|
@@ -62952,6 +63001,7 @@ var init_ffprobe = __esm({
|
|
|
62952
63001
|
};
|
|
62953
63002
|
extractVideoMetadata = extractMediaMetadata;
|
|
62954
63003
|
keyframeCache = /* @__PURE__ */ new Map();
|
|
63004
|
+
PROBLEMATIC_KEYFRAME_INTERVAL_SECONDS = 2;
|
|
62955
63005
|
}
|
|
62956
63006
|
});
|
|
62957
63007
|
|
|
@@ -64117,6 +64167,11 @@ function isUnresolvedAssetPlaceholder(rawSrc) {
|
|
|
64117
64167
|
function cleanAssetUrl(url) {
|
|
64118
64168
|
return url.trim().split(/[?#]/, 1)[0] ?? "";
|
|
64119
64169
|
}
|
|
64170
|
+
function isWithinProjectRoot(projectDir, candidate, pathModule = { resolve: resolve5, relative, isAbsolute }) {
|
|
64171
|
+
const projectRoot = pathModule.resolve(projectDir);
|
|
64172
|
+
const relativePath = pathModule.relative(projectRoot, candidate);
|
|
64173
|
+
return relativePath === "" || !relativePath.startsWith("..") && !pathModule.isAbsolute(relativePath);
|
|
64174
|
+
}
|
|
64120
64175
|
function reanchoredCandidates(variant, baseDir, compiledDir) {
|
|
64121
64176
|
const baseAbs = resolve5(baseDir);
|
|
64122
64177
|
const joinedAbs = resolve5(join8(baseDir, variant));
|
|
@@ -82481,6 +82536,14 @@ function readRegularFile(filePath) {
|
|
|
82481
82536
|
closeSync4(fd);
|
|
82482
82537
|
}
|
|
82483
82538
|
}
|
|
82539
|
+
function resolveContainedPaths(projectDir, compiledDir, relativePath) {
|
|
82540
|
+
const projectPath = join18(projectDir, relativePath);
|
|
82541
|
+
if (!isWithinProjectRoot(projectDir, projectPath)) return null;
|
|
82542
|
+
if (!compiledDir) return { projectPath, compiledPath: null };
|
|
82543
|
+
const compiledPath = join18(compiledDir, relativePath);
|
|
82544
|
+
if (!isWithinProjectRoot(compiledDir, compiledPath)) return null;
|
|
82545
|
+
return { projectPath, compiledPath };
|
|
82546
|
+
}
|
|
82484
82547
|
function createFileServer(options) {
|
|
82485
82548
|
const { projectDir, compiledDir, port = 0, stripEmbeddedRuntime = true } = options;
|
|
82486
82549
|
const headScripts = options.headScripts ?? [];
|
|
@@ -82490,8 +82553,9 @@ function createFileServer(options) {
|
|
|
82490
82553
|
let requestPath = c.req.path;
|
|
82491
82554
|
if (requestPath === "/") requestPath = "/index.html";
|
|
82492
82555
|
const relativePath = requestPath.replace(/^\//, "");
|
|
82493
|
-
const
|
|
82494
|
-
|
|
82556
|
+
const resolved = resolveContainedPaths(projectDir, compiledDir, relativePath);
|
|
82557
|
+
if (!resolved) return c.text("Not found", 404);
|
|
82558
|
+
const content = (resolved.compiledPath ? readRegularFile(resolved.compiledPath) : null) ?? readRegularFile(resolved.projectPath);
|
|
82495
82559
|
if (content === null) return c.text("Not found", 404);
|
|
82496
82560
|
const ext = extname3(relativePath).toLowerCase();
|
|
82497
82561
|
const contentType = MIME_TYPES[ext] || "application/octet-stream";
|
|
@@ -82522,6 +82586,7 @@ var init_fileServer = __esm({
|
|
|
82522
82586
|
"../engine/src/services/fileServer.ts"() {
|
|
82523
82587
|
"use strict";
|
|
82524
82588
|
init_compiler();
|
|
82589
|
+
init_assetResolution();
|
|
82525
82590
|
MIME_TYPES = {
|
|
82526
82591
|
".html": "text/html; charset=utf-8",
|
|
82527
82592
|
".css": "text/css; charset=utf-8",
|
|
@@ -83675,6 +83740,7 @@ __export(src_exports, {
|
|
|
83675
83740
|
classifyVideoExtractionError: () => classifyVideoExtractionError,
|
|
83676
83741
|
cloneCaptureWarning: () => cloneCaptureWarning,
|
|
83677
83742
|
cloneCaptureWarnings: () => cloneCaptureWarnings,
|
|
83743
|
+
closeBrowserPool: () => closeBrowserPool,
|
|
83678
83744
|
closeCaptureSession: () => closeCaptureSession,
|
|
83679
83745
|
completeDeferredDrawElementInit: () => completeDeferredDrawElementInit,
|
|
83680
83746
|
compositionRequiresWebGpu: () => compositionRequiresWebGpu,
|
|
@@ -91976,12 +92042,14 @@ init_dist2();
|
|
|
91976
92042
|
init_mediaTiming();
|
|
91977
92043
|
init_src();
|
|
91978
92044
|
var COMPOSITION_HOST_ATTR = "data-composition-file";
|
|
91979
|
-
|
|
91980
|
-
|
|
91981
|
-
|
|
91982
|
-
|
|
91983
|
-
|
|
92045
|
+
function resolveHostEnd(host, hostStart) {
|
|
92046
|
+
return resolveAuthoredTimingWindow({
|
|
92047
|
+
start: hostStart,
|
|
92048
|
+
duration: host.getAttribute("data-duration"),
|
|
92049
|
+
end: host.getAttribute("data-end")
|
|
92050
|
+
})?.end ?? null;
|
|
91984
92051
|
}
|
|
92052
|
+
var ROOT_WINDOW = { offset: 0, limit: Infinity, basis: "local" };
|
|
91985
92053
|
function resolveHostWindow(element, document2, startCache, visiting) {
|
|
91986
92054
|
const hosts = [];
|
|
91987
92055
|
for (let ancestor2 = element.parentElement; ancestor2; ancestor2 = ancestor2.parentElement) {
|
|
@@ -91992,7 +92060,7 @@ function resolveHostWindow(element, document2, startCache, visiting) {
|
|
|
91992
92060
|
let limit = Infinity;
|
|
91993
92061
|
for (const host of hosts.reverse()) {
|
|
91994
92062
|
const hostStart = resolveReferencedStart(document2, host, startCache, visiting);
|
|
91995
|
-
const hostEnd =
|
|
92063
|
+
const hostEnd = resolveHostEnd(host, hostStart);
|
|
91996
92064
|
if (hostEnd != null) limit = Math.min(limit, offset2 + hostEnd);
|
|
91997
92065
|
offset2 += hostStart;
|
|
91998
92066
|
}
|
|
@@ -95262,10 +95330,11 @@ async function compileForRender(projectDir, htmlPath, downloadDir, options = {})
|
|
|
95262
95330
|
if (isHttpUrl(video.src)) continue;
|
|
95263
95331
|
const videoPath = resolve12(projectDir, video.src);
|
|
95264
95332
|
const reencode = `ffmpeg -i "${video.src}" -c:v libx264 -r 30 -g 30 -keyint_min 30 -movflags +faststart -c:a copy output.mp4`;
|
|
95265
|
-
|
|
95266
|
-
withMediaProbeSlot(() =>
|
|
95267
|
-
|
|
95268
|
-
|
|
95333
|
+
(async () => {
|
|
95334
|
+
const metadata = await withMediaProbeSlot(() => extractMediaMetadata(videoPath));
|
|
95335
|
+
const analysis = await withMediaProbeSlot(
|
|
95336
|
+
() => analyzeKeyframeIntervals(videoPath, metadata)
|
|
95337
|
+
);
|
|
95269
95338
|
if (analysis.isProblematic) {
|
|
95270
95339
|
defaultLogger.warn(
|
|
95271
95340
|
`[Compiler] WARNING: Video "${video.id}" has sparse keyframes (max interval: ${analysis.maxIntervalSeconds}s). This causes seek failures and frame freezing. Re-encode with: ${reencode}`
|
|
@@ -95276,7 +95345,7 @@ async function compileForRender(projectDir, htmlPath, downloadDir, options = {})
|
|
|
95276
95345
|
`[Compiler] Video "${video.id}" is variable frame rate (VFR); the engine will normalize it to CFR before frame extraction. If rendering feels slow on this video, pre-encode once with: ${reencode}`
|
|
95277
95346
|
);
|
|
95278
95347
|
}
|
|
95279
|
-
}).catch(() => {
|
|
95348
|
+
})().catch(() => {
|
|
95280
95349
|
});
|
|
95281
95350
|
}
|
|
95282
95351
|
const { document: document2 } = parseHTML(html);
|
|
@@ -102748,8 +102817,8 @@ async function runFontLocalize(io, localize) {
|
|
|
102748
102817
|
}
|
|
102749
102818
|
|
|
102750
102819
|
// src/version.ts
|
|
102751
|
-
var VERSION = true ? "0.8.
|
|
102752
|
-
var PRODUCER_VERSION = true ? "0.8.
|
|
102820
|
+
var VERSION = true ? "0.8.63" : "0.0.0-dev";
|
|
102821
|
+
var PRODUCER_VERSION = true ? "0.8.63" : "0.0.0-dev";
|
|
102753
102822
|
|
|
102754
102823
|
// src/fontLocalizeCli.ts
|
|
102755
102824
|
async function readStdin() {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";var HyperframesPlayer=(()=>{var Ee=Object.defineProperty;var pn=Object.getOwnPropertyDescriptor;var fn=Object.getOwnPropertyNames;var hn=Object.prototype.hasOwnProperty;var bn=(n,e)=>{for(var t in e)Ee(n,t,{get:e[t],enumerable:!0})},gn=(n,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of fn(e))!hn.call(n,r)&&r!==t&&Ee(n,r,{get:()=>e[r],enumerable:!(i=pn(e,r))||i.enumerable});return n};var _n=n=>gn(Ee({},"__esModule",{value:!0}),n);var Yi={};bn(Yi,{HyperframesPlayer:()=>Ae,SPEED_PRESETS:()=>xe,formatSpeed:()=>G,formatTime:()=>ie});function dt(n){return n.hasRuntime||n.runtimeInjected?!1:!!(n.hasNestedCompositions||n.hasTimelines&&n.attempts>=5)}function H(n){return typeof n=="object"&&n!==null}function ct(n){return H(n)&&typeof n.getDuration=="function"}function mt(n){return H(n)&&typeof n.duration=="function"&&typeof n.time=="function"&&typeof n.seek=="function"&&typeof n.play=="function"&&typeof n.pause=="function"}var L="https://cdn.jsdelivr.net/npm/@hyperframes/core@0.8.
|
|
1
|
+
"use strict";var HyperframesPlayer=(()=>{var Ee=Object.defineProperty;var pn=Object.getOwnPropertyDescriptor;var fn=Object.getOwnPropertyNames;var hn=Object.prototype.hasOwnProperty;var bn=(n,e)=>{for(var t in e)Ee(n,t,{get:e[t],enumerable:!0})},gn=(n,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of fn(e))!hn.call(n,r)&&r!==t&&Ee(n,r,{get:()=>e[r],enumerable:!(i=pn(e,r))||i.enumerable});return n};var _n=n=>gn(Ee({},"__esModule",{value:!0}),n);var Yi={};bn(Yi,{HyperframesPlayer:()=>Ae,SPEED_PRESETS:()=>xe,formatSpeed:()=>G,formatTime:()=>ie});function dt(n){return n.hasRuntime||n.runtimeInjected?!1:!!(n.hasNestedCompositions||n.hasTimelines&&n.attempts>=5)}function H(n){return typeof n=="object"&&n!==null}function ct(n){return H(n)&&typeof n.getDuration=="function"}function mt(n){return H(n)&&typeof n.duration=="function"&&typeof n.time=="function"&&typeof n.seek=="function"&&typeof n.play=="function"&&typeof n.pause=="function"}var L="https://cdn.jsdelivr.net/npm/@hyperframes/core@0.8.63/dist/hyperframe.runtime.iife.js";function V(n){if(n===null)return null;let e=Number.parseInt(n,10);return Number.isFinite(e)&&e>0?e:null}function yn(n){let e=n?.querySelector("[data-composition-id][data-width][data-height]")??n?.querySelector("[data-width][data-height]");if(!e)return null;let t=V(e.getAttribute("data-width")),i=V(e.getAttribute("data-height"));return t!==null&&i!==null?{width:t,height:i}:null}var ne=class{constructor(e,t){this._iframe=e;this._callbacks=t}_iframe;_callbacks;_interval=null;_runtimeInjected=!1;get runtimeInjected(){return this._runtimeInjected}start(){this.stop(),this._runtimeInjected=!1;let e=0;this._interval=setInterval(()=>{e++;try{let t=this._iframe.contentWindow;if(!t)return;let i=!!(t.__hf||t.__player),r=!!(t.__timelines&&Object.keys(t.__timelines).length>0),o=!!this._iframe.contentDocument?.querySelector("[data-composition-src]");if(dt({hasRuntime:i,hasTimelines:r,hasNestedCompositions:o,runtimeInjected:this._runtimeInjected,attempts:e})){this._injectRuntime();return}if(this._runtimeInjected&&!i)return;let a=this._resolvePlaybackDurationAdapter(t);if(a&&a.getDuration()>0){this.stop();let s=yn(this._iframe.contentDocument);this._callbacks.onReady({duration:a.getDuration(),adapter:a,compositionSize:s});return}}catch{}e>=40&&(this.stop(),this._callbacks.onError("Composition timeline not found after 8s"))},200)}stop(){this._interval!==null&&(clearInterval(this._interval),this._interval=null)}resolveDirectTimelineAdapter(){try{let e=this._iframe.contentWindow;return e?this._resolveDirectTimelineAdapterFromWindow(e):null}catch{return null}}resolveDirectTimelineAdapterFromWindow(e){return this._resolveDirectTimelineAdapterFromWindow(e)}hasRuntimeBridge(e){return Reflect.get(e,"__hf")!==void 0||H(Reflect.get(e,"__player"))}_injectRuntime(){this._runtimeInjected=!0;try{let e=this._iframe.contentDocument;if(!e)return;let t=e.createElement("script");t.src=L,(e.head||e.documentElement).appendChild(t),this._callbacks.onRuntimeInjected?.()}catch{}}_resolveDirectTimelineAdapterFromWindow(e){if(this.hasRuntimeBridge(e))return null;let t=Reflect.get(e,"__timelines");if(!H(t))return null;let i=Object.keys(t);if(i.length===0)return null;let r=this._iframe.contentDocument?.querySelector("[data-composition-id]")?.getAttribute("data-composition-id"),o=r&&r in t?r:i[i.length-1],a=t[o];return mt(a)?a:null}_resolvePlaybackDurationAdapter(e){let t=Reflect.get(e,"__player");if(ct(t))return{kind:"runtime",getDuration:()=>t.getDuration()};let i=this._resolveDirectTimelineAdapterFromWindow(e);return i?{kind:"direct-timeline",timeline:i,getDuration:()=>i.duration()}:null}};var pt=`
|
|
2
2
|
:host {
|
|
3
3
|
display: block;
|
|
4
4
|
position: relative;
|
|
@@ -56408,10 +56408,10 @@ function collectSubCompositionSrcs(html) {
|
|
|
56408
56408
|
function cleanAssetUrl(url) {
|
|
56409
56409
|
return url.trim().split(/[?#]/, 1)[0] ?? "";
|
|
56410
56410
|
}
|
|
56411
|
-
function isWithinProjectRoot(projectDir, candidate) {
|
|
56412
|
-
const projectRoot = resolve(projectDir);
|
|
56413
|
-
const relativePath = relative(projectRoot, candidate);
|
|
56414
|
-
return relativePath === "" || !relativePath.startsWith("..") && !isAbsolute(relativePath);
|
|
56411
|
+
function isWithinProjectRoot(projectDir, candidate, pathModule = { resolve, relative, isAbsolute }) {
|
|
56412
|
+
const projectRoot = pathModule.resolve(projectDir);
|
|
56413
|
+
const relativePath = pathModule.relative(projectRoot, candidate);
|
|
56414
|
+
return relativePath === "" || !relativePath.startsWith("..") && !pathModule.isAbsolute(relativePath);
|
|
56415
56415
|
}
|
|
56416
56416
|
function addCandidate(candidates, candidate) {
|
|
56417
56417
|
if (!candidates.includes(candidate)) candidates.push(candidate);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
function round3(n) {
|
|
2
|
+
return Math.round(Number(n) * 1000) / 1000;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
// 30ms in/out ramps kill the click at every concat boundary. A segment shorter
|
|
6
|
+
// than 4x the ramp would spend its whole length fading, so scale down there and
|
|
7
|
+
// skip entirely on a degenerate one. fadeIn/fadeOut are false at the export's
|
|
8
|
+
// own true start/end, where there is no splice to smooth.
|
|
9
|
+
export function fadeFilterFor(durationSeconds, { fadeIn, fadeOut }) {
|
|
10
|
+
if (!fadeIn && !fadeOut) return null;
|
|
11
|
+
const FADE_SECONDS = 0.03;
|
|
12
|
+
if (!Number.isFinite(durationSeconds) || durationSeconds <= 0.01) return null;
|
|
13
|
+
const d = Math.min(FADE_SECONDS, durationSeconds / 4);
|
|
14
|
+
const parts = [];
|
|
15
|
+
if (fadeIn) parts.push(`afade=t=in:st=0:d=${round3(d)}`);
|
|
16
|
+
if (fadeOut) {
|
|
17
|
+
const out = round3(durationSeconds - d);
|
|
18
|
+
if (out > 0) parts.push(`afade=t=out:st=${out}:d=${round3(d)}`);
|
|
19
|
+
}
|
|
20
|
+
return parts.length ? parts.join(",") : null;
|
|
21
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
import { fadeFilterFor } from "./transcriptCutFade.mjs";
|
|
4
|
+
|
|
5
|
+
test("fades in only at the first segment's trailing splice", () => {
|
|
6
|
+
assert.equal(fadeFilterFor(2, { fadeIn: false, fadeOut: true }), "afade=t=out:st=1.97:d=0.03");
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
test("fades both edges of a middle segment's two splices", () => {
|
|
10
|
+
assert.equal(
|
|
11
|
+
fadeFilterFor(2, { fadeIn: true, fadeOut: true }),
|
|
12
|
+
"afade=t=in:st=0:d=0.03,afade=t=out:st=1.97:d=0.03",
|
|
13
|
+
);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test("fades out only at the last segment's leading splice", () => {
|
|
17
|
+
assert.equal(fadeFilterFor(2, { fadeIn: true, fadeOut: false }), "afade=t=in:st=0:d=0.03");
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test("a single kept segment borders no splice at all", () => {
|
|
21
|
+
assert.equal(fadeFilterFor(2, { fadeIn: false, fadeOut: false }), null);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("still scales the ramp down on a short segment with both edges faded", () => {
|
|
25
|
+
assert.equal(
|
|
26
|
+
fadeFilterFor(0.08, { fadeIn: true, fadeOut: true }),
|
|
27
|
+
"afade=t=in:st=0:d=0.02,afade=t=out:st=0.06:d=0.02",
|
|
28
|
+
);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test("skips a degenerate segment even when a splice is requested", () => {
|
|
32
|
+
assert.equal(fadeFilterFor(0.005, { fadeIn: true, fadeOut: true }), null);
|
|
33
|
+
});
|
|
@@ -6,6 +6,7 @@ import { tmpdir } from "node:os";
|
|
|
6
6
|
import { dirname, extname, join, resolve } from "node:path";
|
|
7
7
|
import { parseArgs } from "node:util";
|
|
8
8
|
import { compileCutList } from "./lib/cutlist.mjs";
|
|
9
|
+
import { fadeFilterFor } from "./lib/transcriptCutFade.mjs";
|
|
9
10
|
import { track } from "./lib/telemetry.mjs";
|
|
10
11
|
|
|
11
12
|
const { values: args } = parseArgs({
|
|
@@ -92,11 +93,23 @@ function run() {
|
|
|
92
93
|
|
|
93
94
|
try {
|
|
94
95
|
const parts = segments.map((segment, index) => {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
96
|
+
// Intermediates carry PCM audio, not the final codec. Encoding each
|
|
97
|
+
// segment to a lossy codec separately makes the encoder pad every segment
|
|
98
|
+
// with priming silence (~25-35ms for AAC), which concat then bakes in as a
|
|
99
|
+
// gap at each cut -- a defect distinct from, and surviving, the fades
|
|
100
|
+
// below. PCM has no priming, so audio is encoded exactly once, at concat.
|
|
101
|
+
const ext = args.copy ? extname(outPath) || ".mp4" : isAudioOnly(outPath) ? ".wav" : ".mkv";
|
|
102
|
+
const out = join(tmpDir, `segment-${String(index).padStart(4, "0")}${ext}`);
|
|
103
|
+
// --copy stays fade-free (stream copy cannot filter). A segment's true
|
|
104
|
+
// start/end (index 0's start, the last segment's end) borders nothing
|
|
105
|
+
// kept, so only an interior splice edge gets a ramp.
|
|
106
|
+
const fade = args.copy
|
|
107
|
+
? null
|
|
108
|
+
: fadeFilterFor(segment.end - segment.start, {
|
|
109
|
+
fadeIn: index > 0,
|
|
110
|
+
fadeOut: index < segments.length - 1,
|
|
111
|
+
});
|
|
112
|
+
cutSegment(inputPath, segment, out, args.copy, fade);
|
|
100
113
|
return out;
|
|
101
114
|
});
|
|
102
115
|
const listPath = join(tmpDir, "list.txt");
|
|
@@ -107,9 +120,16 @@ function run() {
|
|
|
107
120
|
// Encode to a sibling temp (same extension so ffmpeg picks the right muxer),
|
|
108
121
|
// then atomic-rename so a SIGKILL mid-encode can't leave a truncated outPath.
|
|
109
122
|
const tmpOut = `${outPath}.part${extname(outPath) || ".mp4"}`;
|
|
123
|
+
// --copy already produced final-codec segments, so concat can stream-copy.
|
|
124
|
+
// Otherwise the PCM intermediates are encoded here, once, for the whole file.
|
|
125
|
+
const concatCodecs = args.copy
|
|
126
|
+
? ["-c", "copy"]
|
|
127
|
+
: isAudioOnly(outPath)
|
|
128
|
+
? encodeArgsFor(extname(outPath).toLowerCase())
|
|
129
|
+
: ["-c:v", "copy", "-c:a", "aac", "-b:a", "192k", "-movflags", "+faststart"];
|
|
110
130
|
execFileSync(
|
|
111
131
|
"ffmpeg",
|
|
112
|
-
["-y", "-f", "concat", "-safe", "0", "-i", listPath,
|
|
132
|
+
["-y", "-f", "concat", "-safe", "0", "-i", listPath, ...concatCodecs, tmpOut],
|
|
113
133
|
{
|
|
114
134
|
stdio: "ignore",
|
|
115
135
|
},
|
|
@@ -158,7 +178,7 @@ function run() {
|
|
|
158
178
|
console.log(`next: resolve --from ${outPath} --type <type>`);
|
|
159
179
|
}
|
|
160
180
|
|
|
161
|
-
function cutSegment(inputPath, segment, outPath, copy) {
|
|
181
|
+
function cutSegment(inputPath, segment, outPath, copy, fade) {
|
|
162
182
|
const argv = [
|
|
163
183
|
"-y",
|
|
164
184
|
"-nostdin",
|
|
@@ -171,8 +191,15 @@ function cutSegment(inputPath, segment, outPath, copy) {
|
|
|
171
191
|
];
|
|
172
192
|
if (copy) {
|
|
173
193
|
argv.push("-c", "copy", "-avoid_negative_ts", "make_zero");
|
|
194
|
+
} else if (extname(outPath).toLowerCase() === ".mkv") {
|
|
195
|
+
// Concat splices raw segment edges together; without a short ramp the
|
|
196
|
+
// waveform steps discontinuously at every boundary and you hear a click.
|
|
197
|
+
if (fade) argv.push("-af", fade);
|
|
198
|
+
// Video intermediate: keep the picture cheap and the audio uncompressed.
|
|
199
|
+
argv.push("-c:v", "libx264", "-preset", "veryfast", "-crf", "18", "-c:a", "pcm_s16le");
|
|
174
200
|
} else {
|
|
175
|
-
argv.push(
|
|
201
|
+
if (fade) argv.push("-af", fade);
|
|
202
|
+
argv.push("-c:a", "pcm_s16le");
|
|
176
203
|
}
|
|
177
204
|
argv.push(outPath);
|
|
178
205
|
execFileSync("ffmpeg", argv, { stdio: "ignore" });
|
|
@@ -180,6 +207,10 @@ function cutSegment(inputPath, segment, outPath, copy) {
|
|
|
180
207
|
|
|
181
208
|
// Codec set per output container. Audio-only outputs must not get the
|
|
182
209
|
// video-centric aac/x264 set (aac inside .wav breaks timing entirely).
|
|
210
|
+
function isAudioOnly(filePath) {
|
|
211
|
+
return [".wav", ".mp3", ".m4a", ".aac", ".flac"].includes(extname(filePath).toLowerCase());
|
|
212
|
+
}
|
|
213
|
+
|
|
183
214
|
function encodeArgsFor(ext) {
|
|
184
215
|
if (ext === ".wav") return ["-c:a", "pcm_s16le"];
|
|
185
216
|
if (ext === ".mp3") return ["-c:a", "libmp3lame", "-q:a", "2"];
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { execFileSync, spawnSync } from "node:child_process";
|
|
3
|
+
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import test from "node:test";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
|
|
9
|
+
const SCRIPT = fileURLToPath(new URL("./transcript-cut.mjs", import.meta.url));
|
|
10
|
+
const HAS_FFMPEG =
|
|
11
|
+
spawnSync("ffmpeg", ["-version"], { stdio: "ignore" }).status === 0 &&
|
|
12
|
+
spawnSync("ffprobe", ["-version"], { stdio: "ignore" }).status === 0;
|
|
13
|
+
|
|
14
|
+
const SAMPLE_RATE = 44100;
|
|
15
|
+
// A continuous tone's own slope between samples never exceeds amplitude * 2*pi*f/rate
|
|
16
|
+
// (~2054 for 440Hz at full scale here); a raw splice between two independently-cut
|
|
17
|
+
// points on the same tone lands at two unrelated phases, so an unfaded join jumps far
|
|
18
|
+
// past that. This threshold sits well above normal tone motion and well below a splice.
|
|
19
|
+
const MAX_CONTINUOUS_STEP = 6000;
|
|
20
|
+
const MIN_SUSPICIOUS_ZERO_RUN = 20;
|
|
21
|
+
|
|
22
|
+
function fixture() {
|
|
23
|
+
const dir = mkdtempSync(join(tmpdir(), "media-use-transcript-cut-"));
|
|
24
|
+
return { dir, cleanup: () => rmSync(dir, { recursive: true, force: true }) };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function run(args) {
|
|
28
|
+
return spawnSync(process.execPath, [SCRIPT, ...args], { encoding: "utf8" });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function readPcm(filePath) {
|
|
32
|
+
const raw = execFileSync("ffmpeg", [
|
|
33
|
+
"-hide_banner",
|
|
34
|
+
"-loglevel",
|
|
35
|
+
"error",
|
|
36
|
+
"-i",
|
|
37
|
+
filePath,
|
|
38
|
+
"-f",
|
|
39
|
+
"s16le",
|
|
40
|
+
"-ac",
|
|
41
|
+
"1",
|
|
42
|
+
"-ar",
|
|
43
|
+
String(SAMPLE_RATE),
|
|
44
|
+
"-",
|
|
45
|
+
]);
|
|
46
|
+
const samples = new Int16Array(raw.buffer, raw.byteOffset, raw.length / 2);
|
|
47
|
+
return samples;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
test(
|
|
51
|
+
"keeps a spliced tone continuous at every cut, with no raw phase jump or silence gap",
|
|
52
|
+
{ skip: !HAS_FFMPEG },
|
|
53
|
+
(t) => {
|
|
54
|
+
const { dir, cleanup } = fixture();
|
|
55
|
+
t.after(cleanup);
|
|
56
|
+
|
|
57
|
+
const source = join(dir, "tone.wav");
|
|
58
|
+
execFileSync("ffmpeg", [
|
|
59
|
+
"-y",
|
|
60
|
+
"-hide_banner",
|
|
61
|
+
"-loglevel",
|
|
62
|
+
"error",
|
|
63
|
+
"-f",
|
|
64
|
+
"lavfi",
|
|
65
|
+
"-i",
|
|
66
|
+
`sine=frequency=440:duration=6:sample_rate=${SAMPLE_RATE}`,
|
|
67
|
+
source,
|
|
68
|
+
]);
|
|
69
|
+
|
|
70
|
+
const transcriptPath = join(dir, "transcript.json");
|
|
71
|
+
writeFileSync(transcriptPath, JSON.stringify({ words: [{ text: "tone", start: 0, end: 6 }] }));
|
|
72
|
+
|
|
73
|
+
const output = join(dir, "out.wav");
|
|
74
|
+
// Two "ugly" removal ranges: neither aligned to the 440Hz period, so the kept
|
|
75
|
+
// segments' cut edges land at unrelated phases of the same continuous tone --
|
|
76
|
+
// exactly the shape that clicks without a fade, and the shape jrusso1020's
|
|
77
|
+
// review proved this branch never actually fades.
|
|
78
|
+
const result = run([
|
|
79
|
+
"--input",
|
|
80
|
+
source,
|
|
81
|
+
"--transcript",
|
|
82
|
+
transcriptPath,
|
|
83
|
+
"--remove",
|
|
84
|
+
"1.37-1.83,3.29-3.71",
|
|
85
|
+
"--out",
|
|
86
|
+
output,
|
|
87
|
+
"--json",
|
|
88
|
+
]);
|
|
89
|
+
assert.equal(result.status, 0, result.stderr || result.stdout);
|
|
90
|
+
|
|
91
|
+
const samples = readPcm(output);
|
|
92
|
+
assert.ok(samples.length > SAMPLE_RATE, "expected several seconds of audio");
|
|
93
|
+
|
|
94
|
+
let maxStep = 0;
|
|
95
|
+
let zeroRun = 0;
|
|
96
|
+
let maxZeroRun = 0;
|
|
97
|
+
for (let i = 1; i < samples.length; i++) {
|
|
98
|
+
const step = Math.abs(samples[i] - samples[i - 1]);
|
|
99
|
+
if (step > maxStep) maxStep = step;
|
|
100
|
+
if (samples[i] === 0) {
|
|
101
|
+
zeroRun++;
|
|
102
|
+
if (zeroRun > maxZeroRun) maxZeroRun = zeroRun;
|
|
103
|
+
} else {
|
|
104
|
+
zeroRun = 0;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
assert.ok(
|
|
109
|
+
maxStep <= MAX_CONTINUOUS_STEP,
|
|
110
|
+
`largest sample-to-sample step was ${maxStep}, expected <= ${MAX_CONTINUOUS_STEP} (a raw, unfaded splice)`,
|
|
111
|
+
);
|
|
112
|
+
assert.ok(
|
|
113
|
+
maxZeroRun < MIN_SUSPICIOUS_ZERO_RUN,
|
|
114
|
+
`found a run of ${maxZeroRun} consecutive zero samples, expected < ${MIN_SUSPICIOUS_ZERO_RUN} (a priming-silence gap)`,
|
|
115
|
+
);
|
|
116
|
+
},
|
|
117
|
+
);
|
package/dist/studio/assets/{hyperframes-player-nmVkPQza.js → hyperframes-player-FmXtbvEE.js}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var we=Object.defineProperty;var Ae=(s,i,e)=>i in s?we(s,i,{enumerable:!0,configurable:!0,writable:!0,value:e}):s[i]=e;var h=(s,i,e)=>Ae(s,typeof i!="symbol"?i+"":i,e);import{r as he,i as Ee,a as w,b as ue,c as Ce,d as Te,s as Re,e as Se,F as Le}from"./index-
|
|
1
|
+
var we=Object.defineProperty;var Ae=(s,i,e)=>i in s?we(s,i,{enumerable:!0,configurable:!0,writable:!0,value:e}):s[i]=e;var h=(s,i,e)=>Ae(s,typeof i!="symbol"?i+"":i,e);import{r as he,i as Ee,a as w,b as ue,c as Ce,d as Te,s as Re,e as Se,F as Le}from"./index-CjueZL58.js";function xe(s){return s.hasRuntime||s.runtimeInjected?!1:!!(s.hasNestedCompositions||s.hasTimelines&&s.attempts>=5)}function $(s){return typeof s=="object"&&s!==null}function Me(s){return $(s)&&typeof s.getDuration=="function"}function De(s){return $(s)&&typeof s.duration=="function"&&typeof s.time=="function"&&typeof s.seek=="function"&&typeof s.play=="function"&&typeof s.pause=="function"}function Pe(s){if(!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(s))throw new Error(`Invalid HyperFrames runtime version: ${s}`);return`https://cdn.jsdelivr.net/npm/@hyperframes/core@${s}/dist/hyperframe.runtime.iife.js`}const H=typeof __HYPERFRAMES_RUNTIME_CDN_URL__=="string"?__HYPERFRAMES_RUNTIME_CDN_URL__:Pe("0.0.0-dev");function V(s){if(s===null)return null;const i=Number.parseInt(s,10);return Number.isFinite(i)&&i>0?i:null}function ke(s){const i=(s==null?void 0:s.querySelector("[data-composition-id][data-width][data-height]"))??(s==null?void 0:s.querySelector("[data-width][data-height]"));if(!i)return null;const e=V(i.getAttribute("data-width")),t=V(i.getAttribute("data-height"));return e!==null&&t!==null?{width:e,height:t}:null}class Ie{constructor(i,e){h(this,"_interval",null);h(this,"_runtimeInjected",!1);this._iframe=i,this._callbacks=e}get runtimeInjected(){return this._runtimeInjected}start(){this.stop(),this._runtimeInjected=!1;let i=0;this._interval=setInterval(()=>{var e;i++;try{const t=this._iframe.contentWindow;if(!t)return;const r=!!(t.__hf||t.__player),n=!!(t.__timelines&&Object.keys(t.__timelines).length>0),a=!!((e=this._iframe.contentDocument)!=null&&e.querySelector("[data-composition-src]"));if(xe({hasRuntime:r,hasTimelines:n,hasNestedCompositions:a,runtimeInjected:this._runtimeInjected,attempts:i})){this._injectRuntime();return}if(this._runtimeInjected&&!r)return;const d=this._resolvePlaybackDurationAdapter(t);if(d&&d.getDuration()>0){this.stop();const c=ke(this._iframe.contentDocument);this._callbacks.onReady({duration:d.getDuration(),adapter:d,compositionSize:c});return}}catch{}i>=40&&(this.stop(),this._callbacks.onError("Composition timeline not found after 8s"))},200)}stop(){this._interval!==null&&(clearInterval(this._interval),this._interval=null)}resolveDirectTimelineAdapter(){try{const i=this._iframe.contentWindow;return i?this._resolveDirectTimelineAdapterFromWindow(i):null}catch{return null}}resolveDirectTimelineAdapterFromWindow(i){return this._resolveDirectTimelineAdapterFromWindow(i)}hasRuntimeBridge(i){return Reflect.get(i,"__hf")!==void 0||$(Reflect.get(i,"__player"))}_injectRuntime(){var i,e;this._runtimeInjected=!0;try{const t=this._iframe.contentDocument;if(!t)return;const r=t.createElement("script");r.src=H,(t.head||t.documentElement).appendChild(r),(e=(i=this._callbacks).onRuntimeInjected)==null||e.call(i)}catch{}}_resolveDirectTimelineAdapterFromWindow(i){var d,c;if(this.hasRuntimeBridge(i))return null;const e=Reflect.get(i,"__timelines");if(!$(e))return null;const t=Object.keys(e);if(t.length===0)return null;const r=(c=(d=this._iframe.contentDocument)==null?void 0:d.querySelector("[data-composition-id]"))==null?void 0:c.getAttribute("data-composition-id"),n=r&&r in e?r:t[t.length-1],a=e[n];return De(a)?a:null}_resolvePlaybackDurationAdapter(i){const e=Reflect.get(i,"__player");if(Me(e))return{kind:"runtime",getDuration:()=>e.getDuration()};const t=this._resolveDirectTimelineAdapterFromWindow(i);return t?{kind:"direct-timeline",timeline:t,getDuration:()=>t.duration()}:null}}const Oe=`
|
|
2
2
|
:host {
|
|
3
3
|
display: block;
|
|
4
4
|
position: relative;
|