mirror of
https://github.com/tiajinsha/JKVideo.git
synced 2026-04-04 22:49:02 +08:00
122 lines
4.1 KiB
TypeScript
122 lines
4.1 KiB
TypeScript
import React, { useState, useRef, useEffect } from 'react';
|
|
import { View, StyleSheet, Text, Platform, Modal, StatusBar, useWindowDimensions } from 'react-native';
|
|
// expo-screen-orientation requires a dev build; gracefully degrade in Expo Go
|
|
let ScreenOrientation: typeof import('expo-screen-orientation') | null = null;
|
|
try { ScreenOrientation = require('expo-screen-orientation'); } catch {}
|
|
import { NativeVideoPlayer, type NativeVideoPlayerRef } from './NativeVideoPlayer';
|
|
import type { PlayUrlResponse, DanmakuItem } from '../services/types';
|
|
|
|
interface Props {
|
|
playData: PlayUrlResponse | null;
|
|
qualities: { qn: number; desc: string }[];
|
|
currentQn: number;
|
|
onQualityChange: (qn: number) => void;
|
|
bvid?: string;
|
|
cid?: number;
|
|
danmakus?: DanmakuItem[];
|
|
onTimeUpdate?: (t: number) => void;
|
|
}
|
|
|
|
export function VideoPlayer({ playData, qualities, currentQn, onQualityChange, bvid, cid, danmakus, onTimeUpdate }: Props) {
|
|
const [fullscreen, setFullscreen] = useState(false);
|
|
const { width, height } = useWindowDimensions();
|
|
const VIDEO_HEIGHT = width * 0.5625;
|
|
const needsRotation = !ScreenOrientation && fullscreen;
|
|
const lastTimeRef = useRef(0);
|
|
const portraitRef = useRef<NativeVideoPlayerRef>(null);
|
|
|
|
const handleEnterFullscreen = async () => {
|
|
if (Platform.OS !== 'web')
|
|
await ScreenOrientation?.lockAsync(ScreenOrientation.OrientationLock.LANDSCAPE_RIGHT);
|
|
setFullscreen(true);
|
|
};
|
|
|
|
const handleExitFullscreen = async () => {
|
|
setFullscreen(false);
|
|
if (Platform.OS !== 'web')
|
|
await ScreenOrientation?.lockAsync(ScreenOrientation.OrientationLock.PORTRAIT_UP);
|
|
};
|
|
|
|
useEffect(() => {
|
|
return () => {
|
|
if (Platform.OS !== 'web')
|
|
ScreenOrientation?.lockAsync(ScreenOrientation.OrientationLock.PORTRAIT_UP);
|
|
};
|
|
}, []);
|
|
|
|
if (!playData) {
|
|
return (
|
|
<View style={[{ width, height: VIDEO_HEIGHT, backgroundColor: '#000' }, styles.placeholder]}>
|
|
<Text style={styles.placeholderText}>视频加载中...</Text>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
if (Platform.OS === 'web') {
|
|
const url = playData.durl?.[0]?.url ?? '';
|
|
return (
|
|
<View style={{ width, height: VIDEO_HEIGHT, backgroundColor: '#000' }}>
|
|
<video
|
|
src={url}
|
|
style={{ width: '100%', height: '100%', backgroundColor: '#000' } as any}
|
|
controls
|
|
playsInline
|
|
/>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<>
|
|
{/* 竖屏和全屏互斥渲染,避免同时挂载两个视频解码器 */}
|
|
{!fullscreen && (
|
|
<NativeVideoPlayer
|
|
ref={portraitRef}
|
|
playData={playData}
|
|
qualities={qualities}
|
|
currentQn={currentQn}
|
|
onQualityChange={onQualityChange}
|
|
onFullscreen={handleEnterFullscreen}
|
|
bvid={bvid}
|
|
cid={cid}
|
|
isFullscreen={false}
|
|
initialTime={lastTimeRef.current}
|
|
onTimeUpdate={(t) => { lastTimeRef.current = t; onTimeUpdate?.(t); }}
|
|
/>
|
|
)}
|
|
|
|
{fullscreen && (
|
|
<Modal visible animationType="none" statusBarTranslucent>
|
|
<StatusBar hidden />
|
|
<View style={{ flex: 1, backgroundColor: '#000', justifyContent: 'center', alignItems: 'center' }}>
|
|
<View style={needsRotation
|
|
? { width: height, height: width, transform: [{ rotate: '90deg' }] }
|
|
: { flex: 1, width: '100%' }
|
|
}>
|
|
<NativeVideoPlayer
|
|
playData={playData}
|
|
qualities={qualities}
|
|
currentQn={currentQn}
|
|
onQualityChange={onQualityChange}
|
|
onFullscreen={handleExitFullscreen}
|
|
bvid={bvid}
|
|
cid={cid}
|
|
danmakus={danmakus}
|
|
isFullscreen={true}
|
|
initialTime={lastTimeRef.current}
|
|
onTimeUpdate={(t) => { lastTimeRef.current = t; onTimeUpdate?.(t); }}
|
|
style={needsRotation ? { width: height, height: width } : { flex: 1 }}
|
|
/>
|
|
</View>
|
|
</View>
|
|
</Modal>
|
|
)}
|
|
</>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
placeholder: { justifyContent: 'center', alignItems: 'center' },
|
|
placeholderText: { color: '#fff', fontSize: 14 },
|
|
});
|