mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-03-11 17:45:38 +00:00
some fixes
This commit is contained in:
parent
8e8353635b
commit
b953e99e3d
3 changed files with 229 additions and 2 deletions
|
|
@ -36,6 +36,7 @@ export interface AppSettings {
|
|||
tmdbLanguagePreference: string; // Preferred language for TMDB logos (ISO 639-1 code)
|
||||
enableInternalProviders: boolean; // Toggle for internal providers like HDRezka
|
||||
episodeLayoutStyle: 'vertical' | 'horizontal'; // Layout style for episode cards
|
||||
autoplayBestStream: boolean; // Automatically play the best available stream
|
||||
}
|
||||
|
||||
export const DEFAULT_SETTINGS: AppSettings = {
|
||||
|
|
@ -54,6 +55,7 @@ export const DEFAULT_SETTINGS: AppSettings = {
|
|||
tmdbLanguagePreference: 'en', // Default to English
|
||||
enableInternalProviders: true, // Enable internal providers by default
|
||||
episodeLayoutStyle: 'horizontal', // Default to the new horizontal layout
|
||||
autoplayBestStream: false, // Disabled by default for user choice
|
||||
};
|
||||
|
||||
const SETTINGS_STORAGE_KEY = 'app_settings';
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import {
|
|||
Platform,
|
||||
TouchableOpacity,
|
||||
StatusBar,
|
||||
Switch,
|
||||
} from 'react-native';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import { useSettings, AppSettings } from '../hooks/useSettings';
|
||||
|
|
@ -219,6 +220,68 @@ const PlayerSettingsScreen: React.FC = () => {
|
|||
))}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={styles.section}>
|
||||
<Text
|
||||
style={[
|
||||
styles.sectionTitle,
|
||||
{ color: currentTheme.colors.textMuted },
|
||||
]}
|
||||
>
|
||||
PLAYBACK OPTIONS
|
||||
</Text>
|
||||
<View
|
||||
style={[
|
||||
styles.card,
|
||||
{
|
||||
backgroundColor: currentTheme.colors.elevation2,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<View style={styles.settingItem}>
|
||||
<View style={styles.settingContent}>
|
||||
<View style={[
|
||||
styles.settingIconContainer,
|
||||
{ backgroundColor: 'rgba(255,255,255,0.1)' }
|
||||
]}>
|
||||
<MaterialIcons
|
||||
name="play-arrow"
|
||||
size={20}
|
||||
color={currentTheme.colors.primary}
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.settingText}>
|
||||
<Text
|
||||
style={[
|
||||
styles.settingTitle,
|
||||
{ color: currentTheme.colors.text },
|
||||
]}
|
||||
>
|
||||
Auto-play Best Stream
|
||||
</Text>
|
||||
<Text
|
||||
style={[
|
||||
styles.settingDescription,
|
||||
{ color: currentTheme.colors.textMuted },
|
||||
]}
|
||||
>
|
||||
Automatically play the highest quality stream when available
|
||||
</Text>
|
||||
</View>
|
||||
<Switch
|
||||
value={settings.autoplayBestStream}
|
||||
onValueChange={(value) => updateSetting('autoplayBestStream', value)}
|
||||
trackColor={{
|
||||
false: 'rgba(255,255,255,0.2)',
|
||||
true: currentTheme.colors.primary + '40'
|
||||
}}
|
||||
thumbColor={settings.autoplayBestStream ? currentTheme.colors.primary : 'rgba(255,255,255,0.8)'}
|
||||
ios_backgroundColor="rgba(255,255,255,0.2)"
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</ScrollView>
|
||||
</SafeAreaView>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -302,6 +302,10 @@ export const StreamsScreen = () => {
|
|||
}
|
||||
}>({});
|
||||
|
||||
// Add state for autoplay functionality
|
||||
const [autoplayTriggered, setAutoplayTriggered] = useState(false);
|
||||
const [isAutoplayWaiting, setIsAutoplayWaiting] = useState(false);
|
||||
|
||||
// Monitor streams loading start and completion - FIXED to prevent loops
|
||||
useEffect(() => {
|
||||
// Skip processing if component is unmounting
|
||||
|
|
@ -438,6 +442,8 @@ export const StreamsScreen = () => {
|
|||
}
|
||||
}, [type, groupedStreams, episodeStreams, loadingStreams, loadingEpisodeStreams, selectedProvider]);
|
||||
|
||||
|
||||
|
||||
React.useEffect(() => {
|
||||
if (type === 'series' && episodeId) {
|
||||
logger.log(`🎬 Loading episode streams for: ${episodeId}`);
|
||||
|
|
@ -455,7 +461,16 @@ export const StreamsScreen = () => {
|
|||
// });
|
||||
loadStreams();
|
||||
}
|
||||
}, [type, episodeId]);
|
||||
|
||||
// Reset autoplay state when content changes
|
||||
setAutoplayTriggered(false);
|
||||
if (settings.autoplayBestStream) {
|
||||
setIsAutoplayWaiting(true);
|
||||
logger.log('🔄 Autoplay enabled, waiting for best stream...');
|
||||
} else {
|
||||
setIsAutoplayWaiting(false);
|
||||
}
|
||||
}, [type, episodeId, settings.autoplayBestStream]);
|
||||
|
||||
React.useEffect(() => {
|
||||
// Trigger entrance animations
|
||||
|
|
@ -500,6 +515,101 @@ export const StreamsScreen = () => {
|
|||
setSelectedProvider(provider);
|
||||
}, []);
|
||||
|
||||
// Function to determine the best stream based on quality, provider priority, and other factors
|
||||
const getBestStream = useCallback((streamsData: typeof groupedStreams): Stream | null => {
|
||||
if (!streamsData || Object.keys(streamsData).length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Helper function to extract quality as number
|
||||
const getQualityNumeric = (title: string | undefined): number => {
|
||||
if (!title) return 0;
|
||||
const matchWithP = title.match(/(\d+)p/i);
|
||||
if (matchWithP) return parseInt(matchWithP[1], 10);
|
||||
|
||||
const qualityPatterns = [
|
||||
/\b(240|360|480|720|1080|1440|2160|4320|8000)\b/i
|
||||
];
|
||||
|
||||
for (const pattern of qualityPatterns) {
|
||||
const match = title.match(pattern);
|
||||
if (match) {
|
||||
const quality = parseInt(match[1], 10);
|
||||
if (quality >= 240 && quality <= 8000) return quality;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
// Provider priority (higher number = higher priority)
|
||||
const getProviderPriority = (addonId: string): number => {
|
||||
if (addonId === 'hdrezka') return 100; // HDRezka highest priority
|
||||
|
||||
// Get Stremio addon installation order (earlier = higher priority)
|
||||
const installedAddons = stremioService.getInstalledAddons();
|
||||
const addonIndex = installedAddons.findIndex(addon => addon.id === addonId);
|
||||
|
||||
if (addonIndex !== -1) {
|
||||
// Higher priority for addons installed earlier (reverse index)
|
||||
return 50 - addonIndex;
|
||||
}
|
||||
|
||||
return 0; // Unknown providers get lowest priority
|
||||
};
|
||||
|
||||
// Collect all streams with metadata
|
||||
const allStreams: Array<{
|
||||
stream: Stream;
|
||||
quality: number;
|
||||
providerPriority: number;
|
||||
isDebrid: boolean;
|
||||
isCached: boolean;
|
||||
}> = [];
|
||||
|
||||
Object.entries(streamsData).forEach(([addonId, { streams }]) => {
|
||||
streams.forEach(stream => {
|
||||
const quality = getQualityNumeric(stream.name || stream.title);
|
||||
const providerPriority = getProviderPriority(addonId);
|
||||
const isDebrid = stream.behaviorHints?.cached || false;
|
||||
const isCached = isDebrid;
|
||||
|
||||
allStreams.push({
|
||||
stream,
|
||||
quality,
|
||||
providerPriority,
|
||||
isDebrid,
|
||||
isCached,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
if (allStreams.length === 0) return null;
|
||||
|
||||
// Sort streams by multiple criteria (best first)
|
||||
allStreams.sort((a, b) => {
|
||||
// 1. Prioritize cached/debrid streams
|
||||
if (a.isCached !== b.isCached) {
|
||||
return a.isCached ? -1 : 1;
|
||||
}
|
||||
|
||||
// 2. Prioritize higher quality
|
||||
if (a.quality !== b.quality) {
|
||||
return b.quality - a.quality;
|
||||
}
|
||||
|
||||
// 3. Prioritize better providers
|
||||
if (a.providerPriority !== b.providerPriority) {
|
||||
return b.providerPriority - a.providerPriority;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
logger.log(`🎯 Best stream selected: ${allStreams[0].stream.name || allStreams[0].stream.title} (Quality: ${allStreams[0].quality}p, Provider Priority: ${allStreams[0].providerPriority}, Cached: ${allStreams[0].isCached})`);
|
||||
|
||||
return allStreams[0].stream;
|
||||
}, []);
|
||||
|
||||
const currentEpisode = useMemo(() => {
|
||||
if (!selectedEpisode) return null;
|
||||
|
||||
|
|
@ -710,6 +820,48 @@ export const StreamsScreen = () => {
|
|||
}
|
||||
}, [settings.preferredPlayer, settings.useExternalPlayer, navigateToPlayer]);
|
||||
|
||||
// Autoplay effect - triggers when streams are available and autoplay is enabled
|
||||
useEffect(() => {
|
||||
if (
|
||||
settings.autoplayBestStream &&
|
||||
!autoplayTriggered &&
|
||||
!loadingStreams &&
|
||||
!loadingEpisodeStreams &&
|
||||
isAutoplayWaiting
|
||||
) {
|
||||
const streams = type === 'series' ? episodeStreams : groupedStreams;
|
||||
|
||||
if (Object.keys(streams).length > 0) {
|
||||
const bestStream = getBestStream(streams);
|
||||
|
||||
if (bestStream) {
|
||||
logger.log('🚀 Autoplay: Best stream found, starting playback...');
|
||||
setAutoplayTriggered(true);
|
||||
setIsAutoplayWaiting(false);
|
||||
|
||||
// Add a small delay to let the UI settle
|
||||
setTimeout(() => {
|
||||
handleStreamPress(bestStream);
|
||||
}, 500);
|
||||
} else {
|
||||
logger.log('⚠️ Autoplay: No suitable stream found');
|
||||
setIsAutoplayWaiting(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [
|
||||
settings.autoplayBestStream,
|
||||
autoplayTriggered,
|
||||
loadingStreams,
|
||||
loadingEpisodeStreams,
|
||||
isAutoplayWaiting,
|
||||
type,
|
||||
episodeStreams,
|
||||
groupedStreams,
|
||||
getBestStream,
|
||||
handleStreamPress
|
||||
]);
|
||||
|
||||
const filterItems = useMemo(() => {
|
||||
const installedAddons = stremioService.getInstalledAddons();
|
||||
const streams = type === 'series' ? episodeStreams : groupedStreams;
|
||||
|
|
@ -1140,7 +1292,17 @@ export const StreamsScreen = () => {
|
|||
style={styles.loadingContainer}
|
||||
>
|
||||
<ActivityIndicator size="large" color={colors.primary} />
|
||||
<Text style={styles.loadingText}>Finding available streams...</Text>
|
||||
<Text style={styles.loadingText}>
|
||||
{isAutoplayWaiting ? 'Finding best stream for autoplay...' : 'Finding available streams...'}
|
||||
</Text>
|
||||
</Animated.View>
|
||||
) : isAutoplayWaiting && !autoplayTriggered ? (
|
||||
<Animated.View
|
||||
entering={FadeIn.duration(300)}
|
||||
style={styles.loadingContainer}
|
||||
>
|
||||
<ActivityIndicator size="large" color={colors.primary} />
|
||||
<Text style={styles.loadingText}>Starting best stream...</Text>
|
||||
</Animated.View>
|
||||
) : Object.keys(streams).length === 0 && !loadingStreams && !loadingEpisodeStreams ? (
|
||||
<Animated.View
|
||||
|
|
|
|||
Loading…
Reference in a new issue