mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-01-11 20:10:25 +00:00
mkv fix
This commit is contained in:
parent
876f77019e
commit
408b1cb366
5 changed files with 98 additions and 3 deletions
|
|
@ -1 +1 @@
|
|||
Subproject commit e63dbd7ed54a8a5eb16e57eb2fb41fad6bc96272
|
||||
Subproject commit 0a040cc12da805fa8b38411d128e607e86e0f919
|
||||
|
|
@ -57,8 +57,18 @@ const VideoPlayer: React.FC = () => {
|
|||
// Use AndroidVideoPlayer for:
|
||||
// - Android devices
|
||||
// - Xprime streams on any platform
|
||||
// - Non-MKV files on iOS
|
||||
if (Platform.OS === 'android' || isXprimeStream || (Platform.OS === 'ios' && !isMkvFile && !forceVlc)) {
|
||||
// - Non-MKV files on iOS (unless forceVlc is set)
|
||||
const shouldUseAndroidPlayer = Platform.OS === 'android' || isXprimeStream || (Platform.OS === 'ios' && !isMkvFile && !forceVlc);
|
||||
logger.log('[VideoPlayer] Player selection:', {
|
||||
platform: Platform.OS,
|
||||
isXprimeStream,
|
||||
isMkvFile,
|
||||
forceVlc: !!forceVlc,
|
||||
selected: shouldUseAndroidPlayer ? 'AndroidVideoPlayer' : 'VLCPlayer',
|
||||
streamProvider,
|
||||
uri
|
||||
});
|
||||
if (shouldUseAndroidPlayer) {
|
||||
return <AndroidVideoPlayer />;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -84,6 +84,7 @@ export type RootStackParamList = {
|
|||
streamProvider?: string;
|
||||
streamName?: string;
|
||||
headers?: { [key: string]: string };
|
||||
forceVlc?: boolean;
|
||||
id?: string;
|
||||
type?: string;
|
||||
episodeId?: string;
|
||||
|
|
|
|||
|
|
@ -840,6 +840,18 @@ export const StreamsScreen = () => {
|
|||
const streamName = stream.name || stream.title || 'Unnamed Stream';
|
||||
const streamProvider = stream.addonId || stream.addonName || stream.name;
|
||||
|
||||
// Determine if we should force VLC on iOS based on provider-declared formats (e.g., MKV)
|
||||
let forceVlc = false;
|
||||
try {
|
||||
const providerId = stream.addonId || (stream as any).addon;
|
||||
if (Platform.OS === 'ios' && providerId) {
|
||||
forceVlc = await localScraperService.supportsFormat(providerId, 'mkv');
|
||||
logger.log(`[StreamsScreen] Provider '${providerId}' MKV support -> ${forceVlc}`);
|
||||
}
|
||||
} catch (e) {
|
||||
logger.warn('[StreamsScreen] MKV support detection failed:', e);
|
||||
}
|
||||
|
||||
// Add pre-navigation orientation lock to reduce glitch
|
||||
try {
|
||||
await ScreenOrientation.lockAsync(ScreenOrientation.OrientationLock.LANDSCAPE);
|
||||
|
|
@ -863,6 +875,8 @@ export const StreamsScreen = () => {
|
|||
streamName: streamName,
|
||||
// Always prefer stream.headers; player will use these for requests
|
||||
headers: stream.headers || undefined,
|
||||
// Force VLC for providers that declare MKV format support on iOS
|
||||
forceVlc,
|
||||
id,
|
||||
type,
|
||||
episodeId: type === 'series' && selectedEpisode ? selectedEpisode : undefined,
|
||||
|
|
@ -877,6 +891,21 @@ export const StreamsScreen = () => {
|
|||
const handleStreamPress = useCallback(async (stream: Stream) => {
|
||||
try {
|
||||
if (stream.url) {
|
||||
// If provider declares MKV support, force the in-app VLC-based player on iOS
|
||||
try {
|
||||
const providerId = stream.addonId || (stream as any).addon;
|
||||
if (Platform.OS === 'ios' && providerId) {
|
||||
const providerRequiresVlc = await localScraperService.supportsFormat(providerId, 'mkv');
|
||||
if (providerRequiresVlc) {
|
||||
logger.log(`[StreamsScreen] Forcing in-app VLC for provider '${providerId}' on iOS due to MKV support`);
|
||||
navigateToPlayer(stream);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
logger.warn('[StreamsScreen] MKV pre-check failed:', err);
|
||||
}
|
||||
|
||||
logger.log('handleStreamPress called with stream:', {
|
||||
url: stream.url,
|
||||
behaviorHints: stream.behaviorHints,
|
||||
|
|
|
|||
|
|
@ -27,6 +27,10 @@ export interface ScraperInfo {
|
|||
manifestEnabled?: boolean; // Whether the scraper is enabled in the manifest
|
||||
supportedPlatforms?: ('ios' | 'android')[]; // Platforms where this scraper is supported
|
||||
disabledPlatforms?: ('ios' | 'android')[]; // Platforms where this scraper is disabled
|
||||
// Optional list of supported output formats for this provider (e.g., ["mkv", "mp4"]).
|
||||
// We support both `formats` and `supportedFormats` keys for manifest flexibility.
|
||||
formats?: string[];
|
||||
supportedFormats?: string[];
|
||||
}
|
||||
|
||||
export interface LocalScraperResult {
|
||||
|
|
@ -103,6 +107,16 @@ class LocalScraperService {
|
|||
if (!scraper.supportedTypes || !Array.isArray(scraper.supportedTypes)) {
|
||||
scraper.supportedTypes = ['movie', 'tv']; // Default to both types
|
||||
}
|
||||
// Normalize formats fields (support both `formats` and `supportedFormats`)
|
||||
if (typeof (scraper as any).formats === 'string') {
|
||||
scraper.formats = [(scraper as any).formats as unknown as string];
|
||||
}
|
||||
if (typeof (scraper as any).supportedFormats === 'string') {
|
||||
scraper.supportedFormats = [(scraper as any).supportedFormats as unknown as string];
|
||||
}
|
||||
if (!scraper.supportedFormats && scraper.formats) {
|
||||
scraper.supportedFormats = scraper.formats;
|
||||
}
|
||||
|
||||
// Ensure other required fields have defaults
|
||||
if (!scraper.description) {
|
||||
|
|
@ -331,6 +345,16 @@ class LocalScraperService {
|
|||
if (!updatedScraperInfo.supportedTypes || !Array.isArray(updatedScraperInfo.supportedTypes)) {
|
||||
updatedScraperInfo.supportedTypes = ['movie', 'tv']; // Default to both types
|
||||
}
|
||||
// Normalize formats fields (support both `formats` and `supportedFormats`)
|
||||
if (typeof (updatedScraperInfo as any).formats === 'string') {
|
||||
updatedScraperInfo.formats = [(updatedScraperInfo as any).formats as unknown as string];
|
||||
}
|
||||
if (typeof (updatedScraperInfo as any).supportedFormats === 'string') {
|
||||
updatedScraperInfo.supportedFormats = [(updatedScraperInfo as any).supportedFormats as unknown as string];
|
||||
}
|
||||
if (!updatedScraperInfo.supportedFormats && updatedScraperInfo.formats) {
|
||||
updatedScraperInfo.supportedFormats = updatedScraperInfo.formats;
|
||||
}
|
||||
|
||||
this.installedScrapers.set(scraperInfo.id, updatedScraperInfo);
|
||||
|
||||
|
|
@ -431,6 +455,18 @@ class LocalScraperService {
|
|||
// If manifest says enabled: true, use installed state or default to false
|
||||
enabled: scraperInfo.enabled ? (installedScraper?.enabled ?? false) : false
|
||||
};
|
||||
|
||||
// Normalize formats fields (support both `formats` and `supportedFormats`)
|
||||
const anyScraper: any = scraperWithManifestData as any;
|
||||
if (typeof anyScraper.formats === 'string') {
|
||||
anyScraper.formats = [anyScraper.formats];
|
||||
}
|
||||
if (typeof anyScraper.supportedFormats === 'string') {
|
||||
anyScraper.supportedFormats = [anyScraper.supportedFormats];
|
||||
}
|
||||
if (!anyScraper.supportedFormats && anyScraper.formats) {
|
||||
anyScraper.supportedFormats = anyScraper.formats;
|
||||
}
|
||||
|
||||
return scraperWithManifestData;
|
||||
});
|
||||
|
|
@ -451,6 +487,25 @@ class LocalScraperService {
|
|||
}
|
||||
}
|
||||
|
||||
// Check if a given scraper declares support for a specific format (e.g., 'mkv')
|
||||
async supportsFormat(scraperId: string, format: string): Promise<boolean> {
|
||||
await this.ensureInitialized();
|
||||
try {
|
||||
const available = await this.getAvailableScrapers();
|
||||
const info = available.find(s => s.id === scraperId);
|
||||
if (!info) return false;
|
||||
const formats = (info.supportedFormats || info.formats || [])
|
||||
.filter(Boolean)
|
||||
.map(f => (typeof f === 'string' ? f.toLowerCase() : String(f).toLowerCase()));
|
||||
const supported = formats.includes((format || '').toLowerCase());
|
||||
logger.log(`[LocalScraperService] supportsFormat('${scraperId}', '${format}') -> ${supported}. Formats: ${JSON.stringify(formats)}`);
|
||||
return supported;
|
||||
} catch (e) {
|
||||
logger.warn(`[LocalScraperService] supportsFormat('${scraperId}', '${format}') failed`, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Enable/disable scraper
|
||||
async setScraperEnabled(scraperId: string, enabled: boolean): Promise<void> {
|
||||
await this.ensureInitialized();
|
||||
|
|
|
|||
Loading…
Reference in a new issue