NuvioStreaming/src/components/player/KSPlayerComponent.tsx
2026-01-11 00:46:30 +05:30

230 lines
9 KiB
TypeScript

import React, { useRef, useImperativeHandle, forwardRef, useEffect, useState } from 'react';
import { View, requireNativeComponent, UIManager, findNodeHandle, NativeModules } from 'react-native';
export interface KSPlayerSource {
uri: string;
headers?: Record<string, string>;
}
interface KSPlayerViewProps {
source?: KSPlayerSource;
paused?: boolean;
volume?: number;
rate?: number;
audioTrack?: number;
textTrack?: number;
allowsExternalPlayback?: boolean;
usesExternalPlaybackWhileExternalScreenIsActive?: boolean;
subtitleBottomOffset?: number;
subtitleFontSize?: number;
subtitleTextColor?: string;
subtitleBackgroundColor?: string;
subtitleOutlineEnabled?: boolean;
resizeMode?: 'contain' | 'cover' | 'stretch';
onLoad?: (data: any) => void;
onProgress?: (data: any) => void;
onBuffering?: (data: any) => void;
onEnd?: () => void;
onError?: (error: any) => void;
onBufferingProgress?: (data: any) => void;
style?: any;
}
const KSPlayerViewManager = requireNativeComponent<KSPlayerViewProps>('KSPlayerView');
const KSPlayerModule = NativeModules.KSPlayerModule;
export interface KSPlayerRef {
seek: (time: number) => void;
setSource: (source: KSPlayerSource) => void;
setPaused: (paused: boolean) => void;
setVolume: (volume: number) => void;
setPlaybackRate: (rate: number) => void;
setAudioTrack: (trackId: number) => void;
setTextTrack: (trackId: number) => void;
getTracks: () => Promise<{ audioTracks: any[]; textTracks: any[] }>;
setAllowsExternalPlayback: (allows: boolean) => void;
setUsesExternalPlaybackWhileExternalScreenIsActive: (uses: boolean) => void;
getAirPlayState: () => Promise<{ allowsExternalPlayback: boolean; usesExternalPlaybackWhileExternalScreenIsActive: boolean; isExternalPlaybackActive: boolean }>;
showAirPlayPicker: () => void;
}
export interface KSPlayerProps {
source?: KSPlayerSource;
paused?: boolean;
volume?: number;
rate?: number;
audioTrack?: number;
textTrack?: number;
allowsExternalPlayback?: boolean;
usesExternalPlaybackWhileExternalScreenIsActive?: boolean;
subtitleBottomOffset?: number;
subtitleFontSize?: number;
subtitleTextColor?: string;
subtitleBackgroundColor?: string;
subtitleOutlineEnabled?: boolean;
resizeMode?: 'contain' | 'cover' | 'stretch';
onLoad?: (data: any) => void;
onProgress?: (data: any) => void;
onBuffering?: (data: any) => void;
onEnd?: () => void;
onError?: (error: any) => void;
onBufferingProgress?: (data: any) => void;
style?: any;
}
const KSPlayer = forwardRef<KSPlayerRef, KSPlayerProps>((props, ref) => {
const nativeRef = useRef<any>(null);
const [key, setKey] = useState(0); // Force re-render when source changes
useImperativeHandle(ref, () => ({
seek: (time: number) => {
if (nativeRef.current) {
const node = findNodeHandle(nativeRef.current);
// @ts-ignore legacy UIManager commands path for Paper
const commandId = UIManager.getViewManagerConfig('KSPlayerView').Commands.seek;
UIManager.dispatchViewManagerCommand(node, commandId, [time]);
}
},
setSource: (source: KSPlayerSource) => {
if (nativeRef.current) {
const node = findNodeHandle(nativeRef.current);
// @ts-ignore legacy UIManager commands path for Paper
const commandId = UIManager.getViewManagerConfig('KSPlayerView').Commands.setSource;
UIManager.dispatchViewManagerCommand(node, commandId, [source]);
}
},
setPaused: (paused: boolean) => {
if (nativeRef.current) {
const node = findNodeHandle(nativeRef.current);
// @ts-ignore legacy UIManager commands path for Paper
const commandId = UIManager.getViewManagerConfig('KSPlayerView').Commands.setPaused;
UIManager.dispatchViewManagerCommand(node, commandId, [paused]);
}
},
setVolume: (volume: number) => {
if (nativeRef.current) {
const node = findNodeHandle(nativeRef.current);
// @ts-ignore legacy UIManager commands path for Paper
const commandId = UIManager.getViewManagerConfig('KSPlayerView').Commands.setVolume;
UIManager.dispatchViewManagerCommand(node, commandId, [volume]);
}
},
setPlaybackRate: (rate: number) => {
if (nativeRef.current) {
const node = findNodeHandle(nativeRef.current);
// @ts-ignore legacy UIManager commands path for Paper
const commandId = UIManager.getViewManagerConfig('KSPlayerView').Commands.setPlaybackRate;
UIManager.dispatchViewManagerCommand(node, commandId, [rate]);
}
},
setAudioTrack: (trackId: number) => {
if (nativeRef.current) {
const node = findNodeHandle(nativeRef.current);
// @ts-ignore legacy UIManager commands path for Paper
const commandId = UIManager.getViewManagerConfig('KSPlayerView').Commands.setAudioTrack;
UIManager.dispatchViewManagerCommand(node, commandId, [trackId]);
}
},
setTextTrack: (trackId: number) => {
console.log('[KSPlayerComponent] setTextTrack called with trackId:', trackId);
if (nativeRef.current) {
const node = findNodeHandle(nativeRef.current);
console.log('[KSPlayerComponent] setTextTrack dispatching command to node:', node);
// @ts-ignore legacy UIManager commands path for Paper
const commandId = UIManager.getViewManagerConfig('KSPlayerView').Commands.setTextTrack;
console.log('[KSPlayerComponent] setTextTrack commandId:', commandId);
UIManager.dispatchViewManagerCommand(node, commandId, [trackId]);
} else {
console.warn('[KSPlayerComponent] setTextTrack: nativeRef.current is null');
}
},
getTracks: async () => {
if (nativeRef.current) {
const node = findNodeHandle(nativeRef.current);
if (node) {
return await KSPlayerModule.getTracks(node);
}
}
return { audioTracks: [], textTracks: [] };
},
setAllowsExternalPlayback: (allows: boolean) => {
if (nativeRef.current) {
const node = findNodeHandle(nativeRef.current);
// @ts-ignore legacy UIManager commands path for Paper
const commandId = UIManager.getViewManagerConfig('KSPlayerView').Commands.setAllowsExternalPlayback;
UIManager.dispatchViewManagerCommand(node, commandId, [allows]);
}
},
setUsesExternalPlaybackWhileExternalScreenIsActive: (uses: boolean) => {
if (nativeRef.current) {
const node = findNodeHandle(nativeRef.current);
// @ts-ignore legacy UIManager commands path for Paper
const commandId = UIManager.getViewManagerConfig('KSPlayerView').Commands.setUsesExternalPlaybackWhileExternalScreenIsActive;
UIManager.dispatchViewManagerCommand(node, commandId, [uses]);
}
},
getAirPlayState: async () => {
if (nativeRef.current) {
const node = findNodeHandle(nativeRef.current);
if (node) {
return await KSPlayerModule.getAirPlayState(node);
}
}
return { allowsExternalPlayback: false, usesExternalPlaybackWhileExternalScreenIsActive: false, isExternalPlaybackActive: false };
},
showAirPlayPicker: () => {
if (nativeRef.current) {
const node = findNodeHandle(nativeRef.current);
if (node) {
console.log('[KSPlayerComponent] Calling showAirPlayPicker with node:', node);
KSPlayerModule.showAirPlayPicker(node);
} else {
console.warn('[KSPlayerComponent] Cannot call showAirPlayPicker: node is null');
}
} else {
console.log('[KSPlayerComponent] nativeRef.current is null');
}
},
}));
// No need for event listeners - events are handled through props
// Force re-render when source changes to ensure proper reloading
useEffect(() => {
if (props.source) {
setKey(prev => prev + 1);
}
}, [props.source?.uri]);
return (
<KSPlayerViewManager
key={key}
ref={nativeRef}
source={props.source}
paused={props.paused}
volume={props.volume}
rate={props.rate}
audioTrack={props.audioTrack}
textTrack={props.textTrack}
allowsExternalPlayback={props.allowsExternalPlayback}
usesExternalPlaybackWhileExternalScreenIsActive={props.usesExternalPlaybackWhileExternalScreenIsActive}
subtitleBottomOffset={props.subtitleBottomOffset}
subtitleFontSize={props.subtitleFontSize}
subtitleTextColor={props.subtitleTextColor}
subtitleBackgroundColor={props.subtitleBackgroundColor}
subtitleOutlineEnabled={props.subtitleOutlineEnabled}
resizeMode={props.resizeMode}
onLoad={(e: any) => props.onLoad?.(e?.nativeEvent ?? e)}
onProgress={(e: any) => props.onProgress?.(e?.nativeEvent ?? e)}
onBuffering={(e: any) => props.onBuffering?.(e?.nativeEvent ?? e)}
onEnd={() => props.onEnd?.()}
onError={(e: any) => props.onError?.(e?.nativeEvent ?? e)}
onBufferingProgress={(e: any) => props.onBufferingProgress?.(e?.nativeEvent ?? e)}
style={props.style}
/>
);
});
KSPlayer.displayName = 'KSPlayer';
export default KSPlayer;