KITSU id prefix fix

This commit is contained in:
tapframe 2025-09-21 13:34:10 +05:30
parent 4367be97f8
commit a0d33be096
3 changed files with 93 additions and 17 deletions

View file

@ -868,6 +868,10 @@ export const useMetadata = ({ id, type, addonId }: UseMetadataProps): UseMetadat
} }
let hasStreamResource = false; let hasStreamResource = false;
let supportsIdPrefix = false;
// Extract ID prefix from the ID
const idPrefix = id.split(':')[0];
for (const resource of addon.resources) { for (const resource of addon.resources) {
// Check if the current element is a ResourceObject // Check if the current element is a ResourceObject
@ -877,6 +881,14 @@ export const useMetadata = ({ id, type, addonId }: UseMetadataProps): UseMetadat
Array.isArray(typedResource.types) && Array.isArray(typedResource.types) &&
typedResource.types.includes(type)) { typedResource.types.includes(type)) {
hasStreamResource = true; hasStreamResource = true;
// Check if this addon supports the ID prefix
if (Array.isArray(typedResource.idPrefixes)) {
supportsIdPrefix = typedResource.idPrefixes.includes(idPrefix);
} else {
// If no idPrefixes specified, assume it supports all prefixes
supportsIdPrefix = true;
}
break; break;
} }
} }
@ -884,12 +896,19 @@ export const useMetadata = ({ id, type, addonId }: UseMetadataProps): UseMetadat
else if (typeof resource === 'string' && resource === 'stream' && addon.types) { else if (typeof resource === 'string' && resource === 'stream' && addon.types) {
if (Array.isArray(addon.types) && addon.types.includes(type)) { if (Array.isArray(addon.types) && addon.types.includes(type)) {
hasStreamResource = true; hasStreamResource = true;
// For simple string resources, check addon-level idPrefixes
if (addon.idPrefixes && Array.isArray(addon.idPrefixes)) {
supportsIdPrefix = addon.idPrefixes.includes(idPrefix);
} else {
// If no idPrefixes specified, assume it supports all prefixes
supportsIdPrefix = true;
}
break; break;
} }
} }
} }
return hasStreamResource; return hasStreamResource && supportsIdPrefix;
}); });
// Initialize scraper statuses for tracking // Initialize scraper statuses for tracking

View file

