some UI fix
This commit is contained in:
parent
7dceb23e3d
commit
0c14d8641d
4 changed files with 211 additions and 24 deletions
|
|
@ -97,7 +97,8 @@ const ActionButtons = memo(({
|
|||
watchProgress,
|
||||
groupedEpisodes,
|
||||
metadata,
|
||||
aiChatEnabled
|
||||
aiChatEnabled,
|
||||
settings
|
||||
}: {
|
||||
handleShowStreams: () => void;
|
||||
toggleLibrary: () => void;
|
||||
|
|
@ -112,6 +113,7 @@ const ActionButtons = memo(({
|
|||
groupedEpisodes?: { [seasonNumber: number]: any[] };
|
||||
metadata: any;
|
||||
aiChatEnabled?: boolean;
|
||||
settings: any;
|
||||
}) => {
|
||||
const { currentTheme } = useTheme();
|
||||
|
||||
|
|
@ -135,7 +137,7 @@ const ActionButtons = memo(({
|
|||
if (!isNaN(parsedId)) {
|
||||
finalTmdbId = parsedId;
|
||||
}
|
||||
} else if (id.startsWith('tt')) {
|
||||
} else if (id.startsWith('tt') && settings.enrichMetadataWithTMDB) {
|
||||
try {
|
||||
const tmdbService = TMDBService.getInstance();
|
||||
const convertedId = await tmdbService.findTMDBIdByIMDB(id);
|
||||
|
|
@ -158,7 +160,7 @@ const ActionButtons = memo(({
|
|||
navigation.navigate('ShowRatings', { showId: finalTmdbId });
|
||||
});
|
||||
}
|
||||
}, [id, navigation]);
|
||||
}, [id, navigation, settings.enrichMetadataWithTMDB]);
|
||||
|
||||
// Optimized play button style calculation
|
||||
const playButtonStyle = useMemo(() => {
|
||||
|
|
@ -1538,6 +1540,7 @@ const HeroSection: React.FC<HeroSectionProps> = memo(({
|
|||
groupedEpisodes={groupedEpisodes}
|
||||
metadata={metadata}
|
||||
aiChatEnabled={settings?.aiChatEnabled}
|
||||
settings={settings}
|
||||
/>
|
||||
</View>
|
||||
</LinearGradient>
|
||||
|
|
|
|||
|
|
@ -232,6 +232,28 @@ export const useMetadata = ({ id, type, addonId }: UseMetadataProps): UseMetadat
|
|||
(streams, addonId, addonName, error) => {
|
||||
const processTime = Date.now() - sourceStartTime;
|
||||
|
||||
console.log('🔍 [processStremioSource] Callback received:', {
|
||||
addonId,
|
||||
addonName,
|
||||
streamCount: streams?.length || 0,
|
||||
error: error?.message || null,
|
||||
processTime
|
||||
});
|
||||
|
||||
// ALWAYS remove from active fetching list when callback is received
|
||||
// This ensures that even failed scrapers are removed from the "Fetching from:" chip
|
||||
if (addonName) {
|
||||
setActiveFetchingScrapers(prev => {
|
||||
const updated = prev.filter(name => name !== addonName);
|
||||
console.log('🔍 [processStremioSource] Removing from activeFetchingScrapers:', {
|
||||
addonName,
|
||||
before: prev,
|
||||
after: updated
|
||||
});
|
||||
return updated;
|
||||
});
|
||||
}
|
||||
|
||||
// Update scraper status when we get a callback
|
||||
if (addonId && addonName) {
|
||||
setScraperStatuses(prevStatuses => {
|
||||
|
|
@ -254,9 +276,6 @@ export const useMetadata = ({ id, type, addonId }: UseMetadataProps): UseMetadat
|
|||
return [...prevStatuses, newStatus];
|
||||
}
|
||||
});
|
||||
|
||||
// Remove from active fetching list
|
||||
setActiveFetchingScrapers(prev => prev.filter(name => name !== addonName));
|
||||
}
|
||||
|
||||
if (error) {
|
||||
|
|
@ -314,7 +333,53 @@ export const useMetadata = ({ id, type, addonId }: UseMetadataProps): UseMetadat
|
|||
} catch (error) {
|
||||
// Catch errors from the initial call to getStreams (e.g., initialization errors)
|
||||
logger.error(`❌ [${logPrefix}:${sourceName}] Initial call failed:`, error);
|
||||
// Maybe update state to show a general Stremio error?
|
||||
|
||||
// Remove all addons and scrapers from active fetching since the entire request failed
|
||||
setActiveFetchingScrapers(prev => {
|
||||
// Get both Stremio addon names and local scraper names
|
||||
const stremioAddons = stremioService.getInstalledAddons();
|
||||
const stremioNames = stremioAddons.map(addon => addon.name);
|
||||
|
||||
// Get local scraper names
|
||||
localScraperService.getInstalledScrapers().then(localScrapers => {
|
||||
const localScraperNames = localScrapers.filter(s => s.enabled).map(s => s.name);
|
||||
const allNames = [...stremioNames, ...localScraperNames];
|
||||
|
||||
// Remove all from active fetching
|
||||
setActiveFetchingScrapers(current =>
|
||||
current.filter(name => !allNames.includes(name))
|
||||
);
|
||||
}).catch(() => {
|
||||
// If we can't get local scrapers, just remove Stremio addons
|
||||
setActiveFetchingScrapers(current =>
|
||||
current.filter(name => !stremioNames.includes(name))
|
||||
);
|
||||
});
|
||||
|
||||
// Immediately remove Stremio addons (local scrapers will be removed async above)
|
||||
return prev.filter(name => !stremioNames.includes(name));
|
||||
});
|
||||
|
||||
// Update scraper statuses to mark all scrapers as failed
|
||||
setScraperStatuses(prevStatuses => {
|
||||
const stremioAddons = stremioService.getInstalledAddons();
|
||||
|
||||
return prevStatuses.map(status => {
|
||||
const isStremioAddon = stremioAddons.some(addon => addon.id === status.id || addon.name === status.name);
|
||||
|
||||
// Mark both Stremio addons and local scrapers as failed
|
||||
if (isStremioAddon || !status.hasCompleted) {
|
||||
return {
|
||||
...status,
|
||||
isLoading: false,
|
||||
hasCompleted: true,
|
||||
error: error instanceof Error ? error.message : 'Initial request failed',
|
||||
endTime: Date.now()
|
||||
};
|
||||
}
|
||||
return status;
|
||||
});
|
||||
});
|
||||
}
|
||||
// Note: This function completes when getStreams returns, not when all callbacks have fired.
|
||||
// Loading indicators should probably be managed based on callbacks completing.
|
||||
|
|
@ -358,9 +423,9 @@ export const useMetadata = ({ id, type, addonId }: UseMetadataProps): UseMetadat
|
|||
}
|
||||
}
|
||||
|
||||
// Handle IMDb IDs or convert to TMDB ID
|
||||
// Handle IMDb IDs or convert to TMDB ID (only if enrichment is enabled)
|
||||
let tmdbId;
|
||||
if (id.startsWith('tt')) {
|
||||
if (id.startsWith('tt') && settings.enrichMetadataWithTMDB) {
|
||||
if (__DEV__) logger.log('[loadCast] Converting IMDb ID to TMDB ID');
|
||||
tmdbId = await tmdbService.findTMDBIdByIMDB(id);
|
||||
}
|
||||
|
|
@ -1119,9 +1184,13 @@ export const useMetadata = ({ id, type, addonId }: UseMetadataProps): UseMetadat
|
|||
} else if (id.startsWith('tt')) {
|
||||
// This is already an IMDB ID, perfect for Stremio
|
||||
stremioId = id;
|
||||
if (__DEV__) console.log('📝 [loadStreams] Converting IMDB ID to TMDB ID...');
|
||||
tmdbId = await withTimeout(tmdbService.findTMDBIdByIMDB(id), API_TIMEOUT);
|
||||
if (__DEV__) console.log('✅ [loadStreams] Converted to TMDB ID:', tmdbId);
|
||||
if (settings.enrichMetadataWithTMDB) {
|
||||
if (__DEV__) console.log('📝 [loadStreams] Converting IMDB ID to TMDB ID...');
|
||||
tmdbId = await withTimeout(tmdbService.findTMDBIdByIMDB(id), API_TIMEOUT);
|
||||
if (__DEV__) console.log('✅ [loadStreams] Converted to TMDB ID:', tmdbId);
|
||||
} else {
|
||||
if (__DEV__) console.log('📝 [loadStreams] TMDB enrichment disabled, skipping IMDB to TMDB conversion');
|
||||
}
|
||||
} else {
|
||||
tmdbId = id;
|
||||
stremioId = id;
|
||||
|
|
@ -1215,6 +1284,7 @@ export const useMetadata = ({ id, type, addonId }: UseMetadataProps): UseMetadat
|
|||
|
||||
setScraperStatuses(initialStatuses);
|
||||
setActiveFetchingScrapers(initialActiveFetching);
|
||||
console.log('🔍 [loadStreams] Initialized activeFetchingScrapers:', initialActiveFetching);
|
||||
} catch (error) {
|
||||
if (__DEV__) console.error('Failed to initialize scraper tracking:', error);
|
||||
}
|
||||
|
|
@ -1243,6 +1313,14 @@ export const useMetadata = ({ id, type, addonId }: UseMetadataProps): UseMetadat
|
|||
clearInterval(completionInterval);
|
||||
setLoadingStreams(false);
|
||||
setActiveFetchingScrapers([]);
|
||||
// Mark all incomplete scrapers as failed
|
||||
setScraperStatuses(prevStatuses =>
|
||||
prevStatuses.map(status =>
|
||||
!status.hasCompleted && !status.error
|
||||
? { ...status, isLoading: false, hasCompleted: true, error: 'Request timed out', endTime: Date.now() }
|
||||
: status
|
||||
)
|
||||
);
|
||||
}, 60000);
|
||||
|
||||
} catch (error) {
|
||||
|
|
@ -1335,6 +1413,7 @@ export const useMetadata = ({ id, type, addonId }: UseMetadataProps): UseMetadat
|
|||
|
||||
setScraperStatuses(initialStatuses);
|
||||
setActiveFetchingScrapers(initialActiveFetching);
|
||||
console.log('🔍 [loadEpisodeStreams] Initialized activeFetchingScrapers:', initialActiveFetching);
|
||||
} catch (error) {
|
||||
if (__DEV__) console.error('Failed to initialize episode scraper tracking:', error);
|
||||
}
|
||||
|
|
@ -1356,7 +1435,9 @@ export const useMetadata = ({ id, type, addonId }: UseMetadataProps): UseMetadat
|
|||
// episodeId format for collections: "tt7888964" (IMDb ID of individual movie)
|
||||
if (episodeId.startsWith('tt')) {
|
||||
// This is an IMDb ID of an individual movie in the collection
|
||||
tmdbId = await withTimeout(tmdbService.findTMDBIdByIMDB(episodeId), API_TIMEOUT);
|
||||
if (settings.enrichMetadataWithTMDB) {
|
||||
tmdbId = await withTimeout(tmdbService.findTMDBIdByIMDB(episodeId), API_TIMEOUT);
|
||||
}
|
||||
stremioEpisodeId = episodeId; // Use the IMDb ID directly for Stremio addons
|
||||
if (__DEV__) console.log('✅ [loadEpisodeStreams] Collection movie - using IMDb ID:', episodeId, 'TMDB ID:', tmdbId);
|
||||
} else {
|
||||
|
|
@ -1397,8 +1478,12 @@ export const useMetadata = ({ id, type, addonId }: UseMetadataProps): UseMetadat
|
|||
}
|
||||
} else if (id.startsWith('tt')) {
|
||||
// This is already an IMDB ID, perfect for Stremio
|
||||
if (__DEV__) console.log('📝 [loadEpisodeStreams] Converting IMDB ID to TMDB ID...');
|
||||
tmdbId = await withTimeout(tmdbService.findTMDBIdByIMDB(id), API_TIMEOUT);
|
||||
if (settings.enrichMetadataWithTMDB) {
|
||||
if (__DEV__) console.log('📝 [loadEpisodeStreams] Converting IMDB ID to TMDB ID...');
|
||||
tmdbId = await withTimeout(tmdbService.findTMDBIdByIMDB(id), API_TIMEOUT);
|
||||
} else {
|
||||
if (__DEV__) console.log('📝 [loadEpisodeStreams] TMDB enrichment disabled, skipping IMDB to TMDB conversion');
|
||||
}
|
||||
if (__DEV__) console.log('✅ [loadEpisodeStreams] Converted to TMDB ID:', tmdbId);
|
||||
// Normalize episode id to 'tt:season:episode' format for addons that expect tt prefix
|
||||
const parts = episodeId.split(':');
|
||||
|
|
@ -1447,6 +1532,14 @@ export const useMetadata = ({ id, type, addonId }: UseMetadataProps): UseMetadat
|
|||
clearInterval(episodeCompletionInterval);
|
||||
setLoadingEpisodeStreams(false);
|
||||
setActiveFetchingScrapers([]);
|
||||
// Mark all incomplete scrapers as failed
|
||||
setScraperStatuses(prevStatuses =>
|
||||
prevStatuses.map(status =>
|
||||
!status.hasCompleted && !status.error
|
||||
? { ...status, isLoading: false, hasCompleted: true, error: 'Request timed out', endTime: Date.now() }
|
||||
: status
|
||||
)
|
||||
);
|
||||
}, 60000);
|
||||
|
||||
} catch (error) {
|
||||
|
|
|
|||
|
|
@ -150,7 +150,7 @@ export const useMetadataAssets = (
|
|||
// Extract or find TMDB ID in one step
|
||||
if (id.startsWith('tmdb:')) {
|
||||
tmdbId = id.split(':')[1];
|
||||
} else if (imdbId) {
|
||||
} else if (imdbId && settings.enrichMetadataWithTMDB) {
|
||||
try {
|
||||
const tmdbService = TMDBService.getInstance();
|
||||
const foundId = await tmdbService.findTMDBIdByIMDB(imdbId);
|
||||
|
|
@ -248,7 +248,7 @@ export const useMetadataAssets = (
|
|||
tmdbId = foundTmdbId;
|
||||
} else if ((metadata as any).tmdbId) {
|
||||
tmdbId = (metadata as any).tmdbId;
|
||||
} else if (imdbId) {
|
||||
} else if (imdbId && settings.enrichMetadataWithTMDB) {
|
||||
try {
|
||||
const tmdbService = TMDBService.getInstance();
|
||||
const foundId = await tmdbService.findTMDBIdByIMDB(imdbId);
|
||||
|
|
|
|||
|
|
@ -1427,6 +1427,18 @@ export const StreamsScreen = () => {
|
|||
const sections = useMemo(() => {
|
||||
const streams = metadata?.videos && metadata.videos.length > 1 && selectedEpisode ? episodeStreams : groupedStreams;
|
||||
const installedAddons = stremioService.getInstalledAddons();
|
||||
|
||||
console.log('🔍 [StreamsScreen] Sections debug:', {
|
||||
streamsKeys: Object.keys(streams),
|
||||
installedAddons: installedAddons.map(a => ({ id: a.id, name: a.name })),
|
||||
selectedProvider,
|
||||
streamDisplayMode: settings.streamDisplayMode,
|
||||
streamsData: Object.entries(streams).map(([key, data]) => ({
|
||||
provider: key,
|
||||
addonName: data.addonName,
|
||||
streamCount: data.streams?.length || 0
|
||||
}))
|
||||
});
|
||||
|
||||
// Filter streams by selected provider
|
||||
const filteredEntries = Object.entries(streams)
|
||||
|
|
@ -1444,8 +1456,18 @@ export const StreamsScreen = () => {
|
|||
|
||||
// Otherwise only show the selected provider
|
||||
return addonId === selectedProvider;
|
||||
})
|
||||
.sort(([addonIdA], [addonIdB]) => {
|
||||
});
|
||||
|
||||
console.log('🔍 [StreamsScreen] Filtered entries:', {
|
||||
filteredCount: filteredEntries.length,
|
||||
filteredEntries: filteredEntries.map(([addonId, data]) => ({
|
||||
addonId,
|
||||
addonName: data.addonName,
|
||||
streamCount: data.streams?.length || 0
|
||||
}))
|
||||
});
|
||||
|
||||
const sortedEntries = filteredEntries.sort(([addonIdA], [addonIdB]) => {
|
||||
// Sort by response order (actual order addons responded)
|
||||
const indexA = addonResponseOrder.indexOf(addonIdA);
|
||||
const indexB = addonResponseOrder.indexOf(addonIdB);
|
||||
|
|
@ -1470,7 +1492,7 @@ export const StreamsScreen = () => {
|
|||
const pluginStreams: Stream[] = [];
|
||||
let totalOriginalCount = 0;
|
||||
|
||||
filteredEntries.forEach(([addonId, { addonName, streams: providerStreams }]) => {
|
||||
sortedEntries.forEach(([addonId, { addonName, streams: providerStreams }]) => {
|
||||
const isInstalledAddon = installedAddons.some(addon => addon.id === addonId);
|
||||
|
||||
// Count original streams before filtering
|
||||
|
|
@ -1572,15 +1594,25 @@ export const StreamsScreen = () => {
|
|||
combinedStreams.push(...pluginStreams);
|
||||
}
|
||||
|
||||
return [{
|
||||
const result = [{
|
||||
title: 'Available Streams',
|
||||
addonId: 'grouped-all',
|
||||
data: combinedStreams,
|
||||
isEmptyDueToQualityFilter: false
|
||||
}];
|
||||
|
||||
console.log('🔍 [StreamsScreen] Grouped mode result:', {
|
||||
resultCount: result.length,
|
||||
combinedStreamsCount: combinedStreams.length,
|
||||
addonStreamsCount: addonStreams.length,
|
||||
pluginStreamsCount: pluginStreams.length,
|
||||
totalOriginalCount
|
||||
});
|
||||
|
||||
return result;
|
||||
} else {
|
||||
// Use separate sections for each provider (current behavior)
|
||||
return filteredEntries.map(([addonId, { addonName, streams: providerStreams }]) => {
|
||||
return sortedEntries.map(([addonId, { addonName, streams: providerStreams }]) => {
|
||||
const isInstalledAddon = installedAddons.some(addon => addon.id === addonId);
|
||||
|
||||
// Count original streams before filtering
|
||||
|
|
@ -1591,8 +1623,25 @@ export const StreamsScreen = () => {
|
|||
|
||||
// Only apply quality filtering to plugins, NOT addons
|
||||
if (!isInstalledAddon) {
|
||||
console.log('🔍 [StreamsScreen] Applying quality filter to plugin:', {
|
||||
addonId,
|
||||
addonName,
|
||||
originalCount,
|
||||
excludedQualities: settings.excludedQualities
|
||||
});
|
||||
filteredStreams = filterStreamsByQuality(providerStreams);
|
||||
isEmptyDueToQualityFilter = originalCount > 0 && filteredStreams.length === 0;
|
||||
console.log('🔍 [StreamsScreen] Quality filter result:', {
|
||||
addonId,
|
||||
filteredCount: filteredStreams.length,
|
||||
isEmptyDueToQualityFilter
|
||||
});
|
||||
} else {
|
||||
console.log('🔍 [StreamsScreen] Skipping quality filter for addon:', {
|
||||
addonId,
|
||||
addonName,
|
||||
originalCount
|
||||
});
|
||||
}
|
||||
|
||||
if (isEmptyDueToQualityFilter) {
|
||||
|
|
@ -1661,15 +1710,38 @@ export const StreamsScreen = () => {
|
|||
});
|
||||
}
|
||||
|
||||
return {
|
||||
const result = {
|
||||
title: addonName,
|
||||
addonId,
|
||||
data: processedStreams,
|
||||
isEmptyDueToQualityFilter: false
|
||||
};
|
||||
|
||||
console.log('🔍 [StreamsScreen] Individual mode result:', {
|
||||
addonId,
|
||||
addonName,
|
||||
processedStreamsCount: processedStreams.length,
|
||||
originalCount,
|
||||
isInstalledAddon
|
||||
});
|
||||
|
||||
return result;
|
||||
});
|
||||
}
|
||||
}, [selectedProvider, type, episodeStreams, groupedStreams, settings.streamDisplayMode, filterStreamsByQuality, addonResponseOrder, settings.streamSortMode]);
|
||||
}, [selectedProvider, type, episodeStreams, groupedStreams, settings.streamDisplayMode, filterStreamsByQuality, addonResponseOrder, settings.streamSortMode, selectedEpisode, metadata]);
|
||||
|
||||
// Debug log for sections result
|
||||
React.useEffect(() => {
|
||||
console.log('🔍 [StreamsScreen] Final sections:', {
|
||||
sectionsCount: sections.length,
|
||||
sections: sections.map(s => ({
|
||||
title: s.title,
|
||||
addonId: s.addonId,
|
||||
dataCount: s.data?.length || 0,
|
||||
isEmptyDueToQualityFilter: s.isEmptyDueToQualityFilter
|
||||
}))
|
||||
});
|
||||
}, [sections]);
|
||||
|
||||
const episodeImage = useMemo(() => {
|
||||
if (episodeThumbnail) {
|
||||
|
|
@ -1726,6 +1798,25 @@ export const StreamsScreen = () => {
|
|||
const showInitialLoading = streamsEmpty && (streamsLoadStart === null || loadElapsed < 10000);
|
||||
const showStillFetching = streamsEmpty && loadElapsed >= 10000;
|
||||
|
||||
// Debug logging for stream availability
|
||||
React.useEffect(() => {
|
||||
console.log('🔍 [StreamsScreen] Streams debug:', {
|
||||
streamsEmpty,
|
||||
streamsKeys: Object.keys(streams),
|
||||
streamsData: Object.entries(streams).map(([key, data]) => ({
|
||||
provider: key,
|
||||
addonName: data.addonName,
|
||||
streamCount: data.streams?.length || 0,
|
||||
streams: data.streams?.slice(0, 3).map(s => ({ name: s.name, title: s.title })) || []
|
||||
})),
|
||||
isLoading,
|
||||
loadingStreams,
|
||||
loadingEpisodeStreams,
|
||||
selectedEpisode,
|
||||
type
|
||||
});
|
||||
}, [streams, streamsEmpty, isLoading, loadingStreams, loadingEpisodeStreams, selectedEpisode, type]);
|
||||
|
||||
|
||||
|
||||
const renderSectionHeader = useCallback(({ section }: { section: { title: string; addonId: string; isEmptyDueToQualityFilter?: boolean } }) => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue