mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-04-22 01:01:56 +00:00
streamscreen to player refactor
This commit is contained in:
parent
6bdc998496
commit
3801e80dd9
5 changed files with 743 additions and 732 deletions
|
|
@ -541,7 +541,7 @@ const SeriesContentComponent: React.FC<SeriesContentProps> = ({
|
|||
return null;
|
||||
}
|
||||
|
||||
if (__DEV__) console.log('[SeriesContent] renderSeasonSelector called, current view mode:', seasonViewMode);
|
||||
|
||||
|
||||
const seasons = Object.keys(groupedEpisodes).map(Number).sort((a, b) => a - b);
|
||||
|
||||
|
|
@ -630,7 +630,7 @@ const SeriesContentComponent: React.FC<SeriesContentProps> = ({
|
|||
|
||||
if (seasonViewMode === 'text') {
|
||||
// Text-only view
|
||||
if (__DEV__) console.log('[SeriesContent] Rendering text view for season:', season, 'View mode ref:', seasonViewMode);
|
||||
|
||||
return (
|
||||
<View
|
||||
key={season}
|
||||
|
|
@ -668,7 +668,7 @@ const SeriesContentComponent: React.FC<SeriesContentProps> = ({
|
|||
}
|
||||
|
||||
// Poster view (current implementation)
|
||||
if (__DEV__) console.log('[SeriesContent] Rendering poster view for season:', season, 'View mode ref:', seasonViewMode);
|
||||
|
||||
return (
|
||||
<View
|
||||
key={season}
|
||||
|
|
@ -796,7 +796,7 @@ const SeriesContentComponent: React.FC<SeriesContentProps> = ({
|
|||
const effectiveVote = imdbRating ?? tmdbRating ?? 0;
|
||||
const isImdbRating = imdbRating !== null;
|
||||
|
||||
logger.log(`[SeriesContent] Vertical card S${episode.season_number}E${episode.episode_number}: IMDb=${imdbRating}, TMDB=${tmdbRating}, effective=${effectiveVote}, isImdb=${isImdbRating}`);
|
||||
|
||||
|
||||
const effectiveRuntime = tmdbOverride?.runtime ?? (episode as any).runtime;
|
||||
if (!episode.still_path && tmdbOverride?.still_path) {
|
||||
|
|
@ -1067,8 +1067,6 @@ const SeriesContentComponent: React.FC<SeriesContentProps> = ({
|
|||
const isImdbRating = imdbRating !== null;
|
||||
const effectiveRuntime = tmdbOverride?.runtime ?? (episode as any).runtime;
|
||||
|
||||
logger.log(`[SeriesContent] Horizontal card S${episode.season_number}E${episode.episode_number}: IMDb=${imdbRating}, TMDB=${tmdbRating}, effective=${effectiveVote}, isImdb=${isImdbRating}`);
|
||||
|
||||
const formatDate = (dateString: string) => {
|
||||
const date = new Date(dateString);
|
||||
return date.toLocaleDateString('en-US', {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useState, useRef, useEffect, useMemo, useCallback } from 'react';
|
||||
import { View, TouchableOpacity, TouchableWithoutFeedback, Dimensions, Animated, ActivityIndicator, Platform, NativeModules, StatusBar, Text, StyleSheet, Modal, AppState, Image } from 'react-native';
|
||||
import { View, TouchableOpacity, TouchableWithoutFeedback, Dimensions, Animated, ActivityIndicator, Platform, NativeModules, StatusBar, Text, StyleSheet, Modal, AppState, Image, InteractionManager } from 'react-native';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import Video, { VideoRef, SelectedTrack, SelectedTrackType, BufferingStrategyType, ViewType } from 'react-native-video';
|
||||
import FastImage from '@d11/react-native-fast-image';
|
||||
|
|
@ -641,6 +641,8 @@ const AndroidVideoPlayer: React.FC = () => {
|
|||
|
||||
// Prefetch backdrop and title logo for faster loading screen appearance
|
||||
useEffect(() => {
|
||||
// Defer prefetching until after navigation animation completes
|
||||
const task = InteractionManager.runAfterInteractions(() => {
|
||||
if (backdrop && typeof backdrop === 'string') {
|
||||
// Reset loading state
|
||||
setIsBackdropLoaded(false);
|
||||
|
|
@ -667,9 +669,13 @@ const AndroidVideoPlayer: React.FC = () => {
|
|||
setIsBackdropLoaded(true);
|
||||
backdropImageOpacityAnim.setValue(0);
|
||||
}
|
||||
});
|
||||
return () => task.cancel();
|
||||
}, [backdrop]);
|
||||
|
||||
useEffect(() => {
|
||||
// Defer logo prefetch until after navigation animation
|
||||
const task = InteractionManager.runAfterInteractions(() => {
|
||||
const logoUrl = (metadata && (metadata as any).logo) as string | undefined;
|
||||
if (logoUrl && typeof logoUrl === 'string') {
|
||||
try {
|
||||
|
|
@ -678,6 +684,8 @@ const AndroidVideoPlayer: React.FC = () => {
|
|||
// Silently ignore logo prefetch errors
|
||||
}
|
||||
}
|
||||
});
|
||||
return () => task.cancel();
|
||||
}, [metadata]);
|
||||
|
||||
// Resolve current episode description for series
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useState, useRef, useEffect, useMemo, useCallback } from 'react';
|
||||
import { View, TouchableOpacity, Dimensions, Animated, ActivityIndicator, Platform, NativeModules, StatusBar, Text, StyleSheet, Modal, AppState } from 'react-native';
|
||||
import { View, TouchableOpacity, Dimensions, Animated, ActivityIndicator, Platform, NativeModules, StatusBar, Text, StyleSheet, Modal, AppState, InteractionManager } from 'react-native';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import { useNavigation, useRoute, RouteProp, useFocusEffect } from '@react-navigation/native';
|
||||
import FastImage from '@d11/react-native-fast-image';
|
||||
|
|
@ -342,6 +342,8 @@ const KSPlayerCore: React.FC = () => {
|
|||
// Load custom backdrop on mount
|
||||
// Prefetch backdrop and title logo for faster loading screen appearance
|
||||
useEffect(() => {
|
||||
// Defer prefetching until after navigation animation completes
|
||||
const task = InteractionManager.runAfterInteractions(() => {
|
||||
if (backdrop && typeof backdrop === 'string') {
|
||||
// Reset loading state
|
||||
setIsBackdropLoaded(false);
|
||||
|
|
@ -368,9 +370,13 @@ const KSPlayerCore: React.FC = () => {
|
|||
setIsBackdropLoaded(true);
|
||||
backdropImageOpacityAnim.setValue(0);
|
||||
}
|
||||
});
|
||||
return () => task.cancel();
|
||||
}, [backdrop]);
|
||||
|
||||
useEffect(() => {
|
||||
// Defer logo prefetch until after navigation animation
|
||||
const task = InteractionManager.runAfterInteractions(() => {
|
||||
const logoUrl = (metadata && (metadata as any).logo) as string | undefined;
|
||||
if (logoUrl && typeof logoUrl === 'string') {
|
||||
try {
|
||||
|
|
@ -379,6 +385,8 @@ const KSPlayerCore: React.FC = () => {
|
|||
// Silently ignore logo prefetch errors
|
||||
}
|
||||
}
|
||||
});
|
||||
return () => task.cancel();
|
||||
}, [metadata]);
|
||||
|
||||
// Log video source configuration with headers
|
||||
|
|
|
|||
|
|
@ -1210,7 +1210,7 @@ const InnerNavigator = ({ initialRouteName }: { initialRouteName?: keyof RootSta
|
|||
options={{
|
||||
animation: 'default',
|
||||
animationDuration: 0,
|
||||
// Force fullscreen presentation on iPad
|
||||
// fullScreenModal required for proper video rendering on iOS
|
||||
presentation: 'fullScreenModal',
|
||||
// Disable gestures during video playback
|
||||
gestureEnabled: false,
|
||||
|
|
|
|||
|
|
@ -169,7 +169,7 @@ export const StreamsScreen = () => {
|
|||
try {
|
||||
setAlertTitle(title);
|
||||
setAlertMessage(message);
|
||||
setAlertActions(actions && actions.length > 0 ? actions : [{ label: 'OK', onPress: () => {} }]);
|
||||
setAlertActions(actions && actions.length > 0 ? actions : [{ label: 'OK', onPress: () => { } }]);
|
||||
setAlertVisible(true);
|
||||
} catch (error) {
|
||||
console.warn('[StreamsScreen] Error showing alert:', error);
|
||||
|
|
@ -180,7 +180,7 @@ export const StreamsScreen = () => {
|
|||
|
||||
// Track when we started fetching streams so we can show an extended loading state
|
||||
const [streamsLoadStart, setStreamsLoadStart] = useState<number | null>(null);
|
||||
const [providerLoadTimes, setProviderLoadTimes] = useState<{[key: string]: number}>({});
|
||||
const [providerLoadTimes, setProviderLoadTimes] = useState<{ [key: string]: number }>({});
|
||||
|
||||
// Prevent excessive re-renders by using this guard
|
||||
const guardedSetState = useCallback((setter: () => void) => {
|
||||
|
|
@ -228,7 +228,7 @@ export const StreamsScreen = () => {
|
|||
} = useMetadata({ id, type });
|
||||
|
||||
// Get backdrop from metadata assets
|
||||
const setMetadataStub = useCallback(() => {}, []);
|
||||
const setMetadataStub = useCallback(() => { }, []);
|
||||
const memoizedSettings = useMemo(() => settings, [settings.logoSourcePreference, settings.tmdbLanguagePreference, settings.enrichMetadataWithTMDB]);
|
||||
const { bannerImage } = useMetadataAssets(metadata, id, type, imdbId, memoizedSettings, setMetadataStub);
|
||||
|
||||
|
|
@ -240,7 +240,7 @@ export const StreamsScreen = () => {
|
|||
|
||||
|
||||
// Add state for provider loading status
|
||||
const [loadingProviders, setLoadingProviders] = useState<{[key: string]: boolean}>({});
|
||||
const [loadingProviders, setLoadingProviders] = useState<{ [key: string]: boolean }>({});
|
||||
|
||||
// Add state for more detailed provider loading tracking
|
||||
const [providerStatus, setProviderStatus] = useState<{
|
||||
|
|
@ -296,7 +296,7 @@ export const StreamsScreen = () => {
|
|||
const map: Record<string, string> = {};
|
||||
// No direct way to iterate Map keys safely without exposing it; copy known ids on demand during render
|
||||
setScraperLogos(prev => prev); // no-op to ensure consistency
|
||||
}).catch(() => {});
|
||||
}).catch(() => { });
|
||||
}
|
||||
};
|
||||
preloadScraperLogos();
|
||||
|
|
@ -826,9 +826,6 @@ export const StreamsScreen = () => {
|
|||
streamName: stream.name || stream.title
|
||||
});
|
||||
|
||||
// Add 50ms delay before navigating to player
|
||||
await new Promise(resolve => setTimeout(resolve, 50));
|
||||
|
||||
// Prepare available streams for the change source feature
|
||||
const streamsToPass = (type === 'series' || (type === 'other' && selectedEpisode)) ? episodeStreams : groupedStreams;
|
||||
|
||||
|
|
@ -881,7 +878,7 @@ export const StreamsScreen = () => {
|
|||
if (!videoType && /xprime/i.test(providerId)) {
|
||||
videoType = 'm3u8';
|
||||
}
|
||||
} catch {}
|
||||
} catch { }
|
||||
|
||||
// Simple platform check - iOS uses KSPlayerCore, Android uses AndroidVideoPlayer
|
||||
const playerRoute = Platform.OS === 'ios' ? 'PlayerIOS' : 'PlayerAndroid';
|
||||
|
|
@ -920,7 +917,7 @@ export const StreamsScreen = () => {
|
|||
if (typeof stream.url === 'string' && stream.url.startsWith('magnet:')) {
|
||||
try {
|
||||
openAlert('Not supported', 'Torrent streaming is not supported yet.');
|
||||
} catch (_e) {}
|
||||
} catch (_e) { }
|
||||
return;
|
||||
}
|
||||
// If stream is actually MKV format, force the in-app VLC-based player on iOS
|
||||
|
|
@ -1137,7 +1134,7 @@ export const StreamsScreen = () => {
|
|||
clearTimeout(renderTimer);
|
||||
};
|
||||
}
|
||||
return () => {};
|
||||
return () => { };
|
||||
}, [])
|
||||
);
|
||||
|
||||
|
|
@ -1687,7 +1684,7 @@ export const StreamsScreen = () => {
|
|||
}
|
||||
// Deduplicate and prefetch
|
||||
Array.from(new Set(urls)).forEach(u => {
|
||||
RNImage.prefetch(u).catch(() => {});
|
||||
RNImage.prefetch(u).catch(() => { });
|
||||
});
|
||||
}, [episodeImage, bannerImage, metadata]);
|
||||
|
||||
|
|
@ -1945,7 +1942,7 @@ export const StreamsScreen = () => {
|
|||
</View>
|
||||
)}
|
||||
|
||||
{currentEpisode && (
|
||||
{currentEpisode && (
|
||||
<View style={[
|
||||
styles.streamsHeroContainer,
|
||||
!settings.enableStreamsBackdrop && { backgroundColor: colors.darkBackground }
|
||||
|
|
@ -2076,7 +2073,7 @@ export const StreamsScreen = () => {
|
|||
)}
|
||||
|
||||
{/* Update the streams/loading state display logic */}
|
||||
{ showNoSourcesError ? (
|
||||
{showNoSourcesError ? (
|
||||
<View
|
||||
style={styles.noStreams}
|
||||
>
|
||||
|
|
|
|||
Loading…
Reference in a new issue