@editframe/elements 0.26.3-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.
Files changed (132) hide show
  1. package/package.json +2 -2
  2. package/scripts/build-css.js +3 -3
  3. package/tsdown.config.ts +1 -1
  4. package/src/elements/ContextProxiesController.ts +0 -124
  5. package/src/elements/CrossUpdateController.ts +0 -22
  6. package/src/elements/EFAudio.browsertest.ts +0 -706
  7. package/src/elements/EFAudio.ts +0 -56
  8. package/src/elements/EFCaptions.browsertest.ts +0 -1960
  9. package/src/elements/EFCaptions.ts +0 -823
  10. package/src/elements/EFImage.browsertest.ts +0 -120
  11. package/src/elements/EFImage.ts +0 -113
  12. package/src/elements/EFMedia/AssetIdMediaEngine.test.ts +0 -224
  13. package/src/elements/EFMedia/AssetIdMediaEngine.ts +0 -110
  14. package/src/elements/EFMedia/AssetMediaEngine.browsertest.ts +0 -140
  15. package/src/elements/EFMedia/AssetMediaEngine.ts +0 -385
  16. package/src/elements/EFMedia/BaseMediaEngine.browsertest.ts +0 -400
  17. package/src/elements/EFMedia/BaseMediaEngine.ts +0 -505
  18. package/src/elements/EFMedia/BufferedSeekingInput.browsertest.ts +0 -386
  19. package/src/elements/EFMedia/BufferedSeekingInput.ts +0 -430
  20. package/src/elements/EFMedia/JitMediaEngine.browsertest.ts +0 -226
  21. package/src/elements/EFMedia/JitMediaEngine.ts +0 -256
  22. package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.browsertest.ts +0 -679
  23. package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.ts +0 -117
  24. package/src/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.ts +0 -246
  25. package/src/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.browsertest.ts +0 -59
  26. package/src/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.ts +0 -27
  27. package/src/elements/EFMedia/audioTasks/makeAudioInputTask.browsertest.ts +0 -55
  28. package/src/elements/EFMedia/audioTasks/makeAudioInputTask.ts +0 -53
  29. package/src/elements/EFMedia/audioTasks/makeAudioSeekTask.chunkboundary.regression.browsertest.ts +0 -207
  30. package/src/elements/EFMedia/audioTasks/makeAudioSeekTask.ts +0 -72
  31. package/src/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.ts +0 -32
  32. package/src/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.ts +0 -29
  33. package/src/elements/EFMedia/audioTasks/makeAudioTasksVideoOnly.browsertest.ts +0 -95
  34. package/src/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.ts +0 -184
  35. package/src/elements/EFMedia/shared/AudioSpanUtils.ts +0 -129
  36. package/src/elements/EFMedia/shared/BufferUtils.ts +0 -342
  37. package/src/elements/EFMedia/shared/GlobalInputCache.ts +0 -77
  38. package/src/elements/EFMedia/shared/MediaTaskUtils.ts +0 -44
  39. package/src/elements/EFMedia/shared/PrecisionUtils.ts +0 -46
  40. package/src/elements/EFMedia/shared/RenditionHelpers.browsertest.ts +0 -246
  41. package/src/elements/EFMedia/shared/RenditionHelpers.ts +0 -56
  42. package/src/elements/EFMedia/shared/ThumbnailExtractor.ts +0 -227
  43. package/src/elements/EFMedia/tasks/makeMediaEngineTask.browsertest.ts +0 -167
  44. package/src/elements/EFMedia/tasks/makeMediaEngineTask.ts +0 -88
  45. package/src/elements/EFMedia/videoTasks/MainVideoInputCache.ts +0 -76
  46. package/src/elements/EFMedia/videoTasks/ScrubInputCache.ts +0 -61
  47. package/src/elements/EFMedia/videoTasks/makeScrubVideoBufferTask.ts +0 -114
  48. package/src/elements/EFMedia/videoTasks/makeScrubVideoInitSegmentFetchTask.ts +0 -35
  49. package/src/elements/EFMedia/videoTasks/makeScrubVideoInputTask.ts +0 -52
  50. package/src/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.ts +0 -124
  51. package/src/elements/EFMedia/videoTasks/makeScrubVideoSegmentFetchTask.ts +0 -44
  52. package/src/elements/EFMedia/videoTasks/makeScrubVideoSegmentIdTask.ts +0 -32
  53. package/src/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.ts +0 -370
  54. package/src/elements/EFMedia/videoTasks/makeVideoBufferTask.ts +0 -109
  55. package/src/elements/EFMedia.browsertest.ts +0 -872
  56. package/src/elements/EFMedia.ts +0 -341
  57. package/src/elements/EFSourceMixin.ts +0 -60
  58. package/src/elements/EFSurface.browsertest.ts +0 -151
  59. package/src/elements/EFSurface.ts +0 -142
  60. package/src/elements/EFTemporal.browsertest.ts +0 -215
  61. package/src/elements/EFTemporal.ts +0 -800
  62. package/src/elements/EFThumbnailStrip.browsertest.ts +0 -585
  63. package/src/elements/EFThumbnailStrip.media-engine.browsertest.ts +0 -714
  64. package/src/elements/EFThumbnailStrip.ts +0 -906
  65. package/src/elements/EFTimegroup.browsertest.ts +0 -934
  66. package/src/elements/EFTimegroup.ts +0 -882
  67. package/src/elements/EFVideo.browsertest.ts +0 -1482
  68. package/src/elements/EFVideo.ts +0 -564
  69. package/src/elements/EFWaveform.ts +0 -547
  70. package/src/elements/FetchContext.browsertest.ts +0 -401
  71. package/src/elements/FetchMixin.ts +0 -38
  72. package/src/elements/SampleBuffer.ts +0 -94
  73. package/src/elements/TargetController.browsertest.ts +0 -230
  74. package/src/elements/TargetController.ts +0 -224
  75. package/src/elements/TimegroupController.ts +0 -26
  76. package/src/elements/durationConverter.ts +0 -35
  77. package/src/elements/parseTimeToMs.ts +0 -9
  78. package/src/elements/printTaskStatus.ts +0 -16
  79. package/src/elements/renderTemporalAudio.ts +0 -108
  80. package/src/elements/updateAnimations.browsertest.ts +0 -1884
  81. package/src/elements/updateAnimations.ts +0 -217
  82. package/src/elements/util.ts +0 -24
  83. package/src/gui/ContextMixin.browsertest.ts +0 -860
  84. package/src/gui/ContextMixin.ts +0 -562
  85. package/src/gui/Controllable.browsertest.ts +0 -258
  86. package/src/gui/Controllable.ts +0 -41
  87. package/src/gui/EFConfiguration.ts +0 -40
  88. package/src/gui/EFControls.browsertest.ts +0 -389
  89. package/src/gui/EFControls.ts +0 -195
  90. package/src/gui/EFDial.browsertest.ts +0 -84
  91. package/src/gui/EFDial.ts +0 -172
  92. package/src/gui/EFFilmstrip.browsertest.ts +0 -712
  93. package/src/gui/EFFilmstrip.ts +0 -1349
  94. package/src/gui/EFFitScale.ts +0 -152
  95. package/src/gui/EFFocusOverlay.ts +0 -79
  96. package/src/gui/EFPause.browsertest.ts +0 -202
  97. package/src/gui/EFPause.ts +0 -73
  98. package/src/gui/EFPlay.browsertest.ts +0 -202
  99. package/src/gui/EFPlay.ts +0 -73
  100. package/src/gui/EFPreview.ts +0 -74
  101. package/src/gui/EFResizableBox.browsertest.ts +0 -79
  102. package/src/gui/EFResizableBox.ts +0 -898
  103. package/src/gui/EFScrubber.ts +0 -151
  104. package/src/gui/EFTimeDisplay.browsertest.ts +0 -237
  105. package/src/gui/EFTimeDisplay.ts +0 -55
  106. package/src/gui/EFToggleLoop.ts +0 -35
  107. package/src/gui/EFTogglePlay.ts +0 -70
  108. package/src/gui/EFWorkbench.ts +0 -115
  109. package/src/gui/PlaybackController.ts +0 -527
  110. package/src/gui/TWMixin.css +0 -6
  111. package/src/gui/TWMixin.ts +0 -61
  112. package/src/gui/TargetOrContextMixin.ts +0 -185
  113. package/src/gui/currentTimeContext.ts +0 -5
  114. package/src/gui/durationContext.ts +0 -3
  115. package/src/gui/efContext.ts +0 -6
  116. package/src/gui/fetchContext.ts +0 -5
  117. package/src/gui/focusContext.ts +0 -7
  118. package/src/gui/focusedElementContext.ts +0 -5
  119. package/src/gui/playingContext.ts +0 -5
  120. package/src/otel/BridgeSpanExporter.ts +0 -150
  121. package/src/otel/setupBrowserTracing.ts +0 -73
  122. package/src/otel/tracingHelpers.ts +0 -251
  123. package/src/transcoding/cache/RequestDeduplicator.test.ts +0 -170
  124. package/src/transcoding/cache/RequestDeduplicator.ts +0 -65
  125. package/src/transcoding/cache/URLTokenDeduplicator.test.ts +0 -182
  126. package/src/transcoding/cache/URLTokenDeduplicator.ts +0 -101
  127. package/src/transcoding/types/index.ts +0 -312
  128. package/src/transcoding/utils/MediaUtils.ts +0 -63
  129. package/src/transcoding/utils/UrlGenerator.ts +0 -68
  130. package/src/transcoding/utils/constants.ts +0 -36
  131. package/src/utils/LRUCache.test.ts +0 -274
  132. package/src/utils/LRUCache.ts +0 -696
