diff --git a/src/screens/StreamsScreen.tsx b/src/screens/StreamsScreen.tsx index e09c4ee5..d7f2f598 100644 --- a/src/screens/StreamsScreen.tsx +++ b/src/screens/StreamsScreen.tsx @@ -306,141 +306,48 @@ export const StreamsScreen = () => { const [autoplayTriggered, setAutoplayTriggered] = useState(false); const [isAutoplayWaiting, setIsAutoplayWaiting] = useState(false); - // Monitor streams loading start and completion - FIXED to prevent loops + // Monitor streams loading and update available providers immediately useEffect(() => { // Skip processing if component is unmounting if (!isMounted.current) return; - const now = Date.now(); - // Define all providers you expect to load. This could be dynamic. - const expectedProviders = ['stremio', 'hdrezka']; - - // Prevent infinite rerendering by using refs - if (loadingStreams || loadingEpisodeStreams) { - // --- Stream Loading has STARTED or is IN PROGRESS --- - // Only log once when loading starts - if (loadStartTimeRef.current === 0) { - logger.log("⏱️ Stream loading started or in progress..."); - // Update ref directly to avoid render cycle - loadStartTimeRef.current = now; - // Also update state for components that need it - setLoadStartTime(now); - } - - // Only update these once per loading cycle - if (!hasDoneInitialLoadRef.current) { - hasDoneInitialLoadRef.current = true; - - // Use the guarded setState to prevent issues after unmount - guardedSetState(() => setProviderLoadTimes({})); - - // Update provider status to loading for all expected providers - guardedSetState(() => setProviderStatus(prevStatus => { - const newStatus = { ...prevStatus }; - expectedProviders.forEach(providerId => { - // If not already marked as loading, or if it's a fresh cycle, set to loading - if (!newStatus[providerId] || !newStatus[providerId].loading) { - newStatus[providerId] = { - loading: true, - success: false, - error: false, - message: 'Loading...', - timeStarted: (newStatus[providerId]?.loading && newStatus[providerId]?.timeStarted) ? newStatus[providerId].timeStarted : now, - timeCompleted: 0, - }; - } - }); - return newStatus; - })); - - // Update simple loading flag for all expected providers - guardedSetState(() => setLoadingProviders(prevLoading => { - const newLoading = { ...prevLoading }; - expectedProviders.forEach(providerId => { - newLoading[providerId] = true; - }); - return newLoading; - })); - } - } else if (loadStartTimeRef.current > 0) { - // --- Stream Loading has FINISHED --- - logger.log("🏁 Stream loading finished. Processing results."); - - const currentStreamsData = type === 'series' ? episodeStreams : groupedStreams; - - // Find all providers that returned streams - const providersWithStreams = Object.entries(currentStreamsData) - .filter(([_, data]) => data.streams && data.streams.length > 0) - .map(([providerId]) => providerId); - + const currentStreamsData = type === 'series' ? episodeStreams : groupedStreams; + + // Update available providers immediately when streams change + const providersWithStreams = Object.entries(currentStreamsData) + .filter(([_, data]) => data.streams && data.streams.length > 0) + .map(([providerId]) => providerId); + + if (providersWithStreams.length > 0) { logger.log(`📊 Providers with streams: ${providersWithStreams.join(', ')}`); - - // Reset refs for next load cycle - loadStartTimeRef.current = 0; - hasDoneInitialLoadRef.current = false; - - // Update states only if component is still mounted - if (isMounted.current) { - // Update simple loading flag: all expected providers are no longer loading - guardedSetState(() => setLoadingProviders(prevLoading => { - const newLoading = { ...prevLoading }; - expectedProviders.forEach(providerId => { - newLoading[providerId] = false; - }); - return newLoading; - })); - - // Update detailed provider status based on results - guardedSetState(() => setProviderStatus(prevStatus => { - const newStatus = { ...prevStatus }; - expectedProviders.forEach(providerId => { - if (newStatus[providerId]) { // Ensure the provider entry exists - const providerHasStreams = currentStreamsData[providerId] && - currentStreamsData[providerId].streams && - currentStreamsData[providerId].streams.length > 0; - - newStatus[providerId] = { - ...newStatus[providerId], // Preserve timeStarted - loading: false, - success: providerHasStreams, - // Mark error if it was loading and now no streams, and wasn't already successful - error: !providerHasStreams && newStatus[providerId].loading && !newStatus[providerId].success, - message: providerHasStreams ? 'Loaded successfully' : (newStatus[providerId].error ? 'Error or no streams' : 'No streams found'), - timeCompleted: now, - }; - } - }); - return newStatus; - })); - - // Update the set of available providers based on what actually loaded streams - const providersWithStreamsSet = new Set(providersWithStreams); - guardedSetState(() => setAvailableProviders(providersWithStreamsSet)); - - // Reset loadStartTime to signify the end of this loading cycle - guardedSetState(() => setLoadStartTime(0)); - } + const providersWithStreamsSet = new Set(providersWithStreams); + setAvailableProviders(providersWithStreamsSet); } - }, [loadingStreams, loadingEpisodeStreams, groupedStreams, episodeStreams, type, guardedSetState]); + + // Update loading states for individual providers + const expectedProviders = ['stremio', 'hdrezka']; + const now = Date.now(); + + setLoadingProviders(prevLoading => { + const newLoading = { ...prevLoading }; + expectedProviders.forEach(providerId => { + // Provider is loading if overall loading is true OR if it doesn't have streams yet + const hasStreams = currentStreamsData[providerId] && + currentStreamsData[providerId].streams && + currentStreamsData[providerId].streams.length > 0; + newLoading[providerId] = (loadingStreams || loadingEpisodeStreams) && !hasStreams; + }); + return newLoading; + }); + + }, [loadingStreams, loadingEpisodeStreams, groupedStreams, episodeStreams, type]); - // Add useEffect to update availableProviders whenever streams change + // Reset the selected provider to 'all' if the current selection is no longer available useEffect(() => { - if (!loadingStreams && !loadingEpisodeStreams) { - const streams = type === 'series' ? episodeStreams : groupedStreams; - // Only include providers that actually have streams - const providers = new Set( - Object.entries(streams) - .filter(([_, data]) => data.streams && data.streams.length > 0) - .map(([providerId]) => providerId) - ); - setAvailableProviders(providers); - - // Also reset the selected provider to 'all' if the current selection is no longer available - if (selectedProvider !== 'all' && !providers.has(selectedProvider)) { - setSelectedProvider('all'); - } + if (selectedProvider !== 'all' && !availableProviders.has(selectedProvider)) { + setSelectedProvider('all'); } - }, [type, groupedStreams, episodeStreams, loadingStreams, loadingEpisodeStreams, selectedProvider]); + }, [selectedProvider, availableProviders]); @@ -820,13 +727,11 @@ export const StreamsScreen = () => { } }, [settings.preferredPlayer, settings.useExternalPlayer, navigateToPlayer]); - // Autoplay effect - triggers when streams are available and autoplay is enabled + // Autoplay effect - triggers immediately when streams are available and autoplay is enabled useEffect(() => { if ( settings.autoplayBestStream && !autoplayTriggered && - !loadingStreams && - !loadingEpisodeStreams && isAutoplayWaiting ) { const streams = type === 'series' ? episodeStreams : groupedStreams; @@ -835,14 +740,12 @@ export const StreamsScreen = () => { const bestStream = getBestStream(streams); if (bestStream) { - logger.log('🚀 Autoplay: Best stream found, starting playback...'); + logger.log('🚀 Autoplay: Best stream found, starting playback immediately...'); setAutoplayTriggered(true); setIsAutoplayWaiting(false); - // Add a small delay to let the UI settle - setTimeout(() => { - handleStreamPress(bestStream); - }, 500); + // Start playback immediately - no delay needed + handleStreamPress(bestStream); } else { logger.log('⚠️ Autoplay: No suitable stream found'); setIsAutoplayWaiting(false); @@ -852,8 +755,6 @@ export const StreamsScreen = () => { }, [ settings.autoplayBestStream, autoplayTriggered, - loadingStreams, - loadingEpisodeStreams, isAutoplayWaiting, type, episodeStreams, @@ -1285,35 +1186,45 @@ export const StreamsScreen = () => { )} - {/* Show streams immediately as they become available, with loading indicators for pending providers */} - {Object.keys(streams).length === 0 && (loadingStreams || loadingEpisodeStreams) ? ( - - - - {isAutoplayWaiting ? 'Finding best stream for autoplay...' : 'Finding available streams...'} - - - ) : isAutoplayWaiting && !autoplayTriggered ? ( - - - Starting best stream... - - ) : Object.keys(streams).length === 0 && !loadingStreams && !loadingEpisodeStreams ? ( - - - No streams available - + {/* Show streams immediately as they become available */} + {Object.keys(streams).length === 0 ? ( + // Only show initial loading if no streams are available yet + (loadingStreams || loadingEpisodeStreams) ? ( + + + + {isAutoplayWaiting ? 'Finding best stream for autoplay...' : 'Finding available streams...'} + + + ) : ( + // No streams and not loading = no streams available + + + No streams available + + ) ) : ( + // Show streams immediately when available, even if still loading others + {/* Show autoplay loading overlay if waiting for autoplay */} + {isAutoplayWaiting && !autoplayTriggered && ( + + + + Starting best stream... + + + )} + item.url || `${item.name}-${item.title}`} @@ -1763,6 +1674,30 @@ const createStyles = (colors: any) => StyleSheet.create({ sectionLoadingText: { marginLeft: 8, }, + autoplayOverlay: { + position: 'absolute', + top: 0, + left: 0, + right: 0, + backgroundColor: 'rgba(0,0,0,0.8)', + padding: 16, + alignItems: 'center', + zIndex: 10, + }, + autoplayIndicator: { + flexDirection: 'row', + alignItems: 'center', + backgroundColor: colors.elevation2, + paddingHorizontal: 16, + paddingVertical: 12, + borderRadius: 8, + }, + autoplayText: { + color: colors.primary, + fontSize: 14, + marginLeft: 8, + fontWeight: '600', + }, }); export default memo(StreamsScreen); \ No newline at end of file