added axios response limit

This commit is contained in:
tapframe 2026-01-23 12:57:16 +05:30
parent 181de15b93
commit b24cd9ca66
4 changed files with 53 additions and 31 deletions

View file

@ -5,6 +5,7 @@ import axios from 'axios';
import { TMDBService } from './tmdbService';
import { logger } from '../utils/logger';
import { getCatalogDisplayName } from '../utils/catalogNameUtils';
import { createSafeAxiosConfig } from '../utils/axiosConfig';
// Add a constant for storing the data source preference
const DATA_SOURCE_KEY = 'discover_data_source';
@ -1476,7 +1477,7 @@ class CatalogService {
logger.warn(`Search failed for catalog in ${addon.name}:`, s.reason);
}
}
if (addonResults.length === 0) {
logger.log(`No results from ${addon.name}`);
return;
@ -1542,7 +1543,7 @@ class CatalogService {
logger.warn(`Addon ${manifest.name} (${manifest.id}) has no URL, skipping search`);
return [];
}
// Extract base URL and preserve query params (same logic as stremioService.getAddonBaseURL)
const [baseUrlPart, queryParams] = chosenUrl.split('?');
let cleanBaseUrl = baseUrlPart.replace(/manifest\.json$/, '').replace(/\/$/, '');
@ -1554,7 +1555,7 @@ class CatalogService {
const encodedCatalogId = encodeURIComponent(catalogId);
const encodedQuery = encodeURIComponent(query);
// Try path-style URL first (per Stremio protocol)
url = `${cleanBaseUrl}/catalog/${type}/${encodedCatalogId}/search=${encodedQuery}.json`;
@ -1566,9 +1567,7 @@ class CatalogService {
logger.log(`Searching ${manifest.name} (${type}/${catalogId}):`, url);
const response = await axios.get<{ metas: any[] }>(url, {
timeout: 10000, // 10 second timeout per addon
});
const response = await axios.get<{ metas: any[] }>(url, createSafeAxiosConfig(10000));
const metas = response.data?.metas || [];

View file

@ -5,6 +5,7 @@ import { logger } from '../utils/logger';
import { Stream } from '../types/streams';
import { cacheService } from './cacheService';
import CryptoJS from 'crypto-js';
import { safeAxiosConfig, createSafeAxiosConfig } from '../utils/axiosConfig';
const MAX_CONCURRENT_SCRAPERS = 5;
const MAX_INFLIGHT_KEYS = 30;
@ -407,13 +408,12 @@ class LocalScraperService {
: `${repositoryUrl}/manifest.json`;
const manifestUrl = `${baseManifestUrl}?t=${Date.now()}`;
const response = await axios.get(manifestUrl, {
timeout: 10000,
const response = await axios.get(manifestUrl, createSafeAxiosConfig(10000, {
headers: {
'Cache-Control': 'no-cache',
'Pragma': 'no-cache'
}
});
}));
if (response.data && response.data.name) {
logger.log('[LocalScraperService] Found repository name in manifest:', response.data.name);
@ -645,14 +645,13 @@ class LocalScraperService {
: `${this.repositoryUrl}/manifest.json`;
const manifestUrl = `${baseManifestUrl}?t=${Date.now()}&v=${Math.random()}`;
const response = await axios.get(manifestUrl, {
timeout: 10000,
const response = await axios.get(manifestUrl, createSafeAxiosConfig(10000, {
headers: {
'Cache-Control': 'no-cache',
'Pragma': 'no-cache',
'Expires': '0'
}
});
}));
const manifest: ScraperManifest = response.data;
// Store repository name from manifest
@ -740,14 +739,13 @@ class LocalScraperService {
: `${repo.url}/manifest.json`;
const manifestUrl = `${baseManifestUrl}?t=${Date.now()}&v=${Math.random()}`;
const response = await axios.get(manifestUrl, {
timeout: 10000,
const response = await axios.get(manifestUrl, createSafeAxiosConfig(10000, {
headers: {
'Cache-Control': 'no-cache',
'Pragma': 'no-cache',
'Expires': '0'
}
});
}));
const manifest: ScraperManifest = response.data;
// Update repository name from manifest
@ -823,14 +821,13 @@ class LocalScraperService {
// Add cache-busting parameters to force fresh download
const scraperUrlWithCacheBust = `${scraperUrl}?t=${Date.now()}&v=${Math.random()}`;
const response = await axios.get(scraperUrlWithCacheBust, {
timeout: 15000,
const response = await axios.get(scraperUrlWithCacheBust, createSafeAxiosConfig(15000, {
headers: {
'Cache-Control': 'no-cache',
'Pragma': 'no-cache',
'Expires': '0'
}
});
}));
const scraperCode = response.data;
// Store scraper info and code

View file

@ -5,6 +5,7 @@ import EventEmitter from 'eventemitter3';
import { localScraperService } from './pluginService';
import { DEFAULT_SETTINGS, AppSettings } from '../hooks/useSettings';
import { TMDBService } from './tmdbService';
import { safeAxiosConfig, createSafeAxiosConfig } from '../utils/axiosConfig';
// Create an event emitter for addon changes
export const addonEmitter = new EventEmitter();
@ -626,7 +627,7 @@ class StremioService {
: `${url.replace(/\/$/, '')}/manifest.json`;
const response = await this.retryRequest(async () => {
return await axios.get(manifestUrl);
return await axios.get(manifestUrl, safeAxiosConfig);
});
const manifest = response.data;
@ -878,7 +879,7 @@ class StremioService {
// For page 1 without filters, try simple URL first (best compatibility)
if (pageSkip === 0 && extraParts.length === 0) {
if (__DEV__) console.log(`🔍 [getCatalog] Trying simple URL for ${manifest.name}: ${urlSimple}`);
response = await this.retryRequest(async () => axios.get(urlSimple));
response = await this.retryRequest(async () => axios.get(urlSimple, safeAxiosConfig));
// Check if we got valid metas - if empty, try other styles
if (!response?.data?.metas || response.data.metas.length === 0) {
throw new Error('Empty response from simple URL');
@ -890,7 +891,7 @@ class StremioService {
try {
// Try path-style URL (correct per protocol)
if (__DEV__) console.log(`🔍 [getCatalog] Trying path-style URL for ${manifest.name}: ${urlPathStyle}`);
response = await this.retryRequest(async () => axios.get(urlPathStyle));
response = await this.retryRequest(async () => axios.get(urlPathStyle, safeAxiosConfig));
// Check if we got valid metas - if empty, try query-style
if (!response?.data?.metas || response.data.metas.length === 0) {
throw new Error('Empty response from path-style URL');
@ -899,7 +900,7 @@ class StremioService {
try {
// Try legacy query-style URL as last resort
if (__DEV__) console.log(`🔍 [getCatalog] Trying query-style URL for ${manifest.name}: ${urlQueryStyle}`);
response = await this.retryRequest(async () => axios.get(urlQueryStyle));
response = await this.retryRequest(async () => axios.get(urlQueryStyle, safeAxiosConfig));
} catch (e3) {
if (__DEV__) console.log(`❌ [getCatalog] All URL styles failed for ${manifest.name}`);
throw e3;
@ -994,7 +995,7 @@ class StremioService {
if (isSupported) {
try {
const response = await this.retryRequest(async () => {
return await axios.get(url, { timeout: 10000 });
return await axios.get(url, createSafeAxiosConfig(10000));
});
@ -1025,7 +1026,7 @@ class StremioService {
const response = await this.retryRequest(async () => {
return await axios.get(url, { timeout: 10000 });
return await axios.get(url, createSafeAxiosConfig(10000));
});
@ -1096,7 +1097,7 @@ class StremioService {
const response = await this.retryRequest(async () => {
return await axios.get(url, { timeout: 10000 });
return await axios.get(url, createSafeAxiosConfig(10000));
});
@ -1412,7 +1413,7 @@ class StremioService {
logger.log(`🔗 [getStreams] Requesting streams from ${addon.name} (${addon.id}): ${url}`);
const response = await this.retryRequest(async () => {
return await axios.get(url);
return await axios.get(url, safeAxiosConfig);
});
let processedStreams: Stream[] = [];
@ -1462,13 +1463,12 @@ class StremioService {
const response = await this.retryRequest(async () => {
logger.log(`Making request to ${url} with timeout ${timeout}ms`);
return await axios.get(url, {
timeout,
return await axios.get(url, createSafeAxiosConfig(timeout, {
headers: {
'Accept': 'application/json',
'User-Agent': 'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Mobile Safari/537.36'
}
});
}));
}, 5); // Increase retries for stream fetching
if (response.data && response.data.streams && Array.isArray(response.data.streams)) {
@ -1770,7 +1770,7 @@ class StremioService {
: `${baseUrl}/subtitles/${type}/${encodedId}.json`;
}
logger.log(`[getSubtitles] Fetching subtitles from ${addon.name}: ${url}`);
const response = await this.retryRequest(async () => axios.get(url, { timeout: 10000 }));
const response = await this.retryRequest(async () => axios.get(url, createSafeAxiosConfig(10000)));
if (response.data && Array.isArray(response.data.subtitles)) {
logger.log(`[getSubtitles] Got ${response.data.subtitles.length} subtitles from ${addon.name}`);
return response.data.subtitles.map((sub: any, index: number) => ({
@ -1910,7 +1910,7 @@ class StremioService {
const url = `${baseUrl}/addon_catalog/${type}/${encodeURIComponent(id)}.json${queryParams ? `?${queryParams}` : ''}`;
logger.log(`[getAddonCatalogs] Fetching from ${addon.name}: ${url}`);
const response = await this.retryRequest(() => axios.get(url, { timeout: 10000 }));
const response = await this.retryRequest(() => axios.get(url, createSafeAxiosConfig(10000)));
if (response.data?.addons && Array.isArray(response.data.addons)) {
results.push(...response.data.addons);

26
src/utils/axiosConfig.ts Normal file
View file

@ -0,0 +1,26 @@
export const MAX_RESPONSE_SIZE = 10 * 1024 * 1024; // 10MB in bytes
/**
* Default axios request configuration with response size limits.
* Apply this to all axios.get() calls to prevent OOM crashes.
*/
export const safeAxiosConfig = {
maxContentLength: MAX_RESPONSE_SIZE,
maxBodyLength: MAX_RESPONSE_SIZE,
};
/**
* Creates a safe axios config with response size limits and optional timeout.
* @param timeout - Optional timeout in milliseconds (default: 10000)
* @param additionalConfig - Additional axios config to merge
* @returns Axios request config with safety limits
*/
export const createSafeAxiosConfig = (
timeout: number = 10000,
additionalConfig?: Record<string, any>
) => ({
...safeAxiosConfig,
timeout,
...additionalConfig,
});