diff --git a/local-scrapers-repo b/local-scrapers-repo index 14cf821b..db674925 160000 --- a/local-scrapers-repo +++ b/local-scrapers-repo @@ -1 +1 @@ -Subproject commit 14cf821b7b126a418c7fb671719f151037948140 +Subproject commit db674925bbf74e1240cc0625d531853505ca941f diff --git a/src/screens/ScraperSettingsScreen.tsx b/src/screens/ScraperSettingsScreen.tsx index b1126d3f..becc7b9b 100644 --- a/src/screens/ScraperSettingsScreen.tsx +++ b/src/screens/ScraperSettingsScreen.tsx @@ -648,7 +648,7 @@ const ScraperSettingsScreen: React.FC = () => { v{scraper.version} - {scraper.supportedTypes.join(', ')} + {scraper.supportedTypes && Array.isArray(scraper.supportedTypes) ? scraper.supportedTypes.join(', ') : 'Unknown'} {scraper.contentLanguage && Array.isArray(scraper.contentLanguage) && scraper.contentLanguage.length > 0 && ( <> diff --git a/src/services/localScraperService.ts b/src/services/localScraperService.ts index e151aa5c..52fb807d 100644 --- a/src/services/localScraperService.ts +++ b/src/services/localScraperService.ts @@ -78,15 +78,59 @@ class LocalScraperService { const storedScrapers = await AsyncStorage.getItem(this.STORAGE_KEY); if (storedScrapers) { const scrapers: ScraperInfo[] = JSON.parse(storedScrapers); + const validScrapers: ScraperInfo[] = []; + scrapers.forEach(scraper => { + // Skip scrapers with missing essential fields + if (!scraper.id || !scraper.name || !scraper.version) { + logger.warn('[LocalScraperService] Skipping invalid scraper with missing essential fields:', scraper); + return; + } + // Ensure contentLanguage is an array (migration for older scrapers) if (!scraper.contentLanguage) { scraper.contentLanguage = ['en']; // Default to English } else if (typeof scraper.contentLanguage === 'string') { scraper.contentLanguage = [scraper.contentLanguage]; // Convert string to array } + + // Ensure supportedTypes is an array (migration for older scrapers) + if (!scraper.supportedTypes || !Array.isArray(scraper.supportedTypes)) { + scraper.supportedTypes = ['movie', 'tv']; // Default to both types + } + + // Ensure other required fields have defaults + if (!scraper.description) { + scraper.description = 'No description available'; + } + if (!scraper.filename) { + scraper.filename = `${scraper.id}.js`; + } + if (scraper.enabled === undefined) { + scraper.enabled = true; + } + this.installedScrapers.set(scraper.id, scraper); + validScrapers.push(scraper); }); + + // Save cleaned scrapers back to storage if any were filtered out + if (validScrapers.length !== scrapers.length) { + logger.log('[LocalScraperService] Cleaned up invalid scrapers, saving valid ones'); + await AsyncStorage.setItem(this.STORAGE_KEY, JSON.stringify(validScrapers)); + + // Clean up cached code for removed scrapers + const validScraperIds = new Set(validScrapers.map(s => s.id)); + const removedScrapers = scrapers.filter(s => s.id && !validScraperIds.has(s.id)); + for (const removedScraper of removedScrapers) { + try { + await AsyncStorage.removeItem(`scraper-code-${removedScraper.id}`); + logger.log('[LocalScraperService] Removed cached code for invalid scraper:', removedScraper.id); + } catch (error) { + logger.error('[LocalScraperService] Failed to remove cached code for', removedScraper.id, ':', error); + } + } + } } // Load scraper code from cache @@ -194,6 +238,11 @@ class LocalScraperService { updatedScraperInfo.contentLanguage = [updatedScraperInfo.contentLanguage]; // Convert string to array } + // Ensure supportedTypes is an array (migration for older scrapers) + if (!updatedScraperInfo.supportedTypes || !Array.isArray(updatedScraperInfo.supportedTypes)) { + updatedScraperInfo.supportedTypes = ['movie', 'tv']; // Default to both types + } + this.installedScrapers.set(scraperInfo.id, updatedScraperInfo); this.scraperCode.set(scraperInfo.id, scraperCode);