diff --git a/src/screens/StreamsScreen.tsx b/src/screens/StreamsScreen.tsx index c3750c5c7..bb8698703 100644 --- a/src/screens/StreamsScreen.tsx +++ b/src/screens/StreamsScreen.tsx @@ -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 = () => { )} - {/* 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 ? ( + + + No streaming sources available + + Please add streaming sources in settings + + navigation.navigate('Settings')} + > + Add Sources + + + ) : Object.keys(streams).length === 0 ? ( (loadingStreams || loadingEpisodeStreams) ? ( { bounces={true} overScrollMode="never" ListFooterComponent={ - (loadingStreams || loadingEpisodeStreams) ? ( + (loadingStreams || loadingEpisodeStreams) && hasStremioStreamProviders ? ( Loading more sources... @@ -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); \ No newline at end of file diff --git a/src/services/stremioService.ts b/src/services/stremioService.ts index 7aef7993d..6415c145c 100644 --- a/src/services/stremioService.ts +++ b/src/services/stremioService.ts @@ -1039,6 +1039,29 @@ class StremioService { } return false; } + + // Check if any installed addons can provide streams + async hasStreamProviders(): Promise { + 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();