added mpv switch toggle

This commit is contained in:
tapframe 2026-01-10 02:22:45 +05:30
parent 30ebba0722
commit 7a439fa814
5 changed files with 77 additions and 9 deletions

View file

@ -1,6 +1,23 @@
#import <React/RCTViewManager.h>
// Dummy
@interface KSPlayerManager : NSObject
#import <AVKit/AVRoutePickerView.h>
@interface AirPlayRoutePickerViewManager : RCTViewManager
@end
@implementation KSPlayerManager
@implementation AirPlayRoutePickerViewManager
// Expose as `AirPlayRoutePickerView` for `requireNativeComponent`
RCT_EXPORT_MODULE(AirPlayRoutePickerView)
- (UIView *)view
{
AVRoutePickerView *picker = [AVRoutePickerView new];
picker.backgroundColor = UIColor.clearColor;
if (@available(iOS 11.0, *)) {
picker.tintColor = UIColor.whiteColor;
picker.activeTintColor = UIColor.whiteColor;
}
return picker;
}
@end

View file

@ -1,5 +1,5 @@
import React, { useEffect, useRef, useState, useCallback } from 'react';
import { View, StatusBar, StyleSheet, Animated, Dimensions, Platform } from 'react-native';
import { View, StatusBar, StyleSheet, Animated, Dimensions, Platform, Alert } from 'react-native';
import { useNavigation, useRoute } from '@react-navigation/native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import axios from 'axios';
@ -502,6 +502,13 @@ const KSPlayerCore: React.FC = () => {
modals.setShowErrorModal(true);
};
// Confirm and execute the switch to MPV (iOS manual switch)
const confirmSwitchToMPV = useCallback(() => {
lastPlaybackTimeRef.current = currentTime || lastPlaybackTimeRef.current || 0;
hasAviOSFailed.current = true; // reuse the resume-on-load path
setUseMpvFallback(true);
}, [currentTime]);
const handleClose = useCallback(() => {
if (isSyncingBeforeClose.current) return;
isSyncingBeforeClose.current = true;
@ -816,10 +823,23 @@ const KSPlayerCore: React.FC = () => {
onSwitchToMPV={
(!shouldUseMpvOnly && !useMpvFallback)
? () => {
// Manual switch (like Android): go to MPV for this session and resume at current time.
lastPlaybackTimeRef.current = currentTime || lastPlaybackTimeRef.current || 0;
hasAviOSFailed.current = true; // reuse the resume-on-load path
setUseMpvFallback(true);
// Manual switch should confirm (like Android)
Alert.alert(
'Switch to MPV Player?',
'This will switch from AVPlayer to MPV player. Use this if you\'re facing playback issues. The switch cannot be undone during this playback session.',
[
{
text: 'Cancel',
style: 'cancel',
},
{
text: 'Switch to MPV',
onPress: confirmSwitchToMPV,
style: 'default',
},
],
{ cancelable: true }
);
}
: undefined
}
@ -1028,6 +1048,7 @@ const KSPlayerCore: React.FC = () => {
onSelectStream={handleEpisodeStreamSelect}
metadata={{ id: id, name: title }}
/>
</View>
);
};

View file

@ -8,6 +8,7 @@ import { useTranslation } from 'react-i18next';
import { styles } from '../utils/playerStyles'; // Updated styles
import { getTrackDisplayName } from '../utils/playerUtils';
import { useTheme } from '../../../contexts/ThemeContext';
import AirPlayRoutePicker from '../ios/components/AirPlayRoutePicker';
interface PlayerControlsProps {
showControls: boolean;
@ -350,8 +351,10 @@ export const PlayerControls: React.FC<PlayerControlsProps> = ({
(Platform.OS === 'ios' && onSwitchToMPV && playerBackend === 'AVPlayer')
) && (
<TouchableOpacity
style={{ padding: 8 }}
style={[styles.topButton, { zIndex: 2 }]}
onPress={onSwitchToMPV}
hitSlop={{ top: 10, left: 10, right: 10, bottom: 10 }}
activeOpacity={0.8}
>
<Ionicons
name="swap-horizontal"
@ -360,6 +363,13 @@ export const PlayerControls: React.FC<PlayerControlsProps> = ({
/>
</TouchableOpacity>
)}
{/* AirPlay picker (iOS + AVPlayer) */}
{Platform.OS === 'ios' && playerBackend === 'AVPlayer' && (
<View style={{ width: closeIconSize + 16, height: closeIconSize + 16, zIndex: 1 }}>
<AirPlayRoutePicker style={{ width: '100%', height: '100%' }} />
</View>
)}
<TouchableOpacity style={styles.closeButton} onPress={handleClose}>
<Ionicons name="close" size={closeIconSize} color="white" />
</TouchableOpacity>

View file

@ -102,6 +102,7 @@ export const AVPlayerSurface: React.FC<AVPlayerSurfaceProps> = ({
volume={volume}
rate={playbackSpeed}
resizeMode={resizeMode as any}
allowsExternalPlayback={true}
selectedAudioTrack={selectedAudioTrack}
selectedTextTrack={selectedTextTrack}
onLoad={handleLoad}

View file

@ -0,0 +1,19 @@
import React from 'react';
import { Platform, requireNativeComponent, ViewStyle, View } from 'react-native';
type Props = {
style?: ViewStyle;
};
const NativeAirPlayRoutePickerView =
Platform.OS === 'ios'
? requireNativeComponent<Props>('AirPlayRoutePickerView')
: null;
export const AirPlayRoutePicker: React.FC<Props> = (props) => {
if (!NativeAirPlayRoutePickerView) return <View style={props.style} />;
return <NativeAirPlayRoutePickerView {...props} />;
};
export default AirPlayRoutePicker;