@ -29,7 +29,11 @@ import { colors } from '../styles';
const ANDROID_STATUSBAR_HEIGHT = StatusBar.currentHeight || 0; const ANDROID_STATUSBAR_HEIGHT = StatusBar.currentHeight || 0;
// Trakt configuration // Trakt configuration
const TRAKT_CLIENT_ID = 'd7271f7dd57d8aeff63e99408610091a6b1ceac3b3a541d1031a48f429b7942c'; const TRAKT_CLIENT_ID = process.env.EXPO_PUBLIC_TRAKT_CLIENT_ID as string;
if (!TRAKT_CLIENT_ID) {
throw new Error('Missing EXPO_PUBLIC_TRAKT_CLIENT_ID environment variable');
}
const discovery = { const discovery = {
authorizationEndpoint: 'https://trakt.tv/oauth/authorize', authorizationEndpoint: 'https://trakt.tv/oauth/authorize',
tokenEndpoint: 'https://api.trakt.tv/oauth/token', tokenEndpoint: 'https://api.trakt.tv/oauth/token',

View file

@ -926,47 +926,72 @@ class StremioService {
// Map Stremio types to local scraper types // Map Stremio types to local scraper types
const scraperType = type === 'series' ? 'tv' : type; const scraperType = type === 'series' ? 'tv' : type;
// Parse the Stremio ID to extract IMDb ID and season/episode info // Parse the Stremio ID to extract ID and season/episode info
let tmdbId: string | null = null; let tmdbId: string | null = null;
let season: number | undefined = undefined; let season: number | undefined = undefined;
let episode: number | undefined = undefined; let episode: number | undefined = undefined;
try { try {
const idParts = id.split(':'); const idParts = id.split(':');
let baseImdbId: string; let baseId: string;
let idType: 'imdb' | 'kitsu' | 'tmdb' = 'imdb';
// Handle different episode ID formats // Handle different episode ID formats
if (idParts[0] === 'series') { if (idParts[0] === 'series') {
// Format: series:imdbId:season:episode // Format: series:imdbId:season:episode or series:kitsu:7442:season:episode
baseImdbId = idParts[1]; baseId = idParts[1];
if (scraperType === 'tv' && idParts.length >= 4) { if (scraperType === 'tv' && idParts.length >= 4) {
season = parseInt(idParts[2], 10); season = parseInt(idParts[2], 10);
episode = parseInt(idParts[3], 10); episode = parseInt(idParts[3], 10);
} }
// Check if it's a kitsu ID
if (idParts[1] === 'kitsu') {
idType = 'kitsu';
baseId = idParts[2]; // kitsu:7442:season:episode -> baseId = 7442
if (scraperType === 'tv' && idParts.length >= 5) {
season = parseInt(idParts[3], 10);
episode = parseInt(idParts[4], 10);
}
}
} else if (idParts[0].startsWith('tt')) { } else if (idParts[0].startsWith('tt')) {
// Format: imdbId:season:episode (direct IMDb ID) // Format: imdbId:season:episode (direct IMDb ID)
baseImdbId = idParts[0]; baseId = idParts[0];
idType = 'imdb';
if (scraperType === 'tv' && idParts.length >= 3) { if (scraperType === 'tv' && idParts.length >= 3) {
season = parseInt(idParts[1], 10); season = parseInt(idParts[1], 10);
episode = parseInt(idParts[2], 10); episode = parseInt(idParts[2], 10);
} }
} else if (idParts[0] === 'kitsu') {
// Format: kitsu:7442:season:episode (direct Kitsu ID)
baseId = idParts[1];
idType = 'kitsu';
if (scraperType === 'tv' && idParts.length >= 4) {
season = parseInt(idParts[2], 10);
episode = parseInt(idParts[3], 10);
}
} else { } else {
// Fallback: assume first part is the ID // Fallback: assume first part is the ID
baseImdbId = idParts[0]; baseId = idParts[0];
if (scraperType === 'tv' && idParts.length >= 3) { if (scraperType === 'tv' && idParts.length >= 3) {
season = parseInt(idParts[1], 10); season = parseInt(idParts[1], 10);
episode = parseInt(idParts[2], 10); episode = parseInt(idParts[2], 10);
} }
} }
// Convert IMDb ID to TMDB ID using TMDBService // Only try to convert to TMDB ID for IMDb IDs (local scrapers need TMDB)
const tmdbService = TMDBService.getInstance(); if (idType === 'imdb') {
const tmdbIdNumber = await tmdbService.findTMDBIdByIMDB(baseImdbId); const tmdbService = TMDBService.getInstance();
const tmdbIdNumber = await tmdbService.findTMDBIdByIMDB(baseId);
if (tmdbIdNumber) {
tmdbId = tmdbIdNumber.toString(); if (tmdbIdNumber) {
tmdbId = tmdbIdNumber.toString();
} else {
return; // Skip local scrapers if we can't convert the ID
}
} else { } else {
return; // Skip local scrapers if we can't convert the ID // For kitsu IDs, skip local scrapers as they don't support kitsu
logger.log('🔧 [getStreams] Skipping local scrapers for kitsu ID:', baseId);
return;
} }
} catch (error) { } catch (error) {
return; // Skip local scrapers if ID parsing fails return; // Skip local scrapers if ID parsing fails
@ -1008,6 +1033,11 @@ class StremioService {
logger.log(`📋 [getStreams] Checking addon ${addon.id} resources:`, JSON.stringify(addon.resources)); logger.log(`📋 [getStreams] Checking addon ${addon.id} resources:`, JSON.stringify(addon.resources));
let hasStreamResource = false; let hasStreamResource = false;
let supportsIdPrefix = false;
// Extract ID prefix from the ID
const idPrefix = id.split(':')[0];
logger.log(`🔍 [getStreams] Checking if addon supports ID prefix: ${idPrefix}`);
// Iterate through the resources array, checking each element // Iterate through the resources array, checking each element
for (const resource of addon.resources) { for (const resource of addon.resources) {
@ -1018,6 +1048,16 @@ class StremioService {
Array.isArray(typedResource.types) && Array.isArray(typedResource.types) &&
typedResource.types.includes(type)) { typedResource.types.includes(type)) {
hasStreamResource = true; hasStreamResource = true;
// Check if this addon supports the ID prefix
if (Array.isArray(typedResource.idPrefixes)) {
supportsIdPrefix = typedResource.idPrefixes.includes(idPrefix);
logger.log(`🔍 [getStreams] Addon ${addon.id} supports prefixes: ${typedResource.idPrefixes.join(', ')}`);
} else {
// If no idPrefixes specified, assume it supports all prefixes
supportsIdPrefix = true;
logger.log(`🔍 [getStreams] Addon ${addon.id} has no prefix restrictions, assuming support`);
}
break; // Found the stream resource object, no need to check further break; // Found the stream resource object, no need to check further
} }
} }
@ -1025,18 +1065,31 @@ class StremioService {
else if (typeof resource === 'string' && resource === 'stream' && addon.types) { else if (typeof resource === 'string' && resource === 'stream' && addon.types) {
if (Array.isArray(addon.types) && addon.types.includes(type)) { if (Array.isArray(addon.types) && addon.types.includes(type)) {
hasStreamResource = true; hasStreamResource = true;
// For simple string resources, check addon-level idPrefixes
if (addon.idPrefixes && Array.isArray(addon.idPrefixes)) {
supportsIdPrefix = addon.idPrefixes.includes(idPrefix);
logger.log(`🔍 [getStreams] Addon ${addon.id} supports prefixes: ${addon.idPrefixes.join(', ')}`);
} else {
// If no idPrefixes specified, assume it supports all prefixes
supportsIdPrefix = true;
logger.log(`🔍 [getStreams] Addon ${addon.id} has no prefix restrictions, assuming support`);
}
break; // Found the simple stream resource string and type support break; // Found the simple stream resource string and type support
} }
} }
} }
const canHandleRequest = hasStreamResource && supportsIdPrefix;
if (!hasStreamResource) { if (!hasStreamResource) {
logger.log(`❌ [getStreams] Addon ${addon.id} does not support streaming ${type}`); logger.log(`❌ [getStreams] Addon ${addon.id} does not support streaming ${type}`);
} else if (!supportsIdPrefix) {
logger.log(`❌ [getStreams] Addon ${addon.id} supports ${type} but not ID prefix ${idPrefix}`);
} else { } else {
logger.log(`✅ [getStreams] Addon ${addon.id} supports streaming ${type}`); logger.log(`✅ [getStreams] Addon ${addon.id} supports streaming ${type} with prefix ${idPrefix}`);
} }
return hasStreamResource; return canHandleRequest;
}); });
logger.log('📊 [getStreams] Stream capable addons:', streamAddons.map(a => a.id)); logger.log('📊 [getStreams] Stream capable addons:', streamAddons.map(a => a.id));