From 294b3f19c309bc377217875640e3caf5121c46ec Mon Sep 17 00:00:00 2001 From: Uruk Date: Wed, 14 Jan 2026 20:13:49 +0100 Subject: [PATCH] feat: add timeout management for playback to prevent race conditions --- components/video-player/controls/Controls.tsx | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/components/video-player/controls/Controls.tsx b/components/video-player/controls/Controls.tsx index aa2e1a84..278343fe 100644 --- a/components/video-player/controls/Controls.tsx +++ b/components/video-player/controls/Controls.tsx @@ -4,7 +4,7 @@ import type { MediaSourceInfo, } from "@jellyfin/sdk/lib/generated-client"; import { useLocalSearchParams } from "expo-router"; -import { type FC, useCallback, useEffect, useState } from "react"; +import { type FC, useCallback, useEffect, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { StyleSheet, useWindowDimensions, View } from "react-native"; import Animated, { @@ -111,6 +111,18 @@ export const Controls: FC = ({ const [episodeView, setEpisodeView] = useState(false); const [showAudioSlider, setShowAudioSlider] = useState(false); + // Ref to track pending play timeout for cleanup and cancellation + const playTimeoutRef = useRef | null>(null); + + // Clean up timeout on unmount + useEffect(() => { + return () => { + if (playTimeoutRef.current) { + clearTimeout(playTimeoutRef.current); + } + }; + }, []); + const { height: screenHeight, width: screenWidth } = useWindowDimensions(); const { previousItem, nextItem } = usePlaybackManager({ item, @@ -317,11 +329,16 @@ export const Controls: FC = ({ // Includes 200ms delay to allow seek operation to complete before resuming playback const seekMs = useCallback( (timeInSeconds: number) => { + // Cancel any pending play call to avoid race conditions + if (playTimeoutRef.current) { + clearTimeout(playTimeoutRef.current); + } seek(timeInSeconds * 1000); // Brief delay ensures the seek operation completes before resuming playback // Without this, playback may resume from the old position - setTimeout(() => { + playTimeoutRef.current = setTimeout(() => { play(); + playTimeoutRef.current = null; }, 200); }, [seek, play],