@editframe/elements 0.20.4-beta.0 → 0.21.0-beta.0
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/DelayedLoadingState.js +0 -27
- package/dist/EF_FRAMEGEN.d.ts +5 -3
- package/dist/EF_FRAMEGEN.js +50 -11
- package/dist/_virtual/_@oxc-project_runtime@0.93.0/helpers/decorate.js +7 -0
- package/dist/elements/ContextProxiesController.js +2 -22
- package/dist/elements/EFAudio.js +4 -8
- package/dist/elements/EFCaptions.js +59 -84
- package/dist/elements/EFImage.js +5 -6
- package/dist/elements/EFMedia/AssetIdMediaEngine.js +2 -4
- package/dist/elements/EFMedia/AssetMediaEngine.js +35 -30
- package/dist/elements/EFMedia/BaseMediaEngine.js +57 -73
- package/dist/elements/EFMedia/BufferedSeekingInput.js +134 -76
- package/dist/elements/EFMedia/JitMediaEngine.js +9 -19
- package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.js +3 -6
- package/dist/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.js +1 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.js +1 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioInputTask.js +6 -5
- package/dist/elements/EFMedia/audioTasks/makeAudioSeekTask.js +1 -3
- package/dist/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.js +1 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.js +1 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.js +1 -1
- package/dist/elements/EFMedia/shared/AudioSpanUtils.js +4 -16
- package/dist/elements/EFMedia/shared/BufferUtils.js +2 -15
- package/dist/elements/EFMedia/shared/GlobalInputCache.js +0 -24
- package/dist/elements/EFMedia/shared/PrecisionUtils.js +0 -21
- package/dist/elements/EFMedia/shared/ThumbnailExtractor.js +0 -17
- package/dist/elements/EFMedia/tasks/makeMediaEngineTask.js +1 -10
- package/dist/elements/EFMedia/videoTasks/MainVideoInputCache.d.ts +29 -0
- package/dist/elements/EFMedia/videoTasks/MainVideoInputCache.js +32 -0
- package/dist/elements/EFMedia/videoTasks/ScrubInputCache.js +1 -15
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoBufferTask.js +1 -7
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoInputTask.js +8 -5
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.js +12 -13
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentIdTask.js +1 -1
- package/dist/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.js +134 -70
- package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.js +7 -11
- package/dist/elements/EFMedia.js +26 -24
- package/dist/elements/EFSourceMixin.js +5 -7
- package/dist/elements/EFSurface.js +6 -9
- package/dist/elements/EFTemporal.js +19 -37
- package/dist/elements/EFThumbnailStrip.js +16 -59
- package/dist/elements/EFTimegroup.js +95 -90
- package/dist/elements/EFVideo.d.ts +6 -2
- package/dist/elements/EFVideo.js +142 -107
- package/dist/elements/EFWaveform.js +18 -27
- package/dist/elements/SampleBuffer.js +2 -5
- package/dist/elements/TargetController.js +3 -3
- package/dist/elements/durationConverter.js +4 -4
- package/dist/elements/updateAnimations.js +14 -35
- package/dist/gui/ContextMixin.js +23 -52
- package/dist/gui/EFConfiguration.js +7 -7
- package/dist/gui/EFControls.js +5 -5
- package/dist/gui/EFFilmstrip.js +77 -98
- package/dist/gui/EFFitScale.js +5 -6
- package/dist/gui/EFFocusOverlay.js +4 -4
- package/dist/gui/EFPreview.js +4 -4
- package/dist/gui/EFScrubber.js +9 -9
- package/dist/gui/EFTimeDisplay.js +5 -5
- package/dist/gui/EFToggleLoop.js +4 -4
- package/dist/gui/EFTogglePlay.js +5 -5
- package/dist/gui/EFWorkbench.js +5 -5
- package/dist/gui/TWMixin2.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/otel/BridgeSpanExporter.d.ts +13 -0
- package/dist/otel/BridgeSpanExporter.js +87 -0
- package/dist/otel/setupBrowserTracing.d.ts +12 -0
- package/dist/otel/setupBrowserTracing.js +30 -0
- package/dist/otel/tracingHelpers.d.ts +34 -0
- package/dist/otel/tracingHelpers.js +113 -0
- package/dist/transcoding/cache/RequestDeduplicator.js +0 -21
- package/dist/transcoding/cache/URLTokenDeduplicator.js +1 -21
- package/dist/transcoding/utils/UrlGenerator.js +2 -19
- package/dist/utils/LRUCache.js +6 -53
- package/package.json +10 -2
- package/src/elements/EFCaptions.browsertest.ts +2 -0
- package/src/elements/EFMedia/AssetMediaEngine.ts +65 -37
- package/src/elements/EFMedia/BaseMediaEngine.ts +110 -52
- package/src/elements/EFMedia/BufferedSeekingInput.ts +218 -101
- package/src/elements/EFMedia/audioTasks/makeAudioInputTask.ts +7 -3
- package/src/elements/EFMedia/videoTasks/MainVideoInputCache.ts +76 -0
- package/src/elements/EFMedia/videoTasks/makeScrubVideoInputTask.ts +16 -10
- package/src/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.ts +7 -1
- package/src/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.ts +222 -116
- package/src/elements/EFMedia.ts +16 -1
- package/src/elements/EFTimegroup.browsertest.ts +10 -8
- package/src/elements/EFTimegroup.ts +164 -76
- package/src/elements/EFVideo.browsertest.ts +19 -27
- package/src/elements/EFVideo.ts +203 -101
- package/src/otel/BridgeSpanExporter.ts +150 -0
- package/src/otel/setupBrowserTracing.ts +68 -0
- package/src/otel/tracingHelpers.ts +251 -0
- package/types.json +1 -1
|
@@ -1,16 +1,11 @@
|
|
|
1
|
+
import { __decorate } from "../_virtual/_@oxc-project_runtime@0.93.0/helpers/decorate.js";
|
|
1
2
|
import { OrderedLRUCache } from "../utils/LRUCache.js";
|
|
2
3
|
import { TargetController } from "./TargetController.js";
|
|
3
4
|
import { Task } from "@lit/task";
|
|
4
5
|
import { LitElement, css, html } from "lit";
|
|
5
6
|
import { customElement, property, state } from "lit/decorators.js";
|
|
6
|
-
import _decorate from "@oxc-project/runtime/helpers/decorate";
|
|
7
7
|
import { createRef, ref } from "lit/directives/ref.js";
|
|
8
|
-
|
|
9
|
-
* Global thumbnail image cache for smooth resize performance
|
|
10
|
-
* Shared across all thumbnail strip instances
|
|
11
|
-
* Uses OrderedLRUCache for efficient timestamp-based searching
|
|
12
|
-
*/
|
|
13
|
-
const thumbnailImageCache = new OrderedLRUCache(200, (a, b) => {
|
|
8
|
+
var thumbnailImageCache = new OrderedLRUCache(200, (a, b) => {
|
|
14
9
|
const partsA = a.split(":");
|
|
15
10
|
const partsB = b.split(":");
|
|
16
11
|
const timeA = Number.parseFloat(partsA[partsA.length - 1] || "0");
|
|
@@ -18,27 +13,16 @@ const thumbnailImageCache = new OrderedLRUCache(200, (a, b) => {
|
|
|
18
13
|
return timeA - timeB;
|
|
19
14
|
});
|
|
20
15
|
globalThis.debugThumbnailCache = thumbnailImageCache;
|
|
21
|
-
/**
|
|
22
|
-
* Quantize timestamp to 30fps frame boundaries for consistent caching
|
|
23
|
-
* This eliminates cache misses from floating point precision differences
|
|
24
|
-
*/
|
|
25
16
|
function quantizeTimestamp(timeMs) {
|
|
26
17
|
const frameIntervalMs = 1e3 / 30;
|
|
27
18
|
return Math.round(timeMs / frameIntervalMs) * frameIntervalMs;
|
|
28
19
|
}
|
|
29
|
-
/**
|
|
30
|
-
* Generate cache key for thumbnail image data (dimension-independent, quantized)
|
|
31
|
-
*/
|
|
32
20
|
function getThumbnailCacheKey(videoSrc, timeMs) {
|
|
33
21
|
const quantizedTimeMs = quantizeTimestamp(timeMs);
|
|
34
22
|
return `${videoSrc}:${quantizedTimeMs}`;
|
|
35
23
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Calculate optimal thumbnail count and timestamps for the strip
|
|
40
|
-
* Groups thumbnails by scrub segment ID for efficient caching
|
|
41
|
-
*/
|
|
24
|
+
var THUMBNAIL_GAP = 1;
|
|
25
|
+
var STRIP_BORDER_PADDING = 4;
|
|
42
26
|
function calculateThumbnailLayout(stripWidth, thumbnailWidth, startTimeMs, endTimeMs, scrubSegmentDurationMs) {
|
|
43
27
|
if (stripWidth <= 0 || thumbnailWidth <= 0 || endTimeMs <= startTimeMs) return {
|
|
44
28
|
count: 0,
|
|
@@ -68,7 +52,7 @@ function calculateThumbnailLayout(stripWidth, thumbnailWidth, startTimeMs, endTi
|
|
|
68
52
|
segments
|
|
69
53
|
};
|
|
70
54
|
}
|
|
71
|
-
|
|
55
|
+
var EFThumbnailStrip = class EFThumbnailStrip$1 extends LitElement {
|
|
72
56
|
constructor(..._args) {
|
|
73
57
|
super(..._args);
|
|
74
58
|
this.canvasRef = createRef();
|
|
@@ -221,9 +205,6 @@ let EFThumbnailStrip = class EFThumbnailStrip$1 extends LitElement {
|
|
|
221
205
|
get stripWidth() {
|
|
222
206
|
return this._stripWidth;
|
|
223
207
|
}
|
|
224
|
-
/**
|
|
225
|
-
* Run thumbnail render task directly with provided layout (bypasses task args dependency)
|
|
226
|
-
*/
|
|
227
208
|
async runThumbnailRenderTask(layout) {
|
|
228
209
|
if (!layout || !this.targetElement || layout.count === 0) return [];
|
|
229
210
|
return this.renderThumbnails(layout, this.targetElement, this.thumbnailWidth);
|
|
@@ -236,9 +217,6 @@ let EFThumbnailStrip = class EFThumbnailStrip$1 extends LitElement {
|
|
|
236
217
|
}
|
|
237
218
|
if (changedProperties.has("thumbnailWidth") || changedProperties.has("startTimeMs") || changedProperties.has("endTimeMs") || changedProperties.has("useIntrinsicDuration")) this.runThumbnailUpdate();
|
|
238
219
|
}
|
|
239
|
-
/**
|
|
240
|
-
* Run thumbnail update with responsive debouncing (based on EFTimegroup currentTime pattern)
|
|
241
|
-
*/
|
|
242
220
|
runThumbnailUpdate() {
|
|
243
221
|
if (this._thumbnailUpdateInProgress) {
|
|
244
222
|
this._pendingThumbnailUpdate = true;
|
|
@@ -257,9 +235,6 @@ let EFThumbnailStrip = class EFThumbnailStrip$1 extends LitElement {
|
|
|
257
235
|
}
|
|
258
236
|
});
|
|
259
237
|
}
|
|
260
|
-
/**
|
|
261
|
-
* Calculate layout with a ready media engine
|
|
262
|
-
*/
|
|
263
238
|
calculateLayoutWithMediaEngine(stripWidth, thumbnailWidth, targetElement, startTimeMs, endTimeMs, useIntrinsicDuration, mediaEngine) {
|
|
264
239
|
if (useIntrinsicDuration) {
|
|
265
240
|
const effectiveStartMs$1 = startTimeMs ?? 0;
|
|
@@ -272,17 +247,10 @@ let EFThumbnailStrip = class EFThumbnailStrip$1 extends LitElement {
|
|
|
272
247
|
const effectiveEndMs = endTimeMs !== void 0 ? sourceStart + endTimeMs : sourceStart + trimmedDuration;
|
|
273
248
|
return this.generateLayoutFromTimeRange(stripWidth, thumbnailWidth, effectiveStartMs, effectiveEndMs, mediaEngine);
|
|
274
249
|
}
|
|
275
|
-
/**
|
|
276
|
-
* Generate layout from calculated time range
|
|
277
|
-
*/
|
|
278
250
|
generateLayoutFromTimeRange(stripWidth, thumbnailWidth, effectiveStartMs, effectiveEndMs, mediaEngine) {
|
|
279
251
|
const scrubSegmentDurationMs = mediaEngine && typeof mediaEngine.getScrubVideoRendition === "function" ? mediaEngine.getScrubVideoRendition()?.segmentDurationMs : void 0;
|
|
280
|
-
|
|
281
|
-
return layout;
|
|
252
|
+
return calculateThumbnailLayout(stripWidth, thumbnailWidth, effectiveStartMs, effectiveEndMs, scrubSegmentDurationMs);
|
|
282
253
|
}
|
|
283
|
-
/**
|
|
284
|
-
* Render thumbnails with provided layout (main rendering logic)
|
|
285
|
-
*/
|
|
286
254
|
async renderThumbnails(layout, targetElement, thumbnailWidth) {
|
|
287
255
|
if (!layout || !targetElement || layout.count === 0) return [];
|
|
288
256
|
const videoSrc = targetElement.src;
|
|
@@ -299,8 +267,7 @@ let EFThumbnailStrip = class EFThumbnailStrip$1 extends LitElement {
|
|
|
299
267
|
const timePlus = thumbnail.timeMs + 5e3;
|
|
300
268
|
const rangeStartKey = `${videoSrc}:${timeMinus}`;
|
|
301
269
|
const rangeEndKey = `${videoSrc}:${timePlus}`;
|
|
302
|
-
const
|
|
303
|
-
const sameVideoHits = nearHits.filter((hit) => hit.key.startsWith(`${videoSrc}:`));
|
|
270
|
+
const sameVideoHits = thumbnailImageCache.findRange(rangeStartKey, rangeEndKey).filter((hit) => hit.key.startsWith(`${videoSrc}:`));
|
|
304
271
|
if (sameVideoHits.length > 0) {
|
|
305
272
|
const nearestHit = sameVideoHits.reduce((closest, current) => {
|
|
306
273
|
const currentParts = current.key.split(":");
|
|
@@ -338,8 +305,7 @@ let EFThumbnailStrip = class EFThumbnailStrip$1 extends LitElement {
|
|
|
338
305
|
this.resizeObserver = new ResizeObserver((entries) => {
|
|
339
306
|
for (const entry of entries) {
|
|
340
307
|
const width = entry.borderBoxSize && entry.borderBoxSize.length > 0 ? entry.borderBoxSize[0]?.inlineSize : entry.contentRect.width;
|
|
341
|
-
|
|
342
|
-
this._stripHeight = height ?? 0;
|
|
308
|
+
this._stripHeight = (entry.borderBoxSize && entry.borderBoxSize.length > 0 ? entry.borderBoxSize[0]?.blockSize : entry.contentRect.height) ?? 0;
|
|
343
309
|
this.stripWidth = width ?? 0;
|
|
344
310
|
}
|
|
345
311
|
});
|
|
@@ -358,9 +324,6 @@ let EFThumbnailStrip = class EFThumbnailStrip$1 extends LitElement {
|
|
|
358
324
|
this._videoPropertyObserver?.disconnect();
|
|
359
325
|
this._videoPropertyObserver = void 0;
|
|
360
326
|
}
|
|
361
|
-
/**
|
|
362
|
-
* Draw thumbnails to the canvas with cache hits and placeholders
|
|
363
|
-
*/
|
|
364
327
|
async drawThumbnails(thumbnails) {
|
|
365
328
|
const canvas = this.canvasRef.value;
|
|
366
329
|
if (!canvas) return;
|
|
@@ -414,9 +377,6 @@ let EFThumbnailStrip = class EFThumbnailStrip$1 extends LitElement {
|
|
|
414
377
|
ctx.setLineDash([]);
|
|
415
378
|
}
|
|
416
379
|
}
|
|
417
|
-
/**
|
|
418
|
-
* Load missing thumbnails using MediaEngine batch extraction
|
|
419
|
-
*/
|
|
420
380
|
async loadMissingThumbnails(thumbnails, targetElement) {
|
|
421
381
|
const mediaEngine = targetElement.mediaEngineTask?.value;
|
|
422
382
|
if (!mediaEngine) return;
|
|
@@ -440,9 +400,6 @@ let EFThumbnailStrip = class EFThumbnailStrip$1 extends LitElement {
|
|
|
440
400
|
}
|
|
441
401
|
await this.drawThumbnails(thumbnails);
|
|
442
402
|
}
|
|
443
|
-
/**
|
|
444
|
-
* Convert Canvas to ImageData for caching
|
|
445
|
-
*/
|
|
446
403
|
canvasToImageData(canvas) {
|
|
447
404
|
const ctx = canvas.getContext("2d");
|
|
448
405
|
if (!ctx) return null;
|
|
@@ -459,21 +416,21 @@ let EFThumbnailStrip = class EFThumbnailStrip$1 extends LitElement {
|
|
|
459
416
|
`;
|
|
460
417
|
}
|
|
461
418
|
};
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
419
|
+
__decorate([state()], EFThumbnailStrip.prototype, "targetElement", null);
|
|
420
|
+
__decorate([property({ type: String })], EFThumbnailStrip.prototype, "target", void 0);
|
|
421
|
+
__decorate([property({
|
|
465
422
|
type: Number,
|
|
466
423
|
attribute: "thumbnail-width"
|
|
467
424
|
})], EFThumbnailStrip.prototype, "thumbnailWidth", void 0);
|
|
468
|
-
|
|
425
|
+
__decorate([property({
|
|
469
426
|
type: Number,
|
|
470
427
|
attribute: "start-time-ms"
|
|
471
428
|
})], EFThumbnailStrip.prototype, "startTimeMs", void 0);
|
|
472
|
-
|
|
429
|
+
__decorate([property({
|
|
473
430
|
type: Number,
|
|
474
431
|
attribute: "end-time-ms"
|
|
475
432
|
})], EFThumbnailStrip.prototype, "endTimeMs", void 0);
|
|
476
|
-
|
|
433
|
+
__decorate([property({
|
|
477
434
|
type: Boolean,
|
|
478
435
|
attribute: "use-intrinsic-duration",
|
|
479
436
|
reflect: true,
|
|
@@ -485,6 +442,6 @@ _decorate([property({
|
|
|
485
442
|
toAttribute: (value) => value ? "true" : null
|
|
486
443
|
}
|
|
487
444
|
})], EFThumbnailStrip.prototype, "useIntrinsicDuration", void 0);
|
|
488
|
-
|
|
489
|
-
EFThumbnailStrip =
|
|
445
|
+
__decorate([state()], EFThumbnailStrip.prototype, "stripWidth", null);
|
|
446
|
+
EFThumbnailStrip = __decorate([customElement("ef-thumbnail-strip")], EFThumbnailStrip);
|
|
490
447
|
export { EFThumbnailStrip };
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { EF_INTERACTIVE } from "../EF_INTERACTIVE.js";
|
|
2
|
+
import { __decorate } from "../_virtual/_@oxc-project_runtime@0.93.0/helpers/decorate.js";
|
|
2
3
|
import { isContextMixin } from "../gui/ContextMixin.js";
|
|
4
|
+
import { isTracingEnabled, withSpan } from "../otel/tracingHelpers.js";
|
|
3
5
|
import { durationConverter } from "./durationConverter.js";
|
|
4
6
|
import { EFTemporal, deepGetElementsWithFrameTasks, flushStartTimeMsCache, resetTemporalCache, shallowGetTemporalElements, timegroupContext } from "./EFTemporal.js";
|
|
5
7
|
import { deepGetMediaElements } from "./EFMedia.js";
|
|
@@ -10,10 +12,9 @@ import { Task, TaskStatus } from "@lit/task";
|
|
|
10
12
|
import debug from "debug";
|
|
11
13
|
import { LitElement, css, html } from "lit";
|
|
12
14
|
import { customElement, property } from "lit/decorators.js";
|
|
13
|
-
import _decorate from "@oxc-project/runtime/helpers/decorate";
|
|
14
15
|
var _EFTimegroup;
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
var log = debug("ef:elements:EFTimegroup");
|
|
17
|
+
var sequenceDurationCache = /* @__PURE__ */ new WeakMap();
|
|
17
18
|
const flushSequenceDurationCache = () => {
|
|
18
19
|
sequenceDurationCache = /* @__PURE__ */ new WeakMap();
|
|
19
20
|
};
|
|
@@ -22,7 +23,7 @@ const shallowGetTimegroups = (element, groups = []) => {
|
|
|
22
23
|
else shallowGetTimegroups(child, groups);
|
|
23
24
|
return groups;
|
|
24
25
|
};
|
|
25
|
-
|
|
26
|
+
var EFTimegroup = class EFTimegroup$1 extends EFTemporal(LitElement) {
|
|
26
27
|
static {
|
|
27
28
|
_EFTimegroup = this;
|
|
28
29
|
}
|
|
@@ -36,11 +37,15 @@ let EFTimegroup = class EFTimegroup$1 extends EFTemporal(LitElement) {
|
|
|
36
37
|
this.frameTask = new Task(this, {
|
|
37
38
|
autoRun: false,
|
|
38
39
|
args: () => [this.ownCurrentTimeMs, this.currentTimeMs],
|
|
39
|
-
task: async ([]) => {
|
|
40
|
-
if (this.isRootTimegroup) {
|
|
40
|
+
task: async ([ownCurrentTimeMs, currentTimeMs]) => {
|
|
41
|
+
if (this.isRootTimegroup) await withSpan("timegroup.frameTask", {
|
|
42
|
+
timegroupId: this.id || "unknown",
|
|
43
|
+
ownCurrentTimeMs,
|
|
44
|
+
currentTimeMs
|
|
45
|
+
}, void 0, async () => {
|
|
41
46
|
await this.waitForFrameTasks();
|
|
42
47
|
updateAnimations(this);
|
|
43
|
-
}
|
|
48
|
+
});
|
|
44
49
|
}
|
|
45
50
|
});
|
|
46
51
|
this.seekTask = new Task(this, {
|
|
@@ -49,14 +54,21 @@ let EFTimegroup = class EFTimegroup$1 extends EFTemporal(LitElement) {
|
|
|
49
54
|
onComplete: () => {},
|
|
50
55
|
task: async ([targetTime]) => {
|
|
51
56
|
if (!this.isRootTimegroup) return;
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
return withSpan("timegroup.seekTask", {
|
|
58
|
+
timegroupId: this.id || "unknown",
|
|
59
|
+
targetTime: targetTime ?? 0,
|
|
60
|
+
durationMs: this.durationMs
|
|
61
|
+
}, void 0, async (span) => {
|
|
62
|
+
await this.waitForMediaDurations();
|
|
63
|
+
const newTime = Math.max(0, Math.min(targetTime ?? 0, this.durationMs / 1e3));
|
|
64
|
+
if (isTracingEnabled()) span.setAttribute("newTime", newTime);
|
|
65
|
+
this.#currentTime = newTime;
|
|
66
|
+
this.requestUpdate("currentTime");
|
|
67
|
+
await this.runThrottledFrameTask();
|
|
68
|
+
this.#saveTimeToLocalStorage(this.#currentTime);
|
|
69
|
+
this.#seekInProgress = false;
|
|
70
|
+
return newTime;
|
|
71
|
+
});
|
|
60
72
|
}
|
|
61
73
|
});
|
|
62
74
|
}
|
|
@@ -94,9 +106,6 @@ let EFTimegroup = class EFTimegroup$1 extends EFTemporal(LitElement) {
|
|
|
94
106
|
#frameTaskInProgress = false;
|
|
95
107
|
#pendingFrameTaskRun = false;
|
|
96
108
|
#processingPendingFrameTask = false;
|
|
97
|
-
/**
|
|
98
|
-
* Throttles frameTask execution to ensure only one runs at a time while preserving the last request
|
|
99
|
-
*/
|
|
100
109
|
async runThrottledFrameTask() {
|
|
101
110
|
if (this.#frameTaskInProgress) {
|
|
102
111
|
this.#pendingFrameTaskRun = true;
|
|
@@ -154,15 +163,9 @@ let EFTimegroup = class EFTimegroup$1 extends EFTemporal(LitElement) {
|
|
|
154
163
|
get currentTimeMs() {
|
|
155
164
|
return this.currentTime * 1e3;
|
|
156
165
|
}
|
|
157
|
-
/**
|
|
158
|
-
* Determines if this is a root timegroup (no parent timegroups)
|
|
159
|
-
*/
|
|
160
166
|
get isRootTimegroup() {
|
|
161
167
|
return !this.parentTimegroup;
|
|
162
168
|
}
|
|
163
|
-
/**
|
|
164
|
-
* Saves time to localStorage (extracted for reuse)
|
|
165
|
-
*/
|
|
166
169
|
#saveTimeToLocalStorage(time) {
|
|
167
170
|
try {
|
|
168
171
|
if (this.id && this.isConnected && !Number.isNaN(time)) localStorage.setItem(this.storageKey, time.toString());
|
|
@@ -182,7 +185,7 @@ let EFTimegroup = class EFTimegroup$1 extends EFTemporal(LitElement) {
|
|
|
182
185
|
maybeLoadTimeFromLocalStorage() {
|
|
183
186
|
if (this.id) try {
|
|
184
187
|
const storedValue = localStorage.getItem(this.storageKey);
|
|
185
|
-
if (storedValue === null) return
|
|
188
|
+
if (storedValue === null) return;
|
|
186
189
|
return Number.parseFloat(storedValue);
|
|
187
190
|
} catch (error) {
|
|
188
191
|
log("Failed to load time from localStorage", error);
|
|
@@ -217,17 +220,15 @@ let EFTimegroup = class EFTimegroup$1 extends EFTemporal(LitElement) {
|
|
|
217
220
|
}
|
|
218
221
|
get intrinsicDurationMs() {
|
|
219
222
|
if (this.hasExplicitDuration) return this.explicitDurationMs;
|
|
220
|
-
return void 0;
|
|
221
223
|
}
|
|
222
224
|
get hasOwnDuration() {
|
|
223
225
|
return this.mode === "contain" || this.mode === "sequence" || this.mode === "fixed" && this.hasExplicitDuration;
|
|
224
226
|
}
|
|
225
227
|
get durationMs() {
|
|
226
228
|
switch (this.mode) {
|
|
227
|
-
case "fit":
|
|
229
|
+
case "fit":
|
|
228
230
|
if (!this.parentTimegroup) return 0;
|
|
229
231
|
return this.parentTimegroup.durationMs;
|
|
230
|
-
}
|
|
231
232
|
case "fixed": return super.durationMs;
|
|
232
233
|
case "sequence": {
|
|
233
234
|
const cachedDuration = sequenceDurationCache.get(this);
|
|
@@ -258,18 +259,15 @@ let EFTimegroup = class EFTimegroup$1 extends EFTemporal(LitElement) {
|
|
|
258
259
|
signal?.throwIfAborted();
|
|
259
260
|
const temporals = deepGetElementsWithFrameTasks(this);
|
|
260
261
|
const timelineTimeMs = (this.#pendingSeekTime ?? this.#currentTime ?? 0) * 1e3;
|
|
261
|
-
const
|
|
262
|
+
const frameTasks = temporals.filter((temporal) => {
|
|
262
263
|
if (!("startTimeMs" in temporal) || !("endTimeMs" in temporal)) return true;
|
|
263
264
|
const epsilon = .001;
|
|
264
265
|
const startTimeMs = temporal.startTimeMs;
|
|
265
266
|
const endTimeMs = temporal.endTimeMs;
|
|
266
267
|
const elementStartsBeforeEnd = startTimeMs <= timelineTimeMs + epsilon;
|
|
267
|
-
const
|
|
268
|
-
const useInclusiveEnd = isRootTimegroup;
|
|
269
|
-
const elementEndsAfterStart = useInclusiveEnd ? endTimeMs >= timelineTimeMs : endTimeMs > timelineTimeMs;
|
|
268
|
+
const elementEndsAfterStart = temporal.tagName.toLowerCase() === "ef-timegroup" && !temporal.parentTimegroup ? endTimeMs >= timelineTimeMs : endTimeMs > timelineTimeMs;
|
|
270
269
|
return elementStartsBeforeEnd && elementEndsAfterStart;
|
|
271
|
-
});
|
|
272
|
-
const frameTasks = activeTemporals.map((temporal) => temporal.frameTask);
|
|
270
|
+
}).map((temporal) => temporal.frameTask);
|
|
273
271
|
frameTasks.forEach((task) => {
|
|
274
272
|
task.run();
|
|
275
273
|
});
|
|
@@ -288,31 +286,45 @@ let EFTimegroup = class EFTimegroup$1 extends EFTemporal(LitElement) {
|
|
|
288
286
|
}
|
|
289
287
|
}
|
|
290
288
|
async waitForFrameTasks() {
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
289
|
+
return await withSpan("timegroup.waitForFrameTasks", {
|
|
290
|
+
timegroupId: this.id || "unknown",
|
|
291
|
+
mode: this.mode
|
|
292
|
+
}, void 0, async (span) => {
|
|
293
|
+
const innerStart = performance.now();
|
|
294
|
+
const temporalElements = deepGetElementsWithFrameTasks(this);
|
|
295
|
+
if (isTracingEnabled()) span.setAttribute("temporalElementsCount", temporalElements.length);
|
|
296
|
+
const visibleElements = temporalElements.filter((element) => {
|
|
297
|
+
return evaluateTemporalStateForAnimation(element).isVisible;
|
|
298
|
+
});
|
|
299
|
+
if (isTracingEnabled()) span.setAttribute("visibleElementsCount", visibleElements.length);
|
|
300
|
+
const promiseStart = performance.now();
|
|
301
|
+
await Promise.all(visibleElements.map((element) => element.frameTask.run()));
|
|
302
|
+
const promiseEnd = performance.now();
|
|
303
|
+
const innerEnd = performance.now();
|
|
304
|
+
if (isTracingEnabled()) {
|
|
305
|
+
span.setAttribute("actualInnerMs", innerEnd - innerStart);
|
|
306
|
+
span.setAttribute("promiseAwaitMs", promiseEnd - promiseStart);
|
|
307
|
+
}
|
|
295
308
|
});
|
|
296
|
-
await Promise.all(visibleElements.map((element) => element.frameTask.run()));
|
|
297
309
|
}
|
|
298
310
|
async waitForMediaDurations() {
|
|
299
311
|
if (!this.mediaDurationsPromise) this.mediaDurationsPromise = this.#waitForMediaDurations();
|
|
300
312
|
return this.mediaDurationsPromise;
|
|
301
313
|
}
|
|
302
|
-
/**
|
|
303
|
-
* Wait for all media elements to load their initial segments.
|
|
304
|
-
* Ideally we would only need the extracted index json data, but
|
|
305
|
-
* that caused issues with constructing audio data. We had negative durations
|
|
306
|
-
* in calculations and it was not clear why.
|
|
307
|
-
*/
|
|
308
314
|
async #waitForMediaDurations() {
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
315
|
+
return withSpan("timegroup.waitForMediaDurations", {
|
|
316
|
+
timegroupId: this.id || "unknown",
|
|
317
|
+
mode: this.mode
|
|
318
|
+
}, void 0, async (span) => {
|
|
319
|
+
await this.updateComplete;
|
|
320
|
+
const mediaElements = deepGetMediaElements(this);
|
|
321
|
+
if (isTracingEnabled()) span.setAttribute("mediaElementsCount", mediaElements.length);
|
|
322
|
+
await Promise.all(mediaElements.map((m) => m.mediaEngineTask.value ? Promise.resolve() : m.mediaEngineTask.run()));
|
|
323
|
+
flushStartTimeMsCache();
|
|
324
|
+
flushSequenceDurationCache();
|
|
325
|
+
this.requestUpdate("currentTime");
|
|
326
|
+
await this.updateComplete;
|
|
327
|
+
});
|
|
316
328
|
}
|
|
317
329
|
get childTemporals() {
|
|
318
330
|
return shallowGetTemporalElements(this);
|
|
@@ -325,15 +337,6 @@ let EFTimegroup = class EFTimegroup$1 extends EFTemporal(LitElement) {
|
|
|
325
337
|
}
|
|
326
338
|
return null;
|
|
327
339
|
}
|
|
328
|
-
/**
|
|
329
|
-
* Returns true if the timegroup should be wrapped with a workbench.
|
|
330
|
-
*
|
|
331
|
-
* A timegroup should be wrapped with a workbench if it is the root-most timegroup
|
|
332
|
-
* and EF_INTERACTIVE is true.
|
|
333
|
-
*
|
|
334
|
-
* If the timegroup is already wrappedin a context provider like ef-preview,
|
|
335
|
-
* it should NOT be wrapped in a workbench.
|
|
336
|
-
*/
|
|
337
340
|
shouldWrapWithWorkbench() {
|
|
338
341
|
return EF_INTERACTIVE && this.closest("ef-timegroup") === this && this.closest("ef-preview") === null && this.closest("ef-workbench") === null && this.closest("test-context") === null;
|
|
339
342
|
}
|
|
@@ -358,8 +361,7 @@ let EFTimegroup = class EFTimegroup$1 extends EFTemporal(LitElement) {
|
|
|
358
361
|
if (mediaElement.mute) return;
|
|
359
362
|
const mediaStartsBeforeEnd = mediaElement.startTimeMs <= toMs;
|
|
360
363
|
const mediaEndsAfterStart = mediaElement.endTimeMs >= fromMs;
|
|
361
|
-
|
|
362
|
-
if (!mediaOverlaps) return;
|
|
364
|
+
if (!(mediaStartsBeforeEnd && mediaEndsAfterStart)) return;
|
|
363
365
|
const mediaLocalFromMs = Math.max(0, fromMs - mediaElement.startTimeMs);
|
|
364
366
|
const mediaLocalToMs = Math.min(mediaElement.endTimeMs - mediaElement.startTimeMs, toMs - mediaElement.startTimeMs);
|
|
365
367
|
if (mediaLocalFromMs >= mediaLocalToMs) return;
|
|
@@ -390,27 +392,30 @@ let EFTimegroup = class EFTimegroup$1 extends EFTemporal(LitElement) {
|
|
|
390
392
|
}));
|
|
391
393
|
}
|
|
392
394
|
async renderAudio(fromMs, toMs) {
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
395
|
+
return withSpan("timegroup.renderAudio", {
|
|
396
|
+
timegroupId: this.id || "unknown",
|
|
397
|
+
fromMs,
|
|
398
|
+
toMs,
|
|
399
|
+
durationMs: toMs - fromMs
|
|
400
|
+
}, void 0, async (span) => {
|
|
401
|
+
const aacFrames = 48e3 * ((toMs - fromMs) / 1e3) / 1024;
|
|
402
|
+
const alignedFrames = Math.round(aacFrames);
|
|
403
|
+
const contextSize = alignedFrames * 1024;
|
|
404
|
+
if (isTracingEnabled()) {
|
|
405
|
+
span.setAttribute("contextSize", contextSize);
|
|
406
|
+
span.setAttribute("alignedFrames", alignedFrames);
|
|
407
|
+
}
|
|
408
|
+
if (contextSize <= 0) throw new Error(`Duration must be greater than 0 when rendering audio. ${contextSize}ms`);
|
|
409
|
+
let audioContext;
|
|
410
|
+
try {
|
|
411
|
+
audioContext = new OfflineAudioContext(2, contextSize, 48e3);
|
|
412
|
+
} catch (error) {
|
|
413
|
+
throw new Error(`[EFTimegroup.renderAudio] Failed to create OfflineAudioContext(2, ${contextSize}, 48000) for renderAudio(${fromMs}, ${toMs}) with contextSize=${contextSize}: ${error instanceof Error ? error.message : String(error)}. This typically happens when audio parameters are invalid (e.g., contextSize <= 0).`);
|
|
414
|
+
}
|
|
415
|
+
await this.#addAudioToContext(audioContext, fromMs, toMs);
|
|
416
|
+
return await audioContext.startRendering();
|
|
417
|
+
});
|
|
418
|
+
}
|
|
414
419
|
async testPlayAudio(fromMs, toMs) {
|
|
415
420
|
const renderedBuffer = await this.renderAudio(fromMs, toMs);
|
|
416
421
|
const playbackContext = new AudioContext();
|
|
@@ -441,20 +446,20 @@ let EFTimegroup = class EFTimegroup$1 extends EFTemporal(LitElement) {
|
|
|
441
446
|
});
|
|
442
447
|
}
|
|
443
448
|
};
|
|
444
|
-
|
|
445
|
-
|
|
449
|
+
__decorate([provide({ context: timegroupContext })], EFTimegroup.prototype, "_timeGroupContext", void 0);
|
|
450
|
+
__decorate([property({
|
|
446
451
|
type: String,
|
|
447
452
|
attribute: "mode"
|
|
448
453
|
})], EFTimegroup.prototype, "mode", null);
|
|
449
|
-
|
|
454
|
+
__decorate([property({
|
|
450
455
|
type: Number,
|
|
451
456
|
converter: durationConverter,
|
|
452
457
|
attribute: "overlap"
|
|
453
458
|
})], EFTimegroup.prototype, "overlapMs", null);
|
|
454
|
-
|
|
455
|
-
|
|
459
|
+
__decorate([property({ type: String })], EFTimegroup.prototype, "fit", void 0);
|
|
460
|
+
__decorate([property({
|
|
456
461
|
type: Number,
|
|
457
462
|
attribute: "currenttime"
|
|
458
463
|
})], EFTimegroup.prototype, "currentTime", null);
|
|
459
|
-
EFTimegroup = _EFTimegroup =
|
|
464
|
+
EFTimegroup = _EFTimegroup = __decorate([customElement("ef-timegroup")], EFTimegroup);
|
|
460
465
|
export { EFTimegroup, flushSequenceDurationCache, shallowGetTimegroups };
|
|
@@ -67,7 +67,11 @@ export declare class EFVideo extends EFVideo_base {
|
|
|
67
67
|
* Set loading state for user feedback
|
|
68
68
|
*/
|
|
69
69
|
private setLoadingState;
|
|
70
|
-
|
|
70
|
+
/**
|
|
71
|
+
* Paint the current video frame to canvas
|
|
72
|
+
* Called by frameTask after seek is complete
|
|
73
|
+
*/
|
|
74
|
+
paint(seekToMs: number, parentSpan?: any): void;
|
|
71
75
|
/**
|
|
72
76
|
* Clear the canvas when element becomes inactive
|
|
73
77
|
*/
|
|
@@ -75,7 +79,7 @@ export declare class EFVideo extends EFVideo_base {
|
|
|
75
79
|
/**
|
|
76
80
|
* Display a video frame on the canvas
|
|
77
81
|
*/
|
|
78
|
-
displayFrame(frame: VideoFrame, seekToMs: number):
|
|
82
|
+
displayFrame(frame: VideoFrame, seekToMs: number, parentSpan?: any): void;
|
|
79
83
|
/**
|
|
80
84
|
* Check if we're in production rendering mode (EF_FRAMEGEN active) vs preview mode
|
|
81
85
|
*/
|