mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-04-20 16:22:04 +00:00
some changes
This commit is contained in:
parent
c91546dc1e
commit
cdec19db1f
5 changed files with 292 additions and 223 deletions
|
|
@ -430,7 +430,7 @@
|
||||||
);
|
);
|
||||||
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
|
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.nuvio.app;
|
PRODUCT_BUNDLE_IDENTIFIER = com.nuvio.app;
|
||||||
PRODUCT_NAME = "Nuvio";
|
PRODUCT_NAME = Nuvio;
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Nuvio/Nuvio-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Nuvio/Nuvio-Bridging-Header.h";
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
|
|
@ -464,7 +464,7 @@
|
||||||
);
|
);
|
||||||
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
|
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.nuvio.app;
|
PRODUCT_BUNDLE_IDENTIFIER = com.nuvio.app;
|
||||||
PRODUCT_NAME = "Nuvio";
|
PRODUCT_NAME = Nuvio;
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Nuvio/Nuvio-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Nuvio/Nuvio-Bridging-Header.h";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
|
@ -527,10 +527,7 @@
|
||||||
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\"";
|
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\"";
|
||||||
MTL_ENABLE_DEBUG_INFO = YES;
|
MTL_ENABLE_DEBUG_INFO = YES;
|
||||||
ONLY_ACTIVE_ARCH = YES;
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
OTHER_LDFLAGS = (
|
OTHER_LDFLAGS = "$(inherited) ";
|
||||||
"$(inherited)",
|
|
||||||
" ",
|
|
||||||
);
|
|
||||||
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
|
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG";
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG";
|
||||||
|
|
@ -585,10 +582,7 @@
|
||||||
);
|
);
|
||||||
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\"";
|
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\"";
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
OTHER_LDFLAGS = (
|
OTHER_LDFLAGS = "$(inherited) ";
|
||||||
"$(inherited)",
|
|
||||||
" ",
|
|
||||||
);
|
|
||||||
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
|
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
USE_HERMES = true;
|
USE_HERMES = true;
|
||||||
|
|
|
||||||
|
|
@ -1,99 +1,99 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||||
<key>CFBundleDisplayName</key>
|
<key>CFBundleDisplayName</key>
|
||||||
<string>Nuvio</string>
|
<string>Nuvio</string>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>$(EXECUTABLE_NAME)</string>
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
<string>6.0</string>
|
<string>6.0</string>
|
||||||
<key>CFBundleName</key>
|
<key>CFBundleName</key>
|
||||||
<string>$(PRODUCT_NAME)</string>
|
<string>$(PRODUCT_NAME)</string>
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>1.2.0</string>
|
<string>1.2.0</string>
|
||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>????</string>
|
<string>????</string>
|
||||||
<key>CFBundleURLTypes</key>
|
<key>CFBundleURLTypes</key>
|
||||||
<array>
|
<array>
|
||||||
<dict>
|
<dict>
|
||||||
<key>CFBundleURLSchemes</key>
|
<key>CFBundleURLSchemes</key>
|
||||||
<array>
|
<array>
|
||||||
<string>nuvio</string>
|
<string>nuvio</string>
|
||||||
<string>com.nuvio.app</string>
|
<string>com.nuvio.app</string>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
<dict>
|
<dict>
|
||||||
<key>CFBundleURLSchemes</key>
|
<key>CFBundleURLSchemes</key>
|
||||||
<array>
|
<array>
|
||||||
<string>exp+nuvio</string>
|
<string>exp+nuvio</string>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>16</string>
|
<string>15</string>
|
||||||
<key>LSMinimumSystemVersion</key>
|
<key>LSMinimumSystemVersion</key>
|
||||||
<string>12.0</string>
|
<string>12.0</string>
|
||||||
<key>LSRequiresIPhoneOS</key>
|
<key>LSRequiresIPhoneOS</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>LSSupportsOpeningDocumentsInPlace</key>
|
<key>LSSupportsOpeningDocumentsInPlace</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>NSAppTransportSecurity</key>
|
<key>NSAppTransportSecurity</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>NSAllowsArbitraryLoads</key>
|
<key>NSAllowsArbitraryLoads</key>
|
||||||
<true/>
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
<key>NSBonjourServices</key>
|
<key>NSBonjourServices</key>
|
||||||
<array>
|
<array>
|
||||||
<string>_http._tcp</string>
|
<string>_http._tcp</string>
|
||||||
</array>
|
</array>
|
||||||
<key>NSLocalNetworkUsageDescription</key>
|
<key>NSLocalNetworkUsageDescription</key>
|
||||||
<string>Allow $(PRODUCT_NAME) to access your local network</string>
|
<string>Allow $(PRODUCT_NAME) to access your local network</string>
|
||||||
<key>NSMicrophoneUsageDescription</key>
|
<key>NSMicrophoneUsageDescription</key>
|
||||||
<string>This app does not require microphone access.</string>
|
<string>This app does not require microphone access.</string>
|
||||||
<key>RCTRootViewBackgroundColor</key>
|
<key>RCTRootViewBackgroundColor</key>
|
||||||
<integer>4278322180</integer>
|
<integer>4278322180</integer>
|
||||||
<key>UIBackgroundModes</key>
|
<key>UIBackgroundModes</key>
|
||||||
<array>
|
<array>
|
||||||
<string>audio</string>
|
<string>audio</string>
|
||||||
</array>
|
</array>
|
||||||
<key>UIFileSharingEnabled</key>
|
<key>UIFileSharingEnabled</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>UILaunchStoryboardName</key>
|
<key>UILaunchStoryboardName</key>
|
||||||
<string>SplashScreen</string>
|
<string>SplashScreen</string>
|
||||||
<key>UIRequiredDeviceCapabilities</key>
|
<key>UIRequiredDeviceCapabilities</key>
|
||||||
<array>
|
<array>
|
||||||
<string>arm64</string>
|
<string>arm64</string>
|
||||||
</array>
|
</array>
|
||||||
<key>UIRequiresFullScreen</key>
|
<key>UIRequiresFullScreen</key>
|
||||||
<false/>
|
<false/>
|
||||||
<key>UIStatusBarStyle</key>
|
<key>UIStatusBarStyle</key>
|
||||||
<string>UIStatusBarStyleDefault</string>
|
<string>UIStatusBarStyleDefault</string>
|
||||||
<key>UISupportedInterfaceOrientations</key>
|
<key>UISupportedInterfaceOrientations</key>
|
||||||
<array>
|
<array>
|
||||||
<string>UIInterfaceOrientationPortrait</string>
|
<string>UIInterfaceOrientationPortrait</string>
|
||||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||||
</array>
|
</array>
|
||||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||||
<array>
|
<array>
|
||||||
<string>UIInterfaceOrientationPortrait</string>
|
<string>UIInterfaceOrientationPortrait</string>
|
||||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||||
</array>
|
</array>
|
||||||
<key>UIUserInterfaceStyle</key>
|
<key>UIUserInterfaceStyle</key>
|
||||||
<string>Dark</string>
|
<string>Dark</string>
|
||||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||||
<false/>
|
<false/>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,5 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict/>
|
||||||
<key>aps-environment</key>
|
</plist>
|
||||||
<string>development</string>
|
|
||||||
<key>com.apple.developer.associated-domains</key>
|
|
||||||
<array/>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
|
|
|
||||||
5
ios/Nuvio/NuvioRelease.entitlements
Normal file
5
ios/Nuvio/NuvioRelease.entitlements
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict/>
|
||||||
|
</plist>
|
||||||
|
|
@ -231,6 +231,12 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
const [vlcRestoreTime, setVlcRestoreTime] = useState<number | undefined>(undefined); // Time to restore after remount
|
const [vlcRestoreTime, setVlcRestoreTime] = useState<number | undefined>(undefined); // Time to restore after remount
|
||||||
const [forceVlcRemount, setForceVlcRemount] = useState(false); // Force complete unmount/remount
|
const [forceVlcRemount, setForceVlcRemount] = useState(false); // Force complete unmount/remount
|
||||||
|
|
||||||
|
// Debounce track updates to prevent excessive processing
|
||||||
|
const trackUpdateTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||||
|
|
||||||
|
// Debounce resize operations to prevent rapid successive clicks
|
||||||
|
const resizeTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||||
|
|
||||||
// Memoize VLC tracks prop to prevent unnecessary re-renders
|
// Memoize VLC tracks prop to prevent unnecessary re-renders
|
||||||
const vlcTracks = useMemo(() => ({
|
const vlcTracks = useMemo(() => ({
|
||||||
audio: vlcSelectedAudioTrack,
|
audio: vlcSelectedAudioTrack,
|
||||||
|
|
@ -238,8 +244,9 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
subtitle: vlcSelectedSubtitleTrack
|
subtitle: vlcSelectedSubtitleTrack
|
||||||
}), [vlcSelectedAudioTrack, vlcSelectedSubtitleTrack]);
|
}), [vlcSelectedAudioTrack, vlcSelectedSubtitleTrack]);
|
||||||
|
|
||||||
// Format VLC tracks to match RN Video format
|
// Format VLC tracks to match RN Video format - optimized version
|
||||||
const formatVlcTracks = useCallback((vlcTracks: Array<{id: number, name: string}>) => {
|
const formatVlcTracks = useCallback((vlcTracks: Array<{id: number, name: string}>) => {
|
||||||
|
if (!Array.isArray(vlcTracks)) return [];
|
||||||
return vlcTracks.map(track => ({
|
return vlcTracks.map(track => ({
|
||||||
id: track.id,
|
id: track.id,
|
||||||
name: track.name || `Track ${track.id + 1}`,
|
name: track.name || `Track ${track.id + 1}`,
|
||||||
|
|
@ -247,18 +254,96 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
}));
|
}));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
// Optimized VLC track processing function
|
||||||
|
const processVlcTracks = useCallback((tracks: any, source: string) => {
|
||||||
|
if (!tracks) return;
|
||||||
|
|
||||||
|
// Clear any pending updates
|
||||||
|
if (trackUpdateTimeoutRef.current) {
|
||||||
|
clearTimeout(trackUpdateTimeoutRef.current);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debounce track updates to prevent excessive processing
|
||||||
|
trackUpdateTimeoutRef.current = setTimeout(() => {
|
||||||
|
const { audio = [], subtitle = [] } = tracks;
|
||||||
|
let hasUpdates = false;
|
||||||
|
|
||||||
|
// Process audio tracks
|
||||||
|
if (Array.isArray(audio) && audio.length > 0) {
|
||||||
|
const formattedAudio = formatVlcTracks(audio);
|
||||||
|
if (formattedAudio.length !== vlcAudioTracks.length ||
|
||||||
|
JSON.stringify(formattedAudio) !== JSON.stringify(vlcAudioTracks)) {
|
||||||
|
setVlcAudioTracks(formattedAudio);
|
||||||
|
hasUpdates = true;
|
||||||
|
// Only log in debug mode or when tracks actually change
|
||||||
|
if (DEBUG_MODE) {
|
||||||
|
console.log(`🎬 [VLC] ${source} - Audio tracks updated:`, formattedAudio.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process subtitle tracks
|
||||||
|
if (Array.isArray(subtitle) && subtitle.length > 0) {
|
||||||
|
const formattedSubs = formatVlcTracks(subtitle);
|
||||||
|
if (formattedSubs.length !== vlcSubtitleTracks.length ||
|
||||||
|
JSON.stringify(formattedSubs) !== JSON.stringify(vlcSubtitleTracks)) {
|
||||||
|
setVlcSubtitleTracks(formattedSubs);
|
||||||
|
hasUpdates = true;
|
||||||
|
if (DEBUG_MODE) {
|
||||||
|
console.log(`🎬 [VLC] ${source} - Subtitle tracks updated:`, formattedSubs.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log summary only if tracks were actually updated
|
||||||
|
if (hasUpdates && DEBUG_MODE) {
|
||||||
|
logger.log(`[AndroidVideoPlayer][VLC] ${source} - Track processing complete. Audio: ${vlcAudioTracks.length}, Subs: ${vlcSubtitleTracks.length}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
trackUpdateTimeoutRef.current = null;
|
||||||
|
}, 100); // 100ms debounce
|
||||||
|
}, [formatVlcTracks, vlcAudioTracks, vlcSubtitleTracks]);
|
||||||
|
|
||||||
// Use VLC tracks directly (they only update when tracks change)
|
// Use VLC tracks directly (they only update when tracks change)
|
||||||
const vlcAudioTracksForModal = vlcAudioTracks;
|
const vlcAudioTracksForModal = vlcAudioTracks;
|
||||||
const vlcSubtitleTracksForModal = vlcSubtitleTracks;
|
const vlcSubtitleTracksForModal = vlcSubtitleTracks;
|
||||||
|
|
||||||
// Debug: log when VLC tracks change
|
// Memoized computed props for child components
|
||||||
useEffect(() => {
|
const ksAudioTracks = useMemo(() =>
|
||||||
console.log('🎬 [VLC] vlcAudioTracks changed:', vlcAudioTracks);
|
useVLC ? vlcAudioTracksForModal : rnVideoAudioTracks,
|
||||||
}, [vlcAudioTracks]);
|
[useVLC, vlcAudioTracksForModal, rnVideoAudioTracks]
|
||||||
|
);
|
||||||
|
|
||||||
|
const computedSelectedAudioTrack = useMemo(() =>
|
||||||
|
useVLC
|
||||||
|
? (vlcSelectedAudioTrack ?? null)
|
||||||
|
: (selectedAudioTrack?.type === SelectedTrackType.INDEX && selectedAudioTrack.value !== undefined
|
||||||
|
? Number(selectedAudioTrack.value)
|
||||||
|
: null),
|
||||||
|
[useVLC, vlcSelectedAudioTrack, selectedAudioTrack]
|
||||||
|
);
|
||||||
|
|
||||||
|
const ksTextTracks = useMemo(() =>
|
||||||
|
useVLC ? vlcSubtitleTracksForModal : rnVideoTextTracks,
|
||||||
|
[useVLC, vlcSubtitleTracksForModal, rnVideoTextTracks]
|
||||||
|
);
|
||||||
|
|
||||||
|
const computedSelectedTextTrack = useMemo(() =>
|
||||||
|
useVLC ? (vlcSelectedSubtitleTrack ?? -1) : selectedTextTrack,
|
||||||
|
[useVLC, vlcSelectedSubtitleTrack, selectedTextTrack]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Clean up timeouts on unmount
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log('🎬 [VLC] vlcSubtitleTracks changed:', vlcSubtitleTracks);
|
return () => {
|
||||||
}, [vlcSubtitleTracks]);
|
if (trackUpdateTimeoutRef.current) {
|
||||||
|
clearTimeout(trackUpdateTimeoutRef.current);
|
||||||
|
}
|
||||||
|
if (resizeTimeoutRef.current) {
|
||||||
|
clearTimeout(resizeTimeoutRef.current);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
// Reset forceVlcRemount when VLC becomes inactive
|
// Reset forceVlcRemount when VLC becomes inactive
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -290,6 +375,40 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
const [isVideoLoaded, setIsVideoLoaded] = useState(false);
|
const [isVideoLoaded, setIsVideoLoaded] = useState(false);
|
||||||
const [videoAspectRatio, setVideoAspectRatio] = useState<number | null>(null);
|
const [videoAspectRatio, setVideoAspectRatio] = useState<number | null>(null);
|
||||||
const [is16by9Content, setIs16by9Content] = useState(false);
|
const [is16by9Content, setIs16by9Content] = useState(false);
|
||||||
|
|
||||||
|
const calculateVideoStyles = (videoWidth: number, videoHeight: number, screenWidth: number, screenHeight: number) => {
|
||||||
|
return {
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
width: screenWidth,
|
||||||
|
height: screenHeight,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Memoize expensive video style calculations
|
||||||
|
const videoStyles = useMemo(() => {
|
||||||
|
if (videoAspectRatio && screenDimensions.width > 0 && screenDimensions.height > 0) {
|
||||||
|
return calculateVideoStyles(
|
||||||
|
videoAspectRatio * 1000,
|
||||||
|
1000,
|
||||||
|
screenDimensions.width,
|
||||||
|
screenDimensions.height
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}, [videoAspectRatio, screenDimensions.width, screenDimensions.height]);
|
||||||
|
|
||||||
|
// Memoize zoom factor calculations to prevent expensive recalculations
|
||||||
|
const zoomFactor = useMemo(() => {
|
||||||
|
if (resizeMode === 'cover' && videoAspectRatio && screenDimensions.width > 0 && screenDimensions.height > 0) {
|
||||||
|
const screenAspect = screenDimensions.width / screenDimensions.height;
|
||||||
|
return Math.max(screenAspect / videoAspectRatio, videoAspectRatio / screenAspect);
|
||||||
|
} else if (resizeMode === 'none') {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 1; // Default for other modes
|
||||||
|
}, [resizeMode, videoAspectRatio, screenDimensions.width, screenDimensions.height]);
|
||||||
const [customVideoStyles, setCustomVideoStyles] = useState<any>({});
|
const [customVideoStyles, setCustomVideoStyles] = useState<any>({});
|
||||||
const [zoomScale, setZoomScale] = useState(1);
|
const [zoomScale, setZoomScale] = useState(1);
|
||||||
const [zoomTranslateX, setZoomTranslateX] = useState(0);
|
const [zoomTranslateX, setZoomTranslateX] = useState(0);
|
||||||
|
|
@ -479,7 +598,7 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
}, [metadata]);
|
}, [metadata]);
|
||||||
|
|
||||||
// Resolve current episode description for series
|
// Resolve current episode description for series
|
||||||
const currentEpisodeDescription = (() => {
|
const currentEpisodeDescription = useMemo(() => {
|
||||||
try {
|
try {
|
||||||
if ((type as any) !== 'series') return '';
|
if ((type as any) !== 'series') return '';
|
||||||
const allEpisodes = Object.values(groupedEpisodes || {}).flat() as any[];
|
const allEpisodes = Object.values(groupedEpisodes || {}).flat() as any[];
|
||||||
|
|
@ -495,7 +614,7 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
} catch {
|
} catch {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
})();
|
}, [type, groupedEpisodes, episodeId, season, episode]);
|
||||||
|
|
||||||
// Find next episode for series
|
// Find next episode for series
|
||||||
const nextEpisode = useMemo(() => {
|
const nextEpisode = useMemo(() => {
|
||||||
|
|
@ -538,15 +657,6 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
}).start(() => setShowControls(false));
|
}).start(() => setShowControls(false));
|
||||||
};
|
};
|
||||||
|
|
||||||
const calculateVideoStyles = (videoWidth: number, videoHeight: number, screenWidth: number, screenHeight: number) => {
|
|
||||||
return {
|
|
||||||
position: 'absolute',
|
|
||||||
top: 0,
|
|
||||||
left: 0,
|
|
||||||
width: screenWidth,
|
|
||||||
height: screenHeight,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const onPinchGestureEvent = (event: PinchGestureHandlerGestureEvent) => {
|
const onPinchGestureEvent = (event: PinchGestureHandlerGestureEvent) => {
|
||||||
const { scale } = event.nativeEvent;
|
const { scale } = event.nativeEvent;
|
||||||
|
|
@ -677,34 +787,15 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Apply memoized calculations to state
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (videoAspectRatio && screenDimensions.width > 0 && screenDimensions.height > 0) {
|
setCustomVideoStyles(videoStyles);
|
||||||
const styles = calculateVideoStyles(
|
setZoomScale(zoomFactor);
|
||||||
videoAspectRatio * 1000,
|
|
||||||
1000,
|
|
||||||
screenDimensions.width,
|
|
||||||
screenDimensions.height
|
|
||||||
);
|
|
||||||
setCustomVideoStyles(styles);
|
|
||||||
|
|
||||||
// Recalculate zoom for cover mode when video aspect ratio changes
|
if (DEBUG_MODE && resizeMode === 'cover') {
|
||||||
if (resizeMode === 'cover') {
|
logger.log(`[AndroidVideoPlayer] Cover zoom updated: ${zoomFactor.toFixed(2)}x (video AR: ${videoAspectRatio?.toFixed(2)})`);
|
||||||
const screenAspect = screenDimensions.width / screenDimensions.height;
|
|
||||||
const zoomFactor = Math.max(screenAspect / videoAspectRatio, videoAspectRatio / screenAspect);
|
|
||||||
setZoomScale(zoomFactor);
|
|
||||||
if (DEBUG_MODE) {
|
|
||||||
logger.log(`[AndroidVideoPlayer] Cover zoom updated: ${zoomFactor.toFixed(2)}x (video AR: ${videoAspectRatio.toFixed(2)})`);
|
|
||||||
}
|
|
||||||
} else if (resizeMode === 'none') {
|
|
||||||
// Ensure none mode has no zoom
|
|
||||||
setZoomScale(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (DEBUG_MODE) {
|
|
||||||
if (__DEV__) logger.log(`[AndroidVideoPlayer] Screen dimensions changed, recalculated styles:`, styles);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, [screenDimensions, videoAspectRatio, resizeMode]);
|
}, [videoStyles, zoomFactor, resizeMode, videoAspectRatio]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const subscription = Dimensions.addEventListener('change', ({ screen }) => {
|
const subscription = Dimensions.addEventListener('change', ({ screen }) => {
|
||||||
|
|
@ -1063,15 +1154,15 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Slider callback functions for React Native Community Slider
|
// Slider callback functions for React Native Community Slider
|
||||||
const handleSliderValueChange = (value: number) => {
|
const handleSliderValueChange = useCallback((value: number) => {
|
||||||
if (isDragging && duration > 0) {
|
if (isDragging && duration > 0) {
|
||||||
const seekTime = Math.min(value, duration - END_EPSILON);
|
const seekTime = Math.min(value, duration - END_EPSILON);
|
||||||
|
|
||||||
pendingSeekValue.current = seekTime;
|
pendingSeekValue.current = seekTime;
|
||||||
}
|
}
|
||||||
};
|
}, [isDragging, duration]);
|
||||||
|
|
||||||
const handleSlidingStart = () => {
|
const handleSlidingStart = useCallback(() => {
|
||||||
setIsDragging(true);
|
setIsDragging(true);
|
||||||
// Keep controls visible while dragging and cancel any hide timeout
|
// Keep controls visible while dragging and cancel any hide timeout
|
||||||
if (!showControls) setShowControls(true);
|
if (!showControls) setShowControls(true);
|
||||||
|
|
@ -1085,9 +1176,9 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
if (!paused) setPaused(true);
|
if (!paused) setPaused(true);
|
||||||
logger.log('[AndroidVideoPlayer] handleSlidingStart: pausing for iOS drag');
|
logger.log('[AndroidVideoPlayer] handleSlidingStart: pausing for iOS drag');
|
||||||
}
|
}
|
||||||
};
|
}, [showControls, paused]);
|
||||||
|
|
||||||
const handleSlidingComplete = (value: number) => {
|
const handleSlidingComplete = useCallback((value: number) => {
|
||||||
setIsDragging(false);
|
setIsDragging(false);
|
||||||
if (duration > 0) {
|
if (duration > 0) {
|
||||||
const seekTime = Math.min(value, duration - END_EPSILON);
|
const seekTime = Math.min(value, duration - END_EPSILON);
|
||||||
|
|
@ -1108,7 +1199,7 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
// Ensure controls are visible, then schedule auto-hide
|
// Ensure controls are visible, then schedule auto-hide
|
||||||
if (!showControls) setShowControls(true);
|
if (!showControls) setShowControls(true);
|
||||||
controlsTimeout.current = setTimeout(hideControls, 5000);
|
controlsTimeout.current = setTimeout(hideControls, 5000);
|
||||||
};
|
}, [duration, showControls]);
|
||||||
|
|
||||||
// Ensure auto-hide resumes after drag ends
|
// Ensure auto-hide resumes after drag ends
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -1367,12 +1458,19 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const skip = (seconds: number) => {
|
const skip = useCallback((seconds: number) => {
|
||||||
const newTime = Math.max(0, Math.min(currentTime + seconds, duration - END_EPSILON));
|
const newTime = Math.max(0, Math.min(currentTime + seconds, duration - END_EPSILON));
|
||||||
seekToTime(newTime);
|
seekToTime(newTime);
|
||||||
};
|
}, [currentTime, duration]);
|
||||||
|
|
||||||
const cycleAspectRatio = () => {
|
const cycleAspectRatio = useCallback(() => {
|
||||||
|
// Prevent rapid successive resize operations
|
||||||
|
if (resizeTimeoutRef.current) {
|
||||||
|
if (DEBUG_MODE) {
|
||||||
|
logger.log('[AndroidVideoPlayer] Resize operation debounced - ignoring rapid click');
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Cycle through allowed resize modes per platform
|
// Cycle through allowed resize modes per platform
|
||||||
// Android: exclude 'contain' for both VLC and RN Video (not well supported)
|
// Android: exclude 'contain' for both VLC and RN Video (not well supported)
|
||||||
let resizeModes: ResizeModeType[];
|
let resizeModes: ResizeModeType[];
|
||||||
|
|
@ -1414,7 +1512,12 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
logger.log(`[AndroidVideoPlayer] Resize mode changed to: ${newResizeMode}`);
|
logger.log(`[AndroidVideoPlayer] Resize mode changed to: ${newResizeMode}`);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
// Debounce for 300ms to prevent rapid successive operations
|
||||||
|
resizeTimeoutRef.current = setTimeout(() => {
|
||||||
|
resizeTimeoutRef.current = null;
|
||||||
|
}, 300);
|
||||||
|
}, [resizeMode]);
|
||||||
|
|
||||||
const enableImmersiveMode = () => {
|
const enableImmersiveMode = () => {
|
||||||
StatusBar.setHidden(true, 'none');
|
StatusBar.setHidden(true, 'none');
|
||||||
|
|
@ -1439,7 +1542,7 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClose = async () => {
|
const handleClose = useCallback(async () => {
|
||||||
// Prevent multiple close attempts
|
// Prevent multiple close attempts
|
||||||
if (isSyncingBeforeClose) {
|
if (isSyncingBeforeClose) {
|
||||||
logger.log('[AndroidVideoPlayer] Close already in progress, ignoring duplicate call');
|
logger.log('[AndroidVideoPlayer] Close already in progress, ignoring duplicate call');
|
||||||
|
|
@ -1515,7 +1618,7 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
|
|
||||||
// Start background sync without blocking UI
|
// Start background sync without blocking UI
|
||||||
backgroundSync();
|
backgroundSync();
|
||||||
};
|
}, [isSyncingBeforeClose, currentTime, duration, traktAutosync, navigation, metadata, imdbId, backdrop]);
|
||||||
|
|
||||||
const handleResume = async () => {
|
const handleResume = async () => {
|
||||||
if (resumePosition) {
|
if (resumePosition) {
|
||||||
|
|
@ -1831,7 +1934,7 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Wrapper function to convert number to SelectedTrack for modal usage
|
// Wrapper function to convert number to SelectedTrack for modal usage
|
||||||
const selectAudioTrackById = (trackId: number) => {
|
const selectAudioTrackById = useCallback((trackId: number) => {
|
||||||
if (useVLC) {
|
if (useVLC) {
|
||||||
// For VLC, directly set the selected track
|
// For VLC, directly set the selected track
|
||||||
selectVlcAudioTrack(trackId);
|
selectVlcAudioTrack(trackId);
|
||||||
|
|
@ -1840,9 +1943,9 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
const trackSelection: SelectedTrack = { type: SelectedTrackType.INDEX, value: trackId };
|
const trackSelection: SelectedTrack = { type: SelectedTrackType.INDEX, value: trackId };
|
||||||
selectAudioTrack(trackSelection);
|
selectAudioTrack(trackSelection);
|
||||||
}
|
}
|
||||||
};
|
}, [useVLC, selectVlcAudioTrack, selectAudioTrack]);
|
||||||
|
|
||||||
const selectTextTrack = (trackId: number) => {
|
const selectTextTrack = useCallback((trackId: number) => {
|
||||||
if (useVLC) {
|
if (useVLC) {
|
||||||
// For VLC, directly set the selected subtitle track and disable custom subtitles
|
// For VLC, directly set the selected subtitle track and disable custom subtitles
|
||||||
if (trackId === -999) {
|
if (trackId === -999) {
|
||||||
|
|
@ -1866,7 +1969,7 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
setSelectedTextTrack(trackId);
|
setSelectedTextTrack(trackId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}, [useVLC, selectVlcSubtitleTrack]);
|
||||||
|
|
||||||
const loadSubtitleSize = async () => {
|
const loadSubtitleSize = async () => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -2071,7 +2174,7 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const togglePlayback = () => {
|
const togglePlayback = useCallback(() => {
|
||||||
const newPausedState = !paused;
|
const newPausedState = !paused;
|
||||||
if (useVLC && vlcRef.current) {
|
if (useVLC && vlcRef.current) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -2089,7 +2192,7 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
if (duration > 0) {
|
if (duration > 0) {
|
||||||
traktAutosync.handleProgressUpdate(currentTime, duration, true);
|
traktAutosync.handleProgressUpdate(currentTime, duration, true);
|
||||||
}
|
}
|
||||||
};
|
}, [paused, useVLC, currentTime, duration, traktAutosync]);
|
||||||
|
|
||||||
// Handle next episode button press
|
// Handle next episode button press
|
||||||
const handlePlayNextEpisode = useCallback(async () => {
|
const handlePlayNextEpisode = useCallback(async () => {
|
||||||
|
|
@ -2906,29 +3009,14 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
autoplay={!paused}
|
autoplay={!paused}
|
||||||
onFirstPlay={(info: any) => {
|
onFirstPlay={(info: any) => {
|
||||||
try {
|
try {
|
||||||
console.log('🎬 [VLC] Video loaded, extracting tracks...');
|
if (DEBUG_MODE) {
|
||||||
|
console.log('🎬 [VLC] Video loaded, extracting tracks...');
|
||||||
|
}
|
||||||
logger.log('[AndroidVideoPlayer][VLC] Video loaded successfully');
|
logger.log('[AndroidVideoPlayer][VLC] Video loaded successfully');
|
||||||
|
|
||||||
// Extract and format VLC tracks
|
// Process VLC tracks using optimized function
|
||||||
if (info?.tracks) {
|
if (info?.tracks) {
|
||||||
const { audio = [], subtitle = [] } = info.tracks;
|
processVlcTracks(info.tracks, 'onFirstPlay');
|
||||||
|
|
||||||
// Format audio tracks
|
|
||||||
if (Array.isArray(audio) && audio.length > 0) {
|
|
||||||
const formattedAudio = formatVlcTracks(audio);
|
|
||||||
setVlcAudioTracks(formattedAudio);
|
|
||||||
console.log('🎬 [VLC] Audio tracks loaded:', formattedAudio.length, formattedAudio);
|
|
||||||
console.log('🎬 [VLC] Setting vlcAudioTracks state:', formattedAudio);
|
|
||||||
} else {
|
|
||||||
console.log('🎬 [VLC] No audio tracks to set');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Format subtitle tracks
|
|
||||||
if (Array.isArray(subtitle) && subtitle.length > 0) {
|
|
||||||
const formattedSubs = formatVlcTracks(subtitle);
|
|
||||||
setVlcSubtitleTracks(formattedSubs);
|
|
||||||
console.log('🎬 [VLC] Subtitle tracks loaded:', formattedSubs.length);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const lenSec = (info?.length ?? 0) / 1000;
|
const lenSec = (info?.length ?? 0) / 1000;
|
||||||
|
|
@ -2938,12 +3026,16 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
|
|
||||||
// Restore playback position after remount (workaround for surface detach)
|
// Restore playback position after remount (workaround for surface detach)
|
||||||
if (vlcRestoreTime !== undefined && vlcRestoreTime > 0) {
|
if (vlcRestoreTime !== undefined && vlcRestoreTime > 0) {
|
||||||
console.log('🎬 [VLC] Restoring playback position:', vlcRestoreTime);
|
if (DEBUG_MODE) {
|
||||||
|
console.log('🎬 [VLC] Restoring playback position:', vlcRestoreTime);
|
||||||
|
}
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (vlcRef.current && typeof vlcRef.current.seek === 'function') {
|
if (vlcRef.current && typeof vlcRef.current.seek === 'function') {
|
||||||
const seekPosition = Math.min(vlcRestoreTime / lenSec, 0.999); // Convert to fraction
|
const seekPosition = Math.min(vlcRestoreTime / lenSec, 0.999); // Convert to fraction
|
||||||
vlcRef.current.seek(seekPosition);
|
vlcRef.current.seek(seekPosition);
|
||||||
console.log('🎬 [VLC] Seeked to restore position');
|
if (DEBUG_MODE) {
|
||||||
|
console.log('🎬 [VLC] Seeked to restore position');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, 500); // Small delay to ensure player is ready
|
}, 500); // Small delay to ensure player is ready
|
||||||
setVlcRestoreTime(undefined); // Clear restore time
|
setVlcRestoreTime(undefined); // Clear restore time
|
||||||
|
|
@ -2973,24 +3065,13 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
}}
|
}}
|
||||||
onESAdded={(tracks: any) => {
|
onESAdded={(tracks: any) => {
|
||||||
try {
|
try {
|
||||||
console.log('🎬 [VLC] ES Added - processing tracks...');
|
if (DEBUG_MODE) {
|
||||||
|
console.log('🎬 [VLC] ES Added - processing tracks...');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process VLC tracks using optimized function
|
||||||
if (tracks) {
|
if (tracks) {
|
||||||
const { audio = [], subtitle = [] } = tracks;
|
processVlcTracks(tracks, 'onESAdded');
|
||||||
|
|
||||||
// Format audio tracks
|
|
||||||
if (Array.isArray(audio) && audio.length > 0) {
|
|
||||||
const formattedAudio = formatVlcTracks(audio);
|
|
||||||
setVlcAudioTracks(formattedAudio);
|
|
||||||
console.log('🎬 [VLC] ES Added - Audio tracks loaded:', formattedAudio.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Format subtitle tracks
|
|
||||||
if (Array.isArray(subtitle) && subtitle.length > 0) {
|
|
||||||
const formattedSubs = formatVlcTracks(subtitle);
|
|
||||||
setVlcSubtitleTracks(formattedSubs);
|
|
||||||
console.log('🎬 [VLC] ES Added - Subtitle tracks loaded:', formattedSubs.length);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('🎬 [VLC] onESAdded error:', e);
|
console.error('🎬 [VLC] onESAdded error:', e);
|
||||||
|
|
@ -3102,8 +3183,8 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
duration={duration}
|
duration={duration}
|
||||||
zoomScale={zoomScale}
|
zoomScale={zoomScale}
|
||||||
currentResizeMode={resizeMode}
|
currentResizeMode={resizeMode}
|
||||||
ksAudioTracks={useVLC ? vlcAudioTracksForModal : rnVideoAudioTracks}
|
ksAudioTracks={ksAudioTracks}
|
||||||
selectedAudioTrack={useVLC ? (vlcSelectedAudioTrack ?? null) : (selectedAudioTrack?.type === SelectedTrackType.INDEX && selectedAudioTrack.value !== undefined ? Number(selectedAudioTrack.value) : null)}
|
selectedAudioTrack={computedSelectedAudioTrack}
|
||||||
availableStreams={availableStreams}
|
availableStreams={availableStreams}
|
||||||
togglePlayback={togglePlayback}
|
togglePlayback={togglePlayback}
|
||||||
skip={skip}
|
skip={skip}
|
||||||
|
|
@ -3698,12 +3779,6 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
|
|
||||||
|
|
||||||
<>
|
<>
|
||||||
{console.log('🎬 [AndroidVideoPlayer] AudioTrackModal props:', {
|
|
||||||
useVLC,
|
|
||||||
vlcAudioTracksForModal,
|
|
||||||
rnVideoAudioTracks,
|
|
||||||
finalTracks: useVLC ? vlcAudioTracksForModal : rnVideoAudioTracks
|
|
||||||
})}
|
|
||||||
<AudioTrackModal
|
<AudioTrackModal
|
||||||
showAudioModal={showAudioModal}
|
showAudioModal={showAudioModal}
|
||||||
setShowAudioModal={setShowAudioModal}
|
setShowAudioModal={setShowAudioModal}
|
||||||
|
|
@ -3721,8 +3796,8 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
isLoadingSubtitles={isLoadingSubtitles}
|
isLoadingSubtitles={isLoadingSubtitles}
|
||||||
customSubtitles={customSubtitles}
|
customSubtitles={customSubtitles}
|
||||||
availableSubtitles={availableSubtitles}
|
availableSubtitles={availableSubtitles}
|
||||||
ksTextTracks={useVLC ? vlcSubtitleTracksForModal : rnVideoTextTracks}
|
ksTextTracks={ksTextTracks}
|
||||||
selectedTextTrack={useVLC ? (vlcSelectedSubtitleTrack ?? -1) : selectedTextTrack}
|
selectedTextTrack={computedSelectedTextTrack}
|
||||||
useCustomSubtitles={useCustomSubtitles}
|
useCustomSubtitles={useCustomSubtitles}
|
||||||
subtitleSize={subtitleSize}
|
subtitleSize={subtitleSize}
|
||||||
subtitleBackground={subtitleBackground}
|
subtitleBackground={subtitleBackground}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue