From 127455b018a2882033a7acacc6f55d659f9a084b Mon Sep 17 00:00:00 2001 From: skoruppa Date: Mon, 2 Mar 2026 09:40:50 +0100 Subject: [PATCH] allow multiple installations of catalog addons --- src/screens/AddonsScreen.tsx | 6 ++-- src/screens/CatalogSettingsScreen.tsx | 41 ++++++++++++++++++++++----- src/screens/HomeScreen.tsx | 2 +- src/services/catalogService.ts | 4 ++- src/services/stremioService.ts | 6 +++- 5 files changed, 46 insertions(+), 13 deletions(-) diff --git a/src/screens/AddonsScreen.tsx b/src/screens/AddonsScreen.tsx index c09ab93e..7b8ca9a9 100644 --- a/src/screens/AddonsScreen.tsx +++ b/src/screens/AddonsScreen.tsx @@ -630,8 +630,8 @@ const AddonsScreen = () => { const existingInstallations = installedAddons.filter(a => a.id === manifest.id); const isAlreadyInstalled = existingInstallations.length > 0; - // Check if addon provides streams - const providesStreams = manifest.resources?.some(resource => { + // Check if addon provides streams or catalogs + const providesStreams = (manifest.catalogs && manifest.catalogs.length > 0) || manifest.resources?.some(resource => { if (typeof resource === 'string') { return resource === 'stream'; } else if (typeof resource === 'object' && resource !== null && 'name' in resource) { @@ -643,7 +643,7 @@ const AddonsScreen = () => { if (isAlreadyInstalled && !providesStreams) { setAlertTitle(t('common.error')); - setAlertMessage('This addon is already installed. Multiple installations are only allowed for stream providers.'); + setAlertMessage('This addon is already installed. Multiple installations are only allowed for stream and catalog providers.'); setAlertActions([{ label: t('common.ok'), onPress: () => setAlertVisible(false) }]); setAlertVisible(true); return; diff --git a/src/screens/CatalogSettingsScreen.tsx b/src/screens/CatalogSettingsScreen.tsx index 320d7252..0738d42b 100644 --- a/src/screens/CatalogSettingsScreen.tsx +++ b/src/screens/CatalogSettingsScreen.tsx @@ -62,6 +62,7 @@ interface GroupedCatalogs { catalogs: CatalogSetting[]; expanded: boolean; enabledCount: number; + installationNumber?: number; }; } @@ -122,6 +123,16 @@ const createStyles = (colors: any) => StyleSheet.create({ marginBottom: 8, letterSpacing: 0.8, }, + priorityBadge: { + paddingHorizontal: 6, + paddingVertical: 2, + borderRadius: 4, + }, + priorityText: { + fontSize: 10, + fontWeight: '700', + color: '#fff', + }, card: { marginHorizontal: 16, borderRadius: 12, @@ -310,7 +321,7 @@ const CatalogSettingsScreen = () => { const uniqueCatalogs = new Map(); addon.catalogs.forEach(catalog => { - const settingKey = `${addon.id}:${catalog.type}:${catalog.id}`; + const settingKey = `${addon.installationId || addon.id}:${catalog.type}:${catalog.id}`; let displayName = catalog.name || catalog.id; const catalogType = catalog.type === 'movie' ? 'Movies' : catalog.type === 'series' ? 'TV Shows' : catalog.type.charAt(0).toUpperCase() + catalog.type.slice(1); @@ -335,7 +346,7 @@ const CatalogSettingsScreen = () => { } uniqueCatalogs.set(settingKey, { - addonId: addon.id, + addonId: addon.installationId || addon.id, catalogId: catalog.id, type: catalog.type, name: displayName, @@ -348,18 +359,27 @@ const CatalogSettingsScreen = () => { } }); + // Count installations per base addon id + const installationCountById: Record = {}; + const installationIndexById: Record = {}; + addons.forEach(addon => { + installationCountById[addon.id] = (installationCountById[addon.id] || 0) + 1; + }); + // Group settings by addon name const grouped: GroupedCatalogs = {}; availableCatalogs.forEach(setting => { - const addon = addons.find(a => a.id === setting.addonId); + const addon = addons.find(a => (a.installationId || a.id) === setting.addonId); if (!addon) return; if (!grouped[setting.addonId]) { + installationIndexById[addon.id] = (installationIndexById[addon.id] || 0) + 1; grouped[setting.addonId] = { name: addon.name, catalogs: [], expanded: true, - enabledCount: 0 + enabledCount: 0, + installationNumber: installationCountById[addon.id] > 1 ? installationIndexById[addon.id] : undefined }; } @@ -620,9 +640,16 @@ const CatalogSettingsScreen = () => { {Object.entries(groupedSettings).map(([addonId, group]) => ( - - {group.name.toUpperCase()} - + + + {group.name.toUpperCase()} + + {group.installationNumber !== undefined && ( + + #{group.installationNumber} + + )} + { if (addon.catalogs) { for (const catalog of addon.catalogs) { // Check if this catalog is enabled (default to true if no setting exists) - const settingKey = `${addon.id}:${catalog.type}:${catalog.id}`; + const settingKey = `${addon.installationId || addon.id}:${catalog.type}:${catalog.id}`; const isEnabled = catalogSettings[settingKey] ?? true; // Only load enabled catalogs diff --git a/src/services/catalogService.ts b/src/services/catalogService.ts index 09d9fb82..c5eedcbe 100644 --- a/src/services/catalogService.ts +++ b/src/services/catalogService.ts @@ -18,6 +18,7 @@ export enum DataSource { export interface StreamingAddon { id: string; + installationId?: string; name: string; version: string; description: string; @@ -313,6 +314,7 @@ class CatalogService { private convertManifestToStreamingAddon(manifest: Manifest): StreamingAddon { return { id: manifest.id, + installationId: manifest.installationId, name: manifest.name, version: manifest.version, description: manifest.description, @@ -339,7 +341,7 @@ class CatalogService { for (const addon of addons) { if (addon.catalogs) { for (const catalog of addon.catalogs) { - const settingKey = `${addon.id}:${catalog.type}:${catalog.id}`; + const settingKey = `${addon.installationId || addon.id}:${catalog.type}:${catalog.id}`; const isEnabled = catalogSettings[settingKey] ?? true; if (isEnabled) { diff --git a/src/services/stremioService.ts b/src/services/stremioService.ts index ec886e95..11b63d68 100644 --- a/src/services/stremioService.ts +++ b/src/services/stremioService.ts @@ -287,6 +287,10 @@ class StremioService { private addonProvidesStreams(manifest: Manifest): boolean { + if (manifest.catalogs && Array.isArray(manifest.catalogs) && manifest.catalogs.length > 0) { + return true; + } + if (!manifest.resources || !Array.isArray(manifest.resources)) { return false; } @@ -699,7 +703,7 @@ class StremioService { // Only allow multiple installations for stream-providing addons if (isAlreadyInstalled && !this.addonProvidesStreams(manifest)) { - throw new Error('This addon is already installed. Multiple installations are only allowed for stream providers.'); + throw new Error('This addon is already installed. Multiple installations are only allowed for stream and catalog providers.'); } // Generate a unique installation ID for this installation