mirror of
https://github.com/zimplexing/OrionTV.git
synced 2026-02-04 03:36:29 +08:00
- Add comprehensive SSL certificate error detection and automatic source switching - Implement smart video source fallback strategy with failed source tracking - Enhance video component with optimized event handlers and useCallback patterns - Add explicit playAsync() call in onLoad to improve auto-play reliability - Integrate performance monitoring with detailed logging throughout playback chain - Optimize Video component props with useMemo and custom useVideoHandlers hook - Add source matching fixes for fallback scenarios in DetailStore - Enhance error handling with user-friendly messages and recovery strategies
131 lines
4.5 KiB
TypeScript
131 lines
4.5 KiB
TypeScript
import { useCallback, RefObject, useMemo } from 'react';
|
||
import { Video, ResizeMode } from 'expo-av';
|
||
import Toast from 'react-native-toast-message';
|
||
import usePlayerStore from '@/stores/playerStore';
|
||
|
||
interface UseVideoHandlersProps {
|
||
videoRef: RefObject<Video>;
|
||
currentEpisode: { url: string; title: string } | undefined;
|
||
initialPosition: number;
|
||
introEndTime?: number;
|
||
playbackRate: number;
|
||
handlePlaybackStatusUpdate: (status: any) => void;
|
||
deviceType: string;
|
||
detail?: { poster?: string };
|
||
}
|
||
|
||
export const useVideoHandlers = ({
|
||
videoRef,
|
||
currentEpisode,
|
||
initialPosition,
|
||
introEndTime,
|
||
playbackRate,
|
||
handlePlaybackStatusUpdate,
|
||
deviceType,
|
||
detail,
|
||
}: UseVideoHandlersProps) => {
|
||
|
||
const onLoad = useCallback(async () => {
|
||
console.info(`[PERF] Video onLoad - video ready to play`);
|
||
|
||
try {
|
||
// 1. 先设置位置(如果需要)
|
||
const jumpPosition = initialPosition || introEndTime || 0;
|
||
if (jumpPosition > 0) {
|
||
console.info(`[PERF] Setting initial position to ${jumpPosition}ms`);
|
||
await videoRef.current?.setPositionAsync(jumpPosition);
|
||
}
|
||
|
||
// 2. 显式调用播放以确保自动播放
|
||
console.info(`[AUTOPLAY] Attempting to start playback after onLoad`);
|
||
await videoRef.current?.playAsync();
|
||
console.info(`[AUTOPLAY] Auto-play successful after onLoad`);
|
||
|
||
usePlayerStore.setState({ isLoading: false });
|
||
console.info(`[PERF] Video loading complete - isLoading set to false`);
|
||
} catch (error) {
|
||
console.warn(`[AUTOPLAY] Failed to auto-play after onLoad:`, error);
|
||
// 即使自动播放失败,也要设置加载完成状态
|
||
usePlayerStore.setState({ isLoading: false });
|
||
// 不显示错误提示,因为自动播放失败是常见且预期的情况
|
||
}
|
||
}, [videoRef, initialPosition, introEndTime]);
|
||
|
||
const onLoadStart = useCallback(() => {
|
||
if (!currentEpisode?.url) return;
|
||
|
||
console.info(`[PERF] Video onLoadStart - starting to load video: ${currentEpisode.url.substring(0, 100)}...`);
|
||
usePlayerStore.setState({ isLoading: true });
|
||
}, [currentEpisode?.url]);
|
||
|
||
const onError = useCallback((error: any) => {
|
||
if (!currentEpisode?.url) return;
|
||
|
||
console.error(`[ERROR] Video playback error:`, error);
|
||
|
||
// 检测SSL证书错误和其他网络错误
|
||
const errorString = (error as any)?.error?.toString() || error?.toString() || '';
|
||
const isSSLError = errorString.includes('SSLHandshakeException') ||
|
||
errorString.includes('CertPathValidatorException') ||
|
||
errorString.includes('Trust anchor for certification path not found');
|
||
const isNetworkError = errorString.includes('HttpDataSourceException') ||
|
||
errorString.includes('IOException') ||
|
||
errorString.includes('SocketTimeoutException');
|
||
|
||
if (isSSLError) {
|
||
console.error(`[SSL_ERROR] SSL certificate validation failed for URL: ${currentEpisode.url}`);
|
||
Toast.show({
|
||
type: "error",
|
||
text1: "SSL证书错误,正在尝试其他播放源...",
|
||
text2: "请稍候"
|
||
});
|
||
usePlayerStore.getState().handleVideoError('ssl', currentEpisode.url);
|
||
} else if (isNetworkError) {
|
||
console.error(`[NETWORK_ERROR] Network connection failed for URL: ${currentEpisode.url}`);
|
||
Toast.show({
|
||
type: "error",
|
||
text1: "网络连接失败,正在尝试其他播放源...",
|
||
text2: "请稍候"
|
||
});
|
||
usePlayerStore.getState().handleVideoError('network', currentEpisode.url);
|
||
} else {
|
||
console.error(`[VIDEO_ERROR] Other video error for URL: ${currentEpisode.url}`);
|
||
Toast.show({
|
||
type: "error",
|
||
text1: "视频播放失败,正在尝试其他播放源...",
|
||
text2: "请稍候"
|
||
});
|
||
usePlayerStore.getState().handleVideoError('other', currentEpisode.url);
|
||
}
|
||
}, [currentEpisode?.url]);
|
||
|
||
// 优化的Video组件props
|
||
const videoProps = useMemo(() => ({
|
||
source: { uri: currentEpisode?.url || '' },
|
||
posterSource: { uri: detail?.poster ?? "" },
|
||
resizeMode: ResizeMode.CONTAIN,
|
||
rate: playbackRate,
|
||
onPlaybackStatusUpdate: handlePlaybackStatusUpdate,
|
||
onLoad,
|
||
onLoadStart,
|
||
onError,
|
||
useNativeControls: deviceType !== 'tv',
|
||
shouldPlay: true,
|
||
}), [
|
||
currentEpisode?.url,
|
||
detail?.poster,
|
||
playbackRate,
|
||
handlePlaybackStatusUpdate,
|
||
onLoad,
|
||
onLoadStart,
|
||
onError,
|
||
deviceType,
|
||
]);
|
||
|
||
return {
|
||
onLoad,
|
||
onLoadStart,
|
||
onError,
|
||
videoProps,
|
||
};
|
||
}; |