NuvioStreaming/src/services/catalog/content-details.ts
2026-03-17 05:46:49 +05:30

282 lines
9.3 KiB
TypeScript

import { stremioService } from '../stremioService';
import { mmkvStorage } from '../mmkvStorage';
import { TMDBService } from '../tmdbService';
import { logger } from '../../utils/logger';
import { convertMetaToStreamingContent, convertMetaToStreamingContentEnhanced } from './content-mappers';
import { addToRecentContent, createLibraryKey, type CatalogLibraryState } from './library';
import { DATA_SOURCE_KEY, DataSource, type StreamingContent } from './types';
export async function getDataSourcePreference(): Promise<DataSource> {
try {
const dataSource = await mmkvStorage.getItem(DATA_SOURCE_KEY);
return (dataSource as DataSource) || DataSource.STREMIO_ADDONS;
} catch (error) {
logger.error('Failed to get data source preference:', error);
return DataSource.STREMIO_ADDONS;
}
}
export async function setDataSourcePreference(dataSource: DataSource): Promise<void> {
try {
await mmkvStorage.setItem(DATA_SOURCE_KEY, dataSource);
} catch (error) {
logger.error('Failed to set data source preference:', error);
}
}
export async function getContentDetails(
state: CatalogLibraryState,
type: string,
id: string,
preferredAddonId?: string
): Promise<StreamingContent | null> {
console.log('🔍 [CatalogService] getContentDetails called:', { type, id, preferredAddonId });
try {
let meta = null;
let lastError = null;
for (let attempt = 0; attempt < 2; attempt += 1) {
try {
console.log(`🔍 [CatalogService] Attempt ${attempt + 1}/2 for getContentDetails:`, { type, id, preferredAddonId });
const isValidId = await stremioService.isValidContentId(type, id);
console.log('🔍 [CatalogService] Content ID validation:', { type, id, isValidId });
if (!isValidId) {
console.log('🔍 [CatalogService] Invalid content ID, breaking retry loop');
break;
}
console.log('🔍 [CatalogService] Calling stremioService.getMetaDetails:', { type, id, preferredAddonId });
meta = await stremioService.getMetaDetails(type, id, preferredAddonId);
console.log('🔍 [CatalogService] stremioService.getMetaDetails result:', {
hasMeta: !!meta,
metaId: meta?.id,
metaName: meta?.name,
metaType: meta?.type,
});
if (meta) {
break;
}
await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, attempt)));
} catch (error) {
lastError = error;
console.log(`🔍 [CatalogService] Attempt ${attempt + 1} failed:`, {
errorMessage: error instanceof Error ? error.message : String(error),
isAxiosError: (error as any)?.isAxiosError,
responseStatus: (error as any)?.response?.status,
responseData: (error as any)?.response?.data,
});
logger.error(`Attempt ${attempt + 1} failed to get content details for ${type}:${id}:`, error);
await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, attempt)));
}
}
if (meta) {
console.log('🔍 [CatalogService] Meta found, converting to StreamingContent:', {
metaId: meta.id,
metaName: meta.name,
metaType: meta.type,
});
const content = convertMetaToStreamingContentEnhanced(meta, state.library);
addToRecentContent(state, content);
content.inLibrary = state.library[createLibraryKey(type, id)] !== undefined;
console.log('🔍 [CatalogService] Successfully converted meta to StreamingContent:', {
contentId: content.id,
contentName: content.name,
contentType: content.type,
inLibrary: content.inLibrary,
});
return content;
}
console.log('🔍 [CatalogService] No meta found, checking lastError:', {
hasLastError: !!lastError,
lastErrorMessage: lastError instanceof Error ? lastError.message : String(lastError),
});
if (lastError) {
console.log('🔍 [CatalogService] Throwing lastError:', {
errorMessage: lastError instanceof Error ? lastError.message : String(lastError),
isAxiosError: (lastError as any)?.isAxiosError,
responseStatus: (lastError as any)?.response?.status,
});
throw lastError;
}
console.log('🔍 [CatalogService] No meta and no error, returning null');
return null;
} catch (error) {
console.log('🔍 [CatalogService] getContentDetails caught error:', {
errorMessage: error instanceof Error ? error.message : String(error),
isAxiosError: (error as any)?.isAxiosError,
responseStatus: (error as any)?.response?.status,
responseData: (error as any)?.response?.data,
});
logger.error(`Failed to get content details for ${type}:${id}:`, error);
return null;
}
}
export async function getEnhancedContentDetails(
state: CatalogLibraryState,
type: string,
id: string,
preferredAddonId?: string
): Promise<StreamingContent | null> {
console.log('🔍 [CatalogService] getEnhancedContentDetails called:', { type, id, preferredAddonId });
logger.log(`🔍 [MetadataScreen] Fetching enhanced metadata for ${type}:${id} ${preferredAddonId ? `from addon ${preferredAddonId}` : ''}`);
try {
const result = await getContentDetails(state, type, id, preferredAddonId);
console.log('🔍 [CatalogService] getEnhancedContentDetails result:', {
hasResult: !!result,
resultId: result?.id,
resultName: result?.name,
resultType: result?.type,
});
return result;
} catch (error) {
console.log('🔍 [CatalogService] getEnhancedContentDetails error:', {
errorMessage: error instanceof Error ? error.message : String(error),
isAxiosError: (error as any)?.isAxiosError,
responseStatus: (error as any)?.response?.status,
responseData: (error as any)?.response?.data,
});
throw error;
}
}
export async function getBasicContentDetails(
state: CatalogLibraryState,
type: string,
id: string,
preferredAddonId?: string
): Promise<StreamingContent | null> {
try {
let meta = null;
let lastError = null;
for (let attempt = 0; attempt < 3; attempt += 1) {
try {
if (!(await stremioService.isValidContentId(type, id))) {
break;
}
meta = await stremioService.getMetaDetails(type, id, preferredAddonId);
if (meta) {
break;
}
await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, attempt)));
} catch (error) {
lastError = error;
logger.error(`Attempt ${attempt + 1} failed to get basic content details for ${type}:${id}:`, error);
await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, attempt)));
}
}
if (meta) {
const content = convertMetaToStreamingContent(meta, state.library);
content.inLibrary = state.library[createLibraryKey(type, id)] !== undefined;
return content;
}
if (lastError) {
throw lastError;
}
return null;
} catch (error) {
logger.error(`Failed to get basic content details for ${type}:${id}:`, error);
return null;
}
}
export async function getStremioId(type: string, tmdbId: string): Promise<string | null> {
if (__DEV__) {
console.log('=== CatalogService.getStremioId ===');
console.log('Input type:', type);
console.log('Input tmdbId:', tmdbId);
}
try {
if (type === 'movie') {
if (__DEV__) {
console.log('Processing movie - fetching TMDB details...');
}
const movieDetails = await TMDBService.getInstance().getMovieDetails(tmdbId);
if (__DEV__) {
console.log('Movie details result:', {
id: movieDetails?.id,
title: movieDetails?.title,
imdb_id: movieDetails?.imdb_id,
hasImdbId: !!movieDetails?.imdb_id,
});
}
if (movieDetails?.imdb_id) {
if (__DEV__) {
console.log('Successfully found IMDb ID:', movieDetails.imdb_id);
}
return movieDetails.imdb_id;
}
console.warn('No IMDb ID found for movie:', tmdbId);
return null;
}
if (type === 'tv' || type === 'series') {
if (__DEV__) {
console.log('Processing TV show - fetching TMDB details for IMDb ID...');
}
const externalIds = await TMDBService.getInstance().getShowExternalIds(parseInt(tmdbId, 10));
if (__DEV__) {
console.log('TV show external IDs result:', {
tmdbId,
imdb_id: externalIds?.imdb_id,
hasImdbId: !!externalIds?.imdb_id,
});
}
if (externalIds?.imdb_id) {
if (__DEV__) {
console.log('Successfully found IMDb ID for TV show:', externalIds.imdb_id);
}
return externalIds.imdb_id;
}
console.warn('No IMDb ID found for TV show, falling back to kitsu format:', tmdbId);
const fallbackId = `kitsu:${tmdbId}`;
if (__DEV__) {
console.log('Generated fallback Stremio ID for TV:', fallbackId);
}
return fallbackId;
}
console.warn('Unknown type provided:', type);
return null;
} catch (error: any) {
if (__DEV__) {
console.error('=== Error in getStremioId ===');
console.error('Type:', type);
console.error('TMDB ID:', tmdbId);
console.error('Error details:', error);
console.error('Error message:', error.message);
}
logger.error('Error getting Stremio ID:', error);
return null;
}
}