@wavv/ui 2.4.15-alpha.3 → 2.4.15-alpha.4

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.
@@ -46,6 +46,8 @@ type AudioProps = {
46
46
  playedColor?: string;
47
47
  /** Color for unplayed portion of waveform */
48
48
  unplayedColor?: string;
49
+ /** Force prefetch into blob for remote URLs to fix seeking (use when server range support is unreliable) */
50
+ forceBlobForSeeking?: boolean;
49
51
  };
50
52
  ref?: React.Ref<AudioRef>;
51
53
  onPlay?: (event: ChangeEvent<HTMLAudioElement>) => void;
@@ -12,7 +12,9 @@ const Audio = ({ src, title, width, infoPosition, centerAlignContent, small, tin
12
12
  const [totalDuration, setTotalDuration] = useState(0);
13
13
  const [displayCurrent, setDisplayCurrent] = useState('');
14
14
  const [displayDuration, setDisplayDuration] = useState('0:00');
15
+ const [playbackUrl, setPlaybackUrl] = useState(src);
15
16
  const audioRef = useRef(null);
17
+ const objectUrlRef = useRef(null);
16
18
  const isSeekingRef = useRef(false);
17
19
  const seekedTimeoutRef = useRef(null);
18
20
  const lastRequestedSeekSecondsRef = useRef(null);
@@ -21,7 +23,7 @@ const Audio = ({ src, title, width, infoPosition, centerAlignContent, small, tin
21
23
  const END_EPSILON_SECONDS = 0.75;
22
24
  const SEEK_TOLERANCE_SECONDS = 0.5;
23
25
  const MAX_SEEK_VERIFY_RETRIES = 3;
24
- const { height = 30, showHoverTime = false, playedColor, unplayedColor } = 'object' == typeof waveform ? waveform : {};
26
+ const { height = 30, showHoverTime = false, playedColor, unplayedColor, forceBlobForSeeking = false } = 'object' == typeof waveform ? waveform : {};
25
27
  const play = ()=>{
26
28
  const { current: audio } = audioRef;
27
29
  if (audio) audio.play().catch(()=>{
@@ -100,9 +102,54 @@ const Audio = ({ src, title, width, infoPosition, centerAlignContent, small, tin
100
102
  }, [
101
103
  isPlaying
102
104
  ]);
105
+ useEffect(()=>{
106
+ setPlaybackUrl(src);
107
+ if (objectUrlRef.current) {
108
+ try {
109
+ URL.revokeObjectURL(objectUrlRef.current);
110
+ } catch (_e) {}
111
+ objectUrlRef.current = null;
112
+ }
113
+ if (!waveform || !src || !src.startsWith('http')) return;
114
+ let cancelled = false;
115
+ const buildBlobUrl = async ()=>{
116
+ try {
117
+ const resp = await fetch(src, {
118
+ cache: 'force-cache'
119
+ });
120
+ const buf = await resp.arrayBuffer();
121
+ if (cancelled) return;
122
+ const useBlob = forceBlobForSeeking || 'bytes' !== resp.headers.get('accept-ranges');
123
+ if (useBlob) {
124
+ try {
125
+ if (objectUrlRef.current) URL.revokeObjectURL(objectUrlRef.current);
126
+ } catch (_e) {}
127
+ const blob = new Blob([
128
+ buf
129
+ ], {
130
+ type: resp.headers.get('content-type') || 'audio/mpeg'
131
+ });
132
+ const objectUrl = URL.createObjectURL(blob);
133
+ objectUrlRef.current = objectUrl;
134
+ if (!cancelled) setPlaybackUrl(objectUrl);
135
+ }
136
+ } catch (_e) {}
137
+ };
138
+ buildBlobUrl();
139
+ return ()=>{
140
+ cancelled = true;
141
+ try {
142
+ if (objectUrlRef.current) URL.revokeObjectURL(objectUrlRef.current);
143
+ } catch (_e) {}
144
+ };
145
+ }, [
146
+ src,
147
+ waveform,
148
+ forceBlobForSeeking
149
+ ]);
103
150
  useEffect(()=>{
104
151
  const { current: audio } = audioRef;
105
- if (audio && src) {
152
+ if (audio && playbackUrl) {
106
153
  audio.load();
107
154
  const checkDuration = setTimeout(()=>{
108
155
  if (audio.duration && Number.isFinite(audio.duration) && audio.duration > 0 && audio.duration !== 1 / 0) {
@@ -116,7 +163,7 @@ const Audio = ({ src, title, width, infoPosition, centerAlignContent, small, tin
116
163
  return ()=>clearTimeout(checkDuration);
117
164
  }
118
165
  }, [
119
- src,
166
+ playbackUrl,
120
167
  fallbackDuration
121
168
  ]);
122
169
  useEffect(()=>()=>{
@@ -249,7 +296,7 @@ const Audio = ({ src, title, width, infoPosition, centerAlignContent, small, tin
249
296
  alignData: infoPosition,
250
297
  children: [
251
298
  !hideSlider && (waveform ? /*#__PURE__*/ jsx(Waveform, {
252
- src: src,
299
+ src: playbackUrl,
253
300
  value: progress,
254
301
  maxValue: totalDuration,
255
302
  onChange: handleControlGrab,
@@ -281,7 +328,7 @@ const Audio = ({ src, title, width, infoPosition, centerAlignContent, small, tin
281
328
  'center' === infoPosition && timeData,
282
329
  /*#__PURE__*/ jsx("audio", {
283
330
  ref: audioRef,
284
- src: src,
331
+ src: playbackUrl,
285
332
  onPlay: handleAudioPlay,
286
333
  onPause: handleAudioPause,
287
334
  onEmptied: handleAudioStop,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wavv/ui",
3
- "version": "2.4.15-alpha.3",
3
+ "version": "2.4.15-alpha.4",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {