diff --git a/.App.tsx.swp b/.App.tsx.swp deleted file mode 100644 index 2d308d865..000000000 Binary files a/.App.tsx.swp and /dev/null differ diff --git a/local-scrapers-repo b/local-scrapers-repo index 0a273ba5e..37b9ae429 160000 --- a/local-scrapers-repo +++ b/local-scrapers-repo @@ -1 +1 @@ -Subproject commit 0a273ba5eb2632979a6678e005314c5b3ffb70eb +Subproject commit 37b9ae4298bd38b8119d45e8670f40bec2780a8b diff --git a/package-lock.json b/package-lock.json index 02ba5f1da..68de56f79 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,6 +30,7 @@ "axios": "^1.11.0", "axios-cookiejar-support": "^6.0.4", "cheerio-without-node-native": "^0.20.2", + "crypto-js": "^4.2.0", "date-fns": "^4.1.0", "eventemitter3": "^5.0.1", "expo": "~52.0.47", @@ -76,6 +77,7 @@ }, "devDependencies": { "@babel/core": "^7.25.2", + "@types/crypto-js": "^4.2.2", "@types/react": "~18.3.12", "@types/react-native": "^0.72.8", "babel-plugin-transform-remove-console": "^6.9.4", @@ -5001,6 +5003,13 @@ "@babel/types": "^7.28.2" } }, + "node_modules/@types/crypto-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.2.2.tgz", + "integrity": "sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/graceful-fs": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", @@ -6756,6 +6765,12 @@ "node": "*" } }, + "node_modules/crypto-js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", + "license": "MIT" + }, "node_modules/crypto-random-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", diff --git a/package.json b/package.json index 86d184a92..f7c38c1f8 100644 --- a/package.json +++ b/package.json @@ -30,12 +30,15 @@ "axios": "^1.11.0", "axios-cookiejar-support": "^6.0.4", "cheerio-without-node-native": "^0.20.2", + "crypto-js": "^4.2.0", "date-fns": "^4.1.0", "eventemitter3": "^5.0.1", "expo": "~52.0.47", "expo-application": "~6.0.2", "expo-auth-session": "~6.0.3", "expo-blur": "~14.0.3", + "expo-brightness": "~13.0.3", + "expo-crypto": "~14.0.2", "expo-dev-client": "~5.0.20", "expo-device": "~7.0.3", "expo-file-system": "~18.0.12", @@ -70,12 +73,11 @@ "react-native-video": "^6.12.0", "react-native-vlc-media-player": "^1.0.87", "react-native-web": "~0.19.13", - "react-native-wheel-color-picker": "^1.3.1", - "expo-brightness": "~13.0.3", - "expo-crypto": "~14.0.2" + "react-native-wheel-color-picker": "^1.3.1" }, "devDependencies": { "@babel/core": "^7.25.2", + "@types/crypto-js": "^4.2.2", "@types/react": "~18.3.12", "@types/react-native": "^0.72.8", "babel-plugin-transform-remove-console": "^6.9.4", diff --git a/src/services/localScraperService.ts b/src/services/localScraperService.ts index 7e2471734..48ac54b4e 100644 --- a/src/services/localScraperService.ts +++ b/src/services/localScraperService.ts @@ -4,6 +4,7 @@ import { Platform } from 'react-native'; import { logger } from '../utils/logger'; import { Stream } from '../types/streams'; import { cacheService } from './cacheService'; +import CryptoJS from 'crypto-js'; // Types for local scrapers export interface ScraperManifest { @@ -1043,6 +1044,10 @@ class LocalScraperService { } } + // MovieBox constants - hardcoded for security + const MOVIEBOX_PRIMARY_KEY = '76iRl07s0xSN9jqmEWAt79EBJZulIQIsV64FZr2O'; + const MOVIEBOX_TMDB_API_KEY = '439c478a771f35c05022f9feabcca01c'; + const sandbox = { console: { log: (...args: any[]) => logger.log('[Scraper]', ...args), @@ -1068,32 +1073,106 @@ class LocalScraperService { case 'react-native-cheerio': if (cheerio) return cheerio; throw new Error('react-native-cheerio not available'); + case 'crypto-js': + return CryptoJS; default: throw new Error(`Module '${moduleName}' is not available in sandbox`); } }, - // Add fetch for HTTP requests (using axios as polyfill) + // Add fetch for HTTP requests (using native fetch for MovieBox, axios for others) fetch: async (url: string, options: any = {}) => { - const axiosConfig = { - url, - method: options.method || 'GET', - headers: options.headers || {}, - data: options.body, - timeout: 30000 - }; + const isMovieBoxRequest = url.includes('api.inmoviebox.com') || url.includes('themoviedb.org'); - try { - const response = await axios(axiosConfig); - return { - ok: response.status >= 200 && response.status < 300, - status: response.status, - statusText: response.statusText, - headers: response.headers, - json: async () => response.data, - text: async () => typeof response.data === 'string' ? response.data : JSON.stringify(response.data) + if (isMovieBoxRequest) { + // Always use native fetch for MovieBox requests + try { + logger.log(`[Sandbox] Using native fetch for MovieBox request: ${url}`, { + method: options.method || 'GET', + hasBody: !!options.body + }); + + const nativeResponse = await fetch(url, { + method: options.method || 'GET', + headers: { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36', + 'Accept': 'application/json', + ...options.headers + }, + body: options.body + }); + + const responseData = await nativeResponse.text(); + logger.log(`[Sandbox] Native fetch successful for MovieBox:`, { + status: nativeResponse.status, + ok: nativeResponse.ok + }); + + return { + ok: nativeResponse.ok, + status: nativeResponse.status, + statusText: nativeResponse.statusText || 'OK', + headers: nativeResponse.headers, + json: async () => { + try { + return JSON.parse(responseData); + } catch (e) { + logger.error(`[Sandbox] Failed to parse JSON from native fetch: ${e}`); + throw e; + } + }, + text: async () => responseData + }; + } catch (error: any) { + logger.error(`[Sandbox] Native fetch failed for MovieBox ${url}:`, error.message); + throw new Error(`Fetch failed: ${error.message}`); + } + } else { + // Use axios for other requests + const axiosConfig = { + url, + method: options.method || 'GET', + headers: { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36', + 'Accept': 'application/json', + ...options.headers + }, + data: options.body, + timeout: 30000, + validateStatus: () => true // Don't throw on HTTP error status codes }; - } catch (error: any) { - throw new Error(`Fetch failed: ${error.message}`); + + try { + logger.log(`[Sandbox] Using axios for request: ${url}`, { + method: axiosConfig.method, + headers: axiosConfig.headers, + hasBody: !!axiosConfig.data + }); + const response = await axios(axiosConfig); + logger.log(`[Sandbox] Axios response received:`, { + status: response.status, + statusText: response.statusText, + dataType: typeof response.data + }); + + return { + ok: response.status >= 200 && response.status < 300, + status: response.status, + statusText: response.statusText || 'OK', + headers: response.headers, + json: async () => { + try { + return typeof response.data === 'string' ? JSON.parse(response.data) : response.data; + } catch (e) { + logger.error(`[Sandbox] Failed to parse JSON response: ${e}`); + throw e; + } + }, + text: async () => typeof response.data === 'string' ? response.data : JSON.stringify(response.data) + }; + } catch (error: any) { + logger.error(`[Sandbox] Axios error for ${url}:`, error.message); + throw new Error(`Fetch failed: ${error.message}`); + } } }, // Add axios for HTTP requests @@ -1118,8 +1197,15 @@ class LocalScraperService { const executionPromise = new Promise((resolve, reject) => { try { // Create function from code - const func = new Function('sandbox', 'params', ` + const func = new Function('sandbox', 'params', 'PRIMARY_KEY', 'TMDB_API_KEY', ` const { console, setTimeout, clearTimeout, Promise, JSON, Date, Math, parseInt, parseFloat, encodeURIComponent, decodeURIComponent, require, axios, fetch, module, exports, global, URL_VALIDATION_ENABLED } = sandbox; + + // Inject MovieBox constants into global scope + global.PRIMARY_KEY = PRIMARY_KEY; + global.TMDB_API_KEY = TMDB_API_KEY; + window.PRIMARY_KEY = PRIMARY_KEY; + window.TMDB_API_KEY = TMDB_API_KEY; + ${code} // Call the main function (assuming it's exported) @@ -1134,7 +1220,7 @@ class LocalScraperService { } `); - const result = func(sandbox, params); + const result = func(sandbox, params, MOVIEBOX_PRIMARY_KEY, MOVIEBOX_TMDB_API_KEY); // Handle both sync and async results if (result && typeof result.then === 'function') {