mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-01-11 20:10:25 +00:00
sub/audio init
This commit is contained in:
parent
4cc77b96cd
commit
3ef4f20781
9 changed files with 88 additions and 301 deletions
|
|
@ -3173,7 +3173,7 @@ SPEC CHECKSUMS:
|
|||
lottie-ios: a881093fab623c467d3bce374367755c272bdd59
|
||||
lottie-react-native: cbe3d931a7c24f7891a8e8032c2bb9b2373c4b9c
|
||||
MMKVCore: f2dd4c9befea04277a55e84e7812f930537993df
|
||||
MPVKit: 25ac17c12586bbd3aab014c6a281f6981c3fb6cf
|
||||
MPVKit: 268868ef845bb3130e70360b7764b0be45f6a196
|
||||
NitroMmkv: 4af10c70043b4c3cded3f16547627c7d9d8e3b8b
|
||||
NitroModules: a71a5ab2911caf79e45170e6e12475b5260a12d0
|
||||
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
|
||||
|
|
|
|||
|
|
@ -1,227 +0,0 @@
|
|||
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;
|
||||
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;
|
||||
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}
|
||||
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;
|
||||
|
|
@ -39,7 +39,7 @@ import {
|
|||
} from './hooks';
|
||||
|
||||
// Platform-specific hooks
|
||||
import { useKSPlayer } from './ios/hooks/useKSPlayer';
|
||||
import { useMPVPlayer } from './ios/hooks/useMPVPlayer';
|
||||
|
||||
// App-level Hooks
|
||||
import { useTraktAutosync } from '../../hooks/useTraktAutosync';
|
||||
|
|
@ -107,8 +107,6 @@ const KSPlayerCore: React.FC = () => {
|
|||
screenDimensions, setScreenDimensions,
|
||||
zoomScale, setZoomScale,
|
||||
lastZoomScale, setLastZoomScale,
|
||||
isAirPlayActive,
|
||||
allowsAirPlay,
|
||||
isSeeking,
|
||||
isMounted,
|
||||
} = playerState;
|
||||
|
|
@ -132,7 +130,7 @@ const KSPlayerCore: React.FC = () => {
|
|||
|
||||
const openingAnim = useOpeningAnimation(backdrop, metadata);
|
||||
const tracks = usePlayerTracks();
|
||||
const { ksPlayerRef, seek } = useKSPlayer();
|
||||
const { mpvPlayerRef, seek } = useMPVPlayer();
|
||||
const customSubs = useCustomSubtitles();
|
||||
const { settings } = useSettings();
|
||||
const { currentTheme } = useTheme();
|
||||
|
|
@ -172,7 +170,7 @@ const KSPlayerCore: React.FC = () => {
|
|||
});
|
||||
|
||||
const controls = usePlayerControls({
|
||||
playerRef: ksPlayerRef,
|
||||
playerRef: mpvPlayerRef,
|
||||
paused,
|
||||
setPaused,
|
||||
currentTime,
|
||||
|
|
@ -514,10 +512,8 @@ const KSPlayerCore: React.FC = () => {
|
|||
|
||||
const handleSelectAudioTrack = useCallback((trackId: number) => {
|
||||
tracks.selectAudioTrack(trackId);
|
||||
if (ksPlayerRef.current) {
|
||||
ksPlayerRef.current.setAudioTrack(trackId);
|
||||
}
|
||||
}, [tracks, ksPlayerRef]);
|
||||
mpvPlayerRef.current?.setAudioTrack(trackId);
|
||||
}, [tracks, mpvPlayerRef]);
|
||||
|
||||
// Stream selection handler
|
||||
const handleSelectStream = async (newStream: any) => {
|
||||
|
|
@ -617,7 +613,7 @@ const KSPlayerCore: React.FC = () => {
|
|||
|
||||
{/* Video Surface & Pinch Zoom */}
|
||||
<KSPlayerSurface
|
||||
ksPlayerRef={ksPlayerRef}
|
||||
ksPlayerRef={mpvPlayerRef}
|
||||
uri={uri}
|
||||
headers={headers}
|
||||
paused={paused}
|
||||
|
|
@ -737,10 +733,7 @@ const KSPlayerCore: React.FC = () => {
|
|||
onSlidingComplete={onSlidingComplete}
|
||||
buffered={buffered}
|
||||
formatTime={formatTime}
|
||||
playerBackend="KSAVPlayer"
|
||||
isAirPlayActive={isAirPlayActive}
|
||||
allowsAirPlay={allowsAirPlay}
|
||||
onAirPlayPress={() => ksPlayerRef.current?.showAirPlayPicker()}
|
||||
playerBackend="MPV"
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useRef, useImperativeHandle, forwardRef, useEffect } from 'react';
|
||||
import React, { useRef, useImperativeHandle, forwardRef } from 'react';
|
||||
import { requireNativeComponent, UIManager, findNodeHandle, ViewStyle } from 'react-native';
|
||||
|
||||
interface MPVPlayerProps {
|
||||
|
|
@ -9,15 +9,25 @@ interface MPVPlayerProps {
|
|||
paused?: boolean;
|
||||
volume?: number;
|
||||
rate?: number;
|
||||
audioTrack?: number;
|
||||
textTrack?: number;
|
||||
subtitleTextColor?: string;
|
||||
subtitleBackgroundColor?: string;
|
||||
subtitleFontSize?: number;
|
||||
subtitleBottomOffset?: number;
|
||||
style?: ViewStyle;
|
||||
onLoad?: (event: any) => void;
|
||||
onProgress?: (event: any) => void;
|
||||
onEnd?: (event: any) => void;
|
||||
onError?: (event: any) => void;
|
||||
onAudioTracks?: (event: any) => void;
|
||||
onTextTracks?: (event: any) => void;
|
||||
}
|
||||
|
||||
export interface MPVPlayerRef {
|
||||
seek: (time: number) => void;
|
||||
setAudioTrack: (trackId: number) => void;
|
||||
setTextTrack: (trackId: number) => void;
|
||||
}
|
||||
|
||||
const ComponentName = 'MPVPlayerView';
|
||||
|
|
@ -38,6 +48,28 @@ const MPVPlayerComponent = forwardRef<MPVPlayerRef, MPVPlayerProps>((props, ref)
|
|||
);
|
||||
}
|
||||
},
|
||||
setAudioTrack: (trackId: number) => {
|
||||
if (nativeRef.current) {
|
||||
const node = findNodeHandle(nativeRef.current);
|
||||
UIManager.dispatchViewManagerCommand(
|
||||
node,
|
||||
// @ts-ignore
|
||||
UIManager.getViewManagerConfig(ComponentName).Commands.setAudioTrack,
|
||||
[trackId]
|
||||
);
|
||||
}
|
||||
},
|
||||
setTextTrack: (trackId: number) => {
|
||||
if (nativeRef.current) {
|
||||
const node = findNodeHandle(nativeRef.current);
|
||||
UIManager.dispatchViewManagerCommand(
|
||||
node,
|
||||
// @ts-ignore
|
||||
UIManager.getViewManagerConfig(ComponentName).Commands.setTextTrack,
|
||||
[trackId]
|
||||
);
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
return (
|
||||
|
|
@ -48,6 +80,8 @@ const MPVPlayerComponent = forwardRef<MPVPlayerRef, MPVPlayerProps>((props, ref)
|
|||
onProgress={(e: any) => props.onProgress?.(e.nativeEvent)}
|
||||
onEnd={(e: any) => props.onEnd?.(e.nativeEvent)}
|
||||
onError={(e: any) => props.onError?.(e.nativeEvent)}
|
||||
onAudioTracks={(e: any) => props.onAudioTracks?.(e.nativeEvent)}
|
||||
onTextTracks={(e: any) => props.onTextTracks?.(e.nativeEvent)}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -47,10 +47,6 @@ interface PlayerControlsProps {
|
|||
buffered: number;
|
||||
formatTime: (seconds: number) => string;
|
||||
playerBackend?: string;
|
||||
// AirPlay props
|
||||
isAirPlayActive?: boolean;
|
||||
allowsAirPlay?: boolean;
|
||||
onAirPlayPress?: () => void;
|
||||
// MPV Switch (Android only)
|
||||
onSwitchToMPV?: () => void;
|
||||
useExoPlayer?: boolean;
|
||||
|
|
@ -93,9 +89,6 @@ export const PlayerControls: React.FC<PlayerControlsProps> = ({
|
|||
buffered,
|
||||
formatTime,
|
||||
playerBackend,
|
||||
isAirPlayActive,
|
||||
allowsAirPlay,
|
||||
onAirPlayPress,
|
||||
onSwitchToMPV,
|
||||
useExoPlayer,
|
||||
}) => {
|
||||
|
|
@ -349,19 +342,6 @@ export const PlayerControls: React.FC<PlayerControlsProps> = ({
|
|||
)}
|
||||
</View>
|
||||
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
|
||||
{/* AirPlay Button - iOS only, KSAVPlayer only */}
|
||||
{Platform.OS === 'ios' && onAirPlayPress && playerBackend === 'KSAVPlayer' && (
|
||||
<TouchableOpacity
|
||||
style={{ padding: 8 }}
|
||||
onPress={onAirPlayPress}
|
||||
>
|
||||
<Feather
|
||||
name="airplay"
|
||||
size={closeIconSize}
|
||||
color={isAirPlayActive ? currentTheme.colors.primary : "white"}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
{/* Switch to MPV Button - Android only, when using ExoPlayer */}
|
||||
{Platform.OS === 'android' && onSwitchToMPV && useExoPlayer && (
|
||||
<TouchableOpacity
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import React, { useRef } from 'react';
|
|||
import { Animated } from 'react-native';
|
||||
import { PinchGestureHandler, State, PinchGestureHandlerGestureEvent } from 'react-native-gesture-handler';
|
||||
import MPVPlayerComponent from '../../MPVPlayerComponent';
|
||||
import { MPVPlayerRef } from '../../MPVPlayerComponent';
|
||||
|
||||
|
||||
interface KSPlayerSurfaceProps {
|
||||
|
|
@ -98,7 +97,7 @@ export const KSPlayerSurface: React.FC<KSPlayerSurfaceProps> = ({
|
|||
console.log('[KSPlayerSurface] textTrack prop changed to:', textTrack);
|
||||
}, [textTrack]);
|
||||
|
||||
// Handle buffering - KSPlayerComponent uses onBuffering callback
|
||||
// Handle buffering - MPVPlayerComponent exposes buffering only via events we wire up
|
||||
const handleBuffering = (data: any) => {
|
||||
onBuffer(data?.isBuffering ?? false);
|
||||
};
|
||||
|
|
@ -135,12 +134,20 @@ export const KSPlayerSurface: React.FC<KSPlayerSurfaceProps> = ({
|
|||
paused={paused}
|
||||
volume={volume}
|
||||
rate={playbackSpeed}
|
||||
audioTrack={audioTrack}
|
||||
textTrack={textTrack}
|
||||
subtitleTextColor={subtitleTextColor}
|
||||
subtitleBackgroundColor={subtitleBackgroundColor}
|
||||
subtitleFontSize={subtitleFontSize}
|
||||
subtitleBottomOffset={subtitleBottomOffset}
|
||||
style={customVideoStyles.width ? customVideoStyles : { width: screenWidth, height: screenHeight }}
|
||||
|
||||
onLoad={handleLoad}
|
||||
onProgress={onProgress}
|
||||
onEnd={onEnd}
|
||||
onError={onError}
|
||||
onAudioTracks={onAudioTracks}
|
||||
onTextTracks={onTextTracks}
|
||||
/>
|
||||
|
||||
</Animated.View>
|
||||
|
|
|
|||
|
|
@ -1,15 +0,0 @@
|
|||
import { useRef } from 'react';
|
||||
import { KSPlayerRef } from '../../KSPlayerComponent';
|
||||
|
||||
export const useKSPlayer = () => {
|
||||
const ksPlayerRef = useRef<KSPlayerRef>(null);
|
||||
|
||||
const seek = (time: number) => {
|
||||
ksPlayerRef.current?.seek(time);
|
||||
};
|
||||
|
||||
return {
|
||||
ksPlayerRef,
|
||||
seek
|
||||
};
|
||||
};
|
||||
16
src/components/player/ios/hooks/useMPVPlayer.ts
Normal file
16
src/components/player/ios/hooks/useMPVPlayer.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import { useRef } from 'react';
|
||||
import type { MPVPlayerRef } from '../../MPVPlayerComponent';
|
||||
|
||||
export const useMPVPlayer = () => {
|
||||
const mpvPlayerRef = useRef<MPVPlayerRef>(null);
|
||||
|
||||
const seek = (time: number) => {
|
||||
mpvPlayerRef.current?.seek(time);
|
||||
};
|
||||
|
||||
return {
|
||||
mpvPlayerRef,
|
||||
seek,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
@ -110,7 +110,7 @@ export const SubtitleModals: React.FC<SubtitleModalsProps> = ({
|
|||
const isCompact = width < 360 || height < 640;
|
||||
// Internal subtitle is active when a built-in track is selected AND not using custom/addon subtitles
|
||||
const isUsingInternalSubtitle = selectedTextTrack >= 0 && !useCustomSubtitles;
|
||||
// ExoPlayer has limited styling support - hide unsupported options when using ExoPlayer with internal subs
|
||||
// ExoPlayer internal subtitles have limited styling support
|
||||
const isExoPlayerInternal = useExoPlayer && isUsingInternalSubtitle;
|
||||
const sectionPad = isCompact ? 12 : 16;
|
||||
const chipPadH = isCompact ? 8 : 12;
|
||||
|
|
@ -259,8 +259,8 @@ export const SubtitleModals: React.FC<SubtitleModalsProps> = ({
|
|||
</View>
|
||||
</View>
|
||||
|
||||
{/* Quick Presets - Hidden for ExoPlayer internal subtitles */}
|
||||
{!isExoPlayerInternal && (
|
||||
{/* Quick Presets - only for CustomSubtitles overlay */}
|
||||
{!isUsingInternalSubtitle && (
|
||||
<View style={{ backgroundColor: 'rgba(255,255,255,0.05)', borderRadius: 16, padding: sectionPad }}>
|
||||
<View style={{ flexDirection: 'row', alignItems: 'center', marginBottom: 10 }}>
|
||||
<MaterialIcons name="star" size={16} color="rgba(255,255,255,0.7)" />
|
||||
|
|
@ -329,7 +329,7 @@ export const SubtitleModals: React.FC<SubtitleModalsProps> = ({
|
|||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
{/* Show Background - Not supported on ExoPlayer internal subtitles */}
|
||||
{/* Show Background - not supported on ExoPlayer internal subtitles */}
|
||||
{!isExoPlayerInternal && (
|
||||
<View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
|
||||
|
|
@ -346,28 +346,27 @@ export const SubtitleModals: React.FC<SubtitleModalsProps> = ({
|
|||
)}
|
||||
</View>
|
||||
|
||||
{/* Advanced controls - Limited for ExoPlayer */}
|
||||
{/* Advanced controls */}
|
||||
<View style={{ backgroundColor: 'rgba(255,255,255,0.05)', borderRadius: 16, padding: sectionPad, gap: isCompact ? 10 : 14 }}>
|
||||
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
|
||||
<MaterialIcons name="build" size={16} color="rgba(255,255,255,0.7)" />
|
||||
<Text style={{ color: 'rgba(255,255,255,0.7)', fontSize: 12, marginLeft: 6, fontWeight: '600' }}>{isExoPlayerInternal ? t('player_ui.position') : t('player_ui.advanced')}</Text>
|
||||
<Text style={{ color: 'rgba(255,255,255,0.7)', fontSize: 12, marginLeft: 6, fontWeight: '600' }}>{isUsingInternalSubtitle ? t('player_ui.position') : t('player_ui.advanced')}</Text>
|
||||
</View>
|
||||
{/* Text Color - Not supported on ExoPlayer internal subtitles */}
|
||||
{!isExoPlayerInternal && (
|
||||
<View style={{ marginTop: 8, flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
|
||||
<MaterialIcons name="palette" size={16} color="rgba(255,255,255,0.7)" />
|
||||
<Text style={{ color: 'white', marginLeft: 8, fontWeight: '600' }}>{t('player_ui.text_color')}</Text>
|
||||
</View>
|
||||
<View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 8, justifyContent: 'flex-end' }}>
|
||||
{['#FFFFFF', '#FFD700', '#00E5FF', '#FF5C5C', '#00FF88', '#9b59b6', '#f97316'].map(c => (
|
||||
<TouchableOpacity key={c} onPress={() => setSubtitleTextColor(c)} style={{ width: 22, height: 22, borderRadius: 11, backgroundColor: c, borderWidth: 2, borderColor: subtitleTextColor === c ? '#fff' : 'rgba(255,255,255,0.3)' }} />
|
||||
))}
|
||||
</View>
|
||||
{/* Text Color - supported for MPV built-in, and for CustomSubtitles */}
|
||||
<View style={{ marginTop: 8, flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
|
||||
<MaterialIcons name="palette" size={16} color="rgba(255,255,255,0.7)" />
|
||||
<Text style={{ color: 'white', marginLeft: 8, fontWeight: '600' }}>{t('player_ui.text_color')}</Text>
|
||||
</View>
|
||||
)}
|
||||
{/* Align - Not supported on ExoPlayer internal subtitles */}
|
||||
{!isExoPlayerInternal && (
|
||||
<View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 8, justifyContent: 'flex-end' }}>
|
||||
{['#FFFFFF', '#FFD700', '#00E5FF', '#FF5C5C', '#00FF88', '#9b59b6', '#f97316'].map(c => (
|
||||
<TouchableOpacity key={c} onPress={() => setSubtitleTextColor(c)} style={{ width: 22, height: 22, borderRadius: 11, backgroundColor: c, borderWidth: 2, borderColor: subtitleTextColor === c ? '#fff' : 'rgba(255,255,255,0.3)' }} />
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Align - only supported for CustomSubtitles overlay */}
|
||||
{!isUsingInternalSubtitle && (
|
||||
<View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
<Text style={{ color: 'white', fontWeight: '600' }}>{t('player_ui.align')}</Text>
|
||||
<View style={{ flexDirection: 'row', gap: 8 }}>
|
||||
|
|
|
|||
Loading…
Reference in a new issue