videoplayer fix

This commit is contained in:
tapframe 2025-09-19 14:46:24 +05:30
parent 877a4c5dc6
commit c710a173f2
2 changed files with 82 additions and 1 deletions

View file

@ -276,6 +276,9 @@ const AndroidVideoPlayer: React.FC = () => {
const loadStartAtRef = useRef<number | null>(null);
const firstFrameAtRef = useRef<number | null>(null);
// iOS playback state tracking for system interruptions
const wasPlayingBeforeIOSInterruptionRef = useRef<boolean>(false);
// Pause overlay state
const [showPauseOverlay, setShowPauseOverlay] = useState(false);
const pauseOverlayTimerRef = useRef<NodeJS.Timeout | null>(null);
@ -615,13 +618,34 @@ const AndroidVideoPlayer: React.FC = () => {
const onAppStateChange = (state: string) => {
if (state === 'active') {
enableImmersiveMode();
// On iOS, if we were playing before system interruption and the app becomes active again,
// ensure playback resumes (handles status bar pull-down case)
if (Platform.OS === 'ios' && wasPlayingBeforeIOSInterruptionRef.current && isPlayerReady) {
logger.log('[AndroidVideoPlayer] iOS app active - resuming playback after system interruption');
// Small delay to allow system UI to settle
setTimeout(() => {
if (isMounted.current && wasPlayingBeforeIOSInterruptionRef.current) {
setPaused(false); // Resume playback
wasPlayingBeforeIOSInterruptionRef.current = false; // Reset flag
}
}, 300); // Slightly longer delay for iOS
}
} else if (state === 'background' || state === 'inactive') {
// On iOS, when app goes inactive (like status bar pull), track if we were playing
if (Platform.OS === 'ios') {
wasPlayingBeforeIOSInterruptionRef.current = !paused;
if (!paused) {
logger.log('[AndroidVideoPlayer] iOS app inactive - tracking playing state for resume');
setPaused(true);
}
}
}
};
const sub = AppState.addEventListener('change', onAppStateChange);
return () => {
sub.remove();
};
}, []);
}, [paused, isPlayerReady]);
const startOpeningAnimation = () => {
// Logo entrance animation - optimized for faster appearance

View file

@ -226,6 +226,11 @@ const VideoPlayer: React.FC = () => {
const controlsTimeout = useRef<NodeJS.Timeout | null>(null);
const [isSyncingBeforeClose, setIsSyncingBeforeClose] = useState(false);
// Silent startup-timeout retry state
const startupRetryCountRef = useRef(0);
const startupRetryTimerRef = useRef<NodeJS.Timeout | null>(null);
const MAX_STARTUP_RETRIES = 3;
// Pause overlay state
const [showPauseOverlay, setShowPauseOverlay] = useState(false);
const pauseOverlayTimerRef = useRef<NodeJS.Timeout | null>(null);
@ -979,6 +984,12 @@ const VideoPlayer: React.FC = () => {
if (DEBUG_MODE) {
logger.log('[VideoPlayer] Video loaded:', data);
}
// Clear any pending startup silent retry timers and counters on success
if (startupRetryTimerRef.current) {
clearTimeout(startupRetryTimerRef.current);
startupRetryTimerRef.current = null;
}
startupRetryCountRef.current = 0;
if (!isMounted.current) {
logger.warn('[VideoPlayer] Component unmounted, skipping onLoad');
return;
@ -1374,6 +1385,48 @@ const VideoPlayer: React.FC = () => {
try {
logger.error('[VideoPlayer] Playback Error:', error);
// Detect KSPlayer startup timeout and silently retry without UI
const errText = typeof error === 'string'
? error
: (error?.message || error?.error?.message || error?.title || '');
const isStartupTimeout = /timeout/i.test(errText) && /stream.*ready/i.test(errText);
if (isStartupTimeout && !isVideoLoaded) {
// Suppress any error modal and retry silently
if (errorTimeoutRef.current) {
clearTimeout(errorTimeoutRef.current);
errorTimeoutRef.current = null;
}
setShowErrorModal(false);
const attempt = startupRetryCountRef.current;
if (attempt < MAX_STARTUP_RETRIES) {
const backoffMs = [4000, 8000, 12000][attempt] ?? 8000;
startupRetryCountRef.current = attempt + 1;
logger.warn(`[VideoPlayer] Startup timeout; retrying (${attempt + 1}/${MAX_STARTUP_RETRIES}) in ${backoffMs}ms`);
if (startupRetryTimerRef.current) {
clearTimeout(startupRetryTimerRef.current);
}
startupRetryTimerRef.current = setTimeout(() => {
if (!ksPlayerRef.current) return;
try {
// Reload the same source silently using native bridge
ksPlayerRef.current.setSource({
uri: currentStreamUrl,
headers: headers && Object.keys(headers).length > 0 ? headers : undefined
});
// Ensure playback resumes if not paused
ksPlayerRef.current.setPaused(paused);
logger.log('[VideoPlayer] Retried source load via KSPlayer.setSource');
} catch (e) {
logger.error('[VideoPlayer] Error during silent retry setSource:', e);
}
}, backoffMs);
return; // Exit handler; do not show UI
}
logger.error('[VideoPlayer] Max startup retries reached; proceeding to normal error handling');
}
// Check for audio codec errors (TrueHD, DTS, Dolby, etc.)
const isAudioCodecError =
(error?.message && /(trhd|truehd|true\s?hd|dts|dolby|atmos|e-ac3|ac3)/i.test(error.message)) ||
@ -2063,6 +2116,10 @@ const VideoPlayer: React.FC = () => {
if (brightnessOverlayTimeout.current) {
clearTimeout(brightnessOverlayTimeout.current);
}
if (startupRetryTimerRef.current) {
clearTimeout(startupRetryTimerRef.current);
startupRetryTimerRef.current = null;
}
};
}, []);