Files
KVideo/components/player/MobileVideoPlayer.tsx
T
2025-12-24 23:53:24 +08:00

159 lines
3.6 KiB
TypeScript

'use client';
import { useScreenOrientation } from '@/lib/hooks/useMobilePlayer';
import { useMobilePlayerState } from './hooks/useMobilePlayerState';
import { useMobilePlayerLogic } from './hooks/useMobilePlayerLogic';
import { useMobileGestures } from './hooks/useMobileGestures';
import { useAutoSkip } from './hooks/useAutoSkip';
import { useHlsPlayer } from './hooks/useHlsPlayer';
import { MobileControlsWrapper } from './mobile/MobileControlsWrapper';
import { MobileOverlay } from './mobile/MobileOverlay';
import { MobileSkipIndicator } from './mobile/MobileSkipIndicator';
interface MobileVideoPlayerProps {
src: string;
poster?: string;
onError?: (error: string) => void;
onTimeUpdate?: (currentTime: number, duration: number) => void;
initialTime?: number;
shouldAutoPlay?: boolean;
// Episode navigation props for auto-skip/auto-next
totalEpisodes?: number;
currentEpisodeIndex?: number;
onNextEpisode?: () => void;
}
export function MobileVideoPlayer({
src,
poster,
onError,
onTimeUpdate,
initialTime = 0,
shouldAutoPlay = false,
totalEpisodes = 1,
currentEpisodeIndex = 0,
onNextEpisode,
}: MobileVideoPlayerProps) {
const { refs, state } = useMobilePlayerState();
const {
videoRef,
containerRef,
controlsTimeoutRef
} = refs;
// Initialize HLS Player (same as Desktop - fixes Android Chrome playback)
useHlsPlayer({
videoRef,
src,
autoPlay: shouldAutoPlay,
onError,
});
const {
isPlaying,
isFullscreen,
showControls,
isLoading,
showSkipIndicator,
skipAmount,
skipSide,
toastMessage,
showToast,
currentTime,
duration,
setShowControls,
setIsLoading
} = state;
const logic = useMobilePlayerLogic({
src,
initialTime,
shouldAutoPlay,
onError,
onTimeUpdate,
refs,
state
});
// Auto-skip intro/outro and auto-next episode
useAutoSkip({
videoRef,
currentTime,
duration,
isPlaying,
totalEpisodes,
currentEpisodeIndex,
onNextEpisode,
});
const {
skipVideo,
togglePlay,
handlePlay,
handlePause,
handleTimeUpdateEvent,
handleLoadedMetadata,
handleVideoError,
} = logic;
// Screen orientation management
useScreenOrientation(isFullscreen);
// Double tap handler
const { handleTap } = useMobileGestures({
skipVideo,
showSkipIndicator,
showControls,
setShowControls,
controlsTimeoutRef,
isPlaying,
togglePlay,
});
return (
<div
ref={containerRef}
className="relative aspect-video bg-black rounded-[var(--radius-2xl)] overflow-hidden"
>
{/* Video Element - src is set by useHlsPlayer hook */}
<video
ref={videoRef}
className="w-full h-full object-contain touch-none"
poster={poster}
onPlay={handlePlay}
onPause={handlePause}
onTimeUpdate={handleTimeUpdateEvent}
onLoadedMetadata={handleLoadedMetadata}
onError={handleVideoError}
onWaiting={() => setIsLoading(true)}
onCanPlay={() => setIsLoading(false)}
onTouchEnd={handleTap}
onClick={(e) => e.preventDefault()}
playsInline
webkit-playsinline="true"
x-webkit-airplay="allow"
/>
<MobileOverlay
isLoading={isLoading}
showToast={showToast}
toastMessage={toastMessage}
/>
<MobileSkipIndicator
showSkipIndicator={showSkipIndicator}
skipSide={skipSide}
skipAmount={skipAmount}
/>
<MobileControlsWrapper
src={src}
state={state}
logic={logic}
refs={refs}
/>
</div>
);
}