From ba93f1a27032a3f69d42b63554c028f6197eabb1 Mon Sep 17 00:00:00 2001 From: Nuwan Date: Wed, 14 Jan 2026 11:29:37 +0530 Subject: [PATCH] fix(02-01): handle seek to end edge case in backing track player UAT-004: When seeking to end while paused, then clicking play, the player would break (slider jumps to 0, no audio, play button stops working). Root cause: jamClient enters end-of-track state when position >= duration. Calling SessionStartPlay() without resetting causes undefined behavior. Solution: - Detect when position is at/near end (within 100ms) - Call SessionStopPlay() to reset jamClient state - Seek to position 0 - Then start playback normally Also added diagnostic logging to handlePlay, handleSeek, and polling to help debug playback state transitions. --- .../client/JKSessionBackingTrackPlayer.js | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/jam-ui/src/components/client/JKSessionBackingTrackPlayer.js b/jam-ui/src/components/client/JKSessionBackingTrackPlayer.js index 5bec25c41..7599a0fbb 100644 --- a/jam-ui/src/components/client/JKSessionBackingTrackPlayer.js +++ b/jam-ui/src/components/client/JKSessionBackingTrackPlayer.js @@ -85,6 +85,12 @@ const JKSessionBackingTrackPlayer = ({ const validPosition = parseInt(positionMs, 10) || 0; const validDuration = parseInt(durationInMs, 10) || 0; + // Check if track reached the end + if (validPosition >= validDuration && validDuration > 0) { + console.log('JKSessionBackingTrackPlayer: Track reached end, stopping playback'); + setIsPlaying(false); + } + setCurrentPositionMs(validPosition); setCurrentTime(formatTime(validPosition)); setDurationMs(validDuration); @@ -92,6 +98,7 @@ const JKSessionBackingTrackPlayer = ({ // Sync playing state if changed if (trackIsPlaying !== isPlaying) { + console.log('JKSessionBackingTrackPlayer: Playing state changed from', isPlaying, 'to', trackIsPlaying); setIsPlaying(trackIsPlaying); } } catch (error) { @@ -110,14 +117,34 @@ const JKSessionBackingTrackPlayer = ({ const handlePlay = async () => { try { + console.log('JKSessionBackingTrackPlayer handlePlay:', { + isPlaying, + currentPositionMs, + durationMs + }); + if (isPlaying) { // Pause await jamClient.SessionPausePlay(); setIsPlaying(false); + console.log('JKSessionBackingTrackPlayer: Paused'); } else { + // Check if we're at or very near the end (within 100ms) + if (currentPositionMs >= durationMs - 100 && durationMs > 0) { + console.log('JKSessionBackingTrackPlayer: At end of track, resetting to start before play'); + // Stop first to reset state + await jamClient.SessionStopPlay(); + // Seek to start + await jamClient.SessionTrackSeekMs(0); + // Update UI state + setCurrentPositionMs(0); + setCurrentTime('0:00'); + } + // Play (1 = normal playback mode) await jamClient.SessionStartPlay(1); setIsPlaying(true); + console.log('JKSessionBackingTrackPlayer: Playing'); } } catch (error) { console.error('Error toggling playback:', error); @@ -175,12 +202,21 @@ const JKSessionBackingTrackPlayer = ({ const seekPositionMs = parseInt(e.target.value); try { + console.log('JKSessionBackingTrackPlayer handleSeek:', { + seekPositionMs, + durationMs, + isPlaying, + currentPositionMs + }); + // Update local state immediately for responsive UI setCurrentPositionMs(seekPositionMs); setCurrentTime(formatTime(seekPositionMs)); // Seek the native client to the new position await jamClient.SessionTrackSeekMs(seekPositionMs); + + console.log('JKSessionBackingTrackPlayer: Seek completed'); } catch (error) { console.error('Error seeking:', error); }