mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-05-10 20:10:54 +00:00
improved caching behaviour
This commit is contained in:
parent
90f99985a0
commit
5a22ab54fb
5 changed files with 245 additions and 28 deletions
|
|
@ -1341,6 +1341,17 @@ export const useMetadata = ({ id, type, addonId }: UseMetadataProps): UseMetadat
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!settingsLoaded) return;
|
if (!settingsLoaded) return;
|
||||||
|
|
||||||
|
// Check for cached streams immediately on mount
|
||||||
|
const checkAndLoadCachedStreams = async () => {
|
||||||
|
try {
|
||||||
|
// This will be handled by the StreamsScreen component
|
||||||
|
// The useMetadata hook focuses on metadata and episodes
|
||||||
|
} catch (error) {
|
||||||
|
if (__DEV__) console.log('[useMetadata] Error checking cached streams on mount:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
loadMetadata();
|
loadMetadata();
|
||||||
}, [id, type, settingsLoaded]);
|
}, [id, type, settingsLoaded]);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1293,7 +1293,7 @@ const PluginsScreen: React.FC = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Define available quality options
|
// Define available quality options
|
||||||
const qualityOptions = ['Auto', '2160p', '4K', '1080p', '720p', '360p', 'DV', 'HDR', 'REMUX', '480p', 'CAM', 'TS'];
|
const qualityOptions = ['Auto', 'Adaptive', '2160p', '4K', '1080p', '720p', '360p', 'DV', 'HDR', 'REMUX', '480p', 'CAM', 'TS'];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,10 +37,11 @@ import { useMetadata } from '../hooks/useMetadata';
|
||||||
import { useMetadataAssets } from '../hooks/useMetadataAssets';
|
import { useMetadataAssets } from '../hooks/useMetadataAssets';
|
||||||
import { useTheme } from '../contexts/ThemeContext';
|
import { useTheme } from '../contexts/ThemeContext';
|
||||||
import { useTrailer } from '../contexts/TrailerContext';
|
import { useTrailer } from '../contexts/TrailerContext';
|
||||||
import { Stream } from '../types/metadata';
|
import { Stream, GroupedStreams } from '../types/metadata';
|
||||||
import { tmdbService } from '../services/tmdbService';
|
import { tmdbService } from '../services/tmdbService';
|
||||||
import { stremioService } from '../services/stremioService';
|
import { stremioService } from '../services/stremioService';
|
||||||
import { localScraperService } from '../services/localScraperService';
|
import { localScraperService } from '../services/localScraperService';
|
||||||
|
import { hybridCacheService } from '../services/hybridCacheService';
|
||||||
import { VideoPlayerService } from '../services/videoPlayerService';
|
import { VideoPlayerService } from '../services/videoPlayerService';
|
||||||
import { useSettings } from '../hooks/useSettings';
|
import { useSettings } from '../hooks/useSettings';
|
||||||
import QualityBadge from '../components/metadata/QualityBadge';
|
import QualityBadge from '../components/metadata/QualityBadge';
|
||||||
|
|
@ -728,6 +729,52 @@ export const StreamsScreen = () => {
|
||||||
}
|
}
|
||||||
}, [selectedProvider, availableProviders, episodeStreams, groupedStreams, type]);
|
}, [selectedProvider, availableProviders, episodeStreams, groupedStreams, type]);
|
||||||
|
|
||||||
|
// Check for cached results immediately on mount
|
||||||
|
useEffect(() => {
|
||||||
|
const checkCachedResults = async () => {
|
||||||
|
if (!settings.enableLocalScrapers) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
let season: number | undefined;
|
||||||
|
let episode: number | undefined;
|
||||||
|
|
||||||
|
if (episodeId && episodeId.includes(':')) {
|
||||||
|
const parts = episodeId.split(':');
|
||||||
|
if (parts.length >= 3) {
|
||||||
|
season = parseInt(parts[1], 10);
|
||||||
|
episode = parseInt(parts[2], 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const installedScrapers = await localScraperService.getInstalledScrapers();
|
||||||
|
const userSettings = {
|
||||||
|
enableLocalScrapers: settings.enableLocalScrapers,
|
||||||
|
enabledScrapers: new Set(
|
||||||
|
installedScrapers
|
||||||
|
.filter(scraper => scraper.enabled)
|
||||||
|
.map(scraper => scraper.id)
|
||||||
|
)
|
||||||
|
};
|
||||||
|
const cachedResults = await hybridCacheService.getCachedResults(type, id, season, episode, userSettings);
|
||||||
|
if (cachedResults.validResults.length > 0) {
|
||||||
|
logger.log(`🔍 Found ${cachedResults.validResults.length} cached scraper results on mount`);
|
||||||
|
|
||||||
|
// If we have cached results, trigger the loading flow immediately
|
||||||
|
if (!hasDoneInitialLoadRef.current) {
|
||||||
|
logger.log('🚀 Triggering immediate load due to cached results');
|
||||||
|
// Force a re-render to ensure cached results are displayed
|
||||||
|
setHasStreamProviders(true);
|
||||||
|
setStreamsLoadStart(Date.now());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (__DEV__) console.log('[StreamsScreen] Error checking cached results on mount:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
checkCachedResults();
|
||||||
|
}, [type, id, episodeId, settings.enableLocalScrapers]);
|
||||||
|
|
||||||
// Update useEffect to check for sources
|
// Update useEffect to check for sources
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Reset initial load state when content changes
|
// Reset initial load state when content changes
|
||||||
|
|
@ -755,9 +802,42 @@ export const StreamsScreen = () => {
|
||||||
const hasLocalScrapers = settings.enableLocalScrapers && await localScraperService.hasScrapers();
|
const hasLocalScrapers = settings.enableLocalScrapers && await localScraperService.hasScrapers();
|
||||||
if (__DEV__) console.log('[StreamsScreen] hasLocalScrapers:', hasLocalScrapers, 'enableLocalScrapers:', settings.enableLocalScrapers);
|
if (__DEV__) console.log('[StreamsScreen] hasLocalScrapers:', hasLocalScrapers, 'enableLocalScrapers:', settings.enableLocalScrapers);
|
||||||
|
|
||||||
// We have providers if we have either Stremio addons OR enabled local scrapers
|
// Check for cached results (this covers both local and global cache)
|
||||||
const hasProviders = hasStremioProviders || hasLocalScrapers;
|
let hasCachedResults = false;
|
||||||
logger.log(`[StreamsScreen] provider check: hasProviders=${hasProviders}`);
|
if (settings.enableLocalScrapers) {
|
||||||
|
try {
|
||||||
|
// Check if there are any cached streams for this content
|
||||||
|
let season: number | undefined;
|
||||||
|
let episode: number | undefined;
|
||||||
|
|
||||||
|
if (episodeId && episodeId.includes(':')) {
|
||||||
|
const parts = episodeId.split(':');
|
||||||
|
if (parts.length >= 3) {
|
||||||
|
season = parseInt(parts[1], 10);
|
||||||
|
episode = parseInt(parts[2], 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const installedScrapers = await localScraperService.getInstalledScrapers();
|
||||||
|
const userSettings = {
|
||||||
|
enableLocalScrapers: settings.enableLocalScrapers,
|
||||||
|
enabledScrapers: new Set(
|
||||||
|
installedScrapers
|
||||||
|
.filter(scraper => scraper.enabled)
|
||||||
|
.map(scraper => scraper.id)
|
||||||
|
)
|
||||||
|
};
|
||||||
|
const cachedStreams = await hybridCacheService.getCachedStreams(type, id, season, episode, userSettings);
|
||||||
|
hasCachedResults = cachedStreams.length > 0;
|
||||||
|
if (__DEV__) console.log('[StreamsScreen] hasCachedResults:', hasCachedResults, 'cached streams count:', cachedStreams.length, 'season:', season, 'episode:', episode);
|
||||||
|
} catch (error) {
|
||||||
|
if (__DEV__) console.log('[StreamsScreen] Error checking cached results:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have providers if we have Stremio addons, enabled local scrapers, OR cached results
|
||||||
|
const hasProviders = hasStremioProviders || hasLocalScrapers || hasCachedResults;
|
||||||
|
logger.log(`[StreamsScreen] provider check: hasProviders=${hasProviders} (stremio:${hasStremioProviders}, local:${hasLocalScrapers}, cached:${hasCachedResults})`);
|
||||||
|
|
||||||
if (!isMounted.current) return;
|
if (!isMounted.current) return;
|
||||||
|
|
||||||
|
|
@ -765,12 +845,68 @@ export const StreamsScreen = () => {
|
||||||
setHasStremioStreamProviders(hasStremioProviders);
|
setHasStremioStreamProviders(hasStremioProviders);
|
||||||
|
|
||||||
if (!hasProviders) {
|
if (!hasProviders) {
|
||||||
logger.log('[StreamsScreen] No providers detected; scheduling no-sources UI');
|
// If we have local scrapers enabled but no cached results yet, wait a bit longer
|
||||||
const timer = setTimeout(() => {
|
if (settings.enableLocalScrapers && !hasCachedResults) {
|
||||||
if (isMounted.current) setShowNoSourcesError(true);
|
logger.log('[StreamsScreen] No providers detected but checking for cached results; waiting longer');
|
||||||
}, 500);
|
const timer = setTimeout(() => {
|
||||||
return () => clearTimeout(timer);
|
if (isMounted.current) setShowNoSourcesError(true);
|
||||||
|
}, 2000); // Wait 2 seconds for cached results
|
||||||
|
return () => clearTimeout(timer);
|
||||||
|
} else {
|
||||||
|
logger.log('[StreamsScreen] No providers detected; scheduling no-sources UI');
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
if (isMounted.current) setShowNoSourcesError(true);
|
||||||
|
}, 500);
|
||||||
|
return () => clearTimeout(timer);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// Check for cached streams first before loading
|
||||||
|
if (settings.enableLocalScrapers) {
|
||||||
|
try {
|
||||||
|
let season: number | undefined;
|
||||||
|
let episode: number | undefined;
|
||||||
|
|
||||||
|
if (episodeId && episodeId.includes(':')) {
|
||||||
|
const parts = episodeId.split(':');
|
||||||
|
if (parts.length >= 3) {
|
||||||
|
season = parseInt(parts[1], 10);
|
||||||
|
episode = parseInt(parts[2], 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we have cached streams and load them immediately
|
||||||
|
const cachedStreams = await hybridCacheService.getCachedStreams(type, id, season, episode);
|
||||||
|
if (cachedStreams.length > 0) {
|
||||||
|
logger.log(`🎯 Found ${cachedStreams.length} cached streams, displaying immediately`);
|
||||||
|
|
||||||
|
// Group cached streams by scraper for proper display
|
||||||
|
const groupedCachedStreams: GroupedStreams = {};
|
||||||
|
const scrapersWithCachedResults = new Set<string>();
|
||||||
|
|
||||||
|
// Get cached results to determine which scrapers have results
|
||||||
|
const cachedResults = await hybridCacheService.getCachedResults(type, id, season, episode);
|
||||||
|
|
||||||
|
for (const result of cachedResults.validResults) {
|
||||||
|
if (result.success && result.streams && result.streams.length > 0) {
|
||||||
|
groupedCachedStreams[result.scraperId] = {
|
||||||
|
addonName: result.scraperName,
|
||||||
|
streams: result.streams
|
||||||
|
};
|
||||||
|
scrapersWithCachedResults.add(result.scraperId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the streams state immediately if we have cached results
|
||||||
|
if (Object.keys(groupedCachedStreams).length > 0) {
|
||||||
|
logger.log(`🚀 Immediately displaying ${Object.keys(groupedCachedStreams).length} cached scrapers with streams`);
|
||||||
|
// This will be handled by the useMetadata hook integration
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (__DEV__) console.log('[StreamsScreen] Error checking cached streams:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// For series episodes, do not wait for metadata; load directly when episodeId is present
|
// For series episodes, do not wait for metadata; load directly when episodeId is present
|
||||||
if (episodeId) {
|
if (episodeId) {
|
||||||
logger.log(`🎬 Loading episode streams for: ${episodeId}`);
|
logger.log(`🎬 Loading episode streams for: ${episodeId}`);
|
||||||
|
|
|
||||||
|
|
@ -48,21 +48,39 @@ class HybridCacheService {
|
||||||
* Get cached results with hybrid approach (global first, then local)
|
* Get cached results with hybrid approach (global first, then local)
|
||||||
*/
|
*/
|
||||||
async getCachedResults(
|
async getCachedResults(
|
||||||
type: string,
|
type: string,
|
||||||
tmdbId: string,
|
tmdbId: string,
|
||||||
season?: number,
|
season?: number,
|
||||||
episode?: number
|
episode?: number,
|
||||||
|
userSettings?: { enableLocalScrapers?: boolean; enabledScrapers?: Set<string> }
|
||||||
): Promise<HybridCacheResult> {
|
): Promise<HybridCacheResult> {
|
||||||
try {
|
try {
|
||||||
|
// Filter function to check if scraper is enabled for current user
|
||||||
|
const isScraperEnabled = (scraperId: string): boolean => {
|
||||||
|
if (!userSettings?.enableLocalScrapers) return false;
|
||||||
|
if (userSettings?.enabledScrapers) {
|
||||||
|
return userSettings.enabledScrapers.has(scraperId);
|
||||||
|
}
|
||||||
|
// If no specific scraper settings, assume all are enabled if local scrapers are enabled
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
// Try global cache first if enabled
|
// Try global cache first if enabled
|
||||||
if (this.ENABLE_GLOBAL_CACHE) {
|
if (this.ENABLE_GLOBAL_CACHE) {
|
||||||
try {
|
try {
|
||||||
const globalResults = await supabaseGlobalCacheService.getCachedResults(type, tmdbId, season, episode);
|
const globalResults = await supabaseGlobalCacheService.getCachedResults(type, tmdbId, season, episode);
|
||||||
|
|
||||||
if (globalResults.validResults.length > 0) {
|
// Filter results based on user settings
|
||||||
logger.log(`[HybridCache] Using global cache: ${globalResults.validResults.length} results`);
|
const filteredGlobalResults = {
|
||||||
|
...globalResults,
|
||||||
|
validResults: globalResults.validResults.filter(result => isScraperEnabled(result.scraperId)),
|
||||||
|
expiredScrapers: globalResults.expiredScrapers.filter(scraperId => isScraperEnabled(scraperId))
|
||||||
|
};
|
||||||
|
|
||||||
|
if (filteredGlobalResults.validResults.length > 0) {
|
||||||
|
logger.log(`[HybridCache] Using global cache: ${filteredGlobalResults.validResults.length} results (filtered from ${globalResults.validResults.length})`);
|
||||||
return {
|
return {
|
||||||
...globalResults,
|
...filteredGlobalResults,
|
||||||
source: 'global'
|
source: 'global'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -74,11 +92,18 @@ class HybridCacheService {
|
||||||
// Fallback to local cache
|
// Fallback to local cache
|
||||||
if (this.FALLBACK_TO_LOCAL) {
|
if (this.FALLBACK_TO_LOCAL) {
|
||||||
const localResults = await localScraperCacheService.getCachedResults(type, tmdbId, season, episode);
|
const localResults = await localScraperCacheService.getCachedResults(type, tmdbId, season, episode);
|
||||||
|
|
||||||
if (localResults.validResults.length > 0) {
|
// Filter results based on user settings
|
||||||
logger.log(`[HybridCache] Using local cache: ${localResults.validResults.length} results`);
|
const filteredLocalResults = {
|
||||||
|
...localResults,
|
||||||
|
validResults: localResults.validResults.filter(result => isScraperEnabled(result.scraperId)),
|
||||||
|
expiredScrapers: localResults.expiredScrapers.filter(scraperId => isScraperEnabled(scraperId))
|
||||||
|
};
|
||||||
|
|
||||||
|
if (filteredLocalResults.validResults.length > 0) {
|
||||||
|
logger.log(`[HybridCache] Using local cache: ${filteredLocalResults.validResults.length} results (filtered from ${localResults.validResults.length})`);
|
||||||
return {
|
return {
|
||||||
...localResults,
|
...filteredLocalResults,
|
||||||
source: 'local'
|
source: 'local'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -173,9 +198,10 @@ class HybridCacheService {
|
||||||
tmdbId: string,
|
tmdbId: string,
|
||||||
availableScrapers: Array<{ id: string; name: string }>,
|
availableScrapers: Array<{ id: string; name: string }>,
|
||||||
season?: number,
|
season?: number,
|
||||||
episode?: number
|
episode?: number,
|
||||||
|
userSettings?: { enableLocalScrapers?: boolean; enabledScrapers?: Set<string> }
|
||||||
): Promise<string[]> {
|
): Promise<string[]> {
|
||||||
const { validResults, expiredScrapers } = await this.getCachedResults(type, tmdbId, season, episode);
|
const { validResults, expiredScrapers } = await this.getCachedResults(type, tmdbId, season, episode, userSettings);
|
||||||
|
|
||||||
const validScraperIds = new Set(validResults.map(r => r.scraperId));
|
const validScraperIds = new Set(validResults.map(r => r.scraperId));
|
||||||
const expiredScraperIds = new Set(expiredScrapers);
|
const expiredScraperIds = new Set(expiredScrapers);
|
||||||
|
|
@ -199,10 +225,11 @@ class HybridCacheService {
|
||||||
type: string,
|
type: string,
|
||||||
tmdbId: string,
|
tmdbId: string,
|
||||||
season?: number,
|
season?: number,
|
||||||
episode?: number
|
episode?: number,
|
||||||
|
userSettings?: { enableLocalScrapers?: boolean; enabledScrapers?: Set<string> }
|
||||||
): Promise<Stream[]> {
|
): Promise<Stream[]> {
|
||||||
const { validResults } = await this.getCachedResults(type, tmdbId, season, episode);
|
const { validResults } = await this.getCachedResults(type, tmdbId, season, episode, userSettings);
|
||||||
|
|
||||||
// Flatten all valid streams
|
// Flatten all valid streams
|
||||||
const allStreams: Stream[] = [];
|
const allStreams: Stream[] = [];
|
||||||
for (const result of validResults) {
|
for (const result of validResults) {
|
||||||
|
|
|
||||||
|
|
@ -879,8 +879,11 @@ class LocalScraperService {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get current user settings for enabled scrapers
|
||||||
|
const userSettings = await this.getUserScraperSettings();
|
||||||
|
|
||||||
// Check cache for existing results (hybrid: global first, then local)
|
// Check cache for existing results (hybrid: global first, then local)
|
||||||
const { validResults, expiredScrapers, allExpired, source } = await hybridCacheService.getCachedResults(type, tmdbId, season, episode);
|
const { validResults, expiredScrapers, allExpired, source } = await hybridCacheService.getCachedResults(type, tmdbId, season, episode, userSettings);
|
||||||
|
|
||||||
// Immediately return cached results for valid scrapers
|
// Immediately return cached results for valid scrapers
|
||||||
if (validResults.length > 0) {
|
if (validResults.length > 0) {
|
||||||
|
|
@ -1354,6 +1357,46 @@ class LocalScraperService {
|
||||||
return Array.from(this.installedScrapers.values()).some(scraper => scraper.enabled);
|
return Array.from(this.installedScrapers.values()).some(scraper => scraper.enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get current user scraper settings for cache filtering
|
||||||
|
private async getUserScraperSettings(): Promise<{ enableLocalScrapers?: boolean; enabledScrapers?: Set<string> }> {
|
||||||
|
return this.getUserScraperSettingsWithOverride();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get user scraper settings (can be overridden for testing or external calls)
|
||||||
|
async getUserScraperSettingsWithOverride(overrideSettings?: { enableLocalScrapers?: boolean; enabledScrapers?: Set<string> }): Promise<{ enableLocalScrapers?: boolean; enabledScrapers?: Set<string> }> {
|
||||||
|
try {
|
||||||
|
// If override settings are provided, use them
|
||||||
|
if (overrideSettings) {
|
||||||
|
return {
|
||||||
|
enableLocalScrapers: overrideSettings.enableLocalScrapers,
|
||||||
|
enabledScrapers: overrideSettings.enabledScrapers
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get user settings from AsyncStorage
|
||||||
|
const settingsData = await AsyncStorage.getItem('app_settings');
|
||||||
|
const settings = settingsData ? JSON.parse(settingsData) : {};
|
||||||
|
|
||||||
|
// Get enabled scrapers based on current user settings
|
||||||
|
const enabledScrapers = new Set<string>();
|
||||||
|
const installedScrapers = Array.from(this.installedScrapers.values());
|
||||||
|
|
||||||
|
for (const scraper of installedScrapers) {
|
||||||
|
if (scraper.enabled && settings.enableLocalScrapers) {
|
||||||
|
enabledScrapers.add(scraper.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
enableLocalScrapers: settings.enableLocalScrapers,
|
||||||
|
enabledScrapers: enabledScrapers.size > 0 ? enabledScrapers : undefined
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('[LocalScraperService] Error getting user scraper settings:', error);
|
||||||
|
return { enableLocalScrapers: false };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Cache management methods (hybrid: local + global)
|
// Cache management methods (hybrid: local + global)
|
||||||
async clearScraperCache(): Promise<void> {
|
async clearScraperCache(): Promise<void> {
|
||||||
await hybridCacheService.clearAllCache();
|
await hybridCacheService.clearAllCache();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue