From 5bbc2abe8b99e7e9dfb4f40da7824fb45ccb0c5a Mon Sep 17 00:00:00 2001 From: skoruppa Date: Mon, 2 Mar 2026 09:15:02 +0100 Subject: [PATCH] fix for Kitsu and Mal content being still incorrectly requested --- src/hooks/useMetadata.ts | 21 ++++++---- src/screens/MetadataScreen.tsx | 71 +++++++++++++++++++--------------- src/services/stremioService.ts | 13 ++++++- src/services/tmdbService.ts | 11 ++++-- 4 files changed, 73 insertions(+), 43 deletions(-) diff --git a/src/hooks/useMetadata.ts b/src/hooks/useMetadata.ts index 5daae1de2..5c7e13b1a 100644 --- a/src/hooks/useMetadata.ts +++ b/src/hooks/useMetadata.ts @@ -1877,6 +1877,7 @@ export const useMetadata = ({ id, type, addonId }: UseMetadataProps): UseMetadat } // Get TMDB ID for external sources and determine the correct ID for Stremio addons + const isImdb = id.startsWith('tt'); if (__DEV__) console.log('🔍 [loadEpisodeStreams] Getting TMDB ID for:', id); let tmdbId; let stremioEpisodeId = episodeId; // Default to original episode ID @@ -1901,19 +1902,25 @@ export const useMetadata = ({ id, type, addonId }: UseMetadataProps): UseMetadat const cleanEpisodeId = episodeId.replace(/^series:/, ''); const parts = cleanEpisodeId.split(':'); - if (parts[0] === 'kitsu' && parts.length === 3) { - // kitsu:animeId:episode — no season segment + if (isImdb && parts.length === 3) { + // Format: ttXXX:season:episode + showIdStr = parts[0]; + seasonNum = parts[1]; + episodeNum = parts[2]; + } else if (!isImdb && parts.length === 3) { + // Format: prefix:id:episode (no season for MAL/Kitsu/etc) showIdStr = `${parts[0]}:${parts[1]}`; episodeNum = parts[2]; seasonNum = ''; - } else if (parts.length >= 3) { - episodeNum = parts.pop() || ''; - seasonNum = parts.pop() || ''; - showIdStr = parts.join(':'); } else if (parts.length === 2) { showIdStr = parts[0]; episodeNum = parts[1]; seasonNum = ''; + } else if (parts.length >= 4) { + // Format: prefix:id:season:episode - it is possible that some addons use it + episodeNum = parts.pop() || ''; + seasonNum = parts.pop() || ''; + showIdStr = parts.join(':'); } if (__DEV__) console.log(`🔍 [loadEpisodeStreams] Parsed ID: show=${showIdStr}, s=${seasonNum}, e=${episodeNum}`); @@ -1976,7 +1983,7 @@ export const useMetadata = ({ id, type, addonId }: UseMetadataProps): UseMetadat if (__DEV__) console.log('⚠️ [loadEpisodeStreams] Failed to convert TMDB to IMDb, using TMDB episode ID:', error); } } - } else if (id.startsWith('tt')) { + } else if (isImdb) { // This is already an IMDB ID, perfect for Stremio if (settings.enrichMetadataWithTMDB) { if (__DEV__) console.log('📝 [loadEpisodeStreams] Converting IMDB ID to TMDB ID...'); diff --git a/src/screens/MetadataScreen.tsx b/src/screens/MetadataScreen.tsx index 1738b1c34..c47a2e6b6 100644 --- a/src/screens/MetadataScreen.tsx +++ b/src/screens/MetadataScreen.tsx @@ -586,13 +586,17 @@ const MetadataScreen: React.FC = () => { const handleShowStreams = useCallback(() => { const { watchProgress } = watchProgressData; + const isImdb = id.startsWith('tt'); // Ensure trailer stops immediately before navigating to Streams try { pauseTrailer(); } catch { } // Helper to build episodeId from episode object const buildEpisodeId = (ep: any): string => { - return ep.stremioId || `${id}:${ep.season_number}:${ep.episode_number}`; + if (ep.stremioId) return ep.stremioId; + return isImdb + ? `${id}:${ep.season_number}:${ep.episode_number}` + : `${id}:${ep.episode_number}`; }; if (Object.keys(groupedEpisodes).length > 0) { @@ -611,38 +615,28 @@ const MetadataScreen: React.FC = () => { const parts = watchProgress.episodeId.split(':'); - if (parts.length === 3) { - // showId:season:episode - currentSeason = parseInt(parts[1], 10); - currentEpisode = parseInt(parts[2], 10); - } else if (parts.length === 2) { - // season:episode - currentSeason = parseInt(parts[0], 10); - currentEpisode = parseInt(parts[1], 10); - } else { - // pattern like s5e01 - const match = watchProgress.episodeId.match(/s(\d+)e(\d+)/i); - if (match) { - currentSeason = parseInt(match[1], 10); - currentEpisode = parseInt(match[2], 10); + if (isImdb) { + if (parts.length === 3) { + currentSeason = parseInt(parts[1], 10); + currentEpisode = parseInt(parts[2], 10); + } else if (parts.length === 2) { + currentEpisode = parseInt(parts[1], 10); } + } else { + currentEpisode = parts.length === 3 ? parseInt(parts[2], 10) : null; } - if (currentSeason !== null && currentEpisode !== null) { - // DIRECT APPROACH: Just create the next episode ID directly - // This ensures we navigate to the next episode even if it's not yet in our episodes array - const nextEpisodeId = `${id}:${currentSeason}:${currentEpisode + 1}`; - if (__DEV__) console.log(`[MetadataScreen] Created next episode ID directly: ${nextEpisodeId}`); - - // Still try to find the episode in our list to verify it exists - const nextEpisodeExists = episodes.some(ep => - ep.season_number === currentSeason && ep.episode_number === (currentEpisode + 1) - ); + if (currentEpisode !== null) { + const nextEpisodeId = isImdb + ? `${id}:${currentSeason || episodes[0]?.season_number || 1}:${currentEpisode + 1}` + : `${id}:${currentEpisode + 1}`; + if (__DEV__) console.log(`[MetadataScreen] Created next episode ID: ${nextEpisodeId}`); + const nextEpisodeExists = episodes.some(ep => ep.episode_number === (currentEpisode + 1)); if (nextEpisodeExists) { - if (__DEV__) console.log(`[MetadataScreen] Verified next episode S${currentSeason}E${currentEpisode + 1} exists in episodes list`); + if (__DEV__) console.log(`[MetadataScreen] Verified next episode exists`); } else { - if (__DEV__) console.log(`[MetadataScreen] Warning: Next episode S${currentSeason}E${currentEpisode + 1} not found in episodes list, but proceeding anyway`); + if (__DEV__) console.log(`[MetadataScreen] Warning: Next episode not found`); } targetEpisodeId = nextEpisodeId; @@ -656,10 +650,14 @@ const MetadataScreen: React.FC = () => { } if (targetEpisodeId) { - // Ensure the episodeId has showId prefix (id:season:episode) + // Ensure the episodeId has showId prefix (id:season:episode or id:episode) const epParts = targetEpisodeId.split(':'); let normalizedEpisodeId = targetEpisodeId; - if (epParts.length === 2) { + + if (epParts.length === 2 && !isImdb) { + normalizedEpisodeId = `${id}:${epParts[1]}`; + } + else if (epParts.length === 2 && isImdb) { normalizedEpisodeId = `${id}:${epParts[0]}:${epParts[1]}`; } if (__DEV__) console.log(`[MetadataScreen] Navigating to streams with episodeId: ${normalizedEpisodeId}`); @@ -672,7 +670,9 @@ const MetadataScreen: React.FC = () => { let fallbackEpisodeId = episodeId; if (episodeId && episodeId.split(':').length === 2) { const p = episodeId.split(':'); - fallbackEpisodeId = `${id}:${p[0]}:${p[1]}`; + if (!p[0].startsWith('tt')) { + fallbackEpisodeId = isImdb ? `${id}:${p[0]}:${p[1]}` : `${id}:${p[1]}`; + } } if (__DEV__) console.log(`[MetadataScreen] Navigating with fallback episodeId: ${fallbackEpisodeId}`); navigation.navigate('Streams', { id, type, episodeId: fallbackEpisodeId }); @@ -682,7 +682,16 @@ const MetadataScreen: React.FC = () => { if (!isScreenFocused) return; if (__DEV__) console.log('[MetadataScreen] Selected Episode:', episode.episode_number, episode.season_number); - const episodeId = episode.stremioId || `${id}:${episode.season_number}:${episode.episode_number}`; + + let episodeId: string; + if (episode.stremioId) { + episodeId = episode.stremioId; + } else { + const isImdb = id.startsWith('tt'); + episodeId = isImdb + ? `${id}:${episode.season_number}:${episode.episode_number}` + : `${id}:${episode.episode_number}`; + } // Optimize navigation with requestAnimationFrame requestAnimationFrame(() => { diff --git a/src/services/stremioService.ts b/src/services/stremioService.ts index ec886e952..9859886af 100644 --- a/src/services/stremioService.ts +++ b/src/services/stremioService.ts @@ -325,8 +325,17 @@ class StremioService { return true; } - // Check if the ID matches any supported prefix - return supportedPrefixes.some(prefix => lowerId.startsWith(prefix.toLowerCase())); + // Check if the ID matches any supported prefix. + // For prefixes without a trailing separator (e.g. "mal", "kitsu"), the ID must be + // longer than the prefix itself so that bare prefix strings like "mal" are rejected. + const result = supportedPrefixes.some(prefix => { + const lowerPrefix = prefix.toLowerCase(); + if (!lowerId.startsWith(lowerPrefix)) return false; + if (lowerPrefix.endsWith(':') || lowerPrefix.endsWith('_')) return true; + return lowerId.length > lowerPrefix.length; + }); + if (__DEV__) console.log(`🔍 [isValidContentId] Prefix match result: ${result} for ID '${id}'`); + return result; } // Get all content types supported by installed addons diff --git a/src/services/tmdbService.ts b/src/services/tmdbService.ts index 3abcc6605..de7c0de50 100644 --- a/src/services/tmdbService.ts +++ b/src/services/tmdbService.ts @@ -529,11 +529,16 @@ export class TMDBService { */ async extractTMDBIdFromStremioId(stremioId: string): Promise { try { - // Extract the base IMDB ID (remove season/episode info if present) - const imdbId = stremioId.split(':')[0]; + // Extract the base ID (remove season/episode info if present) + const baseId = stremioId.split(':')[0]; + + // Only try to convert if it's an IMDb ID (starts with 'tt') + if (!baseId.startsWith('tt')) { + return null; + } // Use the existing findTMDBIdByIMDB function to get the TMDB ID - const tmdbId = await this.findTMDBIdByIMDB(imdbId); + const tmdbId = await this.findTMDBIdByIMDB(baseId); return tmdbId; } catch (error) { return null;