@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 &&
|
|
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
|
-
|
|
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:
|
|
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:
|
|
331
|
+
src: playbackUrl,
|
|
285
332
|
onPlay: handleAudioPlay,
|
|
286
333
|
onPause: handleAudioPause,
|
|
287
334
|
onEmptied: handleAudioStop,
|