@editframe/elements 0.26.2-beta.0 → 0.26.4-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/elements/EFTimegroup.js +7 -2
- package/dist/elements/EFTimegroup.js.map +1 -1
- package/package.json +2 -2
- package/scripts/build-css.js +3 -3
- package/tsdown.config.ts +1 -1
- package/types.json +1 -1
- package/src/elements/ContextProxiesController.ts +0 -124
- package/src/elements/CrossUpdateController.ts +0 -22
- package/src/elements/EFAudio.browsertest.ts +0 -706
- package/src/elements/EFAudio.ts +0 -56
- package/src/elements/EFCaptions.browsertest.ts +0 -1960
- package/src/elements/EFCaptions.ts +0 -823
- package/src/elements/EFImage.browsertest.ts +0 -120
- package/src/elements/EFImage.ts +0 -113
- package/src/elements/EFMedia/AssetIdMediaEngine.test.ts +0 -224
- package/src/elements/EFMedia/AssetIdMediaEngine.ts +0 -110
- package/src/elements/EFMedia/AssetMediaEngine.browsertest.ts +0 -140
- package/src/elements/EFMedia/AssetMediaEngine.ts +0 -385
- package/src/elements/EFMedia/BaseMediaEngine.browsertest.ts +0 -400
- package/src/elements/EFMedia/BaseMediaEngine.ts +0 -505
- package/src/elements/EFMedia/BufferedSeekingInput.browsertest.ts +0 -386
- package/src/elements/EFMedia/BufferedSeekingInput.ts +0 -430
- package/src/elements/EFMedia/JitMediaEngine.browsertest.ts +0 -226
- package/src/elements/EFMedia/JitMediaEngine.ts +0 -256
- package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.browsertest.ts +0 -679
- package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.ts +0 -117
- package/src/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.ts +0 -246
- package/src/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.browsertest.ts +0 -59
- package/src/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.ts +0 -27
- package/src/elements/EFMedia/audioTasks/makeAudioInputTask.browsertest.ts +0 -55
- package/src/elements/EFMedia/audioTasks/makeAudioInputTask.ts +0 -53
- package/src/elements/EFMedia/audioTasks/makeAudioSeekTask.chunkboundary.regression.browsertest.ts +0 -207
- package/src/elements/EFMedia/audioTasks/makeAudioSeekTask.ts +0 -72
- package/src/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.ts +0 -32
- package/src/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.ts +0 -29
- package/src/elements/EFMedia/audioTasks/makeAudioTasksVideoOnly.browsertest.ts +0 -95
- package/src/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.ts +0 -184
- package/src/elements/EFMedia/shared/AudioSpanUtils.ts +0 -129
- package/src/elements/EFMedia/shared/BufferUtils.ts +0 -342
- package/src/elements/EFMedia/shared/GlobalInputCache.ts +0 -77
- package/src/elements/EFMedia/shared/MediaTaskUtils.ts +0 -44
- package/src/elements/EFMedia/shared/PrecisionUtils.ts +0 -46
- package/src/elements/EFMedia/shared/RenditionHelpers.browsertest.ts +0 -246
- package/src/elements/EFMedia/shared/RenditionHelpers.ts +0 -56
- package/src/elements/EFMedia/shared/ThumbnailExtractor.ts +0 -227
- package/src/elements/EFMedia/tasks/makeMediaEngineTask.browsertest.ts +0 -167
- package/src/elements/EFMedia/tasks/makeMediaEngineTask.ts +0 -88
- package/src/elements/EFMedia/videoTasks/MainVideoInputCache.ts +0 -76
- package/src/elements/EFMedia/videoTasks/ScrubInputCache.ts +0 -61
- package/src/elements/EFMedia/videoTasks/makeScrubVideoBufferTask.ts +0 -114
- package/src/elements/EFMedia/videoTasks/makeScrubVideoInitSegmentFetchTask.ts +0 -35
- package/src/elements/EFMedia/videoTasks/makeScrubVideoInputTask.ts +0 -52
- package/src/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.ts +0 -124
- package/src/elements/EFMedia/videoTasks/makeScrubVideoSegmentFetchTask.ts +0 -44
- package/src/elements/EFMedia/videoTasks/makeScrubVideoSegmentIdTask.ts +0 -32
- package/src/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.ts +0 -370
- package/src/elements/EFMedia/videoTasks/makeVideoBufferTask.ts +0 -109
- package/src/elements/EFMedia.browsertest.ts +0 -872
- package/src/elements/EFMedia.ts +0 -341
- package/src/elements/EFSourceMixin.ts +0 -60
- package/src/elements/EFSurface.browsertest.ts +0 -151
- package/src/elements/EFSurface.ts +0 -142
- package/src/elements/EFTemporal.browsertest.ts +0 -215
- package/src/elements/EFTemporal.ts +0 -800
- package/src/elements/EFThumbnailStrip.browsertest.ts +0 -585
- package/src/elements/EFThumbnailStrip.media-engine.browsertest.ts +0 -714
- package/src/elements/EFThumbnailStrip.ts +0 -906
- package/src/elements/EFTimegroup.browsertest.ts +0 -870
- package/src/elements/EFTimegroup.ts +0 -878
- package/src/elements/EFVideo.browsertest.ts +0 -1482
- package/src/elements/EFVideo.ts +0 -564
- package/src/elements/EFWaveform.ts +0 -547
- package/src/elements/FetchContext.browsertest.ts +0 -401
- package/src/elements/FetchMixin.ts +0 -38
- package/src/elements/SampleBuffer.ts +0 -94
- package/src/elements/TargetController.browsertest.ts +0 -230
- package/src/elements/TargetController.ts +0 -224
- package/src/elements/TimegroupController.ts +0 -26
- package/src/elements/durationConverter.ts +0 -35
- package/src/elements/parseTimeToMs.ts +0 -9
- package/src/elements/printTaskStatus.ts +0 -16
- package/src/elements/renderTemporalAudio.ts +0 -108
- package/src/elements/updateAnimations.browsertest.ts +0 -1884
- package/src/elements/updateAnimations.ts +0 -217
- package/src/elements/util.ts +0 -24
- package/src/gui/ContextMixin.browsertest.ts +0 -860
- package/src/gui/ContextMixin.ts +0 -562
- package/src/gui/Controllable.browsertest.ts +0 -258
- package/src/gui/Controllable.ts +0 -41
- package/src/gui/EFConfiguration.ts +0 -40
- package/src/gui/EFControls.browsertest.ts +0 -389
- package/src/gui/EFControls.ts +0 -195
- package/src/gui/EFDial.browsertest.ts +0 -84
- package/src/gui/EFDial.ts +0 -172
- package/src/gui/EFFilmstrip.browsertest.ts +0 -712
- package/src/gui/EFFilmstrip.ts +0 -1349
- package/src/gui/EFFitScale.ts +0 -152
- package/src/gui/EFFocusOverlay.ts +0 -79
- package/src/gui/EFPause.browsertest.ts +0 -202
- package/src/gui/EFPause.ts +0 -73
- package/src/gui/EFPlay.browsertest.ts +0 -202
- package/src/gui/EFPlay.ts +0 -73
- package/src/gui/EFPreview.ts +0 -74
- package/src/gui/EFResizableBox.browsertest.ts +0 -79
- package/src/gui/EFResizableBox.ts +0 -898
- package/src/gui/EFScrubber.ts +0 -151
- package/src/gui/EFTimeDisplay.browsertest.ts +0 -237
- package/src/gui/EFTimeDisplay.ts +0 -55
- package/src/gui/EFToggleLoop.ts +0 -35
- package/src/gui/EFTogglePlay.ts +0 -70
- package/src/gui/EFWorkbench.ts +0 -115
- package/src/gui/PlaybackController.ts +0 -527
- package/src/gui/TWMixin.css +0 -6
- package/src/gui/TWMixin.ts +0 -61
- package/src/gui/TargetOrContextMixin.ts +0 -185
- package/src/gui/currentTimeContext.ts +0 -5
- package/src/gui/durationContext.ts +0 -3
- package/src/gui/efContext.ts +0 -6
- package/src/gui/fetchContext.ts +0 -5
- package/src/gui/focusContext.ts +0 -7
- package/src/gui/focusedElementContext.ts +0 -5
- package/src/gui/playingContext.ts +0 -5
- package/src/otel/BridgeSpanExporter.ts +0 -150
- package/src/otel/setupBrowserTracing.ts +0 -73
- package/src/otel/tracingHelpers.ts +0 -251
- package/src/transcoding/cache/RequestDeduplicator.test.ts +0 -170
- package/src/transcoding/cache/RequestDeduplicator.ts +0 -65
- package/src/transcoding/cache/URLTokenDeduplicator.test.ts +0 -182
- package/src/transcoding/cache/URLTokenDeduplicator.ts +0 -101
- package/src/transcoding/types/index.ts +0 -312
- package/src/transcoding/utils/MediaUtils.ts +0 -63
- package/src/transcoding/utils/UrlGenerator.ts +0 -68
- package/src/transcoding/utils/constants.ts +0 -36
- package/src/utils/LRUCache.test.ts +0 -274
- package/src/utils/LRUCache.ts +0 -696
package/src/gui/ContextMixin.ts
DELETED
|
@@ -1,562 +0,0 @@
|
|
|
1
|
-
import { ContextProvider, consume, createContext, provide } from "@lit/context";
|
|
2
|
-
import type { LitElement } from "lit";
|
|
3
|
-
import { property, state } from "lit/decorators.js";
|
|
4
|
-
import { EF_RENDERING } from "../EF_RENDERING.ts";
|
|
5
|
-
import {
|
|
6
|
-
isEFTemporal,
|
|
7
|
-
type TemporalMixinInterface,
|
|
8
|
-
} from "../elements/EFTemporal.js";
|
|
9
|
-
import { globalURLTokenDeduplicator } from "../transcoding/cache/URLTokenDeduplicator.js";
|
|
10
|
-
import { currentTimeContext } from "./currentTimeContext.js";
|
|
11
|
-
import { durationContext } from "./durationContext.js";
|
|
12
|
-
import {
|
|
13
|
-
type EFConfiguration,
|
|
14
|
-
efConfigurationContext,
|
|
15
|
-
} from "./EFConfiguration.ts";
|
|
16
|
-
import { efContext } from "./efContext.js";
|
|
17
|
-
import { fetchContext } from "./fetchContext.js";
|
|
18
|
-
import { type FocusContext, focusContext } from "./focusContext.js";
|
|
19
|
-
import { focusedElementContext } from "./focusedElementContext.js";
|
|
20
|
-
import { loopContext, playingContext } from "./playingContext.js";
|
|
21
|
-
|
|
22
|
-
export const targetTemporalContext =
|
|
23
|
-
createContext<TemporalMixinInterface | null>(Symbol("target-temporal"));
|
|
24
|
-
|
|
25
|
-
export declare class ContextMixinInterface extends LitElement {
|
|
26
|
-
signingURL?: string;
|
|
27
|
-
apiHost?: string;
|
|
28
|
-
rendering: boolean;
|
|
29
|
-
playing: boolean;
|
|
30
|
-
loop: boolean;
|
|
31
|
-
currentTimeMs: number;
|
|
32
|
-
focusedElement?: HTMLElement;
|
|
33
|
-
targetTemporal: TemporalMixinInterface | null;
|
|
34
|
-
play(): Promise<void>;
|
|
35
|
-
pause(): void;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const contextMixinSymbol = Symbol("contextMixin");
|
|
39
|
-
|
|
40
|
-
export function isContextMixin(value: any): value is ContextMixinInterface {
|
|
41
|
-
return (
|
|
42
|
-
typeof value === "object" &&
|
|
43
|
-
value !== null &&
|
|
44
|
-
contextMixinSymbol in value.constructor
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
type Constructor<T = {}> = new (...args: any[]) => T;
|
|
49
|
-
export function ContextMixin<T extends Constructor<LitElement>>(superClass: T) {
|
|
50
|
-
class ContextElement extends superClass {
|
|
51
|
-
static [contextMixinSymbol] = true;
|
|
52
|
-
|
|
53
|
-
@consume({ context: efConfigurationContext, subscribe: true })
|
|
54
|
-
efConfiguration: EFConfiguration | null = null;
|
|
55
|
-
|
|
56
|
-
@provide({ context: focusContext })
|
|
57
|
-
focusContext = this as FocusContext;
|
|
58
|
-
|
|
59
|
-
@provide({ context: focusedElementContext })
|
|
60
|
-
@state()
|
|
61
|
-
focusedElement?: HTMLElement;
|
|
62
|
-
|
|
63
|
-
#playingProvider!: ContextProvider<typeof playingContext>;
|
|
64
|
-
#loopProvider!: ContextProvider<typeof loopContext>;
|
|
65
|
-
#currentTimeMsProvider!: ContextProvider<typeof currentTimeContext>;
|
|
66
|
-
#targetTemporalProvider!: ContextProvider<typeof targetTemporalContext>;
|
|
67
|
-
|
|
68
|
-
#loop = false;
|
|
69
|
-
|
|
70
|
-
#apiHost?: string;
|
|
71
|
-
@property({ type: String, attribute: "api-host" })
|
|
72
|
-
get apiHost() {
|
|
73
|
-
return this.#apiHost ?? this.efConfiguration?.apiHost ?? "";
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
set apiHost(value: string) {
|
|
77
|
-
this.#apiHost = value;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
@provide({ context: efContext })
|
|
81
|
-
efContext = this;
|
|
82
|
-
|
|
83
|
-
#targetTemporal: TemporalMixinInterface | null = null;
|
|
84
|
-
|
|
85
|
-
@state()
|
|
86
|
-
get targetTemporal(): TemporalMixinInterface | null {
|
|
87
|
-
return this.#targetTemporal;
|
|
88
|
-
}
|
|
89
|
-
#controllerSubscribed = false;
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Find the first root temporal element (recursively searches through children)
|
|
93
|
-
* Supports ef-timegroup, ef-video, ef-audio, and any other temporal elements
|
|
94
|
-
* even when they're wrapped in non-temporal elements like divs
|
|
95
|
-
*/
|
|
96
|
-
private findRootTemporal(): TemporalMixinInterface | null {
|
|
97
|
-
const findRecursive = (
|
|
98
|
-
element: Element,
|
|
99
|
-
): TemporalMixinInterface | null => {
|
|
100
|
-
if (isEFTemporal(element)) {
|
|
101
|
-
return element as TemporalMixinInterface & HTMLElement;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
for (const child of element.children) {
|
|
105
|
-
const found = findRecursive(child);
|
|
106
|
-
if (found) return found;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
return null;
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
for (const child of this.children) {
|
|
113
|
-
const found = findRecursive(child);
|
|
114
|
-
if (found) return found;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
return null;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
set targetTemporal(value: TemporalMixinInterface | null) {
|
|
121
|
-
if (this.#targetTemporal === value) return;
|
|
122
|
-
|
|
123
|
-
// Unsubscribe from old controller updates
|
|
124
|
-
if (this.#targetTemporal?.playbackController) {
|
|
125
|
-
this.#targetTemporal.playbackController.removeListener(
|
|
126
|
-
this.#onControllerUpdate,
|
|
127
|
-
);
|
|
128
|
-
this.#controllerSubscribed = false;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
this.#targetTemporal = value;
|
|
132
|
-
this.#targetTemporalProvider?.setValue(value);
|
|
133
|
-
|
|
134
|
-
// Sync all provided contexts
|
|
135
|
-
this.requestUpdate("targetTemporal");
|
|
136
|
-
this.requestUpdate("playing");
|
|
137
|
-
this.requestUpdate("loop");
|
|
138
|
-
this.requestUpdate("currentTimeMs");
|
|
139
|
-
|
|
140
|
-
// If the new targetTemporal has a playbackController, apply stored loop value immediately
|
|
141
|
-
if (value?.playbackController && this.#loop) {
|
|
142
|
-
value.playbackController.setLoop(this.#loop);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// If the new targetTemporal doesn't have a playbackController yet,
|
|
146
|
-
// wait for it to complete its updates (it might be initializing)
|
|
147
|
-
if (value && !value.playbackController) {
|
|
148
|
-
// Wait for the temporal element to initialize
|
|
149
|
-
(value as any).updateComplete?.then(() => {
|
|
150
|
-
if (value === this.#targetTemporal && !this.#controllerSubscribed) {
|
|
151
|
-
this.requestUpdate();
|
|
152
|
-
}
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
#onControllerUpdate = (
|
|
158
|
-
event: import("./PlaybackController.js").PlaybackControllerUpdateEvent,
|
|
159
|
-
) => {
|
|
160
|
-
switch (event.property) {
|
|
161
|
-
case "playing":
|
|
162
|
-
this.#playingProvider.setValue(event.value as boolean);
|
|
163
|
-
break;
|
|
164
|
-
case "loop":
|
|
165
|
-
this.#loopProvider.setValue(event.value as boolean);
|
|
166
|
-
break;
|
|
167
|
-
case "currentTimeMs":
|
|
168
|
-
this.#currentTimeMsProvider.setValue(event.value as number);
|
|
169
|
-
break;
|
|
170
|
-
}
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
// Add reactive properties that depend on the targetTemporal
|
|
174
|
-
@provide({ context: durationContext })
|
|
175
|
-
@property({ type: Number })
|
|
176
|
-
durationMs = 0;
|
|
177
|
-
|
|
178
|
-
@property({ type: Number })
|
|
179
|
-
endTimeMs = 0;
|
|
180
|
-
|
|
181
|
-
@provide({ context: fetchContext })
|
|
182
|
-
fetch = async (url: string, init: RequestInit = {}) => {
|
|
183
|
-
init.headers ||= {};
|
|
184
|
-
Object.assign(init.headers, {
|
|
185
|
-
"Content-Type": "application/json",
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
if (!EF_RENDERING() && this.signingURL) {
|
|
189
|
-
const { cacheKey, signingPayload } = this.#getTokenCacheKey(url);
|
|
190
|
-
|
|
191
|
-
// Use global token deduplicator to share tokens across all context providers
|
|
192
|
-
const urlToken = await globalURLTokenDeduplicator.getToken(
|
|
193
|
-
cacheKey,
|
|
194
|
-
async () => {
|
|
195
|
-
try {
|
|
196
|
-
const response = await fetch(this.signingURL, {
|
|
197
|
-
method: "POST",
|
|
198
|
-
body: JSON.stringify(signingPayload),
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
if (response.ok) {
|
|
202
|
-
const tokenData = await response.json();
|
|
203
|
-
return tokenData.token;
|
|
204
|
-
}
|
|
205
|
-
throw new Error(
|
|
206
|
-
`Failed to sign URL: ${url}. SigningURL: ${this.signingURL} ${response.status} ${response.statusText}`,
|
|
207
|
-
);
|
|
208
|
-
} catch (error) {
|
|
209
|
-
console.error("ContextMixin urlToken fetch error", url, error);
|
|
210
|
-
throw error;
|
|
211
|
-
}
|
|
212
|
-
},
|
|
213
|
-
(token: string) => this.#parseTokenExpiration(token),
|
|
214
|
-
);
|
|
215
|
-
|
|
216
|
-
Object.assign(init.headers, {
|
|
217
|
-
authorization: `Bearer ${urlToken}`,
|
|
218
|
-
});
|
|
219
|
-
} else {
|
|
220
|
-
init.credentials = "include";
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
try {
|
|
224
|
-
return fetch(url, init);
|
|
225
|
-
} catch (error) {
|
|
226
|
-
console.error(
|
|
227
|
-
"ContextMixin fetch error",
|
|
228
|
-
url,
|
|
229
|
-
error,
|
|
230
|
-
window.location.href,
|
|
231
|
-
);
|
|
232
|
-
throw error;
|
|
233
|
-
}
|
|
234
|
-
};
|
|
235
|
-
|
|
236
|
-
// Note: URL token caching is now handled globally via URLTokenDeduplicator
|
|
237
|
-
// Keeping these for any potential backwards compatibility, but they're no longer used
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* Generate a cache key for URL token based on signing strategy
|
|
241
|
-
*
|
|
242
|
-
* Uses unified prefix + parameter matching approach:
|
|
243
|
-
* - For transcode URLs: signs base "/api/v1/transcode" + params like {url: "source.mp4"}
|
|
244
|
-
* - For regular URLs: signs full URL with empty params {}
|
|
245
|
-
* - All validation uses prefix matching + exhaustive parameter matching
|
|
246
|
-
* - Multiple transcode segments with same source share one token (reduces round-trips)
|
|
247
|
-
*/
|
|
248
|
-
#getTokenCacheKey(url: string): {
|
|
249
|
-
cacheKey: string;
|
|
250
|
-
signingPayload: { url: string; params?: Record<string, string> };
|
|
251
|
-
} {
|
|
252
|
-
try {
|
|
253
|
-
const urlObj = new URL(url);
|
|
254
|
-
|
|
255
|
-
// Check if this is a transcode URL pattern
|
|
256
|
-
if (urlObj.pathname.includes("/api/v1/transcode/")) {
|
|
257
|
-
const urlParam = urlObj.searchParams.get("url");
|
|
258
|
-
if (urlParam) {
|
|
259
|
-
// For transcode URLs, sign the base path + url parameter
|
|
260
|
-
const basePath = `${urlObj.origin}/api/v1/transcode`;
|
|
261
|
-
const cacheKey = `${basePath}?url=${urlParam}`;
|
|
262
|
-
return {
|
|
263
|
-
cacheKey,
|
|
264
|
-
signingPayload: { url: basePath, params: { url: urlParam } },
|
|
265
|
-
};
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
// For non-transcode URLs, use full URL (existing behavior)
|
|
270
|
-
return {
|
|
271
|
-
cacheKey: url,
|
|
272
|
-
signingPayload: { url },
|
|
273
|
-
};
|
|
274
|
-
} catch {
|
|
275
|
-
// If URL parsing fails, fall back to full URL
|
|
276
|
-
return {
|
|
277
|
-
cacheKey: url,
|
|
278
|
-
signingPayload: { url },
|
|
279
|
-
};
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
/**
|
|
284
|
-
* Parse JWT token to extract safe expiration time (with buffer)
|
|
285
|
-
* @param token JWT token string
|
|
286
|
-
* @returns Safe expiration timestamp in milliseconds (actual expiry minus buffer), or 0 if parsing fails
|
|
287
|
-
*/
|
|
288
|
-
#parseTokenExpiration(token: string): number {
|
|
289
|
-
try {
|
|
290
|
-
// JWT has 3 parts separated by dots: header.payload.signature
|
|
291
|
-
const parts = token.split(".");
|
|
292
|
-
if (parts.length !== 3) return 0;
|
|
293
|
-
|
|
294
|
-
// Decode the payload (second part)
|
|
295
|
-
const payload = parts[1];
|
|
296
|
-
if (!payload) return 0;
|
|
297
|
-
|
|
298
|
-
const decoded = atob(payload.replace(/-/g, "+").replace(/_/g, "/"));
|
|
299
|
-
const parsed = JSON.parse(decoded);
|
|
300
|
-
|
|
301
|
-
// Extract timestamps (in seconds)
|
|
302
|
-
const exp = parsed.exp;
|
|
303
|
-
const iat = parsed.iat;
|
|
304
|
-
if (!exp) return 0;
|
|
305
|
-
|
|
306
|
-
// Calculate token lifetime and buffer
|
|
307
|
-
const lifetimeSeconds = iat ? exp - iat : 3600; // Default to 1 hour if no iat
|
|
308
|
-
const tenPercentBufferMs = lifetimeSeconds * 0.1 * 1000; // 10% of lifetime in ms
|
|
309
|
-
const fiveMinutesMs = 5 * 60 * 1000; // 5 minutes in ms
|
|
310
|
-
|
|
311
|
-
// Use whichever buffer is smaller (more conservative)
|
|
312
|
-
const bufferMs = Math.min(fiveMinutesMs, tenPercentBufferMs);
|
|
313
|
-
|
|
314
|
-
// Return expiration time minus buffer
|
|
315
|
-
return exp * 1000 - bufferMs;
|
|
316
|
-
} catch {
|
|
317
|
-
return 0;
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
#signingURL?: string;
|
|
322
|
-
/**
|
|
323
|
-
* A URL that will be used to generated signed tokens for accessing media files from the
|
|
324
|
-
* editframe API. This is used to authenticate media requests per-user.
|
|
325
|
-
*/
|
|
326
|
-
@property({ type: String, attribute: "signing-url" })
|
|
327
|
-
get signingURL() {
|
|
328
|
-
return this.#signingURL ?? this.efConfiguration?.signingURL ?? "";
|
|
329
|
-
}
|
|
330
|
-
set signingURL(value: string) {
|
|
331
|
-
this.#signingURL = value;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
@property({ type: Boolean, reflect: true })
|
|
335
|
-
get playing(): boolean {
|
|
336
|
-
return this.targetTemporal?.playbackController?.playing ?? false;
|
|
337
|
-
}
|
|
338
|
-
set playing(value: boolean) {
|
|
339
|
-
if (this.targetTemporal?.playbackController) {
|
|
340
|
-
this.targetTemporal.playbackController.setPlaying(value);
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
@property({ type: Boolean, reflect: true, attribute: "loop" })
|
|
345
|
-
get loop(): boolean {
|
|
346
|
-
return this.targetTemporal?.playbackController?.loop ?? this.#loop;
|
|
347
|
-
}
|
|
348
|
-
set loop(value: boolean) {
|
|
349
|
-
const oldValue = this.#loop;
|
|
350
|
-
this.#loop = value;
|
|
351
|
-
if (this.targetTemporal?.playbackController) {
|
|
352
|
-
this.targetTemporal.playbackController.setLoop(value);
|
|
353
|
-
}
|
|
354
|
-
this.requestUpdate("loop", oldValue);
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
@property({ type: Boolean })
|
|
358
|
-
rendering = false;
|
|
359
|
-
|
|
360
|
-
@property({ type: Number })
|
|
361
|
-
get currentTimeMs(): number {
|
|
362
|
-
return (
|
|
363
|
-
this.targetTemporal?.playbackController?.currentTimeMs ?? Number.NaN
|
|
364
|
-
);
|
|
365
|
-
}
|
|
366
|
-
set currentTimeMs(value: number) {
|
|
367
|
-
if (this.targetTemporal?.playbackController) {
|
|
368
|
-
this.targetTemporal.playbackController.setCurrentTimeMs(value);
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
#timegroupObserver = new MutationObserver((mutations) => {
|
|
373
|
-
let shouldUpdate = false;
|
|
374
|
-
|
|
375
|
-
for (const mutation of mutations) {
|
|
376
|
-
if (mutation.type === "childList") {
|
|
377
|
-
const newTemporal = this.findRootTemporal();
|
|
378
|
-
if (newTemporal !== this.targetTemporal) {
|
|
379
|
-
this.targetTemporal = newTemporal;
|
|
380
|
-
shouldUpdate = true;
|
|
381
|
-
} else if (
|
|
382
|
-
mutation.target instanceof Element &&
|
|
383
|
-
isEFTemporal(mutation.target)
|
|
384
|
-
) {
|
|
385
|
-
// Handle childList changes within existing temporal elements
|
|
386
|
-
shouldUpdate = true;
|
|
387
|
-
}
|
|
388
|
-
} else if (mutation.type === "attributes") {
|
|
389
|
-
// Watch for attribute changes that might affect duration
|
|
390
|
-
const durationAffectingAttributes = [
|
|
391
|
-
"duration",
|
|
392
|
-
"mode",
|
|
393
|
-
"trimstart",
|
|
394
|
-
"trimend",
|
|
395
|
-
"sourcein",
|
|
396
|
-
"sourceout",
|
|
397
|
-
];
|
|
398
|
-
|
|
399
|
-
if (
|
|
400
|
-
durationAffectingAttributes.includes(
|
|
401
|
-
mutation.attributeName || "",
|
|
402
|
-
) ||
|
|
403
|
-
(mutation.target instanceof Element &&
|
|
404
|
-
isEFTemporal(mutation.target))
|
|
405
|
-
) {
|
|
406
|
-
shouldUpdate = true;
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
if (shouldUpdate) {
|
|
412
|
-
// Trigger an update to ensure reactive properties recalculate
|
|
413
|
-
// Use a microtask to ensure DOM updates are complete
|
|
414
|
-
queueMicrotask(() => {
|
|
415
|
-
// Recalculate duration and endTime when temporal element changes
|
|
416
|
-
this.updateDurationProperties();
|
|
417
|
-
this.requestUpdate();
|
|
418
|
-
// Also ensure the targetTemporal updates its computed properties
|
|
419
|
-
if (this.targetTemporal) {
|
|
420
|
-
(this.targetTemporal as any).requestUpdate();
|
|
421
|
-
}
|
|
422
|
-
});
|
|
423
|
-
}
|
|
424
|
-
});
|
|
425
|
-
|
|
426
|
-
/**
|
|
427
|
-
* Update duration properties when temporal element changes
|
|
428
|
-
*/
|
|
429
|
-
updateDurationProperties(): void {
|
|
430
|
-
const newDuration = this.targetTemporal?.durationMs ?? 0;
|
|
431
|
-
const newEndTime = this.targetTemporal?.endTimeMs ?? 0;
|
|
432
|
-
|
|
433
|
-
if (this.durationMs !== newDuration) {
|
|
434
|
-
this.durationMs = newDuration;
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
if (this.endTimeMs !== newEndTime) {
|
|
438
|
-
this.endTimeMs = newEndTime;
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
connectedCallback(): void {
|
|
443
|
-
super.connectedCallback();
|
|
444
|
-
|
|
445
|
-
// Create manual context providers for playback state
|
|
446
|
-
this.#playingProvider = new ContextProvider(this, {
|
|
447
|
-
context: playingContext,
|
|
448
|
-
initialValue: this.playing,
|
|
449
|
-
});
|
|
450
|
-
this.#loopProvider = new ContextProvider(this, {
|
|
451
|
-
context: loopContext,
|
|
452
|
-
initialValue: this.loop,
|
|
453
|
-
});
|
|
454
|
-
this.#currentTimeMsProvider = new ContextProvider(this, {
|
|
455
|
-
context: currentTimeContext,
|
|
456
|
-
initialValue: this.currentTimeMs,
|
|
457
|
-
});
|
|
458
|
-
this.#targetTemporalProvider = new ContextProvider(this, {
|
|
459
|
-
context: targetTemporalContext,
|
|
460
|
-
initialValue: this.targetTemporal,
|
|
461
|
-
});
|
|
462
|
-
|
|
463
|
-
// Initialize targetTemporal to first root temporal element
|
|
464
|
-
this.targetTemporal = this.findRootTemporal();
|
|
465
|
-
// Initialize duration properties
|
|
466
|
-
this.updateDurationProperties();
|
|
467
|
-
|
|
468
|
-
this.#timegroupObserver.observe(this, {
|
|
469
|
-
childList: true,
|
|
470
|
-
subtree: true,
|
|
471
|
-
attributes: true,
|
|
472
|
-
});
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
disconnectedCallback(): void {
|
|
476
|
-
super.disconnectedCallback();
|
|
477
|
-
this.#timegroupObserver.disconnect();
|
|
478
|
-
|
|
479
|
-
// Unsubscribe from controller
|
|
480
|
-
if (this.#targetTemporal?.playbackController) {
|
|
481
|
-
this.#targetTemporal.playbackController.removeListener(
|
|
482
|
-
this.#onControllerUpdate,
|
|
483
|
-
);
|
|
484
|
-
this.#controllerSubscribed = false;
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
this.pause();
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
updated(changedProperties: Map<string | number | symbol, unknown>) {
|
|
491
|
-
super.updated?.(changedProperties);
|
|
492
|
-
|
|
493
|
-
// Subscribe to controller when it becomes available
|
|
494
|
-
if (
|
|
495
|
-
!this.#controllerSubscribed &&
|
|
496
|
-
this.#targetTemporal?.playbackController
|
|
497
|
-
) {
|
|
498
|
-
this.#targetTemporal.playbackController.addListener(
|
|
499
|
-
this.#onControllerUpdate,
|
|
500
|
-
);
|
|
501
|
-
this.#controllerSubscribed = true;
|
|
502
|
-
|
|
503
|
-
// Apply stored loop value when playbackController becomes available
|
|
504
|
-
if (this.#loop) {
|
|
505
|
-
this.#targetTemporal.playbackController.setLoop(this.#loop);
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
// Trigger initial sync of context providers
|
|
509
|
-
this.#playingProvider.setValue(this.playing);
|
|
510
|
-
this.#loopProvider.setValue(this.loop);
|
|
511
|
-
this.#currentTimeMsProvider.setValue(this.currentTimeMs);
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
async play() {
|
|
516
|
-
// If targetTemporal is not set, try to find it now
|
|
517
|
-
// This handles cases where the DOM may not have been fully ready during connectedCallback
|
|
518
|
-
if (!this.targetTemporal) {
|
|
519
|
-
// Wait for any temporal custom elements to be defined
|
|
520
|
-
const potentialTemporalTags = Array.from(this.children)
|
|
521
|
-
.map((el) => el.tagName.toLowerCase())
|
|
522
|
-
.filter((tag) => tag.startsWith("ef-"));
|
|
523
|
-
|
|
524
|
-
await Promise.all(
|
|
525
|
-
potentialTemporalTags.map((tag) =>
|
|
526
|
-
customElements.whenDefined(tag).catch(() => {}),
|
|
527
|
-
),
|
|
528
|
-
);
|
|
529
|
-
|
|
530
|
-
const foundTemporal = this.findRootTemporal();
|
|
531
|
-
if (foundTemporal) {
|
|
532
|
-
this.targetTemporal = foundTemporal;
|
|
533
|
-
// Wait for it to initialize
|
|
534
|
-
await (foundTemporal as any).updateComplete;
|
|
535
|
-
} else {
|
|
536
|
-
console.warn("No temporal element found to play");
|
|
537
|
-
return;
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
// If playbackController doesn't exist yet, wait for it
|
|
542
|
-
if (!this.targetTemporal.playbackController) {
|
|
543
|
-
await (this.targetTemporal as any).updateComplete;
|
|
544
|
-
// After waiting, check again
|
|
545
|
-
if (!this.targetTemporal.playbackController) {
|
|
546
|
-
console.warn("PlaybackController not available for temporal element");
|
|
547
|
-
return;
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
this.targetTemporal.playbackController.play();
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
pause() {
|
|
555
|
-
if (this.targetTemporal?.playbackController) {
|
|
556
|
-
this.targetTemporal.playbackController.pause();
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
return ContextElement as Constructor<ContextMixinInterface> & T;
|
|
562
|
-
}
|