@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.
Files changed (135) hide show
  1. package/dist/elements/EFTimegroup.js +7 -2
  2. package/dist/elements/EFTimegroup.js.map +1 -1
  3. package/package.json +2 -2
  4. package/scripts/build-css.js +3 -3
  5. package/tsdown.config.ts +1 -1
  6. package/types.json +1 -1
  7. package/src/elements/ContextProxiesController.ts +0 -124
  8. package/src/elements/CrossUpdateController.ts +0 -22
  9. package/src/elements/EFAudio.browsertest.ts +0 -706
  10. package/src/elements/EFAudio.ts +0 -56
  11. package/src/elements/EFCaptions.browsertest.ts +0 -1960
  12. package/src/elements/EFCaptions.ts +0 -823
  13. package/src/elements/EFImage.browsertest.ts +0 -120
  14. package/src/elements/EFImage.ts +0 -113
  15. package/src/elements/EFMedia/AssetIdMediaEngine.test.ts +0 -224
  16. package/src/elements/EFMedia/AssetIdMediaEngine.ts +0 -110
  17. package/src/elements/EFMedia/AssetMediaEngine.browsertest.ts +0 -140
  18. package/src/elements/EFMedia/AssetMediaEngine.ts +0 -385
  19. package/src/elements/EFMedia/BaseMediaEngine.browsertest.ts +0 -400
  20. package/src/elements/EFMedia/BaseMediaEngine.ts +0 -505
  21. package/src/elements/EFMedia/BufferedSeekingInput.browsertest.ts +0 -386
  22. package/src/elements/EFMedia/BufferedSeekingInput.ts +0 -430
  23. package/src/elements/EFMedia/JitMediaEngine.browsertest.ts +0 -226
  24. package/src/elements/EFMedia/JitMediaEngine.ts +0 -256
  25. package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.browsertest.ts +0 -679
  26. package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.ts +0 -117
  27. package/src/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.ts +0 -246
  28. package/src/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.browsertest.ts +0 -59
  29. package/src/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.ts +0 -27
  30. package/src/elements/EFMedia/audioTasks/makeAudioInputTask.browsertest.ts +0 -55
  31. package/src/elements/EFMedia/audioTasks/makeAudioInputTask.ts +0 -53
  32. package/src/elements/EFMedia/audioTasks/makeAudioSeekTask.chunkboundary.regression.browsertest.ts +0 -207
  33. package/src/elements/EFMedia/audioTasks/makeAudioSeekTask.ts +0 -72
  34. package/src/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.ts +0 -32
  35. package/src/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.ts +0 -29
  36. package/src/elements/EFMedia/audioTasks/makeAudioTasksVideoOnly.browsertest.ts +0 -95
  37. package/src/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.ts +0 -184
  38. package/src/elements/EFMedia/shared/AudioSpanUtils.ts +0 -129
  39. package/src/elements/EFMedia/shared/BufferUtils.ts +0 -342
  40. package/src/elements/EFMedia/shared/GlobalInputCache.ts +0 -77
  41. package/src/elements/EFMedia/shared/MediaTaskUtils.ts +0 -44
  42. package/src/elements/EFMedia/shared/PrecisionUtils.ts +0 -46
  43. package/src/elements/EFMedia/shared/RenditionHelpers.browsertest.ts +0 -246
  44. package/src/elements/EFMedia/shared/RenditionHelpers.ts +0 -56
  45. package/src/elements/EFMedia/shared/ThumbnailExtractor.ts +0 -227
  46. package/src/elements/EFMedia/tasks/makeMediaEngineTask.browsertest.ts +0 -167
  47. package/src/elements/EFMedia/tasks/makeMediaEngineTask.ts +0 -88
  48. package/src/elements/EFMedia/videoTasks/MainVideoInputCache.ts +0 -76
  49. package/src/elements/EFMedia/videoTasks/ScrubInputCache.ts +0 -61
  50. package/src/elements/EFMedia/videoTasks/makeScrubVideoBufferTask.ts +0 -114
  51. package/src/elements/EFMedia/videoTasks/makeScrubVideoInitSegmentFetchTask.ts +0 -35
  52. package/src/elements/EFMedia/videoTasks/makeScrubVideoInputTask.ts +0 -52
  53. package/src/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.ts +0 -124
  54. package/src/elements/EFMedia/videoTasks/makeScrubVideoSegmentFetchTask.ts +0 -44
  55. package/src/elements/EFMedia/videoTasks/makeScrubVideoSegmentIdTask.ts +0 -32
  56. package/src/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.ts +0 -370
  57. package/src/elements/EFMedia/videoTasks/makeVideoBufferTask.ts +0 -109
  58. package/src/elements/EFMedia.browsertest.ts +0 -872
  59. package/src/elements/EFMedia.ts +0 -341
  60. package/src/elements/EFSourceMixin.ts +0 -60
  61. package/src/elements/EFSurface.browsertest.ts +0 -151
  62. package/src/elements/EFSurface.ts +0 -142
  63. package/src/elements/EFTemporal.browsertest.ts +0 -215
  64. package/src/elements/EFTemporal.ts +0 -800
  65. package/src/elements/EFThumbnailStrip.browsertest.ts +0 -585
  66. package/src/elements/EFThumbnailStrip.media-engine.browsertest.ts +0 -714
  67. package/src/elements/EFThumbnailStrip.ts +0 -906
  68. package/src/elements/EFTimegroup.browsertest.ts +0 -870
  69. package/src/elements/EFTimegroup.ts +0 -878
  70. package/src/elements/EFVideo.browsertest.ts +0 -1482
  71. package/src/elements/EFVideo.ts +0 -564
  72. package/src/elements/EFWaveform.ts +0 -547
  73. package/src/elements/FetchContext.browsertest.ts +0 -401
  74. package/src/elements/FetchMixin.ts +0 -38
  75. package/src/elements/SampleBuffer.ts +0 -94
  76. package/src/elements/TargetController.browsertest.ts +0 -230
  77. package/src/elements/TargetController.ts +0 -224
  78. package/src/elements/TimegroupController.ts +0 -26
  79. package/src/elements/durationConverter.ts +0 -35
  80. package/src/elements/parseTimeToMs.ts +0 -9
  81. package/src/elements/printTaskStatus.ts +0 -16
  82. package/src/elements/renderTemporalAudio.ts +0 -108
  83. package/src/elements/updateAnimations.browsertest.ts +0 -1884
  84. package/src/elements/updateAnimations.ts +0 -217
  85. package/src/elements/util.ts +0 -24
  86. package/src/gui/ContextMixin.browsertest.ts +0 -860
  87. package/src/gui/ContextMixin.ts +0 -562
  88. package/src/gui/Controllable.browsertest.ts +0 -258
  89. package/src/gui/Controllable.ts +0 -41
  90. package/src/gui/EFConfiguration.ts +0 -40
  91. package/src/gui/EFControls.browsertest.ts +0 -389
  92. package/src/gui/EFControls.ts +0 -195
  93. package/src/gui/EFDial.browsertest.ts +0 -84
  94. package/src/gui/EFDial.ts +0 -172
  95. package/src/gui/EFFilmstrip.browsertest.ts +0 -712
  96. package/src/gui/EFFilmstrip.ts +0 -1349
  97. package/src/gui/EFFitScale.ts +0 -152
  98. package/src/gui/EFFocusOverlay.ts +0 -79
  99. package/src/gui/EFPause.browsertest.ts +0 -202
  100. package/src/gui/EFPause.ts +0 -73
  101. package/src/gui/EFPlay.browsertest.ts +0 -202
  102. package/src/gui/EFPlay.ts +0 -73
  103. package/src/gui/EFPreview.ts +0 -74
  104. package/src/gui/EFResizableBox.browsertest.ts +0 -79
  105. package/src/gui/EFResizableBox.ts +0 -898
  106. package/src/gui/EFScrubber.ts +0 -151
  107. package/src/gui/EFTimeDisplay.browsertest.ts +0 -237
  108. package/src/gui/EFTimeDisplay.ts +0 -55
  109. package/src/gui/EFToggleLoop.ts +0 -35
  110. package/src/gui/EFTogglePlay.ts +0 -70
  111. package/src/gui/EFWorkbench.ts +0 -115
  112. package/src/gui/PlaybackController.ts +0 -527
  113. package/src/gui/TWMixin.css +0 -6
  114. package/src/gui/TWMixin.ts +0 -61
  115. package/src/gui/TargetOrContextMixin.ts +0 -185
  116. package/src/gui/currentTimeContext.ts +0 -5
  117. package/src/gui/durationContext.ts +0 -3
  118. package/src/gui/efContext.ts +0 -6
  119. package/src/gui/fetchContext.ts +0 -5
  120. package/src/gui/focusContext.ts +0 -7
  121. package/src/gui/focusedElementContext.ts +0 -5
  122. package/src/gui/playingContext.ts +0 -5
  123. package/src/otel/BridgeSpanExporter.ts +0 -150
  124. package/src/otel/setupBrowserTracing.ts +0 -73
  125. package/src/otel/tracingHelpers.ts +0 -251
  126. package/src/transcoding/cache/RequestDeduplicator.test.ts +0 -170
  127. package/src/transcoding/cache/RequestDeduplicator.ts +0 -65
  128. package/src/transcoding/cache/URLTokenDeduplicator.test.ts +0 -182
  129. package/src/transcoding/cache/URLTokenDeduplicator.ts +0 -101
  130. package/src/transcoding/types/index.ts +0 -312
  131. package/src/transcoding/utils/MediaUtils.ts +0 -63
  132. package/src/transcoding/utils/UrlGenerator.ts +0 -68
  133. package/src/transcoding/utils/constants.ts +0 -36
  134. package/src/utils/LRUCache.test.ts +0 -274
  135. 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
- };