@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
package/dist/gui/EFWorkbench.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import { __decorate } from "../_virtual/_@oxc-project_runtime@0.93.0/helpers/decorate.js";
|
|
1
2
|
import { ContextMixin } from "./ContextMixin.js";
|
|
2
3
|
import { TWMixin } from "./TWMixin2.js";
|
|
3
4
|
import { LitElement, css, html } from "lit";
|
|
4
5
|
import { customElement, eventOptions, property } from "lit/decorators.js";
|
|
5
|
-
import _decorate from "@oxc-project/runtime/helpers/decorate";
|
|
6
6
|
import { createRef, ref } from "lit/directives/ref.js";
|
|
7
|
-
|
|
7
|
+
var EFWorkbench = class EFWorkbench$1 extends ContextMixin(TWMixin(LitElement)) {
|
|
8
8
|
constructor(..._args) {
|
|
9
9
|
super(..._args);
|
|
10
10
|
this.rendering = false;
|
|
@@ -82,10 +82,10 @@ let EFWorkbench = class EFWorkbench$1 extends ContextMixin(TWMixin(LitElement))
|
|
|
82
82
|
`;
|
|
83
83
|
}
|
|
84
84
|
};
|
|
85
|
-
|
|
86
|
-
|
|
85
|
+
__decorate([property({ type: Boolean })], EFWorkbench.prototype, "rendering", void 0);
|
|
86
|
+
__decorate([eventOptions({
|
|
87
87
|
passive: false,
|
|
88
88
|
capture: true
|
|
89
89
|
})], EFWorkbench.prototype, "handleStageWheel", null);
|
|
90
|
-
EFWorkbench =
|
|
90
|
+
EFWorkbench = __decorate([customElement("ef-workbench")], EFWorkbench);
|
|
91
91
|
export { EFWorkbench };
|
package/dist/gui/TWMixin2.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import TWMixin_default from "./TWMixin.js";
|
|
2
|
-
|
|
2
|
+
var twSheet = null;
|
|
3
3
|
if (typeof window !== "undefined" && typeof CSSStyleSheet !== "undefined") try {
|
|
4
4
|
twSheet = new CSSStyleSheet();
|
|
5
5
|
if (typeof twSheet.replaceSync === "function") twSheet.replaceSync(TWMixin_default);
|
package/dist/index.d.ts
CHANGED
|
@@ -19,3 +19,4 @@ export { EFFitScale } from './gui/EFFitScale.js';
|
|
|
19
19
|
export { EFSurface } from './elements/EFSurface.ts';
|
|
20
20
|
export { EFThumbnailStrip } from './elements/EFThumbnailStrip.ts';
|
|
21
21
|
export { getRenderInfo, RenderInfo } from './getRenderInfo.js';
|
|
22
|
+
export type { TraceContext } from './otel/tracingHelpers.js';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ExportResult } from '@opentelemetry/core';
|
|
2
|
+
import { ReadableSpan, SpanExporter } from '@opentelemetry/sdk-trace-base';
|
|
3
|
+
interface BridgeWithSpanExport {
|
|
4
|
+
exportSpans?: (endpoint: string, payload: string) => void;
|
|
5
|
+
}
|
|
6
|
+
export declare class BridgeSpanExporter implements SpanExporter {
|
|
7
|
+
private bridge;
|
|
8
|
+
private endpoint;
|
|
9
|
+
constructor(bridge: BridgeWithSpanExport, endpoint: string);
|
|
10
|
+
export(spans: ReadableSpan[], resultCallback: (result: ExportResult) => void): void;
|
|
11
|
+
shutdown(): Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { ExportResultCode } from "@opentelemetry/core";
|
|
2
|
+
function toHex(value) {
|
|
3
|
+
if (typeof value === "string") return value;
|
|
4
|
+
if (Array.isArray(value)) return value.map((b) => {
|
|
5
|
+
return (typeof b === "number" ? b : 0).toString(16).padStart(2, "0");
|
|
6
|
+
}).join("");
|
|
7
|
+
if (ArrayBuffer.isView(value)) return Array.from(value).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
8
|
+
return String(value);
|
|
9
|
+
}
|
|
10
|
+
function convertAttribute(value) {
|
|
11
|
+
if (typeof value === "string") return { stringValue: value };
|
|
12
|
+
if (typeof value === "number") return Number.isInteger(value) ? { intValue: value } : { doubleValue: value };
|
|
13
|
+
if (typeof value === "boolean") return { boolValue: value };
|
|
14
|
+
if (Array.isArray(value)) return { arrayValue: { values: value.map(convertAttribute) } };
|
|
15
|
+
return { stringValue: String(value) };
|
|
16
|
+
}
|
|
17
|
+
var BridgeSpanExporter = class {
|
|
18
|
+
constructor(bridge, endpoint) {
|
|
19
|
+
this.bridge = bridge;
|
|
20
|
+
this.endpoint = endpoint;
|
|
21
|
+
}
|
|
22
|
+
export(spans, resultCallback) {
|
|
23
|
+
if (!this.bridge?.exportSpans) {
|
|
24
|
+
resultCallback({ code: ExportResultCode.FAILED });
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
const otlpPayload = { resourceSpans: [{
|
|
29
|
+
resource: { attributes: Object.entries(spans[0]?.resource?.attributes || {}).map(([key, value]) => ({
|
|
30
|
+
key,
|
|
31
|
+
value: convertAttribute(value)
|
|
32
|
+
})) },
|
|
33
|
+
scopeSpans: [{
|
|
34
|
+
scope: {
|
|
35
|
+
name: "telecine-browser",
|
|
36
|
+
version: "1.0.0"
|
|
37
|
+
},
|
|
38
|
+
spans: spans.map((span) => {
|
|
39
|
+
const ctx = span.spanContext();
|
|
40
|
+
return {
|
|
41
|
+
traceId: toHex(ctx.traceId),
|
|
42
|
+
spanId: toHex(ctx.spanId),
|
|
43
|
+
parentSpanId: span.parentSpanId ? toHex(span.parentSpanId) : void 0,
|
|
44
|
+
name: span.name,
|
|
45
|
+
kind: span.kind,
|
|
46
|
+
startTimeUnixNano: String(span.startTime[0] * 1e9 + span.startTime[1]),
|
|
47
|
+
endTimeUnixNano: String(span.endTime[0] * 1e9 + span.endTime[1]),
|
|
48
|
+
attributes: Object.entries(span.attributes).map(([key, value]) => ({
|
|
49
|
+
key,
|
|
50
|
+
value: convertAttribute(value)
|
|
51
|
+
})),
|
|
52
|
+
status: span.status,
|
|
53
|
+
events: span.events.map((event) => ({
|
|
54
|
+
timeUnixNano: String(event.time[0] * 1e9 + event.time[1]),
|
|
55
|
+
name: event.name,
|
|
56
|
+
attributes: Object.entries(event.attributes || {}).map(([key, value]) => ({
|
|
57
|
+
key,
|
|
58
|
+
value: convertAttribute(value)
|
|
59
|
+
}))
|
|
60
|
+
})),
|
|
61
|
+
links: span.links.map((link) => ({
|
|
62
|
+
traceId: toHex(link.context.traceId),
|
|
63
|
+
spanId: toHex(link.context.spanId),
|
|
64
|
+
attributes: Object.entries(link.attributes || {}).map(([key, value]) => ({
|
|
65
|
+
key,
|
|
66
|
+
value: convertAttribute(value)
|
|
67
|
+
}))
|
|
68
|
+
}))
|
|
69
|
+
};
|
|
70
|
+
})
|
|
71
|
+
}]
|
|
72
|
+
}] };
|
|
73
|
+
const serializedPayload = JSON.stringify(otlpPayload);
|
|
74
|
+
this.bridge.exportSpans(this.endpoint, serializedPayload);
|
|
75
|
+
resultCallback({ code: ExportResultCode.SUCCESS });
|
|
76
|
+
} catch (error) {
|
|
77
|
+
resultCallback({
|
|
78
|
+
code: ExportResultCode.FAILED,
|
|
79
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
shutdown() {
|
|
84
|
+
return Promise.resolve();
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
export { BridgeSpanExporter };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
interface BridgeWithSpanExport {
|
|
2
|
+
exportSpans?: (endpoint: string, payload: string) => void;
|
|
3
|
+
}
|
|
4
|
+
export interface BrowserTracingConfig {
|
|
5
|
+
otelEndpoint: string;
|
|
6
|
+
serviceName?: string;
|
|
7
|
+
bridge?: BridgeWithSpanExport;
|
|
8
|
+
useBatching?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export declare function setupBrowserTracing(config: BrowserTracingConfig): void;
|
|
11
|
+
export declare function isBrowserTracingInitialized(): boolean;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { BridgeSpanExporter } from "./BridgeSpanExporter.js";
|
|
2
|
+
import { ZoneContextManager } from "@opentelemetry/context-zone";
|
|
3
|
+
import { Resource } from "@opentelemetry/resources";
|
|
4
|
+
import { BatchSpanProcessor, SimpleSpanProcessor } from "@opentelemetry/sdk-trace-base";
|
|
5
|
+
import { WebTracerProvider } from "@opentelemetry/sdk-trace-web";
|
|
6
|
+
import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
|
|
7
|
+
var isInitialized = false;
|
|
8
|
+
var provider = null;
|
|
9
|
+
function setupBrowserTracing(config) {
|
|
10
|
+
if (isInitialized) return;
|
|
11
|
+
try {
|
|
12
|
+
if (!config.bridge) throw new Error("Bridge is required for browser tracing");
|
|
13
|
+
const exporter = new BridgeSpanExporter(config.bridge, config.otelEndpoint);
|
|
14
|
+
const spanProcessor = config.useBatching ? new BatchSpanProcessor(exporter, {
|
|
15
|
+
maxQueueSize: 100,
|
|
16
|
+
maxExportBatchSize: 10,
|
|
17
|
+
scheduledDelayMillis: 500
|
|
18
|
+
}) : new SimpleSpanProcessor(exporter);
|
|
19
|
+
provider = new WebTracerProvider({
|
|
20
|
+
resource: new Resource({ [ATTR_SERVICE_NAME]: config.serviceName || "telecine-browser" }),
|
|
21
|
+
spanProcessors: [spanProcessor]
|
|
22
|
+
});
|
|
23
|
+
provider.register({ contextManager: new ZoneContextManager() });
|
|
24
|
+
isInitialized = true;
|
|
25
|
+
} catch (error) {
|
|
26
|
+
console.error("Failed to initialize browser tracing:", error);
|
|
27
|
+
throw error;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export { setupBrowserTracing };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Context, Span } from '@opentelemetry/api';
|
|
2
|
+
export type TraceContext = Record<string, string>;
|
|
3
|
+
/**
|
|
4
|
+
* Enable tracing globally. Call this during initialization if tracing is requested.
|
|
5
|
+
*/
|
|
6
|
+
export declare function enableTracing(): void;
|
|
7
|
+
/**
|
|
8
|
+
* Check if tracing is currently enabled.
|
|
9
|
+
*/
|
|
10
|
+
export declare function isTracingEnabled(): boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Set the current frame's span. Call this when starting a frame render.
|
|
13
|
+
* All spans created during this frame will use this as parent if
|
|
14
|
+
* Zone.js doesn't provide one via context.active()
|
|
15
|
+
*/
|
|
16
|
+
export declare function setCurrentFrameSpan(span: Span): void;
|
|
17
|
+
/**
|
|
18
|
+
* Clear the current frame span. Call this when a frame completes.
|
|
19
|
+
*/
|
|
20
|
+
export declare function clearCurrentFrameSpan(): void;
|
|
21
|
+
export declare function extractParentContext(traceContext?: TraceContext): Context;
|
|
22
|
+
/**
|
|
23
|
+
* Get the active span's context to pass to child operations
|
|
24
|
+
* Use this when calling functions that create child spans
|
|
25
|
+
*/
|
|
26
|
+
export declare function getActiveContext(): Context;
|
|
27
|
+
/**
|
|
28
|
+
* Wrapper that passes span context explicitly to the function
|
|
29
|
+
* Use this for operations that need to store or propagate context across boundaries
|
|
30
|
+
*/
|
|
31
|
+
export declare function withSpanAndContext<T>(name: string, attributes: Record<string, string | number | boolean> | undefined, parentContext: Context | undefined, fn: (span: Span, activeContext: Context) => Promise<T>): Promise<T>;
|
|
32
|
+
export declare function createSpan(name: string, attributes?: Record<string, string | number | boolean>, parentContext?: Context): Span;
|
|
33
|
+
export declare function withSpan<T>(name: string, attributes: Record<string, string | number | boolean> | undefined, parentContext: Context | undefined, fn: (span: Span) => Promise<T>): Promise<T>;
|
|
34
|
+
export declare function withSpanSync<T>(name: string, attributes: Record<string, string | number | boolean> | undefined, parentContext: Context | undefined, fn: (span: Span) => T): T;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { context, propagation, trace } from "@opentelemetry/api";
|
|
2
|
+
var tracingEnabled = false;
|
|
3
|
+
function enableTracing() {
|
|
4
|
+
tracingEnabled = true;
|
|
5
|
+
}
|
|
6
|
+
function isTracingEnabled() {
|
|
7
|
+
return tracingEnabled;
|
|
8
|
+
}
|
|
9
|
+
var currentFrameSpan;
|
|
10
|
+
function setCurrentFrameSpan(span) {
|
|
11
|
+
currentFrameSpan = span;
|
|
12
|
+
}
|
|
13
|
+
function clearCurrentFrameSpan() {
|
|
14
|
+
currentFrameSpan = void 0;
|
|
15
|
+
}
|
|
16
|
+
function extractParentContext(traceContext) {
|
|
17
|
+
if (!traceContext) return context.active();
|
|
18
|
+
try {
|
|
19
|
+
return propagation.extract(context.active(), traceContext);
|
|
20
|
+
} catch (_error) {
|
|
21
|
+
return context.active();
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
async function withSpanAndContext(name, attributes, parentContext, fn) {
|
|
25
|
+
if (!tracingEnabled) {
|
|
26
|
+
const noopSpan = trace.getTracer("telecine-browser").startSpan(name);
|
|
27
|
+
const ctx$1 = parentContext || context.active();
|
|
28
|
+
const result = await fn(noopSpan, ctx$1);
|
|
29
|
+
noopSpan.end();
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
32
|
+
const tracer = trace.getTracer("telecine-browser");
|
|
33
|
+
let ctx;
|
|
34
|
+
if (parentContext) ctx = parentContext;
|
|
35
|
+
else {
|
|
36
|
+
const activeContext = context.active();
|
|
37
|
+
if (trace.getSpan(activeContext)?.isRecording?.()) ctx = activeContext;
|
|
38
|
+
else if (currentFrameSpan) ctx = trace.setSpan(context.active(), currentFrameSpan);
|
|
39
|
+
else ctx = activeContext;
|
|
40
|
+
}
|
|
41
|
+
const span = tracer.startSpan(name, { attributes }, ctx);
|
|
42
|
+
const spanContext = trace.setSpan(ctx, span);
|
|
43
|
+
try {
|
|
44
|
+
const result = await context.with(spanContext, async () => {
|
|
45
|
+
return fn(span, spanContext);
|
|
46
|
+
});
|
|
47
|
+
span.end();
|
|
48
|
+
return result;
|
|
49
|
+
} catch (error) {
|
|
50
|
+
span.recordException(error);
|
|
51
|
+
span.end();
|
|
52
|
+
throw error;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function createSpan(name, attributes, parentContext) {
|
|
56
|
+
const tracer = trace.getTracer("telecine-browser");
|
|
57
|
+
const ctx = parentContext || context.active();
|
|
58
|
+
return context.with(ctx, () => {
|
|
59
|
+
const span = tracer.startSpan(name);
|
|
60
|
+
if (attributes) span.setAttributes(attributes);
|
|
61
|
+
return span;
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
async function withSpan(name, attributes, parentContext, fn) {
|
|
65
|
+
if (!tracingEnabled) {
|
|
66
|
+
const noopSpan = trace.getTracer("telecine-browser").startSpan(name);
|
|
67
|
+
const result = await fn(noopSpan);
|
|
68
|
+
noopSpan.end();
|
|
69
|
+
return result;
|
|
70
|
+
}
|
|
71
|
+
const tracer = trace.getTracer("telecine-browser");
|
|
72
|
+
let ctx;
|
|
73
|
+
if (parentContext) ctx = parentContext;
|
|
74
|
+
else {
|
|
75
|
+
const activeContext = context.active();
|
|
76
|
+
if (trace.getSpan(activeContext)?.isRecording?.()) ctx = activeContext;
|
|
77
|
+
else if (currentFrameSpan) ctx = trace.setSpan(context.active(), currentFrameSpan);
|
|
78
|
+
else ctx = activeContext;
|
|
79
|
+
}
|
|
80
|
+
const span = tracer.startSpan(name, { attributes }, ctx);
|
|
81
|
+
const spanContext = trace.setSpan(ctx, span);
|
|
82
|
+
try {
|
|
83
|
+
const result = await context.with(spanContext, async () => {
|
|
84
|
+
return fn(span);
|
|
85
|
+
});
|
|
86
|
+
span.end();
|
|
87
|
+
return result;
|
|
88
|
+
} catch (error) {
|
|
89
|
+
span.recordException(error);
|
|
90
|
+
span.end();
|
|
91
|
+
throw error;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
function withSpanSync(name, attributes, parentContext, fn) {
|
|
95
|
+
if (!tracingEnabled) {
|
|
96
|
+
const noopSpan = trace.getTracer("telecine-browser").startSpan(name);
|
|
97
|
+
const result = fn(noopSpan);
|
|
98
|
+
noopSpan.end();
|
|
99
|
+
return result;
|
|
100
|
+
}
|
|
101
|
+
const span = createSpan(name, attributes, parentContext);
|
|
102
|
+
const ctx = parentContext || context.active();
|
|
103
|
+
try {
|
|
104
|
+
const result = context.with(trace.setSpan(ctx, span), () => fn(span));
|
|
105
|
+
span.end();
|
|
106
|
+
return result;
|
|
107
|
+
} catch (error) {
|
|
108
|
+
span.recordException(error);
|
|
109
|
+
span.end();
|
|
110
|
+
throw error;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
export { clearCurrentFrameSpan, enableTracing, extractParentContext, isTracingEnabled, setCurrentFrameSpan, withSpan, withSpanAndContext, withSpanSync };
|
|
@@ -1,16 +1,7 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Request deduplication utility
|
|
3
|
-
* Manages pending requests to prevent concurrent duplicate requests
|
|
4
|
-
*/
|
|
5
1
|
var RequestDeduplicator = class {
|
|
6
2
|
constructor() {
|
|
7
3
|
this.pendingRequests = /* @__PURE__ */ new Map();
|
|
8
4
|
}
|
|
9
|
-
/**
|
|
10
|
-
* Execute a request with deduplication
|
|
11
|
-
* If a request with the same key is already pending, return the existing promise
|
|
12
|
-
* Otherwise, execute the request factory and track the promise
|
|
13
|
-
*/
|
|
14
5
|
async executeRequest(key, requestFactory) {
|
|
15
6
|
const existingRequest = this.pendingRequests.get(key);
|
|
16
7
|
if (existingRequest) return existingRequest;
|
|
@@ -25,27 +16,15 @@ var RequestDeduplicator = class {
|
|
|
25
16
|
throw error;
|
|
26
17
|
}
|
|
27
18
|
}
|
|
28
|
-
/**
|
|
29
|
-
* Clear all pending requests (used in cache clearing)
|
|
30
|
-
*/
|
|
31
19
|
clear() {
|
|
32
20
|
this.pendingRequests.clear();
|
|
33
21
|
}
|
|
34
|
-
/**
|
|
35
|
-
* Get number of pending requests
|
|
36
|
-
*/
|
|
37
22
|
getPendingCount() {
|
|
38
23
|
return this.pendingRequests.size;
|
|
39
24
|
}
|
|
40
|
-
/**
|
|
41
|
-
* Check if a request is pending
|
|
42
|
-
*/
|
|
43
25
|
isPending(key) {
|
|
44
26
|
return this.pendingRequests.has(key);
|
|
45
27
|
}
|
|
46
|
-
/**
|
|
47
|
-
* Get all pending request keys
|
|
48
|
-
*/
|
|
49
28
|
getPendingKeys() {
|
|
50
29
|
return Array.from(this.pendingRequests.keys());
|
|
51
30
|
}
|
|
@@ -2,10 +2,6 @@ var URLTokenDeduplicator = class {
|
|
|
2
2
|
constructor() {
|
|
3
3
|
this.tokenCache = /* @__PURE__ */ new Map();
|
|
4
4
|
}
|
|
5
|
-
/**
|
|
6
|
-
* Get or create a URL token with global deduplication
|
|
7
|
-
* Multiple requests for the same cache key will share the same token promise
|
|
8
|
-
*/
|
|
9
5
|
async getToken(cacheKey, tokenFactory, parseExpiration) {
|
|
10
6
|
const now = Date.now();
|
|
11
7
|
const cached = this.tokenCache.get(cacheKey);
|
|
@@ -27,36 +23,20 @@ var URLTokenDeduplicator = class {
|
|
|
27
23
|
});
|
|
28
24
|
return tokenPromise;
|
|
29
25
|
}
|
|
30
|
-
/**
|
|
31
|
-
* Clear all cached tokens (used in testing)
|
|
32
|
-
*/
|
|
33
26
|
clear() {
|
|
34
27
|
this.tokenCache.clear();
|
|
35
28
|
}
|
|
36
|
-
/**
|
|
37
|
-
* Get number of cached tokens
|
|
38
|
-
*/
|
|
39
29
|
getCachedCount() {
|
|
40
30
|
return this.tokenCache.size;
|
|
41
31
|
}
|
|
42
|
-
/**
|
|
43
|
-
* Check if a token is cached and valid
|
|
44
|
-
*/
|
|
45
32
|
hasValidToken(cacheKey) {
|
|
46
33
|
const cached = this.tokenCache.get(cacheKey);
|
|
47
34
|
if (!cached) return false;
|
|
48
|
-
|
|
49
|
-
return now < cached.expiration;
|
|
35
|
+
return Date.now() < cached.expiration;
|
|
50
36
|
}
|
|
51
|
-
/**
|
|
52
|
-
* Get all cached token keys
|
|
53
|
-
*/
|
|
54
37
|
getCachedKeys() {
|
|
55
38
|
return Array.from(this.tokenCache.keys());
|
|
56
39
|
}
|
|
57
|
-
/**
|
|
58
|
-
* Remove expired tokens from cache
|
|
59
|
-
*/
|
|
60
40
|
cleanup() {
|
|
61
41
|
const now = Date.now();
|
|
62
42
|
for (const [key, entry] of this.tokenCache.entries()) if (now >= entry.expiration) this.tokenCache.delete(key);
|
|
@@ -2,9 +2,6 @@ var UrlGenerator = class {
|
|
|
2
2
|
constructor(baseUrl) {
|
|
3
3
|
this.baseUrl = baseUrl;
|
|
4
4
|
}
|
|
5
|
-
/**
|
|
6
|
-
* Generate video segment URL
|
|
7
|
-
*/
|
|
8
5
|
generateSegmentUrl(segmentId, renditionId, metadata) {
|
|
9
6
|
const audioRendition = metadata.audioRendition;
|
|
10
7
|
const videoRendition = metadata.videoRendition;
|
|
@@ -13,31 +10,17 @@ var UrlGenerator = class {
|
|
|
13
10
|
console.error("Rendition not found", metadata);
|
|
14
11
|
throw new Error(`Rendition ${renditionId} not found`);
|
|
15
12
|
}
|
|
16
|
-
|
|
17
|
-
return template.replace("{rendition}", renditionId).replace("{segmentId}", segmentId.toString()).replace("{src}", metadata.src).replace("{trackId}", rendition.trackId?.toString() ?? "");
|
|
13
|
+
return (segmentId === "init" ? metadata.templates.initSegment : metadata.templates.mediaSegment).replace("{rendition}", renditionId).replace("{segmentId}", segmentId.toString()).replace("{src}", metadata.src).replace("{trackId}", rendition.trackId?.toString() ?? "");
|
|
18
14
|
}
|
|
19
|
-
/**
|
|
20
|
-
* Generate init segment URL
|
|
21
|
-
*/
|
|
22
15
|
generateInitSegmentUrl(mediaUrl, rendition) {
|
|
23
16
|
return `${this.baseUrl()}/api/v1/transcode/${rendition}/init.m4s?url=${encodeURIComponent(mediaUrl)}`;
|
|
24
17
|
}
|
|
25
|
-
/**
|
|
26
|
-
* Generate manifest URL
|
|
27
|
-
*/
|
|
28
18
|
generateManifestUrl(mediaUrl) {
|
|
29
19
|
return `${this.baseUrl()}/api/v1/transcode/manifest.json?url=${encodeURIComponent(mediaUrl)}`;
|
|
30
20
|
}
|
|
31
|
-
/**
|
|
32
|
-
* Generate track fragment index URL
|
|
33
|
-
*/
|
|
34
21
|
generateTrackFragmentIndexUrl(mediaUrl) {
|
|
35
|
-
|
|
36
|
-
return `/@ef-track-fragment-index/${normalizedSrc}`;
|
|
22
|
+
return `/@ef-track-fragment-index/${mediaUrl.startsWith("/") ? mediaUrl.slice(1) : mediaUrl}`;
|
|
37
23
|
}
|
|
38
|
-
/**
|
|
39
|
-
* Generate quality presets URL
|
|
40
|
-
*/
|
|
41
24
|
generatePresetsUrl() {
|
|
42
25
|
return `${this.baseUrl()}/api/v1/transcode/presets`;
|
|
43
26
|
}
|
package/dist/utils/LRUCache.js
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A simple LRU (Least Recently Used) cache implementation
|
|
3
|
-
*/
|
|
4
1
|
var LRUCache = class {
|
|
5
2
|
constructor(maxSize) {
|
|
6
3
|
this.cache = /* @__PURE__ */ new Map();
|
|
@@ -35,10 +32,6 @@ var LRUCache = class {
|
|
|
35
32
|
return this.cache.size;
|
|
36
33
|
}
|
|
37
34
|
};
|
|
38
|
-
/**
|
|
39
|
-
* Size-aware LRU cache that tracks memory usage in bytes
|
|
40
|
-
* Evicts entries when total size exceeds the maximum
|
|
41
|
-
*/
|
|
42
35
|
var SizeAwareLRUCache = class {
|
|
43
36
|
constructor(maxSizeBytes) {
|
|
44
37
|
this.cache = /* @__PURE__ */ new Map();
|
|
@@ -112,17 +105,11 @@ var SizeAwareLRUCache = class {
|
|
|
112
105
|
return this.maxSizeBytes;
|
|
113
106
|
}
|
|
114
107
|
};
|
|
115
|
-
/**
|
|
116
|
-
* Red-Black Tree node colors
|
|
117
|
-
*/
|
|
118
108
|
var Color = /* @__PURE__ */ function(Color$1) {
|
|
119
109
|
Color$1["RED"] = "RED";
|
|
120
110
|
Color$1["BLACK"] = "BLACK";
|
|
121
111
|
return Color$1;
|
|
122
112
|
}(Color || {});
|
|
123
|
-
/**
|
|
124
|
-
* Red-Black Tree node for ordered key storage
|
|
125
|
-
*/
|
|
126
113
|
var RBTreeNode = class {
|
|
127
114
|
constructor(key, color = Color.RED, left = null, right = null, parent = null) {
|
|
128
115
|
this.key = key;
|
|
@@ -132,10 +119,6 @@ var RBTreeNode = class {
|
|
|
132
119
|
this.parent = parent;
|
|
133
120
|
}
|
|
134
121
|
};
|
|
135
|
-
/**
|
|
136
|
-
* Red-Black Tree implementation for O(log n) operations
|
|
137
|
-
* Supports insert, delete, search, range queries, and nearest neighbor
|
|
138
|
-
*/
|
|
139
122
|
var RedBlackTree = class {
|
|
140
123
|
constructor(compareFn) {
|
|
141
124
|
this.root = null;
|
|
@@ -198,16 +181,12 @@ var RedBlackTree = class {
|
|
|
198
181
|
let current = this.root;
|
|
199
182
|
while (current) {
|
|
200
183
|
parent = current;
|
|
201
|
-
|
|
202
|
-
current = cmp < 0 ? current.left : current.right;
|
|
184
|
+
current = this.compareFn(node.key, current.key) < 0 ? current.left : current.right;
|
|
203
185
|
}
|
|
204
186
|
node.parent = parent;
|
|
205
187
|
if (!parent) this.root = node;
|
|
206
|
-
else
|
|
207
|
-
|
|
208
|
-
if (cmp < 0) parent.left = node;
|
|
209
|
-
else parent.right = node;
|
|
210
|
-
}
|
|
188
|
+
else if (this.compareFn(node.key, parent.key) < 0) parent.left = node;
|
|
189
|
+
else parent.right = node;
|
|
211
190
|
}
|
|
212
191
|
fixInsert(node) {
|
|
213
192
|
while (node.parent && node.parent.color === Color.RED) if (node.parent === node.parent.parent?.left) {
|
|
@@ -392,10 +371,6 @@ var RedBlackTree = class {
|
|
|
392
371
|
if (endCmp < 0) this.inorderRange(node.right, start, end, result);
|
|
393
372
|
}
|
|
394
373
|
};
|
|
395
|
-
/**
|
|
396
|
-
* LRU cache with binary search capabilities using Red-Black tree
|
|
397
|
-
* All operations are O(log n) for ordered queries and O(1) for LRU operations
|
|
398
|
-
*/
|
|
399
374
|
var OrderedLRUCache = class {
|
|
400
375
|
constructor(maxSize, compareFn) {
|
|
401
376
|
this.cache = /* @__PURE__ */ new Map();
|
|
@@ -403,9 +378,6 @@ var OrderedLRUCache = class {
|
|
|
403
378
|
this.compareFn = compareFn || ((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
404
379
|
this.tree = new RedBlackTree(this.compareFn);
|
|
405
380
|
}
|
|
406
|
-
/**
|
|
407
|
-
* Get value by exact key (O(1))
|
|
408
|
-
*/
|
|
409
381
|
get(key) {
|
|
410
382
|
const value = this.cache.get(key);
|
|
411
383
|
if (value) {
|
|
@@ -414,12 +386,8 @@ var OrderedLRUCache = class {
|
|
|
414
386
|
}
|
|
415
387
|
return value;
|
|
416
388
|
}
|
|
417
|
-
/**
|
|
418
|
-
* Set key-value pair (O(log n) for tree operations, O(1) for cache)
|
|
419
|
-
*/
|
|
420
389
|
set(key, value) {
|
|
421
|
-
|
|
422
|
-
if (isUpdate) this.cache.delete(key);
|
|
390
|
+
if (this.cache.has(key)) this.cache.delete(key);
|
|
423
391
|
else {
|
|
424
392
|
if (this.cache.size >= this.maxSize) {
|
|
425
393
|
const firstKey = this.cache.keys().next().value;
|
|
@@ -432,18 +400,9 @@ var OrderedLRUCache = class {
|
|
|
432
400
|
}
|
|
433
401
|
this.cache.set(key, value);
|
|
434
402
|
}
|
|
435
|
-
/**
|
|
436
|
-
* Find exact key using tree search (O(log n))
|
|
437
|
-
*/
|
|
438
403
|
findExact(key) {
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
return void 0;
|
|
442
|
-
}
|
|
443
|
-
/**
|
|
444
|
-
* Find keys within distance of center point (O(log n + k) where k is result count)
|
|
445
|
-
* Returns empty array if no keys found in range
|
|
446
|
-
*/
|
|
404
|
+
if (this.tree.find(key) !== null) return this.get(key);
|
|
405
|
+
}
|
|
447
406
|
findNearestInRange(center, distance) {
|
|
448
407
|
const nearestKeys = this.tree.findNearestInRange(center, distance);
|
|
449
408
|
const result = [];
|
|
@@ -456,9 +415,6 @@ var OrderedLRUCache = class {
|
|
|
456
415
|
}
|
|
457
416
|
return result;
|
|
458
417
|
}
|
|
459
|
-
/**
|
|
460
|
-
* Find all key-value pairs in range [start, end] (O(log n + k) where k is result count)
|
|
461
|
-
*/
|
|
462
418
|
findRange(start, end) {
|
|
463
419
|
const keys = this.tree.findRange(start, end);
|
|
464
420
|
const result = [];
|
|
@@ -471,9 +427,6 @@ var OrderedLRUCache = class {
|
|
|
471
427
|
}
|
|
472
428
|
return result;
|
|
473
429
|
}
|
|
474
|
-
/**
|
|
475
|
-
* Get all keys in sorted order (O(n))
|
|
476
|
-
*/
|
|
477
430
|
getSortedKeys() {
|
|
478
431
|
return this.tree.getAllSorted();
|
|
479
432
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@editframe/elements",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0-beta.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -27,9 +27,17 @@
|
|
|
27
27
|
"license": "UNLICENSED",
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@bramus/style-observer": "^1.3.0",
|
|
30
|
-
"@editframe/assets": "0.
|
|
30
|
+
"@editframe/assets": "0.21.0-beta.0",
|
|
31
31
|
"@lit/context": "^1.1.2",
|
|
32
32
|
"@lit/task": "^1.0.1",
|
|
33
|
+
"@opentelemetry/api": "^1.9.0",
|
|
34
|
+
"@opentelemetry/context-zone": "^1.26.0",
|
|
35
|
+
"@opentelemetry/core": "^1.26.0",
|
|
36
|
+
"@opentelemetry/exporter-trace-otlp-http": "^0.53.0",
|
|
37
|
+
"@opentelemetry/resources": "^1.26.0",
|
|
38
|
+
"@opentelemetry/sdk-trace-base": "^1.26.0",
|
|
39
|
+
"@opentelemetry/sdk-trace-web": "^1.26.0",
|
|
40
|
+
"@opentelemetry/semantic-conventions": "^1.37.0",
|
|
33
41
|
"d3": "^7.9.0",
|
|
34
42
|
"debug": "^4.3.5",
|
|
35
43
|
"lit": "^3.1.4",
|
|
@@ -853,6 +853,7 @@ describe("EFCaptions", () => {
|
|
|
853
853
|
// Test at exact boundary 2.6s - should show " test" (the starting word)
|
|
854
854
|
timegroup.currentTimeMs = 2600;
|
|
855
855
|
await timegroup.seekTask.taskComplete;
|
|
856
|
+
await captions.updateComplete;
|
|
856
857
|
await wordContainer.updateComplete;
|
|
857
858
|
|
|
858
859
|
console.log(`At 2600ms: wordText="${wordContainer.wordText}"`);
|
|
@@ -861,6 +862,7 @@ describe("EFCaptions", () => {
|
|
|
861
862
|
// Test just before boundary 2.599s - should show " timing"
|
|
862
863
|
timegroup.currentTimeMs = 2599;
|
|
863
864
|
await timegroup.seekTask.taskComplete;
|
|
865
|
+
await captions.updateComplete;
|
|
864
866
|
await wordContainer.updateComplete;
|
|
865
867
|
|
|
866
868
|
console.log(`At 2599ms: wordText="${wordContainer.wordText}"`);
|