mirror of
https://github.com/R0m1k3/xtremobile.git
synced 2026-10-14 02:36:44 +02:00
feat: Implement a web-based IPTV player with HLS/MPEG-TS support and Flutter communication.
This commit is contained in:
1 parent
2ca92d8425
commit
55acbbc135
28 files changed
+2075
-1448
No files matched your search
@@ -7,14 +7,15 @@ import 'package:dio/io.dart';
|
||||
/// Useful for emulators or restricted networks
|
||||
class DnsFallbackInterceptor extends Interceptor {
|
||||
final Dio _dio;
|
||||
|
||||
|
||||
// Cache for resolved IPs
|
||||
final Map<String, String> _dnsCache = {};
|
||||
|
||||
DnsFallbackInterceptor(this._dio);
|
||||
|
||||
@override
|
||||
Future<void> onError(DioException err, ErrorInterceptorHandler handler) async {
|
||||
Future<void> onError(
|
||||
DioException err, ErrorInterceptorHandler handler) async {
|
||||
// Basic check for connection/host lookup errors
|
||||
if (_isDnsError(err)) {
|
||||
final uri = err.requestOptions.uri;
|
||||
@@ -30,12 +31,12 @@ class DnsFallbackInterceptor extends Interceptor {
|
||||
// Attempt resolution attempts
|
||||
print('DnsFallback: Attempting DoH resolution...');
|
||||
ip = await _resolveWithDoH(host);
|
||||
|
||||
|
||||
if (ip != null) {
|
||||
print('DnsFallback: Resolved $host to $ip');
|
||||
_dnsCache[host] = ip;
|
||||
} else {
|
||||
print('DnsFallback: Failed to resolve $host');
|
||||
print('DnsFallback: Failed to resolve $host');
|
||||
}
|
||||
} catch (e) {
|
||||
print('DnsFallback: Resolution error: $e');
|
||||
@@ -48,30 +49,32 @@ class DnsFallbackInterceptor extends Interceptor {
|
||||
// Clone options but replace host with IP
|
||||
final newUri = uri.replace(host: ip);
|
||||
final options = err.requestOptions.copyWith(path: newUri.toString());
|
||||
|
||||
|
||||
// IMPORTANT: Set Host header so the server handles it correctly
|
||||
options.headers['Host'] = host;
|
||||
|
||||
|
||||
// Retry request
|
||||
print('DnsFallback: Retrying request to $ip');
|
||||
final response = await _dio.fetch(options);
|
||||
return handler.resolve(response);
|
||||
} catch (e) {
|
||||
print('DnsFallback: Retry failed: $e');
|
||||
return handler.next(err);
|
||||
print('DnsFallback: Retry failed: $e');
|
||||
return handler.next(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
handler.next(err);
|
||||
}
|
||||
|
||||
bool _isDnsError(DioException err) {
|
||||
if (err.type == DioExceptionType.connectionError || err.type == DioExceptionType.unknown) {
|
||||
if (err.error is SocketException) {
|
||||
final msg = (err.error as SocketException).message;
|
||||
return msg.contains('Failed host lookup') || msg.contains('No address associated with hostname');
|
||||
}
|
||||
if (err.type == DioExceptionType.connectionError ||
|
||||
err.type == DioExceptionType.unknown) {
|
||||
if (err.error is SocketException) {
|
||||
final msg = (err.error as SocketException).message;
|
||||
return msg.contains('Failed host lookup') ||
|
||||
msg.contains('No address associated with hostname');
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -79,14 +82,17 @@ class DnsFallbackInterceptor extends Interceptor {
|
||||
/// Resolve hostname using multiple DoH providers
|
||||
Future<String?> _resolveWithDoH(String hostname) async {
|
||||
// 1. Google DNS (8.8.8.8)
|
||||
String? ip = await _queryDoH('https://8.8.8.8/resolve?name=$hostname&type=A');
|
||||
String? ip =
|
||||
await _queryDoH('https://8.8.8.8/resolve?name=$hostname&type=A');
|
||||
if (ip != null) return ip;
|
||||
|
||||
// 2. Cloudflare DNS (1.1.1.1)
|
||||
ip = await _queryDoH('https://1.1.1.1/dns-query?name=$hostname&type=A',
|
||||
headers: {'accept': 'application/dns-json'});
|
||||
ip = await _queryDoH(
|
||||
'https://1.1.1.1/dns-query?name=$hostname&type=A',
|
||||
headers: {'accept': 'application/dns-json'},
|
||||
);
|
||||
if (ip != null) return ip;
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -94,26 +100,28 @@ class DnsFallbackInterceptor extends Interceptor {
|
||||
try {
|
||||
// Create a raw HttpClient to avoid loop and control SSL
|
||||
final client = HttpClient()
|
||||
..badCertificateCallback = (cert, host, port) => true; // IGNORE SSL ERRORS just for DoH IP connectivity
|
||||
|
||||
..badCertificateCallback = (cert, host, port) =>
|
||||
true; // IGNORE SSL ERRORS just for DoH IP connectivity
|
||||
|
||||
final request = await client.getUrl(Uri.parse(url));
|
||||
if (headers != null) {
|
||||
headers.forEach((k, v) => request.headers.add(k, v));
|
||||
}
|
||||
|
||||
|
||||
final response = await request.close();
|
||||
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final body = await response.transform(utf8.decoder).join();
|
||||
final json = jsonDecode(body);
|
||||
|
||||
|
||||
// Google/Cloudflare JSON format
|
||||
if (json['Status'] == 0 && json['Answer'] != null) {
|
||||
final answers = json['Answer'] as List;
|
||||
for (final ans in answers) {
|
||||
if (ans['type'] == 1) { // Type A record
|
||||
return ans['data'];
|
||||
}
|
||||
if (ans['type'] == 1) {
|
||||
// Type A record
|
||||
return ans['data'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,12 +23,10 @@ class DnsResolver {
|
||||
);
|
||||
|
||||
// Fallback to Cloudflare via IP (1.1.1.1)
|
||||
if (ip == null) {
|
||||
ip = await _queryDoH(
|
||||
'https://1.1.1.1/dns-query?name=$hostname&type=A',
|
||||
headers: {'accept': 'application/dns-json'},
|
||||
);
|
||||
}
|
||||
ip ??= await _queryDoH(
|
||||
'https://1.1.1.1/dns-query?name=$hostname&type=A',
|
||||
headers: {'accept': 'application/dns-json'},
|
||||
);
|
||||
|
||||
if (ip != null) {
|
||||
print('DnsResolver: Resolved $hostname -> $ip');
|
||||
@@ -40,7 +38,8 @@ class DnsResolver {
|
||||
return ip;
|
||||
}
|
||||
|
||||
static Future<String?> _queryDoH(String url, {Map<String, String>? headers}) async {
|
||||
static Future<String?> _queryDoH(String url,
|
||||
{Map<String, String>? headers}) async {
|
||||
try {
|
||||
final client = HttpClient()
|
||||
..connectionTimeout = const Duration(seconds: 10)
|
||||
|
||||
Reference in new issue
Block a user