diff --git a/src/hooks/useSettings.ts b/src/hooks/useSettings.ts index 3f55f4ec..aa63dce1 100644 --- a/src/hooks/useSettings.ts +++ b/src/hooks/useSettings.ts @@ -34,6 +34,7 @@ export interface AppSettings { selectedHeroCatalogs: string[]; // Array of catalog IDs to display in hero section logoSourcePreference: 'metahub' | 'tmdb'; // Preferred source for title logos tmdbLanguagePreference: string; // Preferred language for TMDB logos (ISO 639-1 code) + enableInternalProviders: boolean; // Toggle for internal providers like HDRezka } export const DEFAULT_SETTINGS: AppSettings = { @@ -50,6 +51,7 @@ export const DEFAULT_SETTINGS: AppSettings = { selectedHeroCatalogs: [], // Empty array means all catalogs are selected logoSourcePreference: 'metahub', // Default to Metahub as first source tmdbLanguagePreference: 'en', // Default to English + enableInternalProviders: true, // Enable internal providers by default }; const SETTINGS_STORAGE_KEY = 'app_settings'; diff --git a/src/screens/SettingsScreen.tsx b/src/screens/SettingsScreen.tsx index c21e6fe9..9dbe1026 100644 --- a/src/screens/SettingsScreen.tsx +++ b/src/screens/SettingsScreen.tsx @@ -406,6 +406,17 @@ const SettingsScreen: React.FC = () => { onPress={() => navigation.navigate('CatalogSettings')} badge={catalogCount} /> + ( + updateSetting('enableInternalProviders', value)} + /> + )} + /> { 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); + + logger.log(`📊 Providers with streams: ${providersWithStreams.join(', ')}`); // Update simple loading flag: all expected providers are no longer loading setLoadingProviders(prevLoading => { @@ -334,8 +341,8 @@ export const StreamsScreen = () => { expectedProviders.forEach(providerId => { if (newStatus[providerId]) { // Ensure the provider entry exists const providerHasStreams = currentStreamsData[providerId] && - currentStreamsData[providerId].streams && - currentStreamsData[providerId].streams.length > 0; + currentStreamsData[providerId].streams && + currentStreamsData[providerId].streams.length > 0; newStatus[providerId] = { ...newStatus[providerId], // Preserve timeStarted @@ -362,14 +369,33 @@ export const StreamsScreen = () => { }); // Update the set of available providers based on what actually loaded streams - const providersWithStreams = new Set(Object.keys(currentStreamsData)); - setAvailableProviders(providersWithStreams); + const providersWithStreamsSet = new Set(providersWithStreams); + setAvailableProviders(providersWithStreamsSet); // Reset loadStartTime to signify the end of this loading cycle - setLoadStartTime(0); + setLoadStartTime(0); } }, [loadingStreams, loadingEpisodeStreams, groupedStreams, episodeStreams, type /* loadStartTime is intentionally omitted from deps here */]); + // Add useEffect to update availableProviders whenever streams change + 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'); + } + } + }, [type, groupedStreams, episodeStreams, loadingStreams, loadingEpisodeStreams, selectedProvider]); + React.useEffect(() => { if (type === 'series' && episodeId) { logger.log(`🎬 Loading episode streams for: ${episodeId}`); @@ -628,10 +654,20 @@ export const StreamsScreen = () => { const filterItems = useMemo(() => { const installedAddons = stremioService.getInstalledAddons(); const streams = type === 'series' ? episodeStreams : groupedStreams; + + // Make sure we include all providers with streams, not just those in availableProviders + const allProviders = new Set([ + ...availableProviders, + ...Object.keys(streams).filter(key => + streams[key] && + streams[key].streams && + streams[key].streams.length > 0 + ) + ]); return [ { id: 'all', name: 'All Providers' }, - ...Array.from(availableProviders) + ...Array.from(allProviders) .sort((a, b) => { // Always put HDRezka at the top if (a === 'hdrezka') return -1; diff --git a/src/services/hdrezkaService.ts b/src/services/hdrezkaService.ts index eb3dd6c6..98b39e3a 100644 --- a/src/services/hdrezkaService.ts +++ b/src/services/hdrezkaService.ts @@ -2,6 +2,8 @@ import { logger } from '../utils/logger'; import { Stream } from '../types/metadata'; import { tmdbService } from './tmdbService'; import axios from 'axios'; +import { settingsEmitter } from '../hooks/useSettings'; +import AsyncStorage from '@react-native-async-storage/async-storage'; // Use node-fetch if available, otherwise fallback to global fetch let fetchImpl: typeof fetch; @@ -418,6 +420,16 @@ class HDRezkaService { try { logger.log(`[HDRezka] Getting streams for ${mediaType} with ID: ${mediaId}`); + // First check if internal providers are enabled + const settingsJson = await AsyncStorage.getItem('app_settings'); + if (settingsJson) { + const settings = JSON.parse(settingsJson); + if (settings.enableInternalProviders === false) { + logger.log('[HDRezka] Internal providers are disabled in settings, skipping HDRezka'); + return []; + } + } + // First, extract the actual title from TMDB if this is an ID let title = mediaId; let year: number | undefined = undefined;