import 'package:flutter/foundation.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart'; abstract class ILinkExtractionService { Future extractVideoUrl(String pageUrl); } class LinkExtractionService implements ILinkExtractionService { HeadlessInAppWebView? _headlessWebView; @override Future extractVideoUrl(String pageUrl) async { // 1. Check if it's already a video link if (_isVideoUrl(pageUrl)) return pageUrl; // 2. Use Headless WebView to find the video String? foundUrl; _headlessWebView = HeadlessInAppWebView( initialUrlRequest: URLRequest(url: WebUri(pageUrl)), initialSettings: InAppWebViewSettings( javaScriptEnabled: true, isInspectable: true, mixedContentMode: MixedContentMode.MIXED_CONTENT_ALWAYS_ALLOW, userAgent: "Mozilla/5.0 (Linux; Android 13; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Mobile Safari/537.36", ), onReceivedServerTrustAuthRequest: (controller, challenge) async { return ServerTrustAuthResponse(action: ServerTrustAuthResponseAction.PROCEED); }, onConsoleMessage: (controller, consoleMessage) { debugPrint("WEBVIEW CONSOLE: ${consoleMessage.message}"); }, onLoadError: (controller, url, code, message) { debugPrint("WEBVIEW ERROR: $message ($code) for $url"); }, onLoadHttpError: (controller, url, statusCode, description) { debugPrint("WEBVIEW HTTP ERROR: $description ($statusCode) for $url"); }, onLoadResource: (controller, resource) { final url = resource.url.toString(); if (_isVideoUrl(url)) { foundUrl = url; _headlessWebView?.dispose(); } }, ); await _headlessWebView?.run(); // Wait for some time to catch resources await Future.delayed(const Duration(seconds: 5)); return foundUrl; } bool _isVideoUrl(String url) { final lower = url.toLowerCase(); return lower.contains('.m3u8') || lower.contains('.mp4') || lower.contains('.mkv'); } }