import 'package:dio/dio.dart'; import '../core/network/gopeed/gopeed_transport.dart'; import 'model/create_task.dart'; import 'model/create_task_batch.dart'; import 'model/downloader_config.dart'; import 'model/extension.dart'; import 'model/install_extension.dart'; import 'model/login.dart'; import 'model/resolve_result.dart'; import 'model/resolve_task.dart'; import 'model/result.dart'; import 'model/switch_extension.dart'; import 'model/task.dart'; import 'model/update_check_extension_resp.dart'; import 'model/update_extension_settings.dart'; class ApiTimeoutException implements Exception { const ApiTimeoutException(this.message); final String message; @override String toString() => message; } GopeedTransport? _transport; void setTransportForTesting(GopeedTransport transport) { _transport = transport; } void init(String network, String address, String apiToken, {void Function()? onUnauthorized}) { _transport = createGopeedTransport( GopeedTransportConfig(network: network, address: address, apiToken: apiToken, onUnauthorized: onUnauthorized), ); } void initDefault({String address = '127.0.0.1:9999', String apiToken = ''}) { if (_transport == null) { init('tcp', address, apiToken); } } Future _request(String path, {String method = 'GET', dynamic data, Map? queryParameters}) { initDefault(); return _transport!.request(path, method: method, data: data, queryParameters: queryParameters); } Future _parse(Future Function() fetch, T Function(dynamic json)? fromJsonT) async { initDefault(); try { final resp = await fetch(); fromJsonT ??= (json) => null as T; final result = Result.fromJson(resp as Map, fromJsonT); if (result.code == 0) { return result.data as T; } final message = result.msg; throw Exception(message == null || message.isEmpty ? 'API error ${result.code}' : message); } on DioException catch (e) { if (e.type == DioExceptionType.sendTimeout || e.type == DioExceptionType.receiveTimeout || e.type == DioExceptionType.connectionTimeout || e.type == DioExceptionType.connectionError) { throw const ApiTimeoutException('request timeout'); } throw Exception(e.message ?? 'request failed'); } } Future resolve(ResolveTask resolveTask) { if (resolveTask.req == null) { throw ArgumentError('resolve request is required'); } return _parse( () => _request('api/v1/resolve', method: 'POST', data: resolveTask), (data) => ResolveResult.fromJson(data as Map), ); } Future createTask(CreateTask createTask) { return _parse(() => _request('api/v1/tasks', method: 'POST', data: createTask), (data) => data as String); } Future> createTaskBatch(CreateTaskBatch createTaskBatch) { return _parse>( () => _request('api/v1/tasks/batch', method: 'POST', data: createTaskBatch), (data) => (data as List).map((e) => e as String).toList(), ); } Future patchTask(String id, ResolveTask patchTask) { return _parse(() => _request('api/v1/tasks/$id', method: 'PATCH', data: patchTask), null); } Future> getTasks(List statuses) { final query = statuses.map((e) => 'status=${e.name}').join('&'); return _parse>( () => _request('/api/v1/tasks?$query'), (data) => (data as List).map((e) => Task.fromJson(e as Map)).toList(), ); } Future getTaskStatus(String id) { return _parse( () => _request('/api/v1/tasks/$id/status'), (data) => TaskRuntimeStatus.fromJson(data as Map), ); } Future> getTaskStats(String id) { return _parse>( () => _request('/api/v1/tasks/$id/stats'), (data) => data is Map ? {for (final entry in data.entries) entry.key.toString(): entry.value} : const {}, ); } Future pauseTask(String id) { return _parse(() => _request('api/v1/tasks/$id/pause', method: 'PUT'), null); } Future continueTask(String id) { return _parse(() => _request('api/v1/tasks/$id/continue', method: 'PUT'), null); } Future pauseAllTasks(List? ids) { return _parse(() => _request('api/v1/tasks/pause', method: 'PUT', queryParameters: {'id': ids}), null); } Future continueAllTasks(List? ids) { return _parse(() => _request('api/v1/tasks/continue', method: 'PUT', queryParameters: {'id': ids}), null); } Future deleteTask(String id, bool force) { return _parse(() => _request('api/v1/tasks/$id', method: 'DELETE', queryParameters: {'force': force}), null); } Future deleteTasks(List? ids, bool force) { return _parse( () => _request('api/v1/tasks', method: 'DELETE', queryParameters: {'id': ids, 'force': force}), null, ); } Future getConfig() { return _parse( () => _request('api/v1/config'), (data) => DownloaderConfig.fromJson(data as Map), ); } Future putConfig(DownloaderConfig config) { return _parse(() => _request('api/v1/config', method: 'PUT', data: config.toJson()), null); } Future installExtension(InstallExtension installExtension) { return _parse( () => _request('api/v1/extensions', method: 'POST', data: installExtension.toJson()), (data) => data as String, ); } Future> getExtensions() { return _parse>( () => _request('api/v1/extensions'), (data) => (data as List).map((e) => Extension.fromJson(e as Map)).toList(), ); } Future updateExtensionSettings(String identity, UpdateExtensionSettings updateExtensionSettings) { return _parse( () => _request('api/v1/extensions/$identity/settings', method: 'PUT', data: updateExtensionSettings.toJson()), null, ); } Future switchExtension(String identity, SwitchExtension switchExtension) { return _parse( () => _request('api/v1/extensions/$identity/switch', method: 'PUT', data: switchExtension.toJson()), null, ); } Future deleteExtension(String identity) { return _parse(() => _request('api/v1/extensions/$identity', method: 'DELETE'), null); } Future upgradeCheckExtension(String identity) { return _parse( () => _request('api/v1/extensions/$identity/update'), (data) => UpdateCheckExtensionResp.fromJson(data as Map), ); } Future updateExtension(String identity) { return _parse(() => _request('api/v1/extensions/$identity/update', method: 'POST'), null); } Future testWebhook(String url) { return _parse(() => _request('api/v1/webhook/test', method: 'POST', data: {'url': url}), null); } Future login(LoginReq loginReq) { return _parse(() => _request('api/web/login', method: 'POST', data: loginReq.toJson()), null); } Future> proxyRequest(String uri, {dynamic data, Options? options}) async { initDefault(); return _transport!.proxyRequest(uri, data: data, options: options); } String join(String path) { initDefault(); return _transport!.join(path); } Future forward( String path, { String method = 'GET', dynamic data, Map? queryParameters, }) async { return _request(path, method: method, data: data, queryParameters: queryParameters); }