@@ -1,117 +0,0 @@
1
- import { Task } from "@lit/task";
2
-
3
- import { EF_INTERACTIVE } from "../../../EF_INTERACTIVE";
4
- import { EF_RENDERING } from "../../../EF_RENDERING";
5
- import type { AudioRendition } from "../../../transcoding/types";
6
- import type { EFMedia } from "../../EFMedia";
7
- import {
8
- type MediaBufferConfig,
9
- type MediaBufferState,
10
- manageMediaBuffer,
11
- } from "../shared/BufferUtils";
12
- import { getLatestMediaEngine } from "../tasks/makeMediaEngineTask";
13
-
14
- /**
15
- * Configuration for audio buffering - extends the generic interface
16
- */
17
- export interface AudioBufferConfig extends MediaBufferConfig {}
18
-
19
- /**
20
- * State of the audio buffer - uses the generic interface
21
- */
22
- export interface AudioBufferState extends MediaBufferState {}
23
-
24
- type AudioBufferTask = Task<readonly [number], AudioBufferState>;
25
- export const makeAudioBufferTask = (host: EFMedia): AudioBufferTask => {
26
- let currentState: AudioBufferState = {
27
- currentSeekTimeMs: 0,
28
- requestedSegments: new Set(),
29
- activeRequests: new Set(),
30
- requestQueue: [],
31
- };
32
-
33
- return new Task(host, {
34
- autoRun: EF_INTERACTIVE, // Make lazy - only run when element becomes timeline-active
35
- args: () => [host.desiredSeekTimeMs] as const,
36
- onError: (error) => {
37
- console.error("audioBufferTask error", error);
38
- },
39
- onComplete: (value) => {
40
- currentState = value;
41
- },
42
- task: async ([seekTimeMs], { signal }) => {
43
- // Skip buffering entirely in rendering mode
44
- if (EF_RENDERING()) {
45
- return currentState; // Return existing state without any buffering activity
46
- }
47
-
48
- // Get media engine to potentially override buffer configuration
49
- const mediaEngine = await getLatestMediaEngine(host, signal);
50
-
51
- // Return existing state if no audio rendition available
52
- if (!mediaEngine.audioRendition) {
53
- return currentState;
54
- }
55
-
56
- // Use media engine's buffer config, falling back to host properties
57
- const engineConfig = mediaEngine.getBufferConfig();
58
- const bufferDurationMs = engineConfig.audioBufferDurationMs;
59
- const maxParallelFetches = engineConfig.maxAudioBufferFetches;
60
-
61
- const currentConfig: AudioBufferConfig = {
62
- bufferDurationMs,
63
- maxParallelFetches,
64
- enableBuffering: host.enableAudioBuffering,
65
- bufferThresholdMs: engineConfig.bufferThresholdMs,
66
- };
67
-
68
- // Timeline context for priority-based buffering
69
- const timelineContext =
70
- host.rootTimegroup?.currentTimeMs !== undefined
71
- ? {
72
- elementStartMs: host.startTimeMs,
73
- elementEndMs: host.endTimeMs,
74
- playheadMs: host.rootTimegroup.currentTimeMs,
75
- }
76
- : undefined;
77
-
78
- return manageMediaBuffer<AudioRendition>(
79
- seekTimeMs,
80
- currentConfig,
81
- currentState,
82
- (host as any).intrinsicDurationMs || 10000,
83
- signal,
84
- {
85
- computeSegmentId: async (timeMs, rendition) => {
86
- // Use media engine's computeSegmentId
87
- const mediaEngine = await getLatestMediaEngine(host, signal);
88
- return mediaEngine.computeSegmentId(timeMs, rendition);
89
- },
90
- prefetchSegment: async (segmentId, rendition) => {
91
- // Trigger prefetch through BaseMediaEngine - let it handle caching
92
- const mediaEngine = await getLatestMediaEngine(host, signal);
93
- await mediaEngine.fetchMediaSegment(segmentId, rendition);
94
- // Don't return data - just ensure it's cached in BaseMediaEngine
95
- },
96
- isSegmentCached: (segmentId, rendition) => {
97
- // Check if segment is already cached in BaseMediaEngine
98
- const mediaEngine = host.mediaEngineTask.value;
99
- if (!mediaEngine) return false;
100
-
101
- return mediaEngine.isSegmentCached(segmentId, rendition);
102
- },
103
- getRendition: async () => {
104
- const mediaEngine = await getLatestMediaEngine(host, signal);
105
- const audioRendition = mediaEngine.audioRendition;
106
- if (!audioRendition) {
107
- throw new Error("Audio rendition not available");
108
- }
109
- return audioRendition;
110
- },
111
- logError: console.error,
112
- },
113
- timelineContext,
114
- );
115
- },
116
- });
117
- };
@@ -1,246 +0,0 @@
1
- import { Task } from "@lit/task";
2
- import { EF_INTERACTIVE } from "../../../EF_INTERACTIVE.js";
3
- import { LRUCache } from "../../../utils/LRUCache.js";
4
- import type { EFMedia } from "../../EFMedia.js";
5
-
6
- // DECAY_WEIGHT constant - same as original
7
- const DECAY_WEIGHT = 0.8;
8
-
9
- function processFFTData(
10
- fftData: Uint8Array,
11
- zeroThresholdPercent = 0.1,
12
- ): Uint8Array {
13
- // Step 1: Determine the threshold for zeros
14
- const totalBins = fftData.length;
15
- const zeroThresholdCount = Math.floor(totalBins * zeroThresholdPercent);
16
-
17
- // Step 2: Interrogate the FFT output to find the cutoff point
18
- let zeroCount = 0;
19
- let cutoffIndex = totalBins; // Default to the end of the array
20
-
21
- for (let i = totalBins - 1; i >= 0; i--) {
22
- if (fftData[i] ?? 0 < 10) {
23
- zeroCount++;
24
- } else {
25
- // If we encounter a non-zero value, we can stop
26
- if (zeroCount >= zeroThresholdCount) {
27
- cutoffIndex = i + 1; // Include this index
28
- break;
29
- }
30
- }
31
- }
32
-
33
- if (cutoffIndex < zeroThresholdCount) {
34
- return fftData;
35
- }
36
-
37
- // Step 3: Resample the "good" portion of the data
38
- const goodData = fftData.slice(0, cutoffIndex);
39
- const resampledData = interpolateData(goodData, fftData.length);
40
-
41
- // Step 4: Attenuate the top 10% of interpolated samples
42
- const attenuationStartIndex = Math.floor(totalBins * 0.9);
43
- for (let i = attenuationStartIndex; i < totalBins; i++) {
44
- // Calculate attenuation factor that goes from 1 to 0 over the top 10%
45
- const attenuationProgress =
46
- (i - attenuationStartIndex) / (totalBins - attenuationStartIndex) + 0.2;
47
- const attenuationFactor = Math.max(0, 1 - attenuationProgress);
48
- resampledData[i] = Math.floor((resampledData[i] ?? 0) * attenuationFactor);
49
- }
50
-
51
- return resampledData;
52
- }
53
-
54
- function interpolateData(data: Uint8Array, targetSize: number): Uint8Array {
55
- const resampled = new Uint8Array(targetSize);
56
- const dataLength = data.length;
57
-
58
- for (let i = 0; i < targetSize; i++) {
59
- // Calculate the corresponding index in the original data
60
- const ratio = (i / (targetSize - 1)) * (dataLength - 1);
61
- const index = Math.floor(ratio);
62
- const fraction = ratio - index;
63
-
64
- // Handle edge cases
65
- if (index >= dataLength - 1) {
66
- resampled[i] = data[dataLength - 1] ?? 0; // Last value
67
- } else {
68
- // Linear interpolation
69
- resampled[i] = Math.round(
70
- (data[index] ?? 0) * (1 - fraction) + (data[index + 1] ?? 0) * fraction,
71
- );
72
- }
73
- }
74
-
75
- return resampled;
76
- }
77
-
78
- export function makeAudioFrequencyAnalysisTask(element: EFMedia) {
79
- // Internal cache for this task instance (same as original #frequencyDataCache)
80
- const cache = new LRUCache<string, Uint8Array>(100);
81
-
82
- return new Task(element, {
83
- autoRun: EF_INTERACTIVE,
84
- onError: (error) => {
85
- console.error("frequencyDataTask error", error);
86
- },
87
- args: () =>
88
- [
89
- element.currentSourceTimeMs,
90
- element.fftSize,
91
- element.fftDecay,
92
- element.fftGain,
93
- element.shouldInterpolateFrequencies,
94
- ] as const,
95
- task: async (_, { signal }) => {
96
- if (element.currentSourceTimeMs < 0) return null;
97
-
98
- const currentTimeMs = element.currentSourceTimeMs;
99
-
100
- // Calculate exact audio window needed based on fftDecay and frame timing
101
- const frameIntervalMs = 1000 / 30; // 33.33ms per frame
102
-
103
- // Need audio from earliest frame to current frame
104
- const earliestFrameMs =
105
- currentTimeMs - (element.fftDecay - 1) * frameIntervalMs;
106
- const fromMs = Math.max(0, earliestFrameMs);
107
- const maxToMs = currentTimeMs + frameIntervalMs; // Include current frame
108
- const videoDurationMs = element.intrinsicDurationMs || 0;
109
- const toMs =
110
- videoDurationMs > 0 ? Math.min(maxToMs, videoDurationMs) : maxToMs;
111
-
112
- // If the clamping results in an invalid range (seeking beyond the end), skip analysis silently
113
- if (fromMs >= toMs) {
114
- return null;
115
- }
116
-
117
- // Check cache early - before expensive audio fetching
118
- // Use a preliminary cache key that doesn't depend on actual startOffsetMs from audio span
119
- const preliminaryCacheKey = `${element.shouldInterpolateFrequencies}:${element.fftSize}:${element.fftDecay}:${element.fftGain}:${fromMs}:${currentTimeMs}`;
120
- const cachedSmoothedData = cache.get(preliminaryCacheKey);
121
- if (cachedSmoothedData) {
122
- return cachedSmoothedData;
123
- }
124
-
125
- const { fetchAudioSpanningTime: fetchAudioSpan } = await import(
126
- "../shared/AudioSpanUtils.ts"
127
- );
128
- const audioSpan = await fetchAudioSpan(element, fromMs, toMs, signal);
129
-
130
- if (!audioSpan || !audioSpan.blob) {
131
- console.warn("Frequency analysis skipped: no audio data available");
132
- return null;
133
- }
134
-
135
- // Decode the real audio data
136
- const tempAudioContext = new OfflineAudioContext(2, 48000, 48000);
137
- const arrayBuffer = await audioSpan.blob.arrayBuffer();
138
- const audioBuffer = await tempAudioContext.decodeAudioData(arrayBuffer);
139
-
140
- // Use actual startOffset from audioSpan (relative to requested time)
141
- const startOffsetMs = audioSpan.startMs;
142
-
143
- const framesData = await Promise.all(
144
- Array.from({ length: element.fftDecay }, async (_, i) => {
145
- const frameOffset = i * (1000 / 30);
146
- const startTime = Math.max(
147
- 0,
148
- (currentTimeMs - frameOffset - startOffsetMs) / 1000,
149
- );
150
-
151
- // Cache key for this specific frame
152
- const cacheKey = `${element.shouldInterpolateFrequencies}:${element.fftSize}:${element.fftGain}:${startOffsetMs}:${startTime}`;
153
-
154
- // Check cache for this specific frame
155
- const cachedFrame = cache.get(cacheKey);
156
- if (cachedFrame) {
157
- return cachedFrame;
158
- }
159
-
160
- // Running 48000 * (1 / 30) = 1600 broke something terrible, it came out as 0,
161
- // I'm assuming weird floating point nonsense to do with running on rosetta
162
- const SIZE = 48000 / 30;
163
- let audioContext: OfflineAudioContext;
164
- try {
165
- audioContext = new OfflineAudioContext(2, SIZE, 48000);
166
- } catch (error) {
167
- throw new Error(
168
- `[EFMedia.frequencyDataTask] Failed to create OfflineAudioContext(2, ${SIZE}, 48000) for frame ${i} at time ${startTime}s: ${error instanceof Error ? error.message : String(error)}. This is for audio frequency analysis.`,
169
- );
170
- }
171
- const analyser = audioContext.createAnalyser();
172
- analyser.fftSize = element.fftSize;
173
- analyser.minDecibels = -90;
174
- analyser.maxDecibels = -10;
175
-
176
- const gainNode = audioContext.createGain();
177
- gainNode.gain.value = element.fftGain;
178
-
179
- const filter = audioContext.createBiquadFilter();
180
- filter.type = "bandpass";
181
- filter.frequency.value = 15000;
182
- filter.Q.value = 0.05;
183
-
184
- const audioBufferSource = audioContext.createBufferSource();
185
- audioBufferSource.buffer = audioBuffer;
186
-
187
- audioBufferSource.connect(filter);
188
- filter.connect(gainNode);
189
- gainNode.connect(analyser);
190
- analyser.connect(audioContext.destination);
191
-
192
- audioBufferSource.start(0, startTime, 1 / 30);
193
-
194
- try {
195
- await audioContext.startRendering();
196
- const frameData = new Uint8Array(element.fftSize / 2);
197
- analyser.getByteFrequencyData(frameData);
198
-
199
- // Cache this frame's analysis
200
- cache.set(cacheKey, frameData);
201
- return frameData;
202
- } finally {
203
- audioBufferSource.disconnect();
204
- analyser.disconnect();
205
- }
206
- }),
207
- );
208
-
209
- const frameLength = framesData[0]?.length ?? 0;
210
-
211
- // Combine frames with decay
212
- const smoothedData = new Uint8Array(frameLength);
213
- for (let i = 0; i < frameLength; i++) {
214
- let weightedSum = 0;
215
- let weightSum = 0;
216
-
217
- framesData.forEach((frame: Uint8Array, frameIndex: number) => {
218
- const decayWeight = DECAY_WEIGHT ** frameIndex;
219
- weightedSum += (frame[i] ?? 0) * decayWeight;
220
- weightSum += decayWeight;
221
- });
222
-
223
- smoothedData[i] = Math.min(255, Math.round(weightedSum / weightSum));
224
- }
225
-
226
- // Apply frequency weights using instance FREQ_WEIGHTS
227
- smoothedData.forEach((value, i) => {
228
- const freqWeight = element.FREQ_WEIGHTS[i] ?? 0;
229
- smoothedData[i] = Math.min(255, Math.round(value * freqWeight));
230
- });
231
-
232
- // Only return the lower half of the frequency data
233
- // The top half is zeroed out, which makes for aesthetically unpleasing waveforms
234
- const slicedData = smoothedData.slice(
235
- 0,
236
- Math.floor(smoothedData.length / 2),
237
- );
238
- const processedData = element.shouldInterpolateFrequencies
239
- ? processFFTData(slicedData)
240
- : slicedData;
241
- // Cache with the preliminary key so future requests can skip audio fetching
242
- cache.set(preliminaryCacheKey, processedData);
243
- return processedData;
244
- },
245
- });
246
- }
@@ -1,59 +0,0 @@
1
- import { TaskStatus } from "@lit/task";
2
- import { customElement } from "lit/decorators.js";
3
- import { afterEach, beforeEach, describe, vi } from "vitest";
4
- import { test as baseTest } from "../../../../test/useMSW.js";
5
- import { EFAudio } from "../../EFAudio";
6
- import { makeAudioInitSegmentFetchTask } from "./makeAudioInitSegmentFetchTask";
7
-
8
- @customElement("test-media-audio-init-segment-fetch")
9
- class TestMediaAudioInitSegmentFetch extends EFAudio {}
10
-
11
- declare global {
12
- interface HTMLElementTagNameMap {
13
- "test-media-audio-init-segment-fetch": TestMediaAudioInitSegmentFetch;
14
- }
15
- }
16
-
17
- const test = baseTest.extend<{
18
- element: TestMediaAudioInitSegmentFetch;
19
- }>({
20
- element: async ({}, use) => {
21
- const element = document.createElement(
22
- "test-media-audio-init-segment-fetch",
23
- );
24
- await use(element);
25
- element.remove();
26
- },
27
- });
28
-
29
- describe("makeAudioInitSegmentFetchTask", () => {
30
- beforeEach(() => {
31
- // MSW setup is now handled by test fixtures
32
- });
33
-
34
- afterEach(() => {
35
- const elements = document.querySelectorAll(
36
- "test-media-audio-init-segment-fetch",
37
- );
38
- for (const element of elements) {
39
- element.remove();
40
- }
41
- vi.restoreAllMocks();
42
- });
43
-
44
- test("creates task with correct initial state", ({ element, expect }) => {
45
- const task = makeAudioInitSegmentFetchTask(element);
46
-
47
- expect(task).toBeDefined();
48
- expect(task.status).toBe(TaskStatus.INITIAL);
49
- expect(task.value).toBeUndefined();
50
- expect(task.error).toBeUndefined();
51
- });
52
-
53
- test("task integrates with element properties", ({ element, expect }) => {
54
- const task = makeAudioInitSegmentFetchTask(element);
55
-
56
- expect(task).toBeDefined();
57
- expect(task.status).toBe(TaskStatus.INITIAL);
58
- });
59
- });
@@ -1,27 +0,0 @@
1
- import { Task } from "@lit/task";
2
- import type { MediaEngine } from "../../../transcoding/types";
3
- import type { EFMedia } from "../../EFMedia";
4
- import { getLatestMediaEngine } from "../tasks/makeMediaEngineTask";
5
-
6
- export const makeAudioInitSegmentFetchTask = (
7
- host: EFMedia,
8
- ): Task<readonly [MediaEngine | undefined], ArrayBuffer | undefined> => {
9
- return new Task(host, {
10
- args: () => [host.mediaEngineTask.value] as const,
11
- onError: (error) => {
12
- console.error("audioInitSegmentFetchTask error", error);
13
- },
14
- onComplete: (_value) => {},
15
- task: async ([_mediaEngine], { signal }) => {
16
- const mediaEngine = await getLatestMediaEngine(host, signal);
17
- const audioRendition = mediaEngine.getAudioRendition();
18
-
19
- // Return undefined if no audio rendition available (video-only asset)
20
- if (!audioRendition) {
21
- return undefined;
22
- }
23
-
24
- return mediaEngine.fetchInitSegment(audioRendition, signal);
25
- },
26
- });
27
- };
@@ -1,55 +0,0 @@
1
- import { TaskStatus } from "@lit/task";
2
- import { customElement } from "lit/decorators.js";
3
- import { afterEach, beforeEach, describe, vi } from "vitest";
4
- import { test as baseTest } from "../../../../test/useMSW.js";
5
- import { EFAudio } from "../../EFAudio";
6
- import { makeAudioInputTask } from "./makeAudioInputTask";
7
-
8
- @customElement("test-media-audio-input")
9
- class TestMediaAudioInput extends EFAudio {}
10
-
11
- declare global {
12
- interface HTMLElementTagNameMap {
13
- "test-media-audio-input": TestMediaAudioInput;
14
- }
15
- }
16
-
17
- const test = baseTest.extend<{
18
- element: TestMediaAudioInput;
19
- }>({
20
- element: async ({}, use) => {
21
- const element = document.createElement("test-media-audio-input");
22
- await use(element);
23
- element.remove();
24
- },
25
- });
26
-
27
- describe("makeAudioInputTask", () => {
28
- beforeEach(() => {
29
- // MSW setup is now handled by test fixtures
30
- });
31
-
32
- afterEach(() => {
33
- const elements = document.querySelectorAll("test-media-audio-input");
34
- for (const element of elements) {
35
- element.remove();
36
- }
37
- vi.restoreAllMocks();
38
- });
39
-
40
- test("creates task with correct initial state", ({ element, expect }) => {
41
- const task = makeAudioInputTask(element);
42
-
43
- expect(task).toBeDefined();
44
- expect(task.status).toBe(TaskStatus.INITIAL);
45
- expect(task.value).toBeUndefined();
46
- expect(task.error).toBeUndefined();
47
- });
48
-
49
- test("task integrates with element properties", ({ element, expect }) => {
50
- const task = makeAudioInputTask(element);
51
-
52
- expect(task).toBeDefined();
53
- expect(task.status).toBe(TaskStatus.INITIAL);
54
- });
55
- });
@@ -1,53 +0,0 @@
1
- import { Task } from "@lit/task";
2
- import { EFMedia } from "../../EFMedia";
3
- import { BufferedSeekingInput } from "../BufferedSeekingInput";
4
- import type { InputTask } from "../shared/MediaTaskUtils";
5
-
6
- export const makeAudioInputTask = (host: EFMedia): InputTask => {
7
- return new Task<
8
- readonly [ArrayBuffer | undefined, ArrayBuffer | undefined],
9
- BufferedSeekingInput | undefined
10
- >(host, {
11
- args: () =>
12
- [
13
- host.audioInitSegmentFetchTask.value,
14
- host.audioSegmentFetchTask.value,
15
- ] as const,
16
- onError: (error) => {
17
- console.error("audioInputTask error", error);
18
- },
19
- onComplete: (_value) => {},
20
- task: async (_, { signal }) => {
21
- const mediaEngine = await host.mediaEngineTask.taskComplete;
22
- if (signal.aborted) return undefined;
23
-
24
- const audioRendition = mediaEngine?.audioRendition;
25
-
26
- // Return undefined if no audio rendition available (video-only asset)
27
- if (!audioRendition) {
28
- return undefined;
29
- }
30
-
31
- const initSegment = await host.audioInitSegmentFetchTask.taskComplete;
32
- if (signal.aborted) return undefined;
33
-
34
- const segment = await host.audioSegmentFetchTask.taskComplete;
35
- if (signal.aborted) return undefined;
36
-
37
- if (!initSegment || !segment) {
38
- return undefined;
39
- }
40
-
41
- const startTimeOffsetMs = audioRendition.startTimeOffsetMs;
42
-
43
- const arrayBuffer = await new Blob([initSegment, segment]).arrayBuffer();
44
- if (signal.aborted) return undefined;
45
-
46
- return new BufferedSeekingInput(arrayBuffer, {
47
- videoBufferSize: EFMedia.VIDEO_SAMPLE_BUFFER_SIZE,
48
- audioBufferSize: EFMedia.AUDIO_SAMPLE_BUFFER_SIZE,
49
- startTimeOffsetMs,
50
- });
51
- },
52
- });
53
- };