removed the stream/MKV/URL-validation HEAD probes;

This commit is contained in:
tapframe 2026-01-21 16:25:09 +05:30
parent a4548c69e9
commit 9d0163b4eb
9 changed files with 1 additions and 155 deletions

View file

@ -116,12 +116,7 @@ export const usePlayerSetup = (
const restoreBrightness = async () => {
try {
if (Platform.OS === 'android') {
if (originalSystemBrightnessModeRef.current !== null) {
await (Brightness as any).setSystemBrightnessModeAsync?.(originalSystemBrightnessModeRef.current);
}
if (originalSystemBrightnessRef.current !== null) {
await (Brightness as any).setSystemBrightnessAsync?.(originalSystemBrightnessRef.current);
}
}
if (originalAppBrightnessRef.current !== null) {
await Brightness.setBrightnessAsync(originalAppBrightnessRef.current);

View file

@ -3,48 +3,6 @@ import { logger } from '../utils/logger';
import { TMDBService } from '../services/tmdbService';
import { isTmdbUrl } from '../utils/logoUtils';
import FastImage from '@d11/react-native-fast-image';
import { mmkvStorage } from '../services/mmkvStorage';
// Cache for image availability checks
const imageAvailabilityCache: Record<string, boolean> = {};
// Helper function to check image availability with caching
const checkImageAvailability = async (url: string): Promise<boolean> => {
// Check memory cache first
if (imageAvailabilityCache[url] !== undefined) {
return imageAvailabilityCache[url];
}
// Check AsyncStorage cache
try {
const cachedResult = await mmkvStorage.getItem(`image_available:${url}`);
if (cachedResult !== null) {
const isAvailable = cachedResult === 'true';
imageAvailabilityCache[url] = isAvailable;
return isAvailable;
}
} catch (error) {
// Ignore AsyncStorage errors
}
// Perform actual check
try {
const response = await fetch(url, { method: 'HEAD' });
const isAvailable = response.ok;
// Update caches
imageAvailabilityCache[url] = isAvailable;
try {
await mmkvStorage.setItem(`image_available:${url}`, isAvailable ? 'true' : 'false');
} catch (error) {
// Ignore AsyncStorage errors
}
return isAvailable;
} catch (error) {
return false;
}
};
export const useMetadataAssets = (
metadata: any,

View file

@ -49,7 +49,6 @@ export interface AppSettings {
scraperRepositoryUrl: string; // URL to the scraper repository
enableLocalScrapers: boolean; // Enable/disable local scraper functionality
scraperTimeout: number; // Timeout for scraper execution in seconds
enableScraperUrlValidation: boolean; // Enable/disable URL validation for scrapers
streamDisplayMode: 'separate' | 'grouped'; // How to display streaming links - separately by provider or grouped under one name
streamSortMode: 'scraper-then-quality' | 'quality-then-scraper'; // How to sort streams - by scraper first or quality first
showScraperLogos: boolean; // Show scraper logos next to streaming links
@ -138,7 +137,6 @@ export const DEFAULT_SETTINGS: AppSettings = {
scraperRepositoryUrl: '',
enableLocalScrapers: true,
scraperTimeout: 60, // 60 seconds timeout
enableScraperUrlValidation: true, // Enable URL validation by default
streamDisplayMode: 'separate', // Default to separate display by provider
streamSortMode: 'scraper-then-quality', // Default to current behavior (scraper first, then quality)
showScraperLogos: true, // Show scraper logos by default

View file

@ -1443,10 +1443,6 @@ const PluginsScreen: React.FC = () => {
}
};
const handleToggleUrlValidation = async (enabled: boolean) => {
await updateSetting('enableScraperUrlValidation', enabled);
};
const handleToggleQualityExclusion = async (quality: string) => {
const currentExcluded = settings.excludedQualities || [];
const isExcluded = currentExcluded.includes(quality);
@ -1971,22 +1967,6 @@ const PluginsScreen: React.FC = () => {
colors={colors}
styles={styles}
>
<View style={styles.settingRow}>
<View style={styles.settingInfo}>
<Text style={styles.settingTitle}>{t('plugins.enable_url_validation')}</Text>
<Text style={styles.settingDescription}>
{t('plugins.url_validation_desc')}
</Text>
</View>
<Switch
value={settings.enableScraperUrlValidation && settings.enableLocalScrapers}
onValueChange={handleToggleUrlValidation}
trackColor={{ false: colors.elevation3, true: colors.primary }}
thumbColor={settings.enableScraperUrlValidation && settings.enableLocalScrapers ? colors.white : '#f4f3f4'}
disabled={!settings.enableLocalScrapers}
/>
</View>
<View style={styles.settingRow}>
<View style={styles.settingInfo}>
<Text style={styles.settingTitle}>{t('plugins.group_streams')}</Text>

View file

@ -23,7 +23,6 @@ import {
filterStreamsByQuality,
filterStreamsByLanguage,
getQualityNumeric,
detectMkvViaHead,
inferVideoTypeFromUrl,
sortStreamsByQuality,
} from './utils';
@ -37,7 +36,6 @@ import {
TMDBEpisodeOverride,
AlertAction,
} from './types';
import { MKV_HEAD_TIMEOUT_MS } from './constants';
// Cache for scraper logos
const scraperLogoCache = new Map<string, string>();
@ -469,36 +467,6 @@ export const useStreamsScreen = () => {
return;
}
// iOS MKV detection
if (Platform.OS === 'ios' && settings.preferredPlayer === 'internal') {
const lowerUrl = (stream.url || '').toLowerCase();
const isMkvByPath =
lowerUrl.includes('.mkv') ||
/[?&]ext=mkv\b/i.test(lowerUrl) ||
/format=mkv\b/i.test(lowerUrl) ||
/container=mkv\b/i.test(lowerUrl);
const isHttp = lowerUrl.startsWith('http://') || lowerUrl.startsWith('https://');
if (!isMkvByPath && isHttp) {
try {
const mkvDetected = await Promise.race<boolean>([
detectMkvViaHead(stream.url, (stream.headers as any) || undefined),
new Promise<boolean>(res => setTimeout(() => res(false), MKV_HEAD_TIMEOUT_MS)),
]);
if (mkvDetected) {
const mergedHeaders = {
...(stream.headers || {}),
'Content-Type': 'video/x-matroska',
} as Record<string, string>;
navigateToPlayer(stream, { headers: mergedHeaders });
return;
}
} catch (e) {
logger.warn('[StreamsScreen] MKV detection failed:', e);
}
}
}
// iOS external player
if (Platform.OS === 'ios' && settings.preferredPlayer !== 'internal') {
try {

View file

@ -1,5 +1,4 @@
import { Stream } from '../../types/metadata';
import { MKV_HEAD_TIMEOUT_MS } from './constants';
/**
* Language variations for filtering
@ -150,30 +149,6 @@ export const sortStreamsByQuality = (streams: Stream[]): Stream[] => {
});
};
/**
* Detect MKV format via HEAD request
*/
export const detectMkvViaHead = async (
url: string,
headers?: Record<string, string>
): Promise<boolean> => {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), MKV_HEAD_TIMEOUT_MS);
try {
const res = await fetch(url, {
method: 'HEAD',
headers,
signal: controller.signal as any,
} as any);
const contentType = res.headers.get('content-type') || '';
return /matroska|x-matroska/i.test(contentType);
} catch (_e) {
return false;
} finally {
clearTimeout(timeout);
}
};
/**
* Infer video type from URL
*/

View file

@ -1164,10 +1164,6 @@ class LocalScraperService {
private async executePlugin(code: string, params: any, consoleOverride?: any): Promise<LocalScraperResult[]> {
try {
const settingsData = await mmkvStorage.getItem('app_settings');
const settings = settingsData ? JSON.parse(settingsData) : {};
const urlValidationEnabled = settings.enableScraperUrlValidation ?? true;
const allScraperSettingsRaw = await mmkvStorage.getItem(this.SCRAPER_SETTINGS_KEY);
const allScraperSettings = allScraperSettingsRaw ? JSON.parse(allScraperSettingsRaw) : {};
let perScraperSettings = (params && params.scraperId && allScraperSettings[params.scraperId])
@ -1299,7 +1295,6 @@ class LocalScraperService {
'params',
'PRIMARY_KEY',
'TMDB_API_KEY',
'URL_VALIDATION_ENABLED',
'SCRAPER_SETTINGS',
'SCRAPER_ID',
`
@ -1311,7 +1306,6 @@ class LocalScraperService {
globalScope.TMDB_API_KEY = TMDB_API_KEY;
globalScope.SCRAPER_SETTINGS = SCRAPER_SETTINGS;
globalScope.SCRAPER_ID = SCRAPER_ID;
globalScope.URL_VALIDATION_ENABLED = URL_VALIDATION_ENABLED;
} else {
logger.error('[Plugin Sandbox] Could not find global scope to inject settings');
}
@ -1346,7 +1340,6 @@ class LocalScraperService {
params,
MOVIEBOX_PRIMARY_KEY,
MOVIEBOX_TMDB_API_KEY,
urlValidationEnabled,
perScraperSettings,
params?.scraperId
);

View file

@ -149,26 +149,6 @@ class StreamCacheService {
return episodeId ? `${baseKey}:${episodeId}` : baseKey;
}
/**
* Validate if a stream URL is still accessible
*/
private async validateStreamUrl(url: string): Promise<boolean> {
try {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 3000); // 3 second timeout
const response = await fetch(url, {
method: 'HEAD',
signal: controller.signal as any,
} as any);
clearTimeout(timeout);
return response.ok;
} catch (error) {
return false;
}
}
/**
* Get cache info for debugging
*/

View file

@ -4,7 +4,6 @@
*/
import { Platform } from 'react-native';
import { isMkvStream } from './mkvDetection';
export interface PlayerSelectionOptions {
uri: string;