feat(03-01): implement UAT-003 fix for seek while paused

Decision: Implement fix (user-approved)

Implemented state machine workaround for jamClient limitation where
SessionTrackSeekMs() doesn't register when track is paused.

Fix approach:
- Verify position after seek attempt
- If paused and position unchanged, store in pendingSeekPositionRef
- Before resuming playback:
  1. SessionStopPlay() to reset state
  2. SessionTrackSeekMs() to apply stored position
  3. SessionStartPlay() to resume

Result: Seek now works consistently whether playing or paused.

Cleaned up diagnostic logging for production use.
This commit is contained in:
Nuwan 2026-01-14 15:47:34 +05:30
parent e19051f426
commit 5797ef2fdf
1 changed files with 19 additions and 45 deletions

View File

@ -132,12 +132,11 @@ const JKSessionBackingTrackPlayer = ({
const handlePlay = async () => {
try {
console.log('[BTP-UAT003] handlePlay:', {
console.log('[BTP] handlePlay:', {
isPlaying,
currentPositionMs,
durationMs,
hasPendingSeek: pendingSeekPositionRef.current !== null,
pendingSeekPosition: pendingSeekPositionRef.current
hasPendingSeek: pendingSeekPositionRef.current !== null
});
if (isPlaying) {
@ -146,16 +145,13 @@ const JKSessionBackingTrackPlayer = ({
setIsPlaying(false);
console.log('[BTP] Paused');
} else {
// UAT-003 Investigation Approach 2: Apply pending seek position before resuming
// UAT-003 Fix: Apply pending seek position before resuming
if (pendingSeekPositionRef.current !== null) {
console.log('[BTP-UAT003] Applying pending seek position before play:', pendingSeekPositionRef.current);
console.log('[BTP] Applying pending seek position:', pendingSeekPositionRef.current);
// Try stopping first, then seeking, then playing
// Stop, seek, then play
await jamClient.SessionStopPlay();
console.log('[BTP-UAT003] Stop completed for pending seek');
await jamClient.SessionTrackSeekMs(pendingSeekPositionRef.current);
console.log('[BTP-UAT003] Seek to pending position completed');
// Update UI state
setCurrentPositionMs(pendingSeekPositionRef.current);
@ -268,22 +264,17 @@ const JKSessionBackingTrackPlayer = ({
};
/*
* UAT-003 Investigation: Seek while paused limitation
* UAT-003 Fix: Seek while paused limitation workaround
*
* Problem: When track is paused and user drags slider to new position, the seek doesn't
* register with jamClient. On resume, audio plays from old position.
* Problem: jamClient.SessionTrackSeekMs() doesn't register when track is paused.
*
* Approaches tested:
* 1. Verify position after SessionTrackSeekMs() call - confirms seek doesn't take effect when paused
* 2. State machine approach - store pending seek position, apply before resume with:
* - SessionStopPlay() to reset state
* - SessionTrackSeekMs() to seek to stored position
* - SessionStartPlay() to resume playback
* Solution: State machine approach
* - When seeking while paused, verify if position changed
* - If not, store pending seek position in pendingSeekPositionRef
* - Before resuming playback, apply pending seek with:
* SessionStopPlay() SessionTrackSeekMs() SessionStartPlay()
*
* This implementation combines both approaches:
* - Diagnostic logging to confirm the issue
* - Pending seek position stored in pendingSeekPositionRef
* - Applied in handlePlay() before resuming playback
* This provides consistent UX: seek works whether playing or paused.
*
* See: .planning/phases/02-backing-track-seek-controls/02-01-ISSUES.md (UAT-003)
*/
@ -291,47 +282,30 @@ const JKSessionBackingTrackPlayer = ({
const seekPositionMs = parseInt(e.target.value);
try {
console.log('[BTP-UAT003] handleSeek BEFORE:', {
seekPositionMs,
durationMs,
isPlaying,
currentPositionMs,
isPaused: !isPlaying
});
console.log('[BTP] handleSeek:', { seekPositionMs, isPlaying });
// Update local state immediately for responsive UI
setCurrentPositionMs(seekPositionMs);
setCurrentTime(formatTime(seekPositionMs));
// UAT-003 Investigation: Check if track is playing before seek
const trackIsPlayingBefore = await jamClient.isSessionTrackPlaying();
console.log('[BTP-UAT003] Track playing state before seek:', trackIsPlayingBefore);
// Seek the native client to the new position
await jamClient.SessionTrackSeekMs(seekPositionMs);
console.log('[BTP-UAT003] SessionTrackSeekMs called with:', seekPositionMs);
// UAT-003 Investigation: Verify position after seek
// UAT-003: Verify position after seek (especially when paused)
const positionAfterSeek = await jamClient.SessionCurrrentPlayPosMs();
const parsedPosition = parseInt(positionAfterSeek, 10) || 0;
console.log('[BTP-UAT003] Position after seek:', {
returned: positionAfterSeek,
parsed: parsedPosition,
expected: seekPositionMs,
matched: parsedPosition === seekPositionMs
});
// UAT-003 Investigation Approach 1: If paused and position didn't change, store for later
// If paused and position didn't change, store for application on resume
if (!isPlaying && parsedPosition !== seekPositionMs) {
console.warn('[BTP-UAT003] Seek while paused did not take effect. Storing pending position.');
console.log('[BTP] Seek while paused - storing pending position:', seekPositionMs);
pendingSeekPositionRef.current = seekPositionMs;
} else {
pendingSeekPositionRef.current = null;
}
console.log('[BTP-UAT003] Seek completed');
console.log('[BTP] Seek completed');
} catch (error) {
console.error('[BTP-UAT003] Error seeking:', error);
console.error('[BTP] Error seeking:', error);
}
};