@wavv/ui 2.4.15-alpha.1 → 2.4.15-alpha.3
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/build/components/Audio.js +71 -8
- package/package.json +1 -1
|
@@ -15,7 +15,12 @@ const Audio = ({ src, title, width, infoPosition, centerAlignContent, small, tin
|
|
|
15
15
|
const audioRef = useRef(null);
|
|
16
16
|
const isSeekingRef = useRef(false);
|
|
17
17
|
const seekedTimeoutRef = useRef(null);
|
|
18
|
+
const lastRequestedSeekSecondsRef = useRef(null);
|
|
19
|
+
const seekVerifyRetryCountRef = useRef(0);
|
|
20
|
+
const seekVerifyTimeoutRef = useRef(null);
|
|
18
21
|
const END_EPSILON_SECONDS = 0.75;
|
|
22
|
+
const SEEK_TOLERANCE_SECONDS = 0.5;
|
|
23
|
+
const MAX_SEEK_VERIFY_RETRIES = 3;
|
|
19
24
|
const { height = 30, showHoverTime = false, playedColor, unplayedColor } = 'object' == typeof waveform ? waveform : {};
|
|
20
25
|
const play = ()=>{
|
|
21
26
|
const { current: audio } = audioRef;
|
|
@@ -35,9 +40,21 @@ const Audio = ({ src, title, width, infoPosition, centerAlignContent, small, tin
|
|
|
35
40
|
const { current: audio } = audioRef;
|
|
36
41
|
return audio ? !audio.paused : false;
|
|
37
42
|
};
|
|
43
|
+
const isNearEnd = (sec)=>totalDuration > 0 && totalDuration - sec <= END_EPSILON_SECONDS;
|
|
38
44
|
const togglePlayPause = ()=>{
|
|
39
45
|
if (isPlaying) pause();
|
|
40
|
-
else
|
|
46
|
+
else {
|
|
47
|
+
if (isNearEnd(progress)) {
|
|
48
|
+
lastRequestedSeekSecondsRef.current = null;
|
|
49
|
+
setProgress(0);
|
|
50
|
+
setDisplayCurrent('0:00');
|
|
51
|
+
const { current: audio } = audioRef;
|
|
52
|
+
if (audio) try {
|
|
53
|
+
audio.currentTime = 0;
|
|
54
|
+
} catch (_e) {}
|
|
55
|
+
}
|
|
56
|
+
play();
|
|
57
|
+
}
|
|
41
58
|
};
|
|
42
59
|
useImperativeHandle(ref, ()=>({
|
|
43
60
|
play,
|
|
@@ -57,8 +74,19 @@ const Audio = ({ src, title, width, infoPosition, centerAlignContent, small, tin
|
|
|
57
74
|
if (onPause) onPause(event);
|
|
58
75
|
};
|
|
59
76
|
const handleAudioStop = (event)=>{
|
|
77
|
+
lastRequestedSeekSecondsRef.current = null;
|
|
60
78
|
setIsPlaying(false);
|
|
61
79
|
setIsCleared(true);
|
|
80
|
+
setProgress(0);
|
|
81
|
+
setDisplayCurrent('0:00');
|
|
82
|
+
const { current: audio } = audioRef;
|
|
83
|
+
if (audio) try {
|
|
84
|
+
audio.currentTime = 0;
|
|
85
|
+
window.setTimeout(()=>{
|
|
86
|
+
const ct = audio.currentTime ?? 0;
|
|
87
|
+
if (ct > 0.5) audio.currentTime = 0;
|
|
88
|
+
}, 120);
|
|
89
|
+
} catch (_e) {}
|
|
62
90
|
if (onStop) onStop(event);
|
|
63
91
|
};
|
|
64
92
|
const handleAudioError = ()=>{
|
|
@@ -93,6 +121,7 @@ const Audio = ({ src, title, width, infoPosition, centerAlignContent, small, tin
|
|
|
93
121
|
]);
|
|
94
122
|
useEffect(()=>()=>{
|
|
95
123
|
if (seekedTimeoutRef.current) clearTimeout(seekedTimeoutRef.current);
|
|
124
|
+
if (seekVerifyTimeoutRef.current) clearTimeout(seekVerifyTimeoutRef.current);
|
|
96
125
|
}, []);
|
|
97
126
|
const formatTime = (time)=>{
|
|
98
127
|
if (!Number.isFinite(time) || time < 0) return '0:00';
|
|
@@ -100,17 +129,41 @@ const Audio = ({ src, title, width, infoPosition, centerAlignContent, small, tin
|
|
|
100
129
|
const secs = Math.floor(time % 60);
|
|
101
130
|
return `${mins}:${secs.toString().padStart(2, '0')}`;
|
|
102
131
|
};
|
|
132
|
+
const performSeek = (targetSeconds)=>{
|
|
133
|
+
const { current: audio } = audioRef;
|
|
134
|
+
if (!audio || !Number.isFinite(targetSeconds)) return false;
|
|
135
|
+
try {
|
|
136
|
+
audio.currentTime = targetSeconds;
|
|
137
|
+
return true;
|
|
138
|
+
} catch (_e) {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
};
|
|
103
142
|
const updateProgress = ()=>{
|
|
104
143
|
if (isSeekingRef.current) return;
|
|
105
144
|
const { current: audio } = audioRef;
|
|
106
|
-
if (audio)
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
145
|
+
if (!audio) return;
|
|
146
|
+
const currentTime = audio.currentTime;
|
|
147
|
+
if (!Number.isFinite(currentTime) || currentTime < 0) return;
|
|
148
|
+
const requestedSeek = lastRequestedSeekSecondsRef.current;
|
|
149
|
+
if ('number' == typeof requestedSeek) {
|
|
150
|
+
const drift = Math.abs(currentTime - requestedSeek);
|
|
151
|
+
if (drift > SEEK_TOLERANCE_SECONDS) {
|
|
152
|
+
if (seekVerifyRetryCountRef.current < MAX_SEEK_VERIFY_RETRIES) {
|
|
153
|
+
seekVerifyRetryCountRef.current += 1;
|
|
154
|
+
performSeek(requestedSeek);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
lastRequestedSeekSecondsRef.current = null;
|
|
158
|
+
seekVerifyRetryCountRef.current = 0;
|
|
159
|
+
return;
|
|
112
160
|
}
|
|
161
|
+
lastRequestedSeekSecondsRef.current = null;
|
|
162
|
+
seekVerifyRetryCountRef.current = 0;
|
|
113
163
|
}
|
|
164
|
+
const current = formatTime(currentTime);
|
|
165
|
+
setProgress(currentTime);
|
|
166
|
+
setDisplayCurrent(current);
|
|
114
167
|
};
|
|
115
168
|
const handleLoad = ()=>{
|
|
116
169
|
const { current: audio } = audioRef;
|
|
@@ -148,6 +201,8 @@ const Audio = ({ src, title, width, infoPosition, centerAlignContent, small, tin
|
|
|
148
201
|
const maxSeekTarget = Math.max(0, totalDuration - END_EPSILON_SECONDS);
|
|
149
202
|
const clamped = Math.min(Math.max(sliderPosition, 0), maxSeekTarget);
|
|
150
203
|
isSeekingRef.current = true;
|
|
204
|
+
lastRequestedSeekSecondsRef.current = clamped;
|
|
205
|
+
seekVerifyRetryCountRef.current = 0;
|
|
151
206
|
if (seekedTimeoutRef.current) clearTimeout(seekedTimeoutRef.current);
|
|
152
207
|
seekedTimeoutRef.current = setTimeout(()=>{
|
|
153
208
|
isSeekingRef.current = false;
|
|
@@ -155,7 +210,15 @@ const Audio = ({ src, title, width, infoPosition, centerAlignContent, small, tin
|
|
|
155
210
|
}, 300);
|
|
156
211
|
setProgress(clamped);
|
|
157
212
|
setDisplayCurrent(formatTime(clamped));
|
|
158
|
-
|
|
213
|
+
performSeek(clamped);
|
|
214
|
+
if (seekVerifyTimeoutRef.current) clearTimeout(seekVerifyTimeoutRef.current);
|
|
215
|
+
seekVerifyTimeoutRef.current = window.setTimeout(()=>{
|
|
216
|
+
seekVerifyTimeoutRef.current = null;
|
|
217
|
+
const { current: audio } = audioRef;
|
|
218
|
+
if (!audio || null === lastRequestedSeekSecondsRef.current) return;
|
|
219
|
+
const ct = audio.currentTime ?? 0;
|
|
220
|
+
if (Math.abs(ct - clamped) > SEEK_TOLERANCE_SECONDS) performSeek(clamped);
|
|
221
|
+
}, 150);
|
|
159
222
|
};
|
|
160
223
|
const hideSlider = isCleared && hideSliderOnStop;
|
|
161
224
|
const hideTime = isCleared && hideTimeOnStop;
|