import 'dart:async' show Future, StreamController; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_qjs/flutter_qjs.dart'; import 'package:venera/foundation/js_engine.dart'; import 'package:venera/network/images.dart'; import 'package:venera/utils/io.dart'; import 'base_image_provider.dart'; import 'reader_image.dart' as image_provider; import 'package:venera/foundation/appdata.dart'; class ReaderImageProvider extends BaseImageProvider { /// Image provider for normal image. const ReaderImageProvider(this.imageKey, this.sourceKey, this.cid, this.eid); final String imageKey; final String? sourceKey; final String cid; final String eid; @override Future load(StreamController chunkEvents) async { Uint8List? imageBytes; if (imageKey.startsWith('file://')) { var file = File(imageKey); if (await file.exists()) { imageBytes = await file.readAsBytes(); } else { throw "Error: File not found."; } } else { await for (var event in ImageDownloader.loadComicImage(imageKey, sourceKey, cid, eid)) { chunkEvents.add(ImageChunkEvent( cumulativeBytesLoaded: event.currentBytes, expectedTotalBytes: event.totalBytes, )); if (event.imageBytes != null) { imageBytes = event.imageBytes; break; } } } if (imageBytes == null) { throw "Error: Empty response body."; } if (appdata.settings['enableCustomImageProcessing']) { var script = appdata.settings['customImageProcessing'].toString(); if (!script.contains('async function processImage')) { return imageBytes; } var func = JsEngine().runCode(''' (() => { $script return processImage; })() '''); if (func is JSInvokable) { var result = await func.invoke([imageBytes, cid, eid]); func.free(); if (result is Uint8List) { return result; } } } return imageBytes; } @override Future obtainKey(ImageConfiguration configuration) { return SynchronousFuture(this); } @override String get key => "$imageKey@$sourceKey@$cid@$eid"; @override bool get enableResize => true; }