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);