Added cronet_http & cupertino_http, remove unnecessary packages

This commit is contained in:
kodjomoustapha 2024-03-14 17:40:15 +01:00
parent 6fa0e8a89f
commit d168b6ddcd
30 changed files with 1148 additions and 971 deletions

1315
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -2,6 +2,7 @@ import 'dart:convert';
import 'package:dart_eval/dart_eval.dart';
import 'package:dart_eval/dart_eval_bridge.dart';
import 'package:dart_eval/stdlib/core.dart';
import 'package:flutter_qjs/flutter_qjs.dart';
import 'package:mangayomi/eval/dart/bridge/document.dart';
import 'package:mangayomi/eval/dart/bridge/filter.dart';
import 'package:mangayomi/eval/dart/bridge/m_manga.dart';
@ -16,7 +17,6 @@ import 'package:mangayomi/eval/dart/model/m_manga.dart';
import 'package:mangayomi/eval/dart/model/m_provider.dart';
import 'package:mangayomi/models/video.dart';
import 'package:mangayomi/modules/browse/extension/providers/extension_preferences_providers.dart';
import 'package:mangayomi/services/boa_js.dart';
class $MProvider extends MProvider with $Bridge<MProvider> {
static $MProvider $construct(
@ -748,8 +748,10 @@ class $MProvider extends MProvider with $Bridge<MProvider> {
$Value? $bridgeGet(String identifier) {
return switch (identifier) {
'evalJs' => $Function((_, __, List<$Value?> args) {
return $Future
.wrap(evalJs(args[0]!.$reified).then((value) => $String(value)));
final runtime = getJavascriptRuntime();
return $Future.wrap(runtime
.evaluateAsync(args[0]!.$reified)
.then((value) => $String(value.stringResult)));
}),
'getUrlWithoutDomain' => $Function((_, __, List<$Value?> args) {
final uri = Uri.parse(args[0]!.$value.replaceAll(' ', '%20'));

View file

@ -4,7 +4,9 @@ import 'package:desktop_webview_window/desktop_webview_window.dart';
import 'package:flex_color_scheme/flex_color_scheme.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:google_api_availability/google_api_availability.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:intl/date_symbol_data_local.dart';
import 'package:intl/intl.dart';
@ -25,6 +27,9 @@ import 'package:window_manager/window_manager.dart';
// Global instance of the Isar database.
late Isar isar;
GooglePlayServicesAvailability playStoreAvailability =
GooglePlayServicesAvailability.unknown;
/// Overrides the default HTTP client to allow all certificates
class MyHttpoverrides extends HttpOverrides {
@override
@ -57,6 +62,14 @@ void main(List<String> args) async {
isar = await StorageProvider().initDB(null, inspector: kDebugMode);
await StorageProvider().requestPermission();
GoogleFonts.aBeeZee();
if (Platform.isAndroid) {
try {
playStoreAvailability = await GoogleApiAvailability.instance
.checkGooglePlayServicesAvailability();
} on PlatformException {
playStoreAvailability = GooglePlayServicesAvailability.unknown;
}
}
// Start the app.
runApp(const ProviderScope(child: MyApp()));
}

View file

@ -107,7 +107,8 @@ class Source {
version = json['version'];
versionLast = json['versionLast'];
additionalParams = json['additionalParams'] ?? "";
sourceCodeLanguage = SourceCodeLanguage.values[json['sourceCodeLanguage']];
sourceCodeLanguage =
SourceCodeLanguage.values[json['sourceCodeLanguage'] ?? 0];
}
Map<String, dynamic> toJson() => {

View file

@ -1,6 +1,6 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:isar/isar.dart';
import 'package:mangayomi/eval/dart/model/source_preference.dart';
import 'package:mangayomi/main.dart';
import 'package:mangayomi/models/source.dart';
@ -8,6 +8,8 @@ import 'package:mangayomi/modules/browse/extension/providers/extension_preferenc
import 'package:mangayomi/modules/browse/extension/widgets/source_preference_widget.dart';
import 'package:mangayomi/providers/l10n_providers.dart';
import 'package:mangayomi/services/get_source_preference.dart';
import 'package:mangayomi/sources/source_test.dart';
import 'package:mangayomi/utils/cached_network.dart';
import 'package:mangayomi/utils/extensions/build_context_extensions.dart';
import 'package:mangayomi/utils/language.dart';
@ -22,6 +24,7 @@ class ExtensionDetail extends ConsumerStatefulWidget {
class _ExtensionDetailState extends ConsumerState<ExtensionDetail> {
late Source source = widget.source;
List<SourcePreference> sourcePreference = [];
bool _isLoading = true;
@override
void initState() {
getSourcePreferenceAsync(source: source).then((value) {
@ -30,10 +33,11 @@ class _ExtensionDetailState extends ConsumerState<ExtensionDetail> {
sourcePreference = value
.map((e) => getSourcePreferenceEntry(e.key!, source.id!))
.toList();
_isLoading = false;
});
}
});
print(source.id);
super.initState();
}
@ -56,20 +60,19 @@ class _ExtensionDetailState extends ConsumerState<ExtensionDetail> {
borderRadius: BorderRadius.circular(10)),
child: widget.source.iconUrl!.isEmpty
? const Icon(Icons.source_outlined, size: 140)
: CachedNetworkImage(
: cachedNetworkImage(
imageUrl: widget.source.iconUrl!,
fit: BoxFit.contain,
width: 140,
height: 140,
errorWidget: (context, url, error) {
return const SizedBox(
width: 140,
height: 140,
child: Center(
child: Icon(Icons.source_outlined, size: 140),
),
);
},
errorWidget: const SizedBox(
width: 140,
height: 140,
child: Center(
child: Icon(Icons.source_outlined, size: 140),
),
),
headers: {},
),
),
),
@ -176,11 +179,34 @@ class _ExtensionDetailState extends ConsumerState<ExtensionDetail> {
),
TextButton(
onPressed: () {
isar.writeTxnSync(() =>
final sourcePrefsIds = isar
.sourcePreferences
.filter()
.sourceIdEqualTo(source.id!)
.findAllSync()
.map((e) => e.id!)
.toList();
final sourcePrefsStringIds = isar
.sourcePreferenceStringValues
.filter()
.sourceIdEqualTo(source.id!)
.findAllSync()
.map((e) => e.id)
.toList();
isar.writeTxnSync(() {
if (!useTestSourceCode) {
isar.sources.putSync(widget.source
..sourceCode = ""
..isAdded = false
..isPinned = false));
..isPinned = false);
}
isar.sourcePreferences
.deleteAllSync(sourcePrefsIds);
isar.sourcePreferenceStringValues
.deleteAllSync(
sourcePrefsStringIds);
});
Navigator.pop(ctx);
Navigator.pop(context);
},
@ -198,8 +224,9 @@ class _ExtensionDetailState extends ConsumerState<ExtensionDetail> {
)),
),
),
SourcePreferenceWidget(
sourcePreference: sourcePreference, source: source)
if (!_isLoading)
SourcePreferenceWidget(
sourcePreference: sourcePreference, source: source)
],
),
),

View file

@ -1,4 +1,3 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
@ -7,6 +6,7 @@ import 'package:mangayomi/models/source.dart';
import 'package:mangayomi/modules/browse/extension/providers/fetch_anime_sources.dart';
import 'package:mangayomi/modules/browse/extension/providers/fetch_manga_sources.dart';
import 'package:mangayomi/providers/l10n_providers.dart';
import 'package:mangayomi/utils/cached_network.dart';
import 'package:mangayomi/utils/language.dart';
class ExtensionListTileWidget extends ConsumerStatefulWidget {
@ -68,20 +68,19 @@ class _ExtensionListTileWidgetState
borderRadius: BorderRadius.circular(5)),
child: widget.source.iconUrl!.isEmpty
? const Icon(Icons.source_outlined)
: CachedNetworkImage(
: cachedNetworkImage(
imageUrl: widget.source.iconUrl!,
fit: BoxFit.contain,
width: 37,
height: 37,
errorWidget: (context, url, error) {
return const SizedBox(
width: 37,
height: 37,
child: Center(
child: Icon(Icons.source_outlined),
),
);
},
errorWidget: const SizedBox(
width: 37,
height: 37,
child: Center(
child: Icon(Icons.source_outlined),
),
),
headers: {},
),
),
title: Text(widget.source.name!),

View file

@ -1,4 +1,3 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:grouped_list/grouped_list.dart';
@ -7,6 +6,7 @@ import 'package:mangayomi/main.dart';
import 'package:mangayomi/models/source.dart';
import 'package:mangayomi/modules/more/settings/browse/providers/browse_state_provider.dart';
import 'package:mangayomi/providers/l10n_providers.dart';
import 'package:mangayomi/utils/cached_network.dart';
import 'package:mangayomi/utils/language.dart';
class SourcesFilterScreen extends ConsumerWidget {
@ -86,20 +86,18 @@ class SourcesFilterScreen extends ConsumerWidget {
borderRadius: BorderRadius.circular(5)),
child: element.iconUrl!.isEmpty
? const Icon(Icons.source_outlined)
: CachedNetworkImage(
: cachedNetworkImage(
imageUrl: element.iconUrl!,
fit: BoxFit.contain,
width: 37,
height: 37,
errorWidget: (context, url, error) {
return const SizedBox(
width: 37,
height: 37,
child: Center(
child: Icon(Icons.source_outlined),
),
);
},
errorWidget: const SizedBox(
width: 37,
height: 37,
child: Center(
child: Icon(Icons.source_outlined),
),
),
),
),
onChanged: (bool? value) {

View file

@ -6,9 +6,9 @@ import 'package:mangayomi/main.dart';
import 'package:mangayomi/models/source.dart';
import 'package:mangayomi/providers/l10n_providers.dart';
import 'package:mangayomi/sources/source_test.dart';
import 'package:mangayomi/utils/cached_network.dart';
import 'package:mangayomi/utils/extensions/build_context_extensions.dart';
import 'package:mangayomi/utils/language.dart';
import 'package:cached_network_image/cached_network_image.dart';
class SourceListTile extends StatelessWidget {
final bool isManga;
@ -46,20 +46,18 @@ class SourceListTile extends StatelessWidget {
borderRadius: BorderRadius.circular(5)),
child: source.iconUrl!.isEmpty
? const Icon(Icons.source_outlined)
: CachedNetworkImage(
: cachedNetworkImage(
imageUrl: source.iconUrl!,
fit: BoxFit.contain,
width: 37,
height: 37,
errorWidget: (context, url, error) {
return const SizedBox(
width: 37,
height: 37,
child: Center(
child: Icon(Icons.source_outlined),
),
);
},
errorWidget: const SizedBox(
width: 37,
height: 37,
child: Center(
child: Icon(Icons.source_outlined),
),
),
),
),
subtitle: Row(

View file

@ -1,5 +1,4 @@
import 'dart:typed_data';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:isar/isar.dart';
@ -10,6 +9,7 @@ import 'package:mangayomi/modules/library/providers/library_state_provider.dart'
import 'package:mangayomi/modules/library/widgets/measure_widget_sync.dart';
import 'package:mangayomi/modules/manga/reader/providers/push_router.dart';
import 'package:mangayomi/models/manga.dart';
import 'package:mangayomi/modules/widgets/custom_extended_image_provider.dart';
import 'package:mangayomi/utils/extensions/build_context_extensions.dart';
import 'package:mangayomi/utils/constant.dart';
import 'package:mangayomi/utils/headers.dart';
@ -103,7 +103,7 @@ class _LibraryGridViewWidgetState extends State<LibraryGridViewWidget> {
image: entry.customCoverImage != null
? MemoryImage(entry.customCoverImage as Uint8List)
as ImageProvider
: CachedNetworkImageProvider(
: CustomExtendedNetworkImageProvider(
toImgUrl(entry.customCoverFromTracker ??
entry.imageUrl!),
headers: entry.isLocalArchive!

View file

@ -1,5 +1,4 @@
import 'dart:typed_data';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:isar/isar.dart';
@ -9,6 +8,7 @@ import 'package:mangayomi/modules/history/providers/isar_providers.dart';
import 'package:mangayomi/modules/library/providers/library_state_provider.dart';
import 'package:mangayomi/modules/manga/reader/providers/push_router.dart';
import 'package:mangayomi/models/manga.dart';
import 'package:mangayomi/modules/widgets/custom_extended_image_provider.dart';
import 'package:mangayomi/utils/extensions/build_context_extensions.dart';
import 'package:mangayomi/utils/constant.dart';
import 'package:mangayomi/utils/headers.dart';
@ -98,7 +98,7 @@ class LibraryListViewWidget extends StatelessWidget {
image: entry.customCoverImage != null
? MemoryImage(entry.customCoverImage
as Uint8List) as ImageProvider
: CachedNetworkImageProvider(
: CustomExtendedNetworkImageProvider(
toImgUrl(
entry.customCoverFromTracker ??
entry.imageUrl!),

View file

@ -1,6 +1,5 @@
import 'dart:io';
import 'dart:typed_data';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:draggable_menu/draggable_menu.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
@ -24,6 +23,7 @@ import 'package:mangayomi/modules/manga/reader/providers/reader_controller_provi
import 'package:mangayomi/modules/more/settings/appearance/providers/pure_black_dark_mode_state_provider.dart';
import 'package:mangayomi/modules/more/settings/track/widgets/track_listile.dart';
import 'package:mangayomi/modules/widgets/custom_draggable_tabbar.dart';
import 'package:mangayomi/modules/widgets/custom_extended_image_provider.dart';
import 'package:mangayomi/modules/widgets/draggable_scroll_bar.dart';
import 'package:mangayomi/providers/l10n_providers.dart';
import 'package:mangayomi/providers/storage_provider.dart';
@ -1366,7 +1366,7 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
final imageProvider = widget.manga!.customCoverImage != null
? MemoryImage(widget.manga!.customCoverImage as Uint8List)
as ImageProvider
: CachedNetworkImageProvider(
: CustomExtendedNetworkImageProvider(
toImgUrl(widget.manga!.customCoverFromTracker ??
widget.manga!.imageUrl!),
headers: widget.manga!.isLocalArchive!

View file

@ -1,10 +1,10 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:draggable_menu/draggable_menu.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:mangayomi/models/track.dart';
import 'package:mangayomi/models/track_search.dart';
import 'package:mangayomi/modules/manga/detail/providers/track_state_providers.dart';
import 'package:mangayomi/modules/widgets/custom_extended_image_provider.dart';
import 'package:mangayomi/modules/widgets/progress_center.dart';
import 'package:mangayomi/utils/extensions/build_context_extensions.dart';
@ -84,8 +84,9 @@ class _TrackerWidgetSearchState extends ConsumerState<TrackerWidgetSearch> {
height: 120,
width: 80,
fit: BoxFit.cover,
image: CachedNetworkImageProvider(
tracks![index].coverUrl!),
image:
CustomExtendedNetworkImageProvider(
tracks![index].coverUrl!),
),
),
const SizedBox(

View file

@ -7,6 +7,7 @@ import 'package:mangayomi/modules/manga/reader/providers/reader_controller_provi
import 'package:mangayomi/modules/manga/reader/reader_view.dart';
import 'package:mangayomi/modules/manga/reader/widgets/color_filter_widget.dart';
import 'package:mangayomi/modules/more/settings/reader/providers/reader_state_provider.dart';
import 'package:mangayomi/modules/widgets/custom_extended_image_provider.dart';
import 'package:mangayomi/utils/headers.dart';
import 'package:mangayomi/utils/reg_exp_matcher.dart';
@ -43,7 +44,7 @@ class ImageViewCenter extends ConsumerWidget {
? ExtendedMemoryImageProvider(archiveImage)
: ExtendedFileImageProvider(
File('${datas.path!.path}${padIndex(datas.index! + 1)}.jpg'))
: ExtendedNetworkImageProvider(datas.url!.trim().trimLeft().trimRight(),
: CustomExtendedNetworkImageProvider(datas.url!.trim().trimLeft().trimRight(),
cache: true,
cacheMaxAge: const Duration(days: 7),
headers: ref.watch(headersProvider(

View file

@ -7,6 +7,7 @@ import 'package:mangayomi/modules/manga/reader/providers/reader_controller_provi
import 'package:mangayomi/modules/manga/reader/reader_view.dart';
import 'package:mangayomi/modules/manga/reader/widgets/color_filter_widget.dart';
import 'package:mangayomi/modules/more/settings/reader/providers/reader_state_provider.dart';
import 'package:mangayomi/modules/widgets/custom_extended_image_provider.dart';
import 'package:mangayomi/providers/l10n_providers.dart';
import 'package:mangayomi/utils/extensions/build_context_extensions.dart';
import 'package:mangayomi/utils/headers.dart';
@ -42,7 +43,7 @@ class ImageViewVertical extends ConsumerWidget {
? ExtendedMemoryImageProvider(archiveImage)
: ExtendedFileImageProvider(
File('${datas.path!.path}${padIndex(datas.index! + 1)}.jpg'))
: ExtendedNetworkImageProvider(datas.url!.trim().trimLeft().trimRight(),
: CustomExtendedNetworkImageProvider(datas.url!.trim().trimLeft().trimRight(),
cache: true,
cacheMaxAge: const Duration(days: 7),
headers: ref.watch(headersProvider(

View file

@ -0,0 +1,364 @@
// ignore_for_file: non_nullable_equals_parameter, depend_on_referenced_packages, implementation_imports
import 'dart:async';
import 'dart:io';
import 'dart:ui' as ui show Codec;
import 'package:extended_image_library/src/extended_image_provider.dart';
import 'package:extended_image_library/src/platform.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:http_client_helper/http_client_helper.dart';
import 'package:mangayomi/services/http/interceptor.dart';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'package:extended_image_library/src/network/extended_network_image_provider.dart'
as image_provider;
class CustomExtendedNetworkImageProvider
extends ImageProvider<image_provider.ExtendedNetworkImageProvider>
with ExtendedImageProvider<image_provider.ExtendedNetworkImageProvider>
implements image_provider.ExtendedNetworkImageProvider {
/// Creates an object that fetches the image at the given URL.
///
/// The arguments must not be null.
CustomExtendedNetworkImageProvider(
this.url, {
this.scale = 1.0,
this.headers,
this.cache = true,
this.retries = 3,
this.timeLimit,
this.timeRetry = const Duration(milliseconds: 100),
this.cacheKey,
this.printError = true,
this.cacheRawData = false,
this.cancelToken,
this.imageCacheName,
this.cacheMaxAge = const Duration(days: 30),
});
/// The name of [ImageCache], you can define custom [ImageCache] to store this provider.
@override
final String? imageCacheName;
/// Whether cache raw data if you need to get raw data directly.
/// For example, we need raw image data to edit,
/// but [ui.Image.toByteData()] is very slow. So we cache the image
/// data here.
@override
final bool cacheRawData;
/// The time limit to request image
@override
final Duration? timeLimit;
/// The time to retry to request
@override
final int retries;
/// The time duration to retry to request
@override
final Duration timeRetry;
/// Whether cache image to local
@override
final bool cache;
/// The URL from which the image will be fetched.
@override
final String url;
/// The scale to place in the [ImageInfo] object of the image.
@override
final double scale;
/// The HTTP headers that will be used with [HttpClient.get] to fetch image from network.
@override
final Map<String, String>? headers;
/// The token to cancel network request
@override
final CancellationToken? cancelToken;
/// Custom cache key
@override
final String? cacheKey;
/// print error
@override
final bool printError;
/// The max duration to cahce image.
/// After this time the cache is expired and the image is reloaded.
@override
final Duration? cacheMaxAge;
@override
ImageStreamCompleter loadImage(
image_provider.ExtendedNetworkImageProvider key,
ImageDecoderCallback decode,
) {
// Ownership of this controller is handed off to [_loadAsync]; it is that
// method's responsibility to close the controller's stream when the image
// has been loaded or an error is thrown.
final StreamController<ImageChunkEvent> chunkEvents =
StreamController<ImageChunkEvent>();
return MultiFrameImageStreamCompleter(
codec: _loadAsync(
key as CustomExtendedNetworkImageProvider,
chunkEvents,
decode,
),
scale: key.scale,
chunkEvents: chunkEvents.stream,
debugLabel: key.url,
informationCollector: () {
return <DiagnosticsNode>[
DiagnosticsProperty<ImageProvider>('Image provider', this),
DiagnosticsProperty<image_provider.ExtendedNetworkImageProvider>(
'Image key', key),
];
},
);
}
@override
Future<CustomExtendedNetworkImageProvider> obtainKey(
ImageConfiguration configuration) {
return SynchronousFuture<CustomExtendedNetworkImageProvider>(this);
}
Future<ui.Codec> _loadAsync(
CustomExtendedNetworkImageProvider key,
StreamController<ImageChunkEvent> chunkEvents,
ImageDecoderCallback decode,
) async {
assert(key == this);
final String md5Key = cacheKey ?? keyToMd5(key.url);
ui.Codec? result;
if (cache) {
try {
final Uint8List? data = await _loadCache(
key,
chunkEvents,
md5Key,
);
if (data != null) {
result = await instantiateImageCodec(data, decode);
}
} catch (e) {
if (kDebugMode) {
print(e);
}
}
}
if (result == null) {
try {
final Uint8List? data = await _loadNetwork(
key,
chunkEvents,
);
if (data != null) {
result = await instantiateImageCodec(data, decode);
}
} catch (e) {
if (kDebugMode) {
print(e);
}
}
}
//Failed to load
if (result == null) {
//result = await ui.instantiateImageCodec(kTransparentImage);
return Future<ui.Codec>.error(StateError('Failed to load $url.'));
}
return result;
}
/// Get the image from cache folder.
Future<Uint8List?> _loadCache(
CustomExtendedNetworkImageProvider key,
StreamController<ImageChunkEvent>? chunkEvents,
String md5Key,
) async {
final Directory cacheImagesDirectory = Directory(
join((await getTemporaryDirectory()).path, cacheImageFolderName));
Uint8List? data;
// exist, try to find cache image file
if (cacheImagesDirectory.existsSync()) {
final File cacheFlie = File(join(cacheImagesDirectory.path, md5Key));
if (cacheFlie.existsSync()) {
if (key.cacheMaxAge != null) {
final DateTime now = DateTime.now();
final FileStat fs = cacheFlie.statSync();
if (now.subtract(key.cacheMaxAge!).isAfter(fs.changed)) {
cacheFlie.deleteSync(recursive: true);
} else {
data = await cacheFlie.readAsBytes();
}
} else {
data = await cacheFlie.readAsBytes();
}
}
}
// create folder
else {
await cacheImagesDirectory.create();
}
// load from network
if (data == null) {
data = await _loadNetwork(
key,
chunkEvents,
);
if (data != null) {
// cache image file
await File(join(cacheImagesDirectory.path, md5Key)).writeAsBytes(data);
}
}
return data;
}
/// Get the image from network.
Future<Uint8List?> _loadNetwork(
CustomExtendedNetworkImageProvider key,
StreamController<ImageChunkEvent>? chunkEvents,
) async {
try {
final Uri resolved = Uri.base.resolve(key.url);
final StreamedResponse? response = await _tryGetResponse(resolved);
List<int> bytes = [];
final int total = response!.contentLength ?? 0;
if (response.statusCode == HttpStatus.ok) {
int received = 0;
response.stream.asBroadcastStream();
await for (var chunk in response.stream) {
bytes.addAll(chunk);
try {
received += chunk.length;
if (chunkEvents != null) {}
chunkEvents!.add(ImageChunkEvent(
cumulativeBytesLoaded: received, expectedTotalBytes: total));
} catch (e) {
if (kDebugMode) {
print(e);
}
}
}
} else {
return null;
}
if (bytes.isEmpty) {
return Future<Uint8List>.error(
StateError('NetworkImage is an empty file: $resolved'));
}
return Uint8List.fromList(bytes);
} on OperationCanceledError catch (_) {
if (kDebugMode) {
print('User cancel request $url.');
}
return Future<Uint8List>.error(StateError('User cancel request $url.'));
} catch (e) {
if (kDebugMode) {
print(e);
}
} finally {
await chunkEvents?.close();
}
return null;
}
Future<StreamedResponse> _getResponse(Uri resolved) async {
var request = Request('GET', resolved);
request.headers.addAll(headers ?? {});
StreamedResponse response = await MInterceptor.init().send(request);
return response;
}
// Http get with cancel, delay try again
Future<StreamedResponse?> _tryGetResponse(
Uri resolved,
) async {
cancelToken?.throwIfCancellationRequested();
return await RetryHelper.tryRun<StreamedResponse>(
() {
return CancellationTokenSource.register(
cancelToken,
_getResponse(resolved),
);
},
cancelToken: cancelToken,
timeRetry: timeRetry,
retries: retries,
);
}
@override
bool operator ==(dynamic other) {
if (other.runtimeType != runtimeType) {
return false;
}
return other is CustomExtendedNetworkImageProvider &&
url == other.url &&
scale == other.scale &&
cacheRawData == other.cacheRawData &&
timeLimit == other.timeLimit &&
cancelToken == other.cancelToken &&
timeRetry == other.timeRetry &&
cache == other.cache &&
cacheKey == other.cacheKey &&
//headers == other.headers &&
retries == other.retries &&
imageCacheName == other.imageCacheName &&
cacheMaxAge == other.cacheMaxAge;
}
@override
int get hashCode => Object.hash(
url,
scale,
cacheRawData,
timeLimit,
cancelToken,
timeRetry,
cache,
cacheKey,
//headers,
retries,
imageCacheName,
cacheMaxAge,
);
@override
String toString() => '$runtimeType("$url", scale: $scale)';
@override
/// Get network image data from cached
Future<Uint8List?> getNetworkImageData({
StreamController<ImageChunkEvent>? chunkEvents,
}) async {
final String uId = cacheKey ?? keyToMd5(url);
if (cache) {
return await _loadCache(
this,
chunkEvents,
uId,
);
}
return await _loadNetwork(
this,
chunkEvents,
);
}
}

View file

@ -1,5 +1,4 @@
import 'dart:typed_data';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
@ -10,6 +9,7 @@ import 'package:mangayomi/models/manga.dart';
import 'package:mangayomi/models/settings.dart';
import 'package:mangayomi/models/source.dart';
import 'package:mangayomi/modules/manga/detail/manga_detail_main.dart';
import 'package:mangayomi/modules/widgets/custom_extended_image_provider.dart';
import 'package:mangayomi/router/router.dart';
import 'package:mangayomi/utils/extensions/build_context_extensions.dart';
import 'package:mangayomi/utils/constant.dart';
@ -52,7 +52,7 @@ class MangaImageCardWidget extends ConsumerWidget {
? MemoryImage(
snapshot.data!.first.customCoverImage as Uint8List)
as ImageProvider
: CachedNetworkImageProvider(
: CustomExtendedNetworkImageProvider(
toImgUrl(hasData
? snapshot.data!.first.customCoverFromTracker ??
snapshot.data!.first.imageUrl ??
@ -60,7 +60,8 @@ class MangaImageCardWidget extends ConsumerWidget {
: getMangaDetail!.imageUrl!),
headers: ref.watch(headersProvider(
source: source.name!, lang: source.lang!)),
),
cache: true,
cacheMaxAge: const Duration(days: 7)),
onTap: () {
pushToMangaReaderDetail(
context: context,
@ -125,7 +126,7 @@ class MangaImageCardListTileWidget extends ConsumerWidget {
final image = hasData && snapshot.data!.first.customCoverImage != null
? MemoryImage(snapshot.data!.first.customCoverImage as Uint8List)
as ImageProvider
: CachedNetworkImageProvider(
: CustomExtendedNetworkImageProvider(
toImgUrl(hasData
? snapshot.data!.first.customCoverFromTracker ??
snapshot.data!.first.imageUrl ??

View file

@ -1,24 +0,0 @@
import 'package:mangayomi/messages/boa_js.pb.dart';
import 'dart:async';
int nextId = 0;
Future<String> evalJs(String script) async {
final currentId = nextId;
nextId++;
final completer = Completer<String>();
BoaInput(
interactionId: currentId,
codeScript: script,
).sendSignalToRust(null);
final stream = BoaOutput.rustSignalStream;
final subscription = stream.listen((rustSignal) {
if (rustSignal.message.interactionId == currentId) {
completer.complete(rustSignal.message.response);
}
});
final response = await completer.future;
subscription.cancel();
return response;
}

View file

@ -1,4 +1,6 @@
import 'package:cupertino_http/cupertino_http.dart';
import 'package:flutter/foundation.dart';
import 'package:google_api_availability/google_api_availability.dart';
import 'package:http_interceptor/http_interceptor.dart';
import 'package:mangayomi/eval/dart/model/m_bridge.dart';
import 'dart:async';
@ -8,16 +10,39 @@ import 'package:mangayomi/main.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart'
as flutter_inappwebview;
import 'package:mangayomi/models/settings.dart';
import 'package:cronet_http/cronet_http.dart';
import 'package:http/io_client.dart';
class MInterceptor {
static final flutter_inappwebview.CookieManager _cookieManager =
flutter_inappwebview.CookieManager.instance();
MInterceptor();
static Client httpClient() {
if (Platform.isAndroid) {
if (playStoreAvailability == GooglePlayServicesAvailability.unknown) {
return IOClient(HttpClient());
}
final engine = CronetEngine.build(
enablePublicKeyPinningBypassForLocalTrustAnchors: true,
enableHttp2: true,
enableBrotli: true,
cacheMode: CacheMode.memory,
cacheMaxSize: 5 * 1024 * 1024);
return CronetClient.fromCronetEngine(engine);
}
if (Platform.isIOS || Platform.isMacOS) {
final config = URLSessionConfiguration.ephemeralSessionConfiguration()
..cache = URLCache.withCapacity(memoryCapacity: 5 * 1024 * 1024);
return CupertinoClient.fromSessionConfiguration(config);
}
return IOClient(HttpClient());
}
static InterceptedClient init(
{MSource? source, Map<String, dynamic>? reqcopyWith}) {
return InterceptedClient.build(
client: httpClient(),
interceptors: [MCookieManager(reqcopyWith), LoggerInterceptor()]);
}

View file

@ -14,7 +14,7 @@ import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'torrent_server.g.dart';
class MTorrentServer {
final http = MInterceptor.init();
final http = Client();
Future<bool> removeTorrent(String? inforHash) async {
if (inforHash == null || inforHash.isEmpty) return false;
try {
@ -44,7 +44,8 @@ class MTorrentServer {
Future<String> getInfohash(String url) async {
try {
final torrentByte = (await http.get(Uri.parse(url))).bodyBytes;
final torrentByte =
(await MInterceptor.init().get(Uri.parse(url))).bodyBytes;
var request =
MultipartRequest('POST', Uri.parse('$_baseUrl/torrent/add'));

View file

@ -1,33 +1,29 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/foundation.dart';
import 'package:extended_image/extended_image.dart';
import 'package:flutter/material.dart';
import 'package:mangayomi/modules/widgets/custom_extended_image_provider.dart';
Widget cachedNetworkImage(
{required Map<String, String>? headers,
{Map<String, String>? headers,
required String imageUrl,
required double? width,
required double? height,
required BoxFit? fit,
AlignmentGeometry? alignment}) {
if (kIsWeb) {
return Image.network(
imageUrl,
width: width,
height: height,
fit: fit,
alignment: alignment ?? Alignment.center,
);
} else {
return CachedNetworkImage(
httpHeaders: headers ?? {},
imageUrl: imageUrl,
fit: fit,
width: width,
height: height,
errorWidget: (context, url, error) => const Icon(
Icons.error,
size: 50,
),
);
}
AlignmentGeometry? alignment,
Widget errorWidget = const Icon(Icons.error, size: 50)}) {
return ExtendedImage(
image: CustomExtendedNetworkImageProvider(imageUrl, headers: headers),
width: width,
height: height,
fit: fit,
filterQuality: FilterQuality.medium,
enableMemoryCache: true,
mode: ExtendedImageMode.gesture,
handleLoadingProgress: true,
loadStateChanged: (state) {
if (state.extendedImageLoadState == LoadState.failed) {
return errorWidget;
}
return null;
},
);
}

View file

@ -14,6 +14,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
jni
media_kit_native_event_loop
rinf
)

View file

@ -16,7 +16,6 @@ import path_provider_foundation
import screen_brightness_macos
import screen_retriever
import share_plus
import sqflite
import url_launcher_macos
import wakelock_plus
import window_manager
@ -34,7 +33,6 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
ScreenBrightnessMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenBrightnessMacosPlugin"))
ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin"))
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
WakelockPlusMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockPlusMacosPlugin"))
WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin"))

View file

@ -1,14 +0,0 @@
syntax = "proto3";
package boa_js;
// [RINF:DART-SIGNAL]
message BoaInput {
int32 interaction_id = 1;
string code_script = 2;
}
// [RINF:RUST-SIGNAL]
message BoaOutput {
int32 interaction_id = 1;
string response = 2;
}

View file

@ -12,10 +12,9 @@ edition = "2021"
crate-type = ["lib", "cdylib", "staticlib"]
[dependencies]
rinf = "6.4.0"
rinf = "6.6.3"
allo-isolate = "0.1.24"
wasm-bindgen = "0.2.90"
prost = "0.12.3"
tokio_with_wasm = "0.4.0"
image = "0.24.8"
boa_engine = "0.17.3"
tokio_with_wasm = "0.4.3"
image = "0.25.0"

View file

@ -1,20 +0,0 @@
use crate::messages;
use boa_engine::{Context, Source};
pub async fn eval_js() {
use messages::boa_js::*;
let mut receiver = BoaInput::get_dart_signal_receiver();
while let Some(dart_signal) = receiver.recv().await {
let mut context = Context::default();
let code_script = dart_signal.message.code_script;
BoaOutput {
interaction_id: dart_signal.message.interaction_id,
response: match context.eval(Source::from_bytes(code_script.as_bytes())) {
Ok(res) => res.to_string(&mut context).unwrap().to_std_string_escaped(),
Err(_e) => "error".to_string(),
},
}
.send_signal_to_dart(None);
}
}

View file

@ -20,7 +20,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.*/
use crate::messages;
use image::{DynamicImage, GenericImageView, ImageOutputFormat, ImageResult, Rgba};
use image::{DynamicImage, GenericImageView, ImageResult, Rgba};
use std::io::Cursor;
pub struct Point {
@ -145,7 +145,7 @@ pub async fn start_croping() {
let image = dart_signal.blob.unwrap();
let res = crop_image(image);
let mut image_data: Vec<u8> = Vec::new();
res.write_to(&mut Cursor::new(&mut image_data), ImageOutputFormat::Png)
res.write_to(&mut Cursor::new(&mut image_data), image::ImageFormat::Png)
.unwrap();
CropBordersOutput {
interaction_id: dart_signal.message.interaction_id,

View file

@ -1,6 +1,5 @@
use tokio_with_wasm::tokio;
mod boa_js;
mod imagecrop;
mod messages;
@ -11,6 +10,5 @@ rinf::write_interface!();
async fn main() {
// Repeat `crate::spawn` anywhere in your code
// if more concurrent tasks are needed.
tokio::spawn(boa_js::eval_js());
tokio::spawn(imagecrop::start_croping());
}

View file

@ -146,30 +146,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "8.9.0"
cached_network_image:
dependency: "direct main"
description:
name: cached_network_image
sha256: "28ea9690a8207179c319965c13cd8df184d5ee721ae2ce60f398ced1219cea1f"
url: "https://pub.dev"
source: hosted
version: "3.3.1"
cached_network_image_platform_interface:
dependency: transitive
description:
name: cached_network_image_platform_interface
sha256: "9e90e78ae72caa874a323d78fa6301b3fb8fa7ea76a8f96dc5b5bf79f283bf2f"
url: "https://pub.dev"
source: hosted
version: "4.0.0"
cached_network_image_web:
dependency: transitive
description:
name: cached_network_image_web
sha256: "42a835caa27c220d1294311ac409a43361088625a4f23c820b006dd9bffb3316"
url: "https://pub.dev"
source: hosted
version: "1.1.1"
characters:
dependency: transitive
description:
@ -234,6 +210,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.1.1"
cronet_http:
dependency: "direct main"
description:
name: cronet_http
sha256: a38ffbb49797ae1e0f774fc884d128a7023986b14a3165e1e39841f9f7e22137
url: "https://pub.dev"
source: hosted
version: "1.1.1"
cross_file:
dependency: transitive
description:
@ -258,6 +242,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.0"
cupertino_http:
dependency: "direct main"
description:
name: cupertino_http
sha256: "0e3ed481280ad41d42d4881d4b6f89c083c17aa010205c9381ecb4c7ea7bdf1f"
url: "https://pub.dev"
source: hosted
version: "1.3.0"
cupertino_icons:
dependency: "direct main"
description:
@ -462,14 +454,6 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_cache_manager:
dependency: transitive
description:
name: flutter_cache_manager
sha256: "8207f27539deb83732fdda03e259349046a39a4c767269285f449ade355d54ba"
url: "https://pub.dev"
source: hosted
version: "3.3.1"
flutter_inappwebview:
dependency: "direct main"
description:
@ -599,6 +583,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "12.1.3"
google_api_availability:
dependency: "direct main"
description:
name: google_api_availability
sha256: "3e9548cfd991d983d11425a2436d5bd957d048c279cc9e145ffe3f36fd847385"
url: "https://pub.dev"
source: hosted
version: "5.0.0"
google_api_availability_android:
dependency: transitive
description:
name: google_api_availability_android
sha256: d95429ae78083585c312de2c6578085e7d53d100a94656d691bce0bb0ce435be
url: "https://pub.dev"
source: hosted
version: "1.0.1"
google_api_availability_platform_interface:
dependency: transitive
description:
name: google_api_availability_platform_interface
sha256: "65b7da62fe5b582bb3d508628ad827d36d890710ea274766a992a56fa5420da6"
url: "https://pub.dev"
source: hosted
version: "1.0.1"
google_fonts:
dependency: "direct main"
description:
@ -632,7 +640,7 @@ packages:
source: hosted
version: "0.15.4"
http:
dependency: "direct overridden"
dependency: "direct main"
description:
name: http
sha256: a2bbf9d017fcced29139daa8ed2bba4ece450ab222871df93ca9eec6f80c34ba
@ -735,6 +743,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.1.0+1"
jni:
dependency: transitive
description:
name: jni
sha256: "0d88790bdf7e298aa65a9094c62b58ea231169a2deb84f23defc7d7955885b43"
url: "https://pub.dev"
source: hosted
version: "0.7.2"
js:
dependency: transitive
description:
@ -936,14 +952,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.2"
octo_image:
dependency: transitive
description:
name: octo_image
sha256: "45b40f99622f11901238e18d48f5f12ea36426d8eced9f4cbf58479c7aa2430d"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
package_config:
dependency: transitive
description:
@ -1349,22 +1357,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "7.0.0"
sqflite:
dependency: transitive
description:
name: sqflite
sha256: a9016f495c927cb90557c909ff26a6d92d9bd54fc42ba92e19d4e79d61e798c6
url: "https://pub.dev"
source: hosted
version: "2.3.2"
sqflite_common:
dependency: transitive
description:
name: sqflite_common
sha256: "28d8c66baee4968519fb8bd6cdbedad982d6e53359091f0b74544a9f32ec72d5"
url: "https://pub.dev"
source: hosted
version: "2.5.3"
stack_trace:
dependency: transitive
description:

View file

@ -14,7 +14,6 @@ dependencies:
sdk: flutter
go_router: ^12.1.3
flutter_riverpod: ^2.4.10
cached_network_image: ^3.3.1
riverpod_annotation: ^2.3.3
html: ^0.15.4
font_awesome_flutter: ^10.6.0
@ -75,6 +74,10 @@ dependencies:
git:
url: https://github.com/kodjodevf/flutter_qjs.git
ref: main
cronet_http: ^1.1.1
cupertino_http: ^1.3.0
http: ^1.2.0
google_api_availability: ^5.0.0
dependency_overrides:

View file

@ -18,6 +18,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
jni
media_kit_native_event_loop
rinf
)