/** * Enhanced MKV stream detection utility * Provides multiple methods to detect if a video stream is in MKV format */ export interface StreamDetectionResult { isMkv: boolean; method: string; confidence: 'high' | 'medium' | 'low'; } /** * Comprehensive MKV stream detection * Uses multiple detection methods for maximum accuracy */ export const detectMkvStream = (streamUri: string, streamHeaders?: Record): StreamDetectionResult => { if (!streamUri) { return { isMkv: false, method: 'none', confidence: 'high' }; } const lowerUri = streamUri.toLowerCase(); const contentType = (streamHeaders && (streamHeaders['Content-Type'] || streamHeaders['content-type'])) || ''; // Method 1: Content-Type header detection (most reliable) if (typeof contentType === 'string') { if (/video\/x-matroska|application\/x-matroska/i.test(contentType)) { return { isMkv: true, method: 'content-type', confidence: 'high' }; } if (/matroska/i.test(contentType)) { return { isMkv: true, method: 'content-type', confidence: 'high' }; } } // Method 2: File extension detection const mkvExtensions = ['.mkv', '.mka', '.mks', '.mk3d']; for (const ext of mkvExtensions) { if (lowerUri.includes(ext)) { return { isMkv: true, method: 'extension', confidence: 'high' }; } } // Method 3: URL parameter detection const urlPatterns = [ /[?&]ext=mkv\b/, /[?&]format=mkv\b/, /[?&]container=mkv\b/, /[?&]codec=mkv\b/, /[?&]format=matroska\b/, /[?&]type=mkv\b/, /[?&]file_format=mkv\b/ ]; for (const pattern of urlPatterns) { if (pattern.test(lowerUri)) { return { isMkv: true, method: 'url-pattern', confidence: 'high' }; } } // Method 4: Known MKV streaming patterns (medium confidence) const streamingPatterns = [ /mkv|matroska/i, /video\/x-matroska/i, /ebml/i, // EBML is the container format MKV uses ]; for (const pattern of streamingPatterns) { if (pattern.test(lowerUri)) { return { isMkv: true, method: 'streaming-pattern', confidence: 'medium' }; } } // Method 5: Provider-specific patterns (lower confidence) const providerPatterns = [ /vidsrc|embedsu|multiembed/i, // Providers that often serve MKV ]; for (const pattern of providerPatterns) { if (pattern.test(lowerUri)) { // These providers often serve MKV, but not guaranteed return { isMkv: true, method: 'provider-pattern', confidence: 'low' }; } } return { isMkv: false, method: 'none', confidence: 'high' }; }; /** * Async HEAD request detection for MKV * Most reliable method but requires network request */ export const detectMkvViaHeadRequest = async ( url: string, headers?: Record, timeoutMs: number = 2000 ): Promise => { const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), timeoutMs); try { const response = await fetch(url, { method: 'HEAD', headers, signal: controller.signal as any, } as any); const contentType = response.headers.get('content-type') || ''; if (/video\/x-matroska|application\/x-matroska/i.test(contentType)) { return { isMkv: true, method: 'head-request', confidence: 'high' }; } if (/matroska/i.test(contentType)) { return { isMkv: true, method: 'head-request', confidence: 'high' }; } return { isMkv: false, method: 'head-request', confidence: 'high' }; } catch (error) { return { isMkv: false, method: 'head-request-failed', confidence: 'low' }; } finally { clearTimeout(timeout); } }; /** * Combined detection: fast local detection + optional HEAD request */ export const detectMkvComprehensive = async ( streamUri: string, streamHeaders?: Record, useHeadRequest: boolean = false, headTimeoutMs: number = 2000 ): Promise => { // First try fast local detection const localResult = detectMkvStream(streamUri, streamHeaders); if (localResult.isMkv && localResult.confidence === 'high') { return localResult; } // If local detection is inconclusive and HEAD request is enabled, try network detection if (useHeadRequest) { const headResult = await detectMkvViaHeadRequest(streamUri, streamHeaders, headTimeoutMs); if (headResult.isMkv || headResult.method === 'head-request') { return headResult; } } return localResult; }; /** * Simple boolean wrapper for backward compatibility */ export const isMkvStream = (streamUri: string, streamHeaders?: Record): boolean => { const result = detectMkvStream(streamUri, streamHeaders); return result.isMkv; };