diff --git a/lib/network/images.dart b/lib/network/images.dart index 29c9005..1c26f89 100644 --- a/lib/network/images.dart +++ b/lib/network/images.dart @@ -1,5 +1,6 @@ import 'dart:typed_data'; +import 'package:flutter_qjs/flutter_qjs.dart'; import 'package:venera/foundation/cache_manager.dart'; import 'package:venera/foundation/comic_source/comic_source.dart'; import 'package:venera/foundation/consts.dart'; @@ -83,61 +84,92 @@ class ImageDownloader { ); } + Future?> Function()? onLoadFailed; + var configs = {}; if (sourceKey != null) { var comicSource = ComicSource.find(sourceKey); configs = (await comicSource!.getImageLoadingConfig - ?.call(imageKey, cid, eid)) ?? {}; + ?.call(imageKey, cid, eid)) ?? + {}; } - configs['headers'] ??= { - 'user-agent': webUA, - }; + var retryLimit = 5; + while (true) { + try { + configs['headers'] ??= { + 'user-agent': webUA, + }; - var dio = AppDio(BaseOptions( - headers: configs['headers'], - method: configs['method'] ?? 'GET', - responseType: ResponseType.stream, - )); + if (configs['onLoadFailed'] is JSInvokable) { + onLoadFailed = () async { + dynamic result = configs['onLoadFailed'](); + if (result is Future) { + result = await result; + } + if (result is! Map) return null; + return result; + }; + } - var req = await dio.request(configs['url'] ?? imageKey, - data: configs['data']); - var stream = req.data?.stream ?? (throw "Error: Empty response body."); - int? expectedBytes = req.data!.contentLength; - if (expectedBytes == -1) { - expectedBytes = null; - } - var buffer = []; - await for (var data in stream) { - buffer.addAll(data); - if (expectedBytes != null) { + var dio = AppDio(BaseOptions( + headers: configs['headers'], + method: configs['method'] ?? 'GET', + responseType: ResponseType.stream, + )); + + var req = await dio.request(configs['url'] ?? imageKey, + data: configs['data']); + var stream = req.data?.stream ?? (throw "Error: Empty response body."); + int? expectedBytes = req.data!.contentLength; + if (expectedBytes == -1) { + expectedBytes = null; + } + var buffer = []; + await for (var data in stream) { + buffer.addAll(data); + if (expectedBytes != null) { + yield ImageDownloadProgress( + currentBytes: buffer.length, + totalBytes: expectedBytes, + ); + } + } + + if (configs['onResponse'] != null) { + buffer = configs['onResponse'](buffer); + } + + var data = Uint8List.fromList(buffer); + buffer.clear(); + + if (configs['modifyImage'] != null) { + var newData = await modifyImageWithScript( + data, + configs['modifyImage'], + ); + data = newData; + } + + await CacheManager().writeCache(cacheKey, data); yield ImageDownloadProgress( - currentBytes: buffer.length, - totalBytes: expectedBytes, + currentBytes: data.length, + totalBytes: data.length, + imageBytes: data, ); + return; + } catch (e) { + if(retryLimit < 0 || onLoadFailed == null) { + rethrow; + } + var newConfig = await onLoadFailed(); + onLoadFailed = null; + if(newConfig == null) { + rethrow; + } + configs = newConfig; + retryLimit--; } } - - if (configs['onResponse'] != null) { - buffer = configs['onResponse'](buffer); - } - - var data = Uint8List.fromList(buffer); - buffer.clear(); - - if (configs['modifyImage'] != null) { - var newData = await modifyImageWithScript( - data, - configs['modifyImage'], - ); - data = newData; - } - - await CacheManager().writeCache(cacheKey, data); - yield ImageDownloadProgress( - currentBytes: data.length, - totalBytes: data.length, - imageBytes: data, - ); } }