diff --git a/src/components/player/modals/EpisodeStreamsModal.tsx b/src/components/player/modals/EpisodeStreamsModal.tsx index 969687a..66bba44 100644 --- a/src/components/player/modals/EpisodeStreamsModal.tsx +++ b/src/components/player/modals/EpisodeStreamsModal.tsx @@ -1,8 +1,8 @@ import React, { useState, useEffect } from 'react'; -import { View, Text, TouchableOpacity, ScrollView, ActivityIndicator, Dimensions } from 'react-native'; +import { View, Text, TouchableOpacity, ScrollView, ActivityIndicator, Dimensions, StyleSheet, Platform } from 'react-native'; import { MaterialIcons } from '@expo/vector-icons'; -import Animated, { - FadeIn, +import Animated, { + FadeIn, FadeOut, SlideInRight, SlideOutRight, @@ -25,43 +25,31 @@ const MENU_WIDTH = Math.min(width * 0.85, 400); const QualityBadge = ({ quality }: { quality: string | null }) => { if (!quality) return null; - + const qualityNum = parseInt(quality); let color = '#8B5CF6'; let label = `${quality}p`; - + if (qualityNum >= 2160) { color = '#F59E0B'; label = '4K'; } else if (qualityNum >= 1080) { - color = '#EF4444'; - label = 'FHD'; + color = '#3B82F6'; + label = '1080p'; } else if (qualityNum >= 720) { color = '#10B981'; - label = 'HD'; + label = '720p'; } - + return ( - - - {label} - + + {label} ); }; @@ -89,35 +77,31 @@ export const EpisodeStreamsModal: React.FC = ({ const fetchStreams = async () => { if (!episode || !metadata?.id) return; - + setIsLoading(true); setHasErrors([]); setAvailableStreams({}); - + try { const episodeId = episode.stremioId || `${metadata.id}:${episode.season_number}:${episode.episode_number}`; let completedProviders = 0; const expectedProviders = new Set(); const respondedProviders = new Set(); - + const installedAddons = stremioService.getInstalledAddons(); - const streamAddons = installedAddons.filter((addon: any) => + const streamAddons = installedAddons.filter((addon: any) => addon.resources && addon.resources.includes('stream') ); - + streamAddons.forEach((addon: any) => expectedProviders.add(addon.id)); - - logger.log(`[EpisodeStreamsModal] Fetching streams for ${episodeId}, expecting ${expectedProviders.size} providers`); - + await stremioService.getStreams('series', episodeId, (streams: any, addonId: any, addonName: any, error: any) => { completedProviders++; respondedProviders.add(addonId); - + if (error) { - logger.warn(`[EpisodeStreamsModal] Error from ${addonName || addonId}:`, error); setHasErrors(prev => [...prev, `${addonName || addonId}: ${error.message || 'Unknown error'}`]); } else if (streams && streams.length > 0) { - // Update state incrementally for each provider setAvailableStreams(prev => ({ ...prev, [addonId]: { @@ -125,29 +109,20 @@ export const EpisodeStreamsModal: React.FC = ({ addonName: addonName || addonId } })); - logger.log(`[EpisodeStreamsModal] Added ${streams.length} streams from ${addonName || addonId}`); - } else { - logger.log(`[EpisodeStreamsModal] No streams from ${addonName || addonId}`); } - + if (completedProviders >= expectedProviders.size) { - logger.log(`[EpisodeStreamsModal] All providers completed. Total providers responded: ${respondedProviders.size}`); setIsLoading(false); } }); - - // Fallback timeout + setTimeout(() => { if (respondedProviders.size === 0) { - logger.warn(`[EpisodeStreamsModal] Timeout: No providers responded`); - setHasErrors(prev => [...prev, 'Timeout: No providers responded']); setIsLoading(false); } }, 8000); - + } catch (error) { - logger.error('[EpisodeStreamsModal] Error fetching streams:', error); - setHasErrors(prev => [...prev, `Failed to fetch streams: ${error}`]); setIsLoading(false); } }; @@ -158,36 +133,24 @@ export const EpisodeStreamsModal: React.FC = ({ return match ? match[1] : null; }; - const handleClose = () => { - onClose(); - }; - if (!visible) return null; const sortedProviders = Object.entries(availableStreams); return ( - <> + {/* Backdrop */} - - - + {/* Side Menu */} = ({ right: 0, bottom: 0, width: MENU_WIDTH, - backgroundColor: '#1A1A1A', - zIndex: 9999, - elevation: 20, - shadowColor: '#000', - shadowOffset: { width: -5, height: 0 }, - shadowOpacity: 0.3, - shadowRadius: 10, - borderTopLeftRadius: 20, - borderBottomLeftRadius: 20, + backgroundColor: '#0f0f0f', + borderLeftWidth: 1, + borderColor: 'rgba(255,255,255,0.1)', }} > {/* Header */} - - - {episode?.name || 'Select Stream'} - - {episode && ( - - S{episode.season_number}E{episode.episode_number} + + + + {episode?.name || 'Sources'} - )} + {episode && ( + + S{episode.season_number} • E{episode.episode_number} + + )} + - - - - - {isLoading && ( - - - - Finding available streams... - + {isLoading && sortedProviders.length === 0 && ( + + + Finding sources... )} - {!isLoading && sortedProviders.length > 0 && ( - sortedProviders.map(([providerId, providerData]) => ( - - - {providerData.addonName} ({providerData.streams.length}) - - - - {providerData.streams.map((stream, index) => { - const quality = getQualityFromTitle(stream.title) || stream.quality; - - return ( - onSelectStream(stream)} - activeOpacity={0.7} - > - - - - - {stream.title || stream.name || `Stream ${index + 1}`} - - {quality && } - - - {(stream.size || stream.lang) && ( - - {stream.size && ( - - - - {(stream.size / (1024 * 1024 * 1024)).toFixed(1)} GB - - - )} - {stream.lang && ( - - - - {stream.lang.toUpperCase()} - - - )} - - )} - - - - - - - - ); - })} - - - )) - )} - - {!isLoading && sortedProviders.length === 0 && hasErrors.length === 0 && ( - - - - No sources available - + {sortedProviders.map(([providerId, providerData]) => ( + - Try searching for different content + {providerData.addonName} + + + {providerData.streams.map((stream, index) => { + const quality = getQualityFromTitle(stream.title) || stream.quality; + + return ( + { + onSelectStream(stream); + onClose(); + }} + activeOpacity={0.7} + > + + + + + {stream.name || 'Unknown Source'} + + + + {stream.title && ( + + {stream.title} + + )} + + + + ); + })} + + + ))} + + {!isLoading && sortedProviders.length === 0 && ( + + + No sources found )} - {!isLoading && hasErrors.length > 0 && ( - - - - - Errors occurred - - - {hasErrors.map((error, index) => ( - - {error} - - ))} - + {hasErrors.length > 0 && ( + + Sources might be limited due to provider errors. + )} - + ); }; -