From 25e648e135c12b79c31ec1522caf52c3204faec1 Mon Sep 17 00:00:00 2001 From: tapframe Date: Fri, 20 Jun 2025 15:51:19 +0530 Subject: [PATCH] Refactor StreamsScreen for improved stream loading and autoplay handling This update enhances the StreamsScreen component by streamlining the logic for monitoring stream loading and updating available providers. The autoplay feature now triggers immediately when streams are available, improving user experience. Additionally, loading indicators and state management have been refined for better clarity and responsiveness during stream availability checks. --- src/screens/StreamsScreen.tsx | 261 +++++++++++++--------------------- 1 file changed, 98 insertions(+), 163 deletions(-) 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