mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-04-29 20:33:42 +00:00
Some fixes
This commit is contained in:
parent
4f04ae874f
commit
dbaadbe61b
6 changed files with 238 additions and 147 deletions
|
|
@ -245,7 +245,7 @@ const FeaturedContent = ({ featuredContent, isSaved, handleSaveToLibrary }: Feat
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
featuredContainer: {
|
featuredContainer: {
|
||||||
width: '100%',
|
width: '100%',
|
||||||
height: height * 0.5,
|
height: height * 0.48,
|
||||||
marginTop: 0,
|
marginTop: 0,
|
||||||
marginBottom: 8,
|
marginBottom: 8,
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
|
|
@ -285,7 +285,7 @@ const styles = StyleSheet.create({
|
||||||
flex: 1,
|
flex: 1,
|
||||||
justifyContent: 'flex-end',
|
justifyContent: 'flex-end',
|
||||||
paddingHorizontal: 16,
|
paddingHorizontal: 16,
|
||||||
paddingBottom: 12,
|
paddingBottom: 4,
|
||||||
},
|
},
|
||||||
featuredLogo: {
|
featuredLogo: {
|
||||||
width: width * 0.7,
|
width: width * 0.7,
|
||||||
|
|
@ -308,7 +308,7 @@ const styles = StyleSheet.create({
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
marginBottom: 8,
|
marginBottom: 4,
|
||||||
flexWrap: 'wrap',
|
flexWrap: 'wrap',
|
||||||
gap: 4,
|
gap: 4,
|
||||||
},
|
},
|
||||||
|
|
@ -331,7 +331,7 @@ const styles = StyleSheet.create({
|
||||||
justifyContent: 'space-evenly',
|
justifyContent: 'space-evenly',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
flex: 1,
|
flex: 1,
|
||||||
maxHeight: 60,
|
maxHeight: 55,
|
||||||
paddingTop: 0,
|
paddingTop: 0,
|
||||||
},
|
},
|
||||||
playButton: {
|
playButton: {
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,13 @@ const persistentStore = {
|
||||||
featuredContent: null as StreamingContent | null,
|
featuredContent: null as StreamingContent | null,
|
||||||
allFeaturedContent: [] as StreamingContent[],
|
allFeaturedContent: [] as StreamingContent[],
|
||||||
lastFetchTime: 0,
|
lastFetchTime: 0,
|
||||||
isFirstLoad: true
|
isFirstLoad: true,
|
||||||
|
// Track last used settings to detect changes on app restart
|
||||||
|
lastSettings: {
|
||||||
|
showHeroSection: true,
|
||||||
|
featuredContentSource: 'tmdb' as 'tmdb' | 'catalogs',
|
||||||
|
selectedHeroCatalogs: [] as string[]
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Cache timeout in milliseconds (e.g., 5 minutes)
|
// Cache timeout in milliseconds (e.g., 5 minutes)
|
||||||
|
|
@ -30,7 +36,7 @@ export function useFeaturedContent() {
|
||||||
|
|
||||||
const { genreMap, loadingGenres } = useGenres();
|
const { genreMap, loadingGenres } = useGenres();
|
||||||
|
|
||||||
// Update local state when settings change
|
// Simple update for state variables
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setContentSource(settings.featuredContentSource);
|
setContentSource(settings.featuredContentSource);
|
||||||
setSelectedCatalogs(settings.selectedHeroCatalogs || []);
|
setSelectedCatalogs(settings.selectedHeroCatalogs || []);
|
||||||
|
|
@ -44,6 +50,14 @@ export function useFeaturedContent() {
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const loadFeaturedContent = useCallback(async (forceRefresh = false) => {
|
const loadFeaturedContent = useCallback(async (forceRefresh = false) => {
|
||||||
|
// First, ensure contentSource matches current settings (could be outdated due to async updates)
|
||||||
|
if (contentSource !== settings.featuredContentSource) {
|
||||||
|
console.log(`Updating content source from ${contentSource} to ${settings.featuredContentSource}`);
|
||||||
|
setContentSource(settings.featuredContentSource);
|
||||||
|
// We return here and let the effect triggered by contentSource change handle the loading
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if we should use cached data
|
// Check if we should use cached data
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
const cacheAge = now - persistentStore.lastFetchTime;
|
const cacheAge = now - persistentStore.lastFetchTime;
|
||||||
|
|
@ -53,6 +67,7 @@ export function useFeaturedContent() {
|
||||||
persistentStore.allFeaturedContent.length > 0 &&
|
persistentStore.allFeaturedContent.length > 0 &&
|
||||||
cacheAge < CACHE_TIMEOUT) {
|
cacheAge < CACHE_TIMEOUT) {
|
||||||
// Use cached data
|
// Use cached data
|
||||||
|
console.log('Using cached featured content data');
|
||||||
setFeaturedContent(persistentStore.featuredContent);
|
setFeaturedContent(persistentStore.featuredContent);
|
||||||
setAllFeaturedContent(persistentStore.allFeaturedContent);
|
setAllFeaturedContent(persistentStore.allFeaturedContent);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
|
@ -60,6 +75,7 @@ export function useFeaturedContent() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(`Loading featured content from ${contentSource}`);
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
cleanup();
|
cleanup();
|
||||||
abortControllerRef.current = new AbortController();
|
abortControllerRef.current = new AbortController();
|
||||||
|
|
@ -176,32 +192,75 @@ export function useFeaturedContent() {
|
||||||
}
|
}
|
||||||
}, [cleanup, genreMap, loadingGenres, contentSource, selectedCatalogs]);
|
}, [cleanup, genreMap, loadingGenres, contentSource, selectedCatalogs]);
|
||||||
|
|
||||||
|
// Check for settings changes, including during app restart
|
||||||
|
useEffect(() => {
|
||||||
|
// Check if settings changed while app was closed
|
||||||
|
const settingsChanged =
|
||||||
|
persistentStore.lastSettings.showHeroSection !== settings.showHeroSection ||
|
||||||
|
persistentStore.lastSettings.featuredContentSource !== settings.featuredContentSource ||
|
||||||
|
JSON.stringify(persistentStore.lastSettings.selectedHeroCatalogs) !== JSON.stringify(settings.selectedHeroCatalogs);
|
||||||
|
|
||||||
|
// Update our tracking of last used settings
|
||||||
|
persistentStore.lastSettings = {
|
||||||
|
showHeroSection: settings.showHeroSection,
|
||||||
|
featuredContentSource: settings.featuredContentSource,
|
||||||
|
selectedHeroCatalogs: [...settings.selectedHeroCatalogs]
|
||||||
|
};
|
||||||
|
|
||||||
|
// Force refresh if settings changed during app restart
|
||||||
|
if (settingsChanged) {
|
||||||
|
loadFeaturedContent(true);
|
||||||
|
}
|
||||||
|
}, [settings, loadFeaturedContent]);
|
||||||
|
|
||||||
// Subscribe directly to settings emitter for immediate updates
|
// Subscribe directly to settings emitter for immediate updates
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleSettingsChange = () => {
|
const handleSettingsChange = () => {
|
||||||
// Force refresh when settings change
|
// Only refresh if current content source is different from settings
|
||||||
loadFeaturedContent(true);
|
// This prevents duplicate refreshes when HomeScreen also handles this event
|
||||||
|
if (contentSource !== settings.featuredContentSource) {
|
||||||
|
console.log('Content source changed, refreshing featured content');
|
||||||
|
console.log('Current content source:', contentSource);
|
||||||
|
console.log('New settings source:', settings.featuredContentSource);
|
||||||
|
// Content source will be updated in the next render cycle due to state updates
|
||||||
|
// No need to call loadFeaturedContent here as it will be triggered by contentSource change
|
||||||
|
} else if (
|
||||||
|
contentSource === 'catalogs' &&
|
||||||
|
JSON.stringify(selectedCatalogs) !== JSON.stringify(settings.selectedHeroCatalogs)
|
||||||
|
) {
|
||||||
|
// Only refresh if using catalogs and selected catalogs changed
|
||||||
|
console.log('Selected catalogs changed, refreshing featured content');
|
||||||
|
loadFeaturedContent(true);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Subscribe to settings changes
|
// Subscribe to settings changes
|
||||||
const unsubscribe = settingsEmitter.addListener(handleSettingsChange);
|
const unsubscribe = settingsEmitter.addListener(handleSettingsChange);
|
||||||
|
|
||||||
return unsubscribe;
|
return unsubscribe;
|
||||||
}, [loadFeaturedContent]);
|
}, [loadFeaturedContent, settings, contentSource, selectedCatalogs]);
|
||||||
|
|
||||||
// Load featured content initially and when content source changes
|
// Load featured content initially and when content source changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const shouldForceRefresh = contentSource === 'tmdb' &&
|
// Force refresh when switching to catalogs or when catalog selection changes
|
||||||
contentSource !== persistentStore.featuredContent?.type;
|
if (contentSource === 'catalogs') {
|
||||||
|
// Clear cache when switching to catalogs mode
|
||||||
if (shouldForceRefresh) {
|
|
||||||
setAllFeaturedContent([]);
|
setAllFeaturedContent([]);
|
||||||
setFeaturedContent(null);
|
setFeaturedContent(null);
|
||||||
persistentStore.allFeaturedContent = [];
|
persistentStore.allFeaturedContent = [];
|
||||||
persistentStore.featuredContent = null;
|
persistentStore.featuredContent = null;
|
||||||
|
loadFeaturedContent(true);
|
||||||
|
} else if (contentSource === 'tmdb' && contentSource !== persistentStore.featuredContent?.type) {
|
||||||
|
// Clear cache when switching to TMDB mode from catalogs
|
||||||
|
setAllFeaturedContent([]);
|
||||||
|
setFeaturedContent(null);
|
||||||
|
persistentStore.allFeaturedContent = [];
|
||||||
|
persistentStore.featuredContent = null;
|
||||||
|
loadFeaturedContent(true);
|
||||||
|
} else {
|
||||||
|
// Normal load (might use cache if available)
|
||||||
|
loadFeaturedContent(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
loadFeaturedContent(shouldForceRefresh);
|
|
||||||
}, [loadFeaturedContent, contentSource, selectedCatalogs]);
|
}, [loadFeaturedContent, contentSource, selectedCatalogs]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
|
||||||
|
|
@ -84,8 +84,11 @@ export const useSettings = () => {
|
||||||
try {
|
try {
|
||||||
await AsyncStorage.setItem(SETTINGS_STORAGE_KEY, JSON.stringify(newSettings));
|
await AsyncStorage.setItem(SETTINGS_STORAGE_KEY, JSON.stringify(newSettings));
|
||||||
setSettings(newSettings);
|
setSettings(newSettings);
|
||||||
|
console.log(`Setting updated: ${key}`, value);
|
||||||
|
|
||||||
// Notify all subscribers that settings have changed (if requested)
|
// Notify all subscribers that settings have changed (if requested)
|
||||||
if (emitEvent) {
|
if (emitEvent) {
|
||||||
|
console.log('Emitting settings change event');
|
||||||
settingsEmitter.emit();
|
settingsEmitter.emit();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
||||||
|
|
@ -393,40 +393,27 @@ const HomeScreen = () => {
|
||||||
setShowHeroSection(settings.showHeroSection);
|
setShowHeroSection(settings.showHeroSection);
|
||||||
setFeaturedContentSource(settings.featuredContentSource);
|
setFeaturedContentSource(settings.featuredContentSource);
|
||||||
|
|
||||||
// If hero section is enabled, force a refresh of featured content
|
// The featured content refresh is now handled by the useFeaturedContent hook
|
||||||
if (settings.showHeroSection) {
|
// No need to call refreshFeatured() here to avoid duplicate refreshes
|
||||||
refreshFeatured();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Subscribe to settings changes
|
// Subscribe to settings changes
|
||||||
const unsubscribe = settingsEmitter.addListener(handleSettingsChange);
|
const unsubscribe = settingsEmitter.addListener(handleSettingsChange);
|
||||||
|
|
||||||
return unsubscribe;
|
return unsubscribe;
|
||||||
}, [refreshFeatured, settings]);
|
}, [settings]);
|
||||||
|
|
||||||
// Update the featured content refresh logic to handle persistence
|
// Update the featured content refresh logic to handle persistence
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
// This effect was causing duplicate refreshes - it's now handled in useFeaturedContent
|
||||||
|
// We'll keep it just to sync the local state with settings
|
||||||
if (showHeroSection && featuredContentSource !== settings.featuredContentSource) {
|
if (showHeroSection && featuredContentSource !== settings.featuredContentSource) {
|
||||||
// Clear any existing timeout
|
// Just update the local state
|
||||||
if (refreshTimeoutRef.current) {
|
setFeaturedContentSource(settings.featuredContentSource);
|
||||||
clearTimeout(refreshTimeoutRef.current);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set a new timeout to debounce the refresh - only when settings actually change
|
|
||||||
refreshTimeoutRef.current = setTimeout(() => {
|
|
||||||
refreshFeatured();
|
|
||||||
refreshTimeoutRef.current = null;
|
|
||||||
}, 300);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cleanup the timeout on unmount
|
// No timeout needed since we're not refreshing here
|
||||||
return () => {
|
}, [settings.featuredContentSource, showHeroSection]);
|
||||||
if (refreshTimeoutRef.current) {
|
|
||||||
clearTimeout(refreshTimeoutRef.current);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}, [featuredContentSource, settings.featuredContentSource, showHeroSection, refreshFeatured]);
|
|
||||||
|
|
||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
useCallback(() => {
|
useCallback(() => {
|
||||||
|
|
|
||||||
|
|
@ -269,7 +269,10 @@ const HomeScreenSettings: React.FC = () => {
|
||||||
<View style={styles.radioCardContainer}>
|
<View style={styles.radioCardContainer}>
|
||||||
<RadioOption
|
<RadioOption
|
||||||
selected={settings.featuredContentSource === 'tmdb'}
|
selected={settings.featuredContentSource === 'tmdb'}
|
||||||
onPress={() => handleUpdateSetting('featuredContentSource', 'tmdb')}
|
onPress={() => {
|
||||||
|
console.log('Selected TMDB source');
|
||||||
|
handleUpdateSetting('featuredContentSource', 'tmdb');
|
||||||
|
}}
|
||||||
label="TMDB Trending Movies"
|
label="TMDB Trending Movies"
|
||||||
/>
|
/>
|
||||||
<View style={styles.radioDescription}>
|
<View style={styles.radioDescription}>
|
||||||
|
|
@ -282,7 +285,10 @@ const HomeScreenSettings: React.FC = () => {
|
||||||
<View style={styles.radioCardContainer}>
|
<View style={styles.radioCardContainer}>
|
||||||
<RadioOption
|
<RadioOption
|
||||||
selected={settings.featuredContentSource === 'catalogs'}
|
selected={settings.featuredContentSource === 'catalogs'}
|
||||||
onPress={() => handleUpdateSetting('featuredContentSource', 'catalogs')}
|
onPress={() => {
|
||||||
|
console.log('Selected Catalogs source');
|
||||||
|
handleUpdateSetting('featuredContentSource', 'catalogs');
|
||||||
|
}}
|
||||||
label="Installed Catalogs"
|
label="Installed Catalogs"
|
||||||
/>
|
/>
|
||||||
<View style={styles.radioDescription}>
|
<View style={styles.radioDescription}>
|
||||||
|
|
|
||||||
|
|
@ -147,12 +147,14 @@ class CatalogService {
|
||||||
|
|
||||||
async getHomeCatalogs(): Promise<CatalogContent[]> {
|
async getHomeCatalogs(): Promise<CatalogContent[]> {
|
||||||
const addons = await this.getAllAddons();
|
const addons = await this.getAllAddons();
|
||||||
const catalogs: CatalogContent[] = [];
|
|
||||||
|
|
||||||
// Load enabled/disabled settings
|
// Load enabled/disabled settings
|
||||||
const catalogSettingsJson = await AsyncStorage.getItem(CATALOG_SETTINGS_KEY);
|
const catalogSettingsJson = await AsyncStorage.getItem(CATALOG_SETTINGS_KEY);
|
||||||
const catalogSettings = catalogSettingsJson ? JSON.parse(catalogSettingsJson) : {};
|
const catalogSettings = catalogSettingsJson ? JSON.parse(catalogSettingsJson) : {};
|
||||||
|
|
||||||
|
// Create an array of promises for all catalog fetches
|
||||||
|
const catalogPromises: Promise<CatalogContent | null>[] = [];
|
||||||
|
|
||||||
// Process addons in order (they're already returned in order from getAllAddons)
|
// Process addons in order (they're already returned in order from getAllAddons)
|
||||||
for (const addon of addons) {
|
for (const addon of addons) {
|
||||||
if (addon.catalogs) {
|
if (addon.catalogs) {
|
||||||
|
|
@ -161,54 +163,65 @@ class CatalogService {
|
||||||
const isEnabled = catalogSettings[settingKey] ?? true;
|
const isEnabled = catalogSettings[settingKey] ?? true;
|
||||||
|
|
||||||
if (isEnabled) {
|
if (isEnabled) {
|
||||||
try {
|
// Create a promise for each catalog fetch
|
||||||
const addonManifest = await stremioService.getInstalledAddonsAsync();
|
const catalogPromise = (async () => {
|
||||||
const manifest = addonManifest.find(a => a.id === addon.id);
|
try {
|
||||||
if (!manifest) continue;
|
const addonManifest = await stremioService.getInstalledAddonsAsync();
|
||||||
|
const manifest = addonManifest.find(a => a.id === addon.id);
|
||||||
|
if (!manifest) return null;
|
||||||
|
|
||||||
const metas = await stremioService.getCatalog(manifest, catalog.type, catalog.id, 1);
|
const metas = await stremioService.getCatalog(manifest, catalog.type, catalog.id, 1);
|
||||||
if (metas && metas.length > 0) {
|
if (metas && metas.length > 0) {
|
||||||
const items = metas.map(meta => this.convertMetaToStreamingContent(meta));
|
const items = metas.map(meta => this.convertMetaToStreamingContent(meta));
|
||||||
|
|
||||||
// Get potentially custom display name
|
// Get potentially custom display name
|
||||||
let displayName = await getCatalogDisplayName(addon.id, catalog.type, catalog.id, catalog.name);
|
let displayName = await getCatalogDisplayName(addon.id, catalog.type, catalog.id, catalog.name);
|
||||||
|
|
||||||
// Remove duplicate words and clean up the name (case-insensitive)
|
// Remove duplicate words and clean up the name (case-insensitive)
|
||||||
const words = displayName.split(' ');
|
const words = displayName.split(' ');
|
||||||
const uniqueWords = [];
|
const uniqueWords = [];
|
||||||
const seenWords = new Set();
|
const seenWords = new Set();
|
||||||
for (const word of words) {
|
for (const word of words) {
|
||||||
const lowerWord = word.toLowerCase();
|
const lowerWord = word.toLowerCase();
|
||||||
if (!seenWords.has(lowerWord)) {
|
if (!seenWords.has(lowerWord)) {
|
||||||
uniqueWords.push(word);
|
uniqueWords.push(word);
|
||||||
seenWords.add(lowerWord);
|
seenWords.add(lowerWord);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
displayName = uniqueWords.join(' ');
|
||||||
displayName = uniqueWords.join(' ');
|
|
||||||
|
|
||||||
// Add content type if not present
|
// Add content type if not present
|
||||||
const contentType = catalog.type === 'movie' ? 'Movies' : 'TV Shows';
|
const contentType = catalog.type === 'movie' ? 'Movies' : 'TV Shows';
|
||||||
if (!displayName.toLowerCase().includes(contentType.toLowerCase())) {
|
if (!displayName.toLowerCase().includes(contentType.toLowerCase())) {
|
||||||
displayName = `${displayName} ${contentType}`;
|
displayName = `${displayName} ${contentType}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
catalogs.push({
|
return {
|
||||||
addon: addon.id,
|
addon: addon.id,
|
||||||
type: catalog.type,
|
type: catalog.type,
|
||||||
id: catalog.id,
|
id: catalog.id,
|
||||||
name: displayName,
|
name: displayName,
|
||||||
items
|
items
|
||||||
});
|
};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`Failed to load ${catalog.name} from ${addon.name}:`, error);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
})();
|
||||||
logger.error(`Failed to load ${catalog.name} from ${addon.name}:`, error);
|
|
||||||
}
|
catalogPromises.push(catalogPromise);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return catalogs;
|
// Wait for all catalog fetch promises to resolve in parallel
|
||||||
|
const catalogResults = await Promise.all(catalogPromises);
|
||||||
|
|
||||||
|
// Filter out null results
|
||||||
|
return catalogResults.filter(catalog => catalog !== null) as CatalogContent[];
|
||||||
}
|
}
|
||||||
|
|
||||||
async getCatalogByType(type: string, genreFilter?: string): Promise<CatalogContent[]> {
|
async getCatalogByType(type: string, genreFilter?: string): Promise<CatalogContent[]> {
|
||||||
|
|
@ -222,46 +235,58 @@ class CatalogService {
|
||||||
|
|
||||||
// Otherwise use the original Stremio addons method
|
// Otherwise use the original Stremio addons method
|
||||||
const addons = await this.getAllAddons();
|
const addons = await this.getAllAddons();
|
||||||
const catalogs: CatalogContent[] = [];
|
|
||||||
|
|
||||||
const typeAddons = addons.filter(addon =>
|
const typeAddons = addons.filter(addon =>
|
||||||
addon.catalogs && addon.catalogs.some(catalog => catalog.type === type)
|
addon.catalogs && addon.catalogs.some(catalog => catalog.type === type)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Create an array of promises for all catalog fetches
|
||||||
|
const catalogPromises: Promise<CatalogContent | null>[] = [];
|
||||||
|
|
||||||
for (const addon of typeAddons) {
|
for (const addon of typeAddons) {
|
||||||
const typeCatalogs = addon.catalogs.filter(catalog => catalog.type === type);
|
const typeCatalogs = addon.catalogs.filter(catalog => catalog.type === type);
|
||||||
|
|
||||||
for (const catalog of typeCatalogs) {
|
for (const catalog of typeCatalogs) {
|
||||||
try {
|
const catalogPromise = (async () => {
|
||||||
const addonManifest = await stremioService.getInstalledAddonsAsync();
|
try {
|
||||||
const manifest = addonManifest.find(a => a.id === addon.id);
|
const addonManifest = await stremioService.getInstalledAddonsAsync();
|
||||||
if (!manifest) continue;
|
const manifest = addonManifest.find(a => a.id === addon.id);
|
||||||
|
if (!manifest) return null;
|
||||||
|
|
||||||
const filters = genreFilter ? [{ title: 'genre', value: genreFilter }] : [];
|
const filters = genreFilter ? [{ title: 'genre', value: genreFilter }] : [];
|
||||||
const metas = await stremioService.getCatalog(manifest, type, catalog.id, 1, filters);
|
const metas = await stremioService.getCatalog(manifest, type, catalog.id, 1, filters);
|
||||||
|
|
||||||
if (metas && metas.length > 0) {
|
if (metas && metas.length > 0) {
|
||||||
const items = metas.map(meta => this.convertMetaToStreamingContent(meta));
|
const items = metas.map(meta => this.convertMetaToStreamingContent(meta));
|
||||||
|
|
||||||
// Get potentially custom display name
|
// Get potentially custom display name
|
||||||
const displayName = await getCatalogDisplayName(addon.id, catalog.type, catalog.id, catalog.name);
|
const displayName = await getCatalogDisplayName(addon.id, catalog.type, catalog.id, catalog.name);
|
||||||
|
|
||||||
catalogs.push({
|
return {
|
||||||
addon: addon.id,
|
addon: addon.id,
|
||||||
type,
|
type,
|
||||||
id: catalog.id,
|
id: catalog.id,
|
||||||
name: displayName,
|
name: displayName,
|
||||||
genre: genreFilter,
|
genre: genreFilter,
|
||||||
items
|
items
|
||||||
});
|
};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`Failed to get catalog ${catalog.id} for addon ${addon.id}:`, error);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
})();
|
||||||
logger.error(`Failed to get catalog ${catalog.id} for addon ${addon.id}:`, error);
|
|
||||||
}
|
catalogPromises.push(catalogPromise);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return catalogs;
|
// Wait for all catalog fetch promises to resolve in parallel
|
||||||
|
const catalogResults = await Promise.all(catalogPromises);
|
||||||
|
|
||||||
|
// Filter out null results
|
||||||
|
return catalogResults.filter(catalog => catalog !== null) as CatalogContent[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -277,64 +302,75 @@ class CatalogService {
|
||||||
|
|
||||||
// If no genre filter or All is selected, get multiple catalogs
|
// If no genre filter or All is selected, get multiple catalogs
|
||||||
if (!genreFilter || genreFilter === 'All') {
|
if (!genreFilter || genreFilter === 'All') {
|
||||||
// Get trending
|
// Create an array of promises for all catalog fetches
|
||||||
const trendingItems = await tmdbService.getTrending(tmdbType, 'week');
|
const catalogFetchPromises = [
|
||||||
const trendingItemsPromises = trendingItems.map(item => this.convertTMDBToStreamingContent(item, tmdbType));
|
// Trending catalog
|
||||||
const trendingStreamingItems = await Promise.all(trendingItemsPromises);
|
(async () => {
|
||||||
|
const trendingItems = await tmdbService.getTrending(tmdbType, 'week');
|
||||||
|
const trendingItemsPromises = trendingItems.map(item => this.convertTMDBToStreamingContent(item, tmdbType));
|
||||||
|
const trendingStreamingItems = await Promise.all(trendingItemsPromises);
|
||||||
|
|
||||||
catalogs.push({
|
return {
|
||||||
addon: 'tmdb',
|
addon: 'tmdb',
|
||||||
type,
|
type,
|
||||||
id: 'trending',
|
id: 'trending',
|
||||||
name: `Trending ${type === 'movie' ? 'Movies' : 'TV Shows'}`,
|
name: `Trending ${type === 'movie' ? 'Movies' : 'TV Shows'}`,
|
||||||
items: trendingStreamingItems
|
items: trendingStreamingItems
|
||||||
});
|
};
|
||||||
|
})(),
|
||||||
|
|
||||||
// Get popular
|
// Popular catalog
|
||||||
const popularItems = await tmdbService.getPopular(tmdbType, 1);
|
(async () => {
|
||||||
const popularItemsPromises = popularItems.map(item => this.convertTMDBToStreamingContent(item, tmdbType));
|
const popularItems = await tmdbService.getPopular(tmdbType, 1);
|
||||||
const popularStreamingItems = await Promise.all(popularItemsPromises);
|
const popularItemsPromises = popularItems.map(item => this.convertTMDBToStreamingContent(item, tmdbType));
|
||||||
|
const popularStreamingItems = await Promise.all(popularItemsPromises);
|
||||||
|
|
||||||
catalogs.push({
|
return {
|
||||||
addon: 'tmdb',
|
addon: 'tmdb',
|
||||||
type,
|
type,
|
||||||
id: 'popular',
|
id: 'popular',
|
||||||
name: `Popular ${type === 'movie' ? 'Movies' : 'TV Shows'}`,
|
name: `Popular ${type === 'movie' ? 'Movies' : 'TV Shows'}`,
|
||||||
items: popularStreamingItems
|
items: popularStreamingItems
|
||||||
});
|
};
|
||||||
|
})(),
|
||||||
|
|
||||||
// Get upcoming/on air
|
// Upcoming/on air catalog
|
||||||
const upcomingItems = await tmdbService.getUpcoming(tmdbType, 1);
|
(async () => {
|
||||||
const upcomingItemsPromises = upcomingItems.map(item => this.convertTMDBToStreamingContent(item, tmdbType));
|
const upcomingItems = await tmdbService.getUpcoming(tmdbType, 1);
|
||||||
const upcomingStreamingItems = await Promise.all(upcomingItemsPromises);
|
const upcomingItemsPromises = upcomingItems.map(item => this.convertTMDBToStreamingContent(item, tmdbType));
|
||||||
|
const upcomingStreamingItems = await Promise.all(upcomingItemsPromises);
|
||||||
|
|
||||||
catalogs.push({
|
return {
|
||||||
addon: 'tmdb',
|
addon: 'tmdb',
|
||||||
type,
|
type,
|
||||||
id: 'upcoming',
|
id: 'upcoming',
|
||||||
name: type === 'movie' ? 'Upcoming Movies' : 'On Air TV Shows',
|
name: type === 'movie' ? 'Upcoming Movies' : 'On Air TV Shows',
|
||||||
items: upcomingStreamingItems
|
items: upcomingStreamingItems
|
||||||
});
|
};
|
||||||
|
})()
|
||||||
|
];
|
||||||
|
|
||||||
|
// Wait for all catalog fetches to complete in parallel
|
||||||
|
return await Promise.all(catalogFetchPromises);
|
||||||
} else {
|
} else {
|
||||||
// Get content by genre
|
// Get content by genre
|
||||||
const genreItems = await tmdbService.discoverByGenre(tmdbType, genreFilter);
|
const genreItems = await tmdbService.discoverByGenre(tmdbType, genreFilter);
|
||||||
const streamingItemsPromises = genreItems.map(item => this.convertTMDBToStreamingContent(item, tmdbType));
|
const streamingItemsPromises = genreItems.map(item => this.convertTMDBToStreamingContent(item, tmdbType));
|
||||||
const streamingItems = await Promise.all(streamingItemsPromises);
|
const streamingItems = await Promise.all(streamingItemsPromises);
|
||||||
|
|
||||||
catalogs.push({
|
return [{
|
||||||
addon: 'tmdb',
|
addon: 'tmdb',
|
||||||
type,
|
type,
|
||||||
id: 'discover',
|
id: 'discover',
|
||||||
name: `${genreFilter} ${type === 'movie' ? 'Movies' : 'TV Shows'}`,
|
name: `${genreFilter} ${type === 'movie' ? 'Movies' : 'TV Shows'}`,
|
||||||
genre: genreFilter,
|
genre: genreFilter,
|
||||||
items: streamingItems
|
items: streamingItems
|
||||||
});
|
}];
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`Failed to get catalog from TMDB for type ${type}, genre ${genreFilter}:`, error);
|
logger.error(`Failed to get catalog from TMDB for type ${type}, genre ${genreFilter}:`, error);
|
||||||
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return catalogs;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue