diff --git a/local-scrapers-repo b/local-scrapers-repo index d2eb5507..d129a1d2 160000 --- a/local-scrapers-repo +++ b/local-scrapers-repo @@ -1 +1 @@ -Subproject commit d2eb5507001e031aaa5d258c390ec56ecfa8f9c8 +Subproject commit d129a1d2799397738c6e4848a6b80314a3326bd9 diff --git a/src/components/player/AndroidVideoPlayer.tsx b/src/components/player/AndroidVideoPlayer.tsx index b23b7586..1da989fb 100644 --- a/src/components/player/AndroidVideoPlayer.tsx +++ b/src/components/player/AndroidVideoPlayer.tsx @@ -1,5 +1,5 @@ import React, { useState, useRef, useEffect } from 'react'; -import { View, TouchableOpacity, Dimensions, Animated, ActivityIndicator, Platform, NativeModules, StatusBar, Text, Image, StyleSheet } from 'react-native'; +import { View, TouchableOpacity, Dimensions, Animated, ActivityIndicator, Platform, NativeModules, StatusBar, Text, Image, StyleSheet, Modal } from 'react-native'; import Video, { VideoRef, SelectedTrack, SelectedTrackType, BufferingStrategyType } from 'react-native-video'; import { useNavigation, useRoute, RouteProp } from '@react-navigation/native'; import { RootStackParamList } from '../../navigation/AppNavigator'; @@ -162,6 +162,9 @@ const AndroidVideoPlayer: React.FC = () => { const isMounted = useRef(true); const controlsTimeout = useRef(null); const [isSyncingBeforeClose, setIsSyncingBeforeClose] = useState(false); + const [showErrorModal, setShowErrorModal] = useState(false); + const [errorDetails, setErrorDetails] = useState(''); + const errorTimeoutRef = useRef(null); // Get metadata to access logo (only if we have a valid id) const shouldLoadMetadata = Boolean(id && type); const metadataResult = useMetadata({ @@ -709,6 +712,44 @@ const AndroidVideoPlayer: React.FC = () => { const handleError = (error: any) => { logger.error('AndroidVideoPlayer error: ', error); + + // Format error details for user display + let errorMessage = 'An unknown error occurred'; + if (error) { + if (typeof error === 'string') { + errorMessage = error; + } else if (error.message) { + errorMessage = error.message; + } else if (error.error && error.error.message) { + errorMessage = error.error.message; + } else if (error.code) { + errorMessage = `Error Code: ${error.code}`; + } else { + errorMessage = JSON.stringify(error, null, 2); + } + } + + setErrorDetails(errorMessage); + setShowErrorModal(true); + + // Clear any existing timeout + if (errorTimeoutRef.current) { + clearTimeout(errorTimeoutRef.current); + } + + // Auto-exit after 5 seconds if user doesn't dismiss + errorTimeoutRef.current = setTimeout(() => { + handleErrorExit(); + }, 5000); + }; + + const handleErrorExit = () => { + if (errorTimeoutRef.current) { + clearTimeout(errorTimeoutRef.current); + errorTimeoutRef.current = null; + } + setShowErrorModal(false); + handleClose(); }; const onBuffer = (data: any) => { @@ -851,6 +892,9 @@ const AndroidVideoPlayer: React.FC = () => { if (seekDebounceTimer.current) { clearTimeout(seekDebounceTimer.current); } + if (errorTimeoutRef.current) { + clearTimeout(errorTimeoutRef.current); + } }; }, []); @@ -1254,6 +1298,100 @@ const AndroidVideoPlayer: React.FC = () => { onSelectStream={handleSelectStream} isChangingSource={isChangingSource} /> + + {/* Error Modal */} + + + + + + Playback Error + + + + + + The video player encountered an error and cannot continue playback: + + + {errorDetails} + + + + + Exit Player + + + + This dialog will auto-close in 5 seconds + + + ); }; diff --git a/src/components/player/VideoPlayer.tsx b/src/components/player/VideoPlayer.tsx index 7a3606ee..100c2f1d 100644 --- a/src/components/player/VideoPlayer.tsx +++ b/src/components/player/VideoPlayer.tsx @@ -1,5 +1,5 @@ import React, { useState, useRef, useEffect } from 'react'; -import { View, TouchableOpacity, Dimensions, Animated, ActivityIndicator, Platform, NativeModules, StatusBar, Text, Image, StyleSheet } from 'react-native'; +import { View, TouchableOpacity, Dimensions, Animated, ActivityIndicator, Platform, NativeModules, StatusBar, Text, Image, StyleSheet, Modal } from 'react-native'; import { VLCPlayer } from 'react-native-vlc-media-player'; import { useNavigation, useRoute, RouteProp } from '@react-navigation/native'; import { RootStackParamList, RootStackNavigationProp } from '../../navigation/AppNavigator'; @@ -167,6 +167,9 @@ const VideoPlayer: React.FC = () => { const [availableStreams, setAvailableStreams] = useState<{ [providerId: string]: { streams: any[]; addonName: string } }>(passedAvailableStreams || {}); const [currentStreamUrl, setCurrentStreamUrl] = useState(uri); const [isChangingSource, setIsChangingSource] = useState(false); + const [showErrorModal, setShowErrorModal] = useState(false); + const [errorDetails, setErrorDetails] = useState(''); + const errorTimeoutRef = useRef(null); const [pendingSeek, setPendingSeek] = useState<{ position: number; shouldPlay: boolean } | null>(null); const [currentQuality, setCurrentQuality] = useState(quality); const [currentStreamProvider, setCurrentStreamProvider] = useState(streamProvider); @@ -794,6 +797,44 @@ const VideoPlayer: React.FC = () => { const handleError = (error: any) => { logger.error('[VideoPlayer] Playback Error:', error); + + // Format error details for user display + let errorMessage = 'An unknown error occurred'; + if (error) { + if (typeof error === 'string') { + errorMessage = error; + } else if (error.message) { + errorMessage = error.message; + } else if (error.error && error.error.message) { + errorMessage = error.error.message; + } else if (error.code) { + errorMessage = `Error Code: ${error.code}`; + } else { + errorMessage = JSON.stringify(error, null, 2); + } + } + + setErrorDetails(errorMessage); + setShowErrorModal(true); + + // Clear any existing timeout + if (errorTimeoutRef.current) { + clearTimeout(errorTimeoutRef.current); + } + + // Auto-exit after 5 seconds if user doesn't dismiss + errorTimeoutRef.current = setTimeout(() => { + handleErrorExit(); + }, 5000); + }; + + const handleErrorExit = () => { + if (errorTimeoutRef.current) { + clearTimeout(errorTimeoutRef.current); + errorTimeoutRef.current = null; + } + setShowErrorModal(false); + handleClose(); }; const onBuffering = (event: any) => { @@ -929,6 +970,9 @@ const VideoPlayer: React.FC = () => { if (seekDebounceTimer.current) { clearTimeout(seekDebounceTimer.current); } + if (errorTimeoutRef.current) { + clearTimeout(errorTimeoutRef.current); + } }; }, []); @@ -1337,6 +1381,100 @@ const VideoPlayer: React.FC = () => { onSelectStream={handleSelectStream} isChangingSource={isChangingSource} /> + + {/* Error Modal */} + + + + + + Playback Error + + + + + + The video player encountered an error and cannot continue playback: + + + {errorDetails} + + + + + Exit Player + + + + This dialog will auto-close in 5 seconds + + + ); };