mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-04-21 00:32:04 +00:00
removed the stream/MKV/URL-validation HEAD probes;
This commit is contained in:
parent
a4548c69e9
commit
9d0163b4eb
9 changed files with 1 additions and 155 deletions
|
|
@ -116,12 +116,7 @@ export const usePlayerSetup = (
|
||||||
const restoreBrightness = async () => {
|
const restoreBrightness = async () => {
|
||||||
try {
|
try {
|
||||||
if (Platform.OS === 'android') {
|
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) {
|
if (originalAppBrightnessRef.current !== null) {
|
||||||
await Brightness.setBrightnessAsync(originalAppBrightnessRef.current);
|
await Brightness.setBrightnessAsync(originalAppBrightnessRef.current);
|
||||||
|
|
|
||||||
|
|
@ -3,48 +3,6 @@ import { logger } from '../utils/logger';
|
||||||
import { TMDBService } from '../services/tmdbService';
|
import { TMDBService } from '../services/tmdbService';
|
||||||
import { isTmdbUrl } from '../utils/logoUtils';
|
import { isTmdbUrl } from '../utils/logoUtils';
|
||||||
import FastImage from '@d11/react-native-fast-image';
|
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 = (
|
export const useMetadataAssets = (
|
||||||
metadata: any,
|
metadata: any,
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,6 @@ export interface AppSettings {
|
||||||
scraperRepositoryUrl: string; // URL to the scraper repository
|
scraperRepositoryUrl: string; // URL to the scraper repository
|
||||||
enableLocalScrapers: boolean; // Enable/disable local scraper functionality
|
enableLocalScrapers: boolean; // Enable/disable local scraper functionality
|
||||||
scraperTimeout: number; // Timeout for scraper execution in seconds
|
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
|
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
|
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
|
showScraperLogos: boolean; // Show scraper logos next to streaming links
|
||||||
|
|
@ -138,7 +137,6 @@ export const DEFAULT_SETTINGS: AppSettings = {
|
||||||
scraperRepositoryUrl: '',
|
scraperRepositoryUrl: '',
|
||||||
enableLocalScrapers: true,
|
enableLocalScrapers: true,
|
||||||
scraperTimeout: 60, // 60 seconds timeout
|
scraperTimeout: 60, // 60 seconds timeout
|
||||||
enableScraperUrlValidation: true, // Enable URL validation by default
|
|
||||||
streamDisplayMode: 'separate', // Default to separate display by provider
|
streamDisplayMode: 'separate', // Default to separate display by provider
|
||||||
streamSortMode: 'scraper-then-quality', // Default to current behavior (scraper first, then quality)
|
streamSortMode: 'scraper-then-quality', // Default to current behavior (scraper first, then quality)
|
||||||
showScraperLogos: true, // Show scraper logos by default
|
showScraperLogos: true, // Show scraper logos by default
|
||||||
|
|
|
||||||
|
|
@ -1443,10 +1443,6 @@ const PluginsScreen: React.FC = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleToggleUrlValidation = async (enabled: boolean) => {
|
|
||||||
await updateSetting('enableScraperUrlValidation', enabled);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleToggleQualityExclusion = async (quality: string) => {
|
const handleToggleQualityExclusion = async (quality: string) => {
|
||||||
const currentExcluded = settings.excludedQualities || [];
|
const currentExcluded = settings.excludedQualities || [];
|
||||||
const isExcluded = currentExcluded.includes(quality);
|
const isExcluded = currentExcluded.includes(quality);
|
||||||
|
|
@ -1971,22 +1967,6 @@ const PluginsScreen: React.FC = () => {
|
||||||
colors={colors}
|
colors={colors}
|
||||||
styles={styles}
|
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.settingRow}>
|
||||||
<View style={styles.settingInfo}>
|
<View style={styles.settingInfo}>
|
||||||
<Text style={styles.settingTitle}>{t('plugins.group_streams')}</Text>
|
<Text style={styles.settingTitle}>{t('plugins.group_streams')}</Text>
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ import {
|
||||||
filterStreamsByQuality,
|
filterStreamsByQuality,
|
||||||
filterStreamsByLanguage,
|
filterStreamsByLanguage,
|
||||||
getQualityNumeric,
|
getQualityNumeric,
|
||||||
detectMkvViaHead,
|
|
||||||
inferVideoTypeFromUrl,
|
inferVideoTypeFromUrl,
|
||||||
sortStreamsByQuality,
|
sortStreamsByQuality,
|
||||||
} from './utils';
|
} from './utils';
|
||||||
|
|
@ -37,7 +36,6 @@ import {
|
||||||
TMDBEpisodeOverride,
|
TMDBEpisodeOverride,
|
||||||
AlertAction,
|
AlertAction,
|
||||||
} from './types';
|
} from './types';
|
||||||
import { MKV_HEAD_TIMEOUT_MS } from './constants';
|
|
||||||
|
|
||||||
// Cache for scraper logos
|
// Cache for scraper logos
|
||||||
const scraperLogoCache = new Map<string, string>();
|
const scraperLogoCache = new Map<string, string>();
|
||||||
|
|
@ -469,36 +467,6 @@ export const useStreamsScreen = () => {
|
||||||
return;
|
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
|
// iOS external player
|
||||||
if (Platform.OS === 'ios' && settings.preferredPlayer !== 'internal') {
|
if (Platform.OS === 'ios' && settings.preferredPlayer !== 'internal') {
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import { Stream } from '../../types/metadata';
|
import { Stream } from '../../types/metadata';
|
||||||
import { MKV_HEAD_TIMEOUT_MS } from './constants';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Language variations for filtering
|
* 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
|
* Infer video type from URL
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -1164,10 +1164,6 @@ class LocalScraperService {
|
||||||
|
|
||||||
private async executePlugin(code: string, params: any, consoleOverride?: any): Promise<LocalScraperResult[]> {
|
private async executePlugin(code: string, params: any, consoleOverride?: any): Promise<LocalScraperResult[]> {
|
||||||
try {
|
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 allScraperSettingsRaw = await mmkvStorage.getItem(this.SCRAPER_SETTINGS_KEY);
|
||||||
const allScraperSettings = allScraperSettingsRaw ? JSON.parse(allScraperSettingsRaw) : {};
|
const allScraperSettings = allScraperSettingsRaw ? JSON.parse(allScraperSettingsRaw) : {};
|
||||||
let perScraperSettings = (params && params.scraperId && allScraperSettings[params.scraperId])
|
let perScraperSettings = (params && params.scraperId && allScraperSettings[params.scraperId])
|
||||||
|
|
@ -1299,7 +1295,6 @@ class LocalScraperService {
|
||||||
'params',
|
'params',
|
||||||
'PRIMARY_KEY',
|
'PRIMARY_KEY',
|
||||||
'TMDB_API_KEY',
|
'TMDB_API_KEY',
|
||||||
'URL_VALIDATION_ENABLED',
|
|
||||||
'SCRAPER_SETTINGS',
|
'SCRAPER_SETTINGS',
|
||||||
'SCRAPER_ID',
|
'SCRAPER_ID',
|
||||||
`
|
`
|
||||||
|
|
@ -1311,7 +1306,6 @@ class LocalScraperService {
|
||||||
globalScope.TMDB_API_KEY = TMDB_API_KEY;
|
globalScope.TMDB_API_KEY = TMDB_API_KEY;
|
||||||
globalScope.SCRAPER_SETTINGS = SCRAPER_SETTINGS;
|
globalScope.SCRAPER_SETTINGS = SCRAPER_SETTINGS;
|
||||||
globalScope.SCRAPER_ID = SCRAPER_ID;
|
globalScope.SCRAPER_ID = SCRAPER_ID;
|
||||||
globalScope.URL_VALIDATION_ENABLED = URL_VALIDATION_ENABLED;
|
|
||||||
} else {
|
} else {
|
||||||
logger.error('[Plugin Sandbox] Could not find global scope to inject settings');
|
logger.error('[Plugin Sandbox] Could not find global scope to inject settings');
|
||||||
}
|
}
|
||||||
|
|
@ -1346,7 +1340,6 @@ class LocalScraperService {
|
||||||
params,
|
params,
|
||||||
MOVIEBOX_PRIMARY_KEY,
|
MOVIEBOX_PRIMARY_KEY,
|
||||||
MOVIEBOX_TMDB_API_KEY,
|
MOVIEBOX_TMDB_API_KEY,
|
||||||
urlValidationEnabled,
|
|
||||||
perScraperSettings,
|
perScraperSettings,
|
||||||
params?.scraperId
|
params?.scraperId
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -149,26 +149,6 @@ class StreamCacheService {
|
||||||
return episodeId ? `${baseKey}:${episodeId}` : baseKey;
|
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
|
* Get cache info for debugging
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Platform } from 'react-native';
|
import { Platform } from 'react-native';
|
||||||
import { isMkvStream } from './mkvDetection';
|
|
||||||
|
|
||||||
export interface PlayerSelectionOptions {
|
export interface PlayerSelectionOptions {
|
||||||
uri: string;
|
uri: string;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue