import 'package:dio/dio.dart'; import 'package:dio_cache_interceptor/dio_cache_interceptor.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'dart:io'; /// Advanced network configuration for streaming class NetworkConfig { final String? proxyUrl; final String? userAgent; final Map customHeaders; final Duration connectTimeout; final Duration receiveTimeout; final bool enableValidateSSL; const NetworkConfig({ this.proxyUrl, this.userAgent = 'VLC/3.0.18 LibVLC/3.0.18', this.customHeaders = const {}, this.connectTimeout = const Duration(seconds: 30), this.receiveTimeout = const Duration(seconds: 60), this.enableValidateSSL = true, }); NetworkConfig copyWith({ String? proxyUrl, String? userAgent, Map? customHeaders, Duration? connectTimeout, Duration? receiveTimeout, bool? enableValidateSSL, }) => NetworkConfig( proxyUrl: proxyUrl ?? this.proxyUrl, userAgent: userAgent ?? this.userAgent, customHeaders: customHeaders ?? this.customHeaders, connectTimeout: connectTimeout ?? this.connectTimeout, receiveTimeout: receiveTimeout ?? this.receiveTimeout, enableValidateSSL: enableValidateSSL ?? this.enableValidateSSL, ); } /// Advanced network service with proxy and optimization support class OptimizedNetworkService { late Dio _dio; late NetworkConfig _config; late CacheInterceptor _cacheInterceptor; OptimizedNetworkService({required NetworkConfig config}) { _config = config; _initializeDio(); } void _initializeDio() { _cacheInterceptor = CacheInterceptor( options: CacheOptions( store: MemCacheStore(), policy: CachePolicy.requestFirst, hitCacheOnErrorExcept: [], allowPostMethod: false, ), ); _dio = Dio( BaseOptions( connectTimeout: _config.connectTimeout, receiveTimeout: _config.receiveTimeout, validateStatus: (status) => status != null && status < 500, headers: { 'User-Agent': _config.userAgent, ..._config.customHeaders, }, ), ); // Configure proxy if specified if (_config.proxyUrl != null) { _configureProxy(_config.proxyUrl!); } // Add interceptors _dio.interceptors.addAll([ _cacheInterceptor, _BandwidthTrackingInterceptor(), _RetryInterceptor(), ]); } /// Configure native Dart HttpClient with proxy void _configureProxy(String proxyUrl) { try { final uri = Uri.parse(proxyUrl); _dio.httpClientAdapter = HttpClientAdapter( onHttpClientCreate: (client) { client.findProxy = (uri) { return 'PROXY ${uri.host}:${uri.port}'; }; return client; }, ); } catch (e) { print('Error configuring proxy: $e'); } } /// Update network configuration void updateConfig(NetworkConfig config) { _config = config; _initializeDio(); } Dio get dio => _dio; /// Download file with resume support Future downloadFile({ required String url, required String savePath, required Function(int, int) onProgress, CancelToken? cancelToken, }) async { try { await _dio.download( url, savePath, onReceiveProgress: onProgress, cancelToken: cancelToken, options: Options( headers: {'Range': 'bytes=0-'}, ), ); } catch (e) { rethrow; } } /// Stream request with large response handling Future streamRequest({ required String url, required Function(List) onData, required VoidCallback onDone, required Function(dynamic) onError, CancelToken? cancelToken, }) async { try { final response = await _dio.get( url, options: Options( responseType: ResponseType.stream, receiveTimeout: const Duration(minutes: 5), ), cancelToken: cancelToken, ); final stream = response.data?.stream; if (stream != null) { stream.listen( onData, onError: onError, onDone: onDone, ); } } catch (e) { onError(e); } } /// Clear cache void clearCache() { _cacheInterceptor.clearCache(); } } /// Interceptor for tracking bandwidth class _BandwidthTrackingInterceptor extends Interceptor { int _bytesReceived = 0; DateTime _lastReset = DateTime.now(); int get bytesReceivedLastSecond { final now = DateTime.now(); final secondsElapsed = now.difference(_lastReset).inSeconds; if (secondsElapsed > 1) { final bps = _bytesReceived ~/ secondsElapsed; _bytesReceived = 0; _lastReset = now; return bps; } return 0; } @override void onResponse(Response response, ResponseInterceptorHandler handler) { if (response.data is ResponseBody) { // Track bytes for streaming responses } else if (response.data is List) { _bytesReceived += (response.data as List).length; } handler.next(response); } } /// Interceptor for automatic retry on failure class _RetryInterceptor extends Interceptor { static const _maxRetries = 3; @override Future onError( DioException err, ErrorInterceptorHandler handler) async { // Only retry on timeout or connection errors if (err.type == DioExceptionType.connectionTimeout || err.type == DioExceptionType.receiveTimeout) { final requestOptions = err.requestOptions; int retryCount = requestOptions.extra['retryCount'] ?? 0; if (retryCount < _maxRetries) { requestOptions.extra['retryCount'] = retryCount + 1; // Exponential backoff: 100ms, 200ms, 400ms final delay = Duration(milliseconds: 100 * (1 << retryCount)); await Future.delayed(delay); final dio = Dio(); try { final response = await dio.request( requestOptions.path, options: Options( method: requestOptions.method, headers: requestOptions.headers, ), data: requestOptions.data, queryParameters: requestOptions.queryParameters, ); return handler.resolve(response); } on DioException catch (e) { return handler.next(e); } } } handler.next(err); } } // Riverpod providers final networkConfigProvider = StateNotifierProvider((ref) { return NetworkConfigNotifier(); }); class NetworkConfigNotifier extends StateNotifier { NetworkConfigNotifier() : super(const NetworkConfig( userAgent: 'VLC/3.0.18 LibVLC/3.0.18', )); void setProxy(String? proxyUrl) { state = state.copyWith(proxyUrl: proxyUrl); } void setUserAgent(String userAgent) { state = state.copyWith(userAgent: userAgent); } void addCustomHeader(String key, String value) { final newHeaders = {...state.customHeaders}; newHeaders[key] = value; state = state.copyWith(customHeaders: newHeaders); } void removeCustomHeader(String key) { final newHeaders = {...state.customHeaders}; newHeaders.remove(key); state = state.copyWith(customHeaders: newHeaders); } void clearCustomHeaders() { state = state.copyWith(customHeaders: {}); } } final networkServiceProvider = Provider((ref) { final config = ref.watch(networkConfigProvider); return OptimizedNetworkService(config: config); });