Implement stream provider checks and error handling in StreamsScreen

This update enhances the StreamsScreen component by adding checks for available streaming sources, improving user feedback when no sources are available. A new state for displaying an error message is introduced, along with logic to handle the loading of streams based on the availability of Stremio addons and internal providers. Additionally, the UI is updated to reflect these changes, ensuring a more responsive and informative user experience during content loading.
This commit is contained in:
tapframe 2025-06-20 18:07:30 +05:30
parent 93a016b96e
commit 71e2d39d96
2 changed files with 131 additions and 51 deletions

View file

@ -390,6 +390,13 @@ export const StreamsScreen = () => {
const [autoplayTriggered, setAutoplayTriggered] = useState(false);
const [isAutoplayWaiting, setIsAutoplayWaiting] = useState(false);
// Add check for available streaming sources
const [hasStreamProviders, setHasStreamProviders] = useState(true); // Assume true initially
const [hasStremioStreamProviders, setHasStremioStreamProviders] = useState(true); // For footer logic
// Add state for no sources error
const [showNoSourcesError, setShowNoSourcesError] = useState(false);
// Monitor streams loading and update available providers immediately
useEffect(() => {
// Skip processing if component is unmounting
@ -433,35 +440,50 @@ export const StreamsScreen = () => {
}
}, [selectedProvider, availableProviders]);
// Update useEffect to check for sources
useEffect(() => {
const checkProviders = async () => {
// Check for both Stremio addons and if the internal provider is enabled
const hasStremioProviders = await stremioService.hasStreamProviders();
const hasProviders = hasStremioProviders || settings.enableInternalProviders;
if (!isMounted.current) return;
React.useEffect(() => {
if (type === 'series' && episodeId) {
logger.log(`🎬 Loading episode streams for: ${episodeId}`);
setLoadingProviders({
'stremio': true,
'hdrezka': true
});
setSelectedEpisode(episodeId);
loadEpisodeStreams(episodeId);
} else if (type === 'movie') {
logger.log(`🎬 Loading movie streams for: ${id}`);
// setLoadingProviders({ // This is now handled by the main effect
// 'stremio': true,
// 'hdrezka': true
// });
loadStreams();
}
setHasStreamProviders(hasProviders);
setHasStremioStreamProviders(hasStremioProviders);
// 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]);
if (!hasProviders) {
const timer = setTimeout(() => {
if (isMounted.current) setShowNoSourcesError(true);
}, 500);
return () => clearTimeout(timer);
} else {
if (type === 'series' && episodeId) {
logger.log(`🎬 Loading episode streams for: ${episodeId}`);
setLoadingProviders({
'stremio': true,
'hdrezka': true
});
setSelectedEpisode(episodeId);
loadEpisodeStreams(episodeId);
} else if (type === 'movie') {
logger.log(`🎬 Loading movie streams for: ${id}`);
loadStreams();
}
// Reset autoplay state when content changes
setAutoplayTriggered(false);
if (settings.autoplayBestStream) {
setIsAutoplayWaiting(true);
logger.log('🔄 Autoplay enabled, waiting for best stream...');
} else {
setIsAutoplayWaiting(false);
}
}
};
checkProviders();
}, [type, id, episodeId, settings.autoplayBestStream, settings.enableInternalProviders]);
React.useEffect(() => {
// Trigger entrance animations
@ -1281,9 +1303,25 @@ export const StreamsScreen = () => {
)}
</Animated.View>
{/* Show streams immediately as they become available */}
{Object.keys(streams).length === 0 ? (
// Only show initial loading if no streams are available yet
{/* Update the streams/loading state display logic */}
{ showNoSourcesError ? (
<Animated.View
entering={FadeIn.duration(300)}
style={styles.noStreams}
>
<MaterialIcons name="error-outline" size={48} color={colors.textMuted} />
<Text style={styles.noStreamsText}>No streaming sources available</Text>
<Text style={styles.noStreamsSubText}>
Please add streaming sources in settings
</Text>
<TouchableOpacity
style={styles.addSourcesButton}
onPress={() => navigation.navigate('Settings')}
>
<Text style={styles.addSourcesButtonText}>Add Sources</Text>
</TouchableOpacity>
</Animated.View>
) : Object.keys(streams).length === 0 ? (
(loadingStreams || loadingEpisodeStreams) ? (
<Animated.View
entering={FadeIn.duration(300)}
@ -1336,7 +1374,7 @@ export const StreamsScreen = () => {
bounces={true}
overScrollMode="never"
ListFooterComponent={
(loadingStreams || loadingEpisodeStreams) ? (
(loadingStreams || loadingEpisodeStreams) && hasStremioStreamProviders ? (
<View style={styles.footerLoading}>
<ActivityIndicator size="small" color={colors.primary} />
<Text style={styles.footerLoadingText}>Loading more sources...</Text>
@ -1394,25 +1432,25 @@ const createStyles = (colors: any) => StyleSheet.create({
flexGrow: 0,
},
filterChip: {
backgroundColor: colors.transparentLight,
backgroundColor: 'transparent',
paddingHorizontal: 16,
paddingVertical: 8,
borderRadius: 20,
marginRight: 8,
borderWidth: 1,
borderColor: colors.transparent,
borderColor: colors.border,
},
filterChipSelected: {
backgroundColor: colors.transparentLight,
backgroundColor: colors.primary,
borderColor: colors.primary,
},
filterChipText: {
color: colors.text,
color: colors.mediumEmphasis,
fontWeight: '500',
},
filterChipTextSelected: {
color: colors.primary,
fontWeight: 'bold',
color: colors.white,
fontWeight: '600',
},
streamsContent: {
flex: 1,
@ -1429,23 +1467,23 @@ const createStyles = (colors: any) => StyleSheet.create({
width: '100%',
},
streamGroupTitle: {
color: colors.text,
fontSize: 16,
fontWeight: '600',
marginBottom: 4,
color: colors.highEmphasis,
fontSize: 15,
fontWeight: '700',
marginBottom: 8,
marginTop: 0,
backgroundColor: 'transparent',
},
streamCard: {
flexDirection: 'row',
alignItems: 'flex-start',
padding: 12,
borderRadius: 12,
marginBottom: 8,
padding: 16,
borderRadius: 10,
marginBottom: 12,
minHeight: 70,
backgroundColor: colors.elevation1,
backgroundColor: colors.card,
borderWidth: 1,
borderColor: 'rgba(255,255,255,0.05)',
borderColor: colors.cardHighlight,
width: '100%',
zIndex: 1,
},
@ -1487,11 +1525,12 @@ const createStyles = (colors: any) => StyleSheet.create({
alignItems: 'center',
},
chip: {
paddingHorizontal: 8,
paddingVertical: 2,
paddingHorizontal: 10,
paddingVertical: 4,
borderRadius: 4,
marginRight: 4,
marginBottom: 4,
backgroundColor: colors.surfaceVariant,
},
chipText: {
color: colors.highEmphasis,
@ -1516,10 +1555,10 @@ const createStyles = (colors: any) => StyleSheet.create({
marginLeft: 8,
},
streamAction: {
width: 40,
height: 40,
borderRadius: 20,
backgroundColor: colors.elevation2,
width: 36,
height: 36,
borderRadius: 18,
backgroundColor: colors.card,
justifyContent: 'center',
alignItems: 'center',
},
@ -1793,6 +1832,24 @@ const createStyles = (colors: any) => StyleSheet.create({
marginLeft: 8,
fontWeight: '600',
},
noStreamsSubText: {
color: colors.mediumEmphasis,
fontSize: 14,
marginTop: 8,
textAlign: 'center',
},
addSourcesButton: {
marginTop: 24,
paddingHorizontal: 20,
paddingVertical: 10,
backgroundColor: colors.primary,
borderRadius: 8,
},
addSourcesButtonText: {
color: colors.white,
fontSize: 14,
fontWeight: '600',
},
});
export default memo(StreamsScreen);

View file

@ -1039,6 +1039,29 @@ class StremioService {
}
return false;
}
// Check if any installed addons can provide streams
async hasStreamProviders(): Promise<boolean> {
await this.ensureInitialized();
const addons = Array.from(this.installedAddons.values());
for (const addon of addons) {
if (addon.resources && Array.isArray(addon.resources)) {
// Check for 'stream' resource in the modern format
const hasStreamResource = addon.resources.some(resource =>
typeof resource === 'string'
? resource === 'stream'
: resource.name === 'stream'
);
if (hasStreamResource) {
return true;
}
}
}
return false;
}
}
export const stremioService = StremioService.getInstance();