import 'dart:async'; import 'dart:io'; import 'package:collection/collection.dart'; import 'package:dio/dio.dart'; import 'package:dio_cache_interceptor/dio_cache_interceptor.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'package:injectable/injectable.dart'; import 'package:synchronized/synchronized.dart'; import 'package:timeago/timeago.dart'; @lazySingleton class RevancedAPI { late Dio _dio = Dio(); final Lock getToolsLock = Lock(); final _cacheOptions = CacheOptions( store: MemCacheStore(), maxStale: const Duration(days: 1), priority: CachePriority.high, ); Future initialize(String apiUrl) async { try { _dio = Dio( BaseOptions( baseUrl: apiUrl, ), ); _dio.interceptors.add(DioCacheInterceptor(options: _cacheOptions)); } on Exception catch (e) { if (kDebugMode) { print(e); } } } Future clearAllCache() async { try { await _cacheOptions.store!.clean(); } on Exception catch (e) { if (kDebugMode) { print(e); } } } Future>> getContributors() async { final Map> contributors = {}; try { final response = await _dio.get('/contributors'); final List repositories = response.data['repositories']; for (final Map repo in repositories) { final String name = repo['name']; contributors[name] = repo['contributors']; } } on Exception catch (e) { if (kDebugMode) { print(e); } return {}; } return contributors; } Future?> _getLatestRelease( String extension, String repoName, ) { return getToolsLock.synchronized(() async { try { final response = await _dio.get('/tools'); final List tools = response.data['tools']; return tools.firstWhereOrNull( (t) => t['repository'] == repoName && (t['name'] as String).endsWith(extension), ); } on Exception catch (e) { if (kDebugMode) { print(e); } return null; } }); } Future getLatestReleaseVersion( String extension, String repoName, ) async { try { final Map? release = await _getLatestRelease( extension, repoName, ); if (release != null) { return release['version']; } } on Exception catch (e) { if (kDebugMode) { print(e); } return null; } return null; } Future getLatestReleaseFile( String extension, String repoName, ) async { try { final Map? release = await _getLatestRelease( extension, repoName, ); if (release != null) { final String url = release['browser_download_url']; return await DefaultCacheManager().getSingleFile(url); } } on Exception catch (e) { if (kDebugMode) { print(e); } return null; } return null; } StreamController managerUpdateProgress = StreamController.broadcast(); void updateManagerDownloadProgress(int progress) { managerUpdateProgress.add(progress.toDouble()); } Stream getManagerUpdateProgress() { return managerUpdateProgress.stream; } void disposeManagerUpdateProgress() { managerUpdateProgress.close(); } Future downloadManager() async { final Map? release = await _getLatestRelease( '.apk', 'revanced/revanced-manager', ); File? outputFile; await for (final result in DefaultCacheManager().getFileStream( release!['browser_download_url'] as String, withProgress: true, )) { if (result is DownloadProgress) { final totalSize = result.totalSize ?? 10000000; final progress = (result.downloaded / totalSize * 100).round(); updateManagerDownloadProgress(progress); } else if (result is FileInfo) { disposeManagerUpdateProgress(); // The download is complete; convert the FileInfo object to a File object outputFile = File(result.file.path); } } return outputFile; } Future getLatestReleaseTime( String extension, String repoName, ) async { try { final Map? release = await _getLatestRelease( extension, repoName, ); if (release != null) { final DateTime timestamp = DateTime.parse(release['timestamp'] as String); return format(timestamp, locale: 'en_short'); } } on Exception catch (e) { if (kDebugMode) { print(e); } return null; } return null; } }