Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion app/src/components/AudioPlayer/AudioPlayer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,21 @@ export function AudioPlayer() {
const wavesurfer = wavesurferRef.current;
if (!wavesurfer) return;

// Update store when time changes
// Update store when time changes, stop if past duration
wavesurfer.on('timeupdate', (time) => {
const dur = usePlayerStore.getState().duration;
if (dur > 0 && time >= dur) {
setCurrentTime(dur);
const loop = usePlayerStore.getState().isLooping;
if (loop) {
wavesurfer.seekTo(0);
wavesurfer.play();
} else {
wavesurfer.pause();
setIsPlaying(false);
}
return;
}
Comment on lines +160 to +174
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In WaveSurfer.js, if pause() is called from a timeupdatecallback when currentTime >= duration, is thefinish event still guaranteed to fire? Please cite docs/issues for the current major version.

💡 Result:

No. In the current major version (WaveSurfer.js v7), the finish event is emitted only in the handler for the media element’s ended event—not when you call pause() yourself. So if you call pause() from a timeupdate callback as soon as currentTime >= duration, you can prevent the underlying ended event from occurring, and therefore finish is not guaranteed to fire. [2]

Docs describe finish as “When the audio finishes playing,” but do not state it will fire if you programmatically pause at (or near) the end. [1]

Sources: WaveSurfer events docs [1], WaveSurfer v7 source (ended ⇒ emit finish) [2].


🏁 Script executed:

# Inspect the AudioPlayer file to understand the full context
# First, check file size to ensure safe reading
wc -l app/src/components/AudioPlayer/AudioPlayer.tsx

Repository: jamiepine/voicebox

Length of output: 112


🏁 Script executed:

# Read the timeupdate handler section (lines 160-174) with context
sed -n '155,180p' app/src/components/AudioPlayer/AudioPlayer.tsx

Repository: jamiepine/voicebox

Length of output: 831


🏁 Script executed:

# Read the finish handler section (lines 410-440) with context
sed -n '410,440p' app/src/components/AudioPlayer/AudioPlayer.tsx

Repository: jamiepine/voicebox

Length of output: 969


Move onFinish callback invocation to timeupdate handler or consolidate all terminal logic in the finish handler.

The timeupdate handler pauses at the end of track (lines 169), which prevents the media element's ended event from firing. Since WaveSurfer.js v7 emits the finish event only when ended fires, the finish handler (lines 416-430) is never reached, making the onFinish callback unreachable.

Either:

  1. Invoke onFinish from the timeupdate handler alongside the pause, or
  2. Remove pause/loop logic from timeupdate and let the media naturally fire endedfinishonFinish.

Option 2 is cleaner and keeps terminal behavior in one place.

Proposed fix (Option 2)
-      // Update store when time changes, stop if past duration
+      // Update store when time changes; clamp to duration.
+      // End-of-track loop/stop/callback behavior is handled in `finish`.
       wavesurfer.on('timeupdate', (time) => {
         const dur = usePlayerStore.getState().duration;
-        if (dur > 0 && time >= dur) {
-          setCurrentTime(dur);
-          const loop = usePlayerStore.getState().isLooping;
-          if (loop) {
-            wavesurfer.seekTo(0);
-            wavesurfer.play();
-          } else {
-            wavesurfer.pause();
-            setIsPlaying(false);
-          }
-          return;
-        }
-        setCurrentTime(time);
+        setCurrentTime(dur > 0 ? Math.min(time, dur) : time);
       });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Update store when time changes, stop if past duration
wavesurfer.on('timeupdate', (time) => {
const dur = usePlayerStore.getState().duration;
if (dur > 0 && time >= dur) {
setCurrentTime(dur);
const loop = usePlayerStore.getState().isLooping;
if (loop) {
wavesurfer.seekTo(0);
wavesurfer.play();
} else {
wavesurfer.pause();
setIsPlaying(false);
}
return;
}
// Update store when time changes; clamp to duration.
// End-of-track loop/stop/callback behavior is handled in `finish`.
wavesurfer.on('timeupdate', (time) => {
const dur = usePlayerStore.getState().duration;
setCurrentTime(dur > 0 ? Math.min(time, dur) : time);
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/components/AudioPlayer/AudioPlayer.tsx` around lines 160 - 174, The
timeupdate handler in wavesurfer.on('timeupdate') is pausing playback when time
>= duration which prevents the media 'ended' event and thus WaveSurfer's
'finish' handler (and your onFinish callback) from running; remove the terminal
pause/loop logic from the timeupdate handler (the block that calls
wavesurfer.pause(), wavesurfer.seekTo(0)/play when isLooping,
setIsPlaying(false), and the early return) and instead let the media naturally
reach 'ended' so the existing wavesurfer.on('finish') handler can run and invoke
onFinish; keep only non-terminal updates (e.g., setCurrentTime) in the
timeupdate handler and rely on the isLooping/finish logic inside the finish
event handler to handle looping and finalization.

setCurrentTime(time);
});

Expand Down
Loading