mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-04-18 23:32:04 +00:00
improved stream fetching logic
This commit is contained in:
parent
7a5ecd3009
commit
91af3a4021
5 changed files with 591 additions and 544 deletions
|
|
@ -1 +1 @@
|
|||
Subproject commit db3b10e64353349d0d72619ca7d779829e36fe4d
|
||||
Subproject commit 1a94da42094b524b94a28902ae43c27e3286460d
|
||||
|
|
@ -1 +1 @@
|
|||
Subproject commit 118cd1ed3d498265e44230e5dbb015bdd59f9dad
|
||||
Subproject commit a31e9a0d270066deb41fe330ed34ddeb0e38f0ab
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1569,6 +1569,11 @@ export const useMetadata = ({ id, type, addonId }: UseMetadataProps): UseMetadat
|
|||
setScraperStatuses(initialStatuses);
|
||||
setActiveFetchingScrapers(initialActiveFetching);
|
||||
console.log('🔍 [loadStreams] Initialized activeFetchingScrapers:', initialActiveFetching);
|
||||
|
||||
// If no scrapers are available, stop loading immediately
|
||||
if (initialStatuses.length === 0) {
|
||||
setLoadingStreams(false);
|
||||
}
|
||||
} catch (error) {
|
||||
if (__DEV__) console.error('Failed to initialize scraper tracking:', error);
|
||||
}
|
||||
|
|
@ -1701,6 +1706,11 @@ export const useMetadata = ({ id, type, addonId }: UseMetadataProps): UseMetadat
|
|||
setScraperStatuses(initialStatuses);
|
||||
setActiveFetchingScrapers(initialActiveFetching);
|
||||
console.log('🔍 [loadEpisodeStreams] Initialized activeFetchingScrapers:', initialActiveFetching);
|
||||
|
||||
// If no scrapers are available, stop loading immediately
|
||||
if (initialStatuses.length === 0) {
|
||||
setLoadingEpisodeStreams(false);
|
||||
}
|
||||
} catch (error) {
|
||||
if (__DEV__) console.error('Failed to initialize episode scraper tracking:', error);
|
||||
}
|
||||
|
|
@ -1715,6 +1725,37 @@ export const useMetadata = ({ id, type, addonId }: UseMetadataProps): UseMetadat
|
|||
const { isCollection: detectedCollection, addon: collectionAddon } = stremioService.isCollectionContent(id);
|
||||
isCollection = detectedCollection;
|
||||
|
||||
|
||||
// Parse season and episode numbers robustly
|
||||
let showIdStr = id;
|
||||
let seasonNum = '';
|
||||
let episodeNum = '';
|
||||
|
||||
try {
|
||||
// Handle various episode ID formats
|
||||
// 1. Internal format: "series:showId:season:episode"
|
||||
// 2. Stremio/IMDb format: "tt12345:1:1"
|
||||
// 3. TMDB format: "tmdb:123:1:1"
|
||||
|
||||
const cleanEpisodeId = episodeId.replace(/^series:/, '');
|
||||
const parts = cleanEpisodeId.split(':');
|
||||
|
||||
if (parts.length >= 3) {
|
||||
episodeNum = parts.pop() || '';
|
||||
seasonNum = parts.pop() || '';
|
||||
showIdStr = parts.join(':');
|
||||
} else if (parts.length === 2) {
|
||||
// Edge case: maybe just id:episode? unlikely but safe fallback
|
||||
episodeNum = parts[1];
|
||||
seasonNum = '1'; // Default
|
||||
showIdStr = parts[0];
|
||||
}
|
||||
|
||||
if (__DEV__) console.log(`🔍 [loadEpisodeStreams] Parsed ID: show=${showIdStr}, s=${seasonNum}, e=${episodeNum}`);
|
||||
} catch (e) {
|
||||
if (__DEV__) console.warn('⚠️ [loadEpisodeStreams] Failed to parse episode ID:', episodeId);
|
||||
}
|
||||
|
||||
if (isCollection && collectionAddon) {
|
||||
if (__DEV__) console.log(`🎬 [loadEpisodeStreams] Detected collection from addon: ${collectionAddon.name}, treating episodes as individual movies`);
|
||||
|
||||
|
|
@ -1728,9 +1769,15 @@ export const useMetadata = ({ id, type, addonId }: UseMetadataProps): UseMetadat
|
|||
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 {
|
||||
// Fallback: try to parse as TMDB ID
|
||||
tmdbId = episodeId;
|
||||
stremioEpisodeId = episodeId;
|
||||
// Fallback: try to verify if it's a tmdb id
|
||||
const isTmdb = episodeId.startsWith('tmdb:') || !isNaN(Number(episodeId));
|
||||
if (isTmdb) {
|
||||
const cleanId = episodeId.replace('tmdb:', '');
|
||||
tmdbId = cleanId;
|
||||
stremioEpisodeId = episodeId;
|
||||
} else {
|
||||
stremioEpisodeId = episodeId;
|
||||
}
|
||||
if (__DEV__) console.log('⚠️ [loadEpisodeStreams] Collection movie - using episodeId as-is:', episodeId);
|
||||
}
|
||||
} else if (id.startsWith('tmdb:')) {
|
||||
|
|
@ -1739,13 +1786,11 @@ export const useMetadata = ({ id, type, addonId }: UseMetadataProps): UseMetadat
|
|||
|
||||
// Try to get IMDb ID from metadata first, then convert if needed
|
||||
if (metadata?.imdb_id) {
|
||||
// Replace the series ID in episodeId with the IMDb ID
|
||||
const [, season, episode] = episodeId.split(':');
|
||||
stremioEpisodeId = `${metadata.imdb_id}:${season}:${episode}`;
|
||||
// Use format: imdb_id:season:episode
|
||||
stremioEpisodeId = `${metadata.imdb_id}:${seasonNum}:${episodeNum}`;
|
||||
if (__DEV__) console.log('✅ [loadEpisodeStreams] Using IMDb ID from metadata for Stremio episode:', stremioEpisodeId);
|
||||
} else if (imdbId) {
|
||||
const [, season, episode] = episodeId.split(':');
|
||||
stremioEpisodeId = `${imdbId}:${season}:${episode}`;
|
||||
stremioEpisodeId = `${imdbId}:${seasonNum}:${episodeNum}`;
|
||||
if (__DEV__) console.log('✅ [loadEpisodeStreams] Using stored IMDb ID for Stremio episode:', stremioEpisodeId);
|
||||
} else {
|
||||
// Convert TMDB ID to IMDb ID for Stremio addons
|
||||
|
|
@ -1753,14 +1798,17 @@ export const useMetadata = ({ id, type, addonId }: UseMetadataProps): UseMetadat
|
|||
const externalIds = await withTimeout(tmdbService.getShowExternalIds(parseInt(tmdbId)), API_TIMEOUT);
|
||||
|
||||
if (externalIds?.imdb_id) {
|
||||
const [, season, episode] = episodeId.split(':');
|
||||
stremioEpisodeId = `${externalIds.imdb_id}:${season}:${episode}`;
|
||||
stremioEpisodeId = `${externalIds.imdb_id}:${seasonNum}:${episodeNum}`;
|
||||
if (__DEV__) console.log('✅ [loadEpisodeStreams] Converted TMDB to IMDb ID for Stremio episode:', stremioEpisodeId);
|
||||
} else {
|
||||
if (__DEV__) console.log('⚠️ [loadEpisodeStreams] No IMDb ID found for TMDB ID, using original episode ID:', stremioEpisodeId);
|
||||
// Fallback to TMDB format if conversions fail
|
||||
// e.g. tmdb:123:1:1
|
||||
stremioEpisodeId = `${id}:${seasonNum}:${episodeNum}`;
|
||||
if (__DEV__) console.log('⚠️ [loadEpisodeStreams] No IMDb ID found for TMDB ID, using TMDB episode ID:', stremioEpisodeId);
|
||||
}
|
||||
} catch (error) {
|
||||
if (__DEV__) console.log('⚠️ [loadEpisodeStreams] Failed to convert TMDB to IMDb, using original episode ID:', error);
|
||||
stremioEpisodeId = `${id}:${seasonNum}:${episodeNum}`;
|
||||
if (__DEV__) console.log('⚠️ [loadEpisodeStreams] Failed to convert TMDB to IMDb, using TMDB episode ID:', error);
|
||||
}
|
||||
}
|
||||
} else if (id.startsWith('tt')) {
|
||||
|
|
@ -1772,20 +1820,18 @@ export const useMetadata = ({ id, type, addonId }: UseMetadataProps): UseMetadat
|
|||
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(':');
|
||||
if (parts.length === 3 && parts[0] === 'series') {
|
||||
stremioEpisodeId = `${id}:${parts[1]}:${parts[2]}`;
|
||||
if (__DEV__) console.log('🔧 [loadEpisodeStreams] Normalized episode ID for addons:', stremioEpisodeId);
|
||||
}
|
||||
|
||||
// Ensure consistent format
|
||||
stremioEpisodeId = `${id}:${seasonNum}:${episodeNum}`;
|
||||
if (__DEV__) console.log('🔧 [loadEpisodeStreams] Normalized episode ID for addons:', stremioEpisodeId);
|
||||
} else {
|
||||
tmdbId = id;
|
||||
stremioEpisodeId = `${id}:${seasonNum}:${episodeNum}`;
|
||||
if (__DEV__) console.log('ℹ️ [loadEpisodeStreams] Using ID as both TMDB and Stremio ID:', tmdbId);
|
||||
}
|
||||
|
||||
// Extract episode info from the episodeId for logging
|
||||
const [, season, episode] = episodeId.split(':');
|
||||
const episodeQuery = `?s=${season}&e=${episode}`;
|
||||
const episodeQuery = `?s=${seasonNum}&e=${episodeNum}`;
|
||||
if (__DEV__) console.log(`ℹ️ [loadEpisodeStreams] Episode query: ${episodeQuery}`);
|
||||
|
||||
if (__DEV__) console.log('🔄 [loadEpisodeStreams] Starting stream requests');
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ export const useStreamsScreen = () => {
|
|||
// Dimension tracking
|
||||
const [dimensions, setDimensions] = useState(Dimensions.get('window'));
|
||||
const prevDimensionsRef = useRef({ width: dimensions.width, height: dimensions.height });
|
||||
|
||||
|
||||
const deviceWidth = dimensions.width;
|
||||
const isTablet = useMemo(() => deviceWidth >= TABLET_BREAKPOINT, [deviceWidth]);
|
||||
|
||||
|
|
@ -119,7 +119,7 @@ export const useStreamsScreen = () => {
|
|||
} = useMetadata({ id, type });
|
||||
|
||||
// Get banner image
|
||||
const setMetadataStub = useCallback(() => {}, []);
|
||||
const setMetadataStub = useCallback(() => { }, []);
|
||||
const memoizedSettings = useMemo(
|
||||
() => settings,
|
||||
[settings.logoSourcePreference, settings.tmdbLanguagePreference, settings.enrichMetadataWithTMDB]
|
||||
|
|
@ -183,7 +183,7 @@ export const useStreamsScreen = () => {
|
|||
try {
|
||||
setAlertTitle(title);
|
||||
setAlertMessage(message);
|
||||
setAlertActions(actions && actions.length > 0 ? actions : [{ label: 'OK', onPress: () => {} }]);
|
||||
setAlertActions(actions && actions.length > 0 ? actions : [{ label: 'OK', onPress: () => { } }]);
|
||||
setAlertVisible(true);
|
||||
} catch (error) {
|
||||
console.warn('[StreamsScreen] Error showing alert:', error);
|
||||
|
|
@ -390,7 +390,7 @@ export const useStreamsScreen = () => {
|
|||
if (!videoType && /xprime/i.test(providerId)) {
|
||||
videoType = 'm3u8';
|
||||
}
|
||||
} catch {}
|
||||
} catch { }
|
||||
|
||||
const playerRoute = Platform.OS === 'ios' ? 'PlayerIOS' : 'PlayerAndroid';
|
||||
|
||||
|
|
@ -438,7 +438,7 @@ export const useStreamsScreen = () => {
|
|||
/format=mkv\b/i.test(lowerUrl) ||
|
||||
/container=mkv\b/i.test(lowerUrl);
|
||||
const isHttp = lowerUrl.startsWith('http://') || lowerUrl.startsWith('https://');
|
||||
|
||||
|
||||
if (!isMkvByPath && isHttp) {
|
||||
try {
|
||||
const mkvDetected = await Promise.race<boolean>([
|
||||
|
|
@ -609,14 +609,14 @@ export const useStreamsScreen = () => {
|
|||
useEffect(() => {
|
||||
// Build a unique key for the current content
|
||||
const currentKey = `${id}:${type}:${episodeId || ''}`;
|
||||
|
||||
|
||||
// Reset refs if content changed
|
||||
if (lastLoadedIdRef.current !== currentKey) {
|
||||
hasDoneInitialLoadRef.current = false;
|
||||
isLoadingStreamsRef.current = false;
|
||||
lastLoadedIdRef.current = currentKey;
|
||||
}
|
||||
|
||||
|
||||
// Only proceed if we haven't done the initial load for this content
|
||||
if (hasDoneInitialLoadRef.current) return;
|
||||
|
||||
|
|
@ -803,18 +803,18 @@ export const useStreamsScreen = () => {
|
|||
const sortedEntries = filteredEntries.sort(([addonIdA], [addonIdB]) => {
|
||||
const isAddonA = installedAddons.some(addon => addon.id === addonIdA);
|
||||
const isAddonB = installedAddons.some(addon => addon.id === addonIdB);
|
||||
|
||||
|
||||
// Addons always come before plugins
|
||||
if (isAddonA && !isAddonB) return -1;
|
||||
if (!isAddonA && isAddonB) return 1;
|
||||
|
||||
|
||||
// Both are addons - sort by installation order
|
||||
if (isAddonA && isAddonB) {
|
||||
const indexA = installedAddons.findIndex(addon => addon.id === addonIdA);
|
||||
const indexB = installedAddons.findIndex(addon => addon.id === addonIdB);
|
||||
return indexA - indexB;
|
||||
}
|
||||
|
||||
|
||||
// Both are plugins - sort by response order
|
||||
const responseIndexA = addonResponseOrder.indexOf(addonIdA);
|
||||
const responseIndexB = addonResponseOrder.indexOf(addonIdB);
|
||||
|
|
@ -1021,8 +1021,9 @@ export const useStreamsScreen = () => {
|
|||
Object.keys(streams).length === 0 ||
|
||||
Object.values(streams).every(provider => !provider.streams || provider.streams.length === 0);
|
||||
const loadElapsed = streamsLoadStart ? Date.now() - streamsLoadStart : 0;
|
||||
const showInitialLoading = streamsEmpty && (streamsLoadStart === null || loadElapsed < 10000);
|
||||
const showStillFetching = streamsEmpty && loadElapsed >= 10000;
|
||||
const isActuallyLoading = isLoading || activeFetchingScrapers.length > 0;
|
||||
const showInitialLoading = streamsEmpty && isActuallyLoading && (streamsLoadStart === null || loadElapsed < 10000);
|
||||
const showStillFetching = streamsEmpty && isActuallyLoading && loadElapsed >= 10000;
|
||||
|
||||
return {
|
||||
// Route params
|
||||
|
|
@ -1031,19 +1032,19 @@ export const useStreamsScreen = () => {
|
|||
episodeId,
|
||||
episodeThumbnail,
|
||||
fromPlayer,
|
||||
|
||||
|
||||
// Theme
|
||||
currentTheme,
|
||||
colors,
|
||||
settings,
|
||||
|
||||
|
||||
// Navigation
|
||||
navigation,
|
||||
handleBack,
|
||||
|
||||
|
||||
// Tablet
|
||||
isTablet,
|
||||
|
||||
|
||||
// Alert
|
||||
alertVisible,
|
||||
alertTitle,
|
||||
|
|
@ -1051,14 +1052,14 @@ export const useStreamsScreen = () => {
|
|||
alertActions,
|
||||
openAlert,
|
||||
closeAlert,
|
||||
|
||||
|
||||
// Metadata
|
||||
metadata,
|
||||
imdbId,
|
||||
bannerImage,
|
||||
currentEpisode,
|
||||
groupedEpisodes,
|
||||
|
||||
|
||||
// Streams
|
||||
streams,
|
||||
groupedStreams,
|
||||
|
|
@ -1068,7 +1069,7 @@ export const useStreamsScreen = () => {
|
|||
selectedProvider,
|
||||
handleProviderChange,
|
||||
handleStreamPress,
|
||||
|
||||
|
||||
// Loading states
|
||||
isLoading,
|
||||
loadingStreams,
|
||||
|
|
@ -1079,19 +1080,19 @@ export const useStreamsScreen = () => {
|
|||
showStillFetching,
|
||||
showNoSourcesError,
|
||||
hasStremioStreamProviders,
|
||||
|
||||
|
||||
// Autoplay
|
||||
isAutoplayWaiting,
|
||||
autoplayTriggered,
|
||||
|
||||
|
||||
// Scrapers
|
||||
activeFetchingScrapers,
|
||||
scraperLogos,
|
||||
|
||||
|
||||
// Movie
|
||||
movieLogoError,
|
||||
setMovieLogoError,
|
||||
|
||||
|
||||
// Episode
|
||||
episodeImage,
|
||||
effectiveEpisodeVote,
|
||||
|
|
@ -1099,7 +1100,7 @@ export const useStreamsScreen = () => {
|
|||
hasIMDbRating,
|
||||
tmdbEpisodeOverride,
|
||||
selectedEpisode,
|
||||
|
||||
|
||||
// Backdrop
|
||||
mobileBackdropSource,
|
||||
gradientColors,
|
||||
|
|
|
|||
Loading…
Reference in a new issue