Fix: Improves Chromecast casting experience

Fixes several issues and improves the overall Chromecast casting experience:

- Implements an AbortController for fetching item data to prevent race conditions.
- Syncs live progress in the mini player more accurately using elapsed real time.
- Prevents event propagation in the mini player's play/pause button.
- Ensures the disconnect callback in the connection menu is always called.
- Retries scrolling in the episode list on failure.
- Handles unmute failures gracefully in volume controls.
- Clamps seek positions to prevent exceeding duration.
- Fixes reporting playback start multiple times
- Improves segment calculation in `useChromecastSegments`
- Prevents race condition with `isPlaying` state in `Controls` component

Also includes minor UI and timing adjustments for a smoother user experience.
This commit is contained in:
Uruk
2026-02-08 15:23:01 +01:00
parent c243fbc0ba
commit 7c81c0ff33
14 changed files with 208 additions and 133 deletions

View File

@@ -124,6 +124,12 @@ export const Controls: FC<Props> = ({
// Ref to track pending play timeout for cleanup and cancellation
const playTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
// Mutable ref tracking isPlaying to avoid stale closures in seekMs timeout
const playingRef = useRef(isPlaying);
useEffect(() => {
playingRef.current = isPlaying;
}, [isPlaying]);
// Clean up timeout on unmount
useEffect(() => {
return () => {
@@ -346,15 +352,15 @@ export const Controls: FC<Props> = ({
seek(timeInSeconds * 1000);
// Brief delay ensures the seek operation completes before resuming playback
// Without this, playback may resume from the old position
// Only resume if currently playing to avoid overriding user pause
if (isPlaying) {
playTimeoutRef.current = setTimeout(() => {
// Read latest isPlaying from ref to avoid stale closure
playTimeoutRef.current = setTimeout(() => {
if (playingRef.current) {
play();
playTimeoutRef.current = null;
}, 200);
}
}
playTimeoutRef.current = null;
}, 200);
},
[seek, play, isPlaying],
[seek, play],
);
// Use unified segment skipper for all segment types