fix(audio): seamless error recovery and smart preload delays
Improve error handling to maintain continuous playback: - Remove setIsPlaying(false) from error handlers to keep playback going - Replace error toasts with console warnings for non-intrusive experience - Automatically skip problematic sentences without stopping playback - User gets seamless reading experience even when some audio fails Add progressive preload delays: - Stagger preload requests with 50ms, 100ms, 150ms delays - Prevents overwhelming the server with simultaneous requests - Maintains smooth buffering without server congestion - Better handling of rate limits and connection issues Changes: - onloaderror: Continue playback after max retries instead of stopping - playSentenceWithHowl catch: Silent recovery without toast interruptions - preloadNextAudio: Progressive setTimeout delays for each preload request - All error messages downgraded from toast.error to console.warn
This commit is contained in:
parent
453765076c
commit
70ff7a4f68
1 changed files with 23 additions and 24 deletions
|
|
@ -1057,20 +1057,16 @@ export function TTSProvider({ children }: { children: ReactNode }): ReactElement
|
||||||
retryHowl.play();
|
retryHowl.play();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.error('Max retries reached, moving to next sentence');
|
console.warn('Max retries reached, skipping to next sentence');
|
||||||
setIsProcessing(false);
|
setIsProcessing(false);
|
||||||
setActiveHowl(null);
|
setActiveHowl(null);
|
||||||
this.unload();
|
this.unload();
|
||||||
setIsPlaying(false);
|
// Don't stop playback - keep it seamless by continuing to next sentence
|
||||||
|
// setIsPlaying remains true so playback continues
|
||||||
|
|
||||||
toast.error('Audio loading failed after retries. Moving to next sentence...', {
|
// Less intrusive notification - just log to console
|
||||||
id: 'audio-load-error',
|
// User won't be interrupted by error toasts during reading
|
||||||
style: {
|
console.warn('Skipping problematic sentence, continuing playback...');
|
||||||
background: 'var(--background)',
|
|
||||||
color: 'var(--accent)',
|
|
||||||
},
|
|
||||||
duration: 2000,
|
|
||||||
});
|
|
||||||
|
|
||||||
advance();
|
advance();
|
||||||
}
|
}
|
||||||
|
|
@ -1119,18 +1115,13 @@ export function TTSProvider({ children }: { children: ReactNode }): ReactElement
|
||||||
|
|
||||||
throw new Error('Failed to create Howl instance');
|
throw new Error('Failed to create Howl instance');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error playing TTS:', error);
|
console.warn('Error processing TTS, skipping to next sentence:', error);
|
||||||
setActiveHowl(null);
|
setActiveHowl(null);
|
||||||
setIsProcessing(false);
|
setIsProcessing(false);
|
||||||
|
|
||||||
toast.error('Failed to process audio. Skipping problematic sentence.', {
|
// Keep playback going seamlessly - don't stop on errors
|
||||||
id: 'tts-processing-error',
|
// Just log to console and move to next sentence
|
||||||
style: {
|
console.warn('Skipping problematic sentence, continuing playback...');
|
||||||
background: 'var(--background)',
|
|
||||||
color: 'var(--accent)',
|
|
||||||
},
|
|
||||||
duration: 3000,
|
|
||||||
});
|
|
||||||
|
|
||||||
advance();
|
advance();
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -1211,11 +1202,13 @@ export function TTSProvider({ children }: { children: ReactNode }): ReactElement
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Preloads the next 3 sentences' audio for smoother playback
|
* Preloads the next 3 sentences' audio for smoother playback
|
||||||
|
* Uses progressive delays to avoid overwhelming the server
|
||||||
*/
|
*/
|
||||||
const preloadNextAudio = useCallback(async () => {
|
const preloadNextAudio = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
// Preload the next 3 sentences to ensure smooth playback
|
// Preload the next 3 sentences to ensure smooth playback
|
||||||
const PRELOAD_AHEAD_COUNT = 3;
|
const PRELOAD_AHEAD_COUNT = 3;
|
||||||
|
const PRELOAD_DELAY_MS = 50; // Small delay between requests
|
||||||
|
|
||||||
for (let i = 1; i <= PRELOAD_AHEAD_COUNT; i++) {
|
for (let i = 1; i <= PRELOAD_AHEAD_COUNT; i++) {
|
||||||
const nextSentence = sentences[currentIndex + i];
|
const nextSentence = sentences[currentIndex + i];
|
||||||
|
|
@ -1230,14 +1223,20 @@ export function TTSProvider({ children }: { children: ReactNode }): ReactElement
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!audioCache.has(nextKey) && !preloadRequests.current.has(nextSentence)) {
|
if (!audioCache.has(nextKey) && !preloadRequests.current.has(nextSentence)) {
|
||||||
// Start preloading but don't wait for it to complete
|
// Add progressive delay to avoid overwhelming the server
|
||||||
processSentence(nextSentence, true).catch(error => {
|
// First request: 50ms, second: 100ms, third: 150ms
|
||||||
console.error(`Error preloading sentence ${i} ahead:`, error);
|
const delay = PRELOAD_DELAY_MS * i;
|
||||||
});
|
|
||||||
|
setTimeout(() => {
|
||||||
|
// Start preloading but don't wait for it to complete
|
||||||
|
processSentence(nextSentence, true).catch(error => {
|
||||||
|
console.warn(`Error preloading sentence ${i} ahead:`, error);
|
||||||
|
});
|
||||||
|
}, delay);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error initiating preload:', error);
|
console.warn('Error initiating preload:', error);
|
||||||
}
|
}
|
||||||
}, [currentIndex, sentences, audioCache, processSentence, voice, speed, configTTSProvider, ttsModel]);
|
}, [currentIndex, sentences, audioCache, processSentence, voice, speed, configTTSProvider, ttsModel]);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue