fix download problem

This commit is contained in:
kodjomoustapha 2024-08-05 15:44:59 +01:00
parent 01af13d5ee
commit ba8ac10cc2
13 changed files with 181 additions and 128 deletions

View file

@ -15,7 +15,6 @@ import 'package:mangayomi/modules/manga/detail/manga_detail_view.dart';
import 'package:mangayomi/modules/manga/detail/providers/state_providers.dart';
import 'package:mangayomi/modules/manga/detail/widgets/chapter_filter_list_tile_widget.dart';
import 'package:mangayomi/modules/more/providers/incognito_mode_state_provider.dart';
import 'package:mangayomi/modules/widgets/progress_center.dart';
import 'package:mangayomi/utils/extensions/chapter.dart';
class MangaDetailsView extends ConsumerStatefulWidget {
@ -59,6 +58,9 @@ class _MangaDetailsViewState extends ConsumerState<MangaDetailsView> {
(q) => q.isMangaEqualTo(widget.manga.isManga!)))
.watch(fireImmediately: true),
builder: (context, snapshot) {
final isFr =
ref.watch(l10nLocaleStateProvider).languageCode ==
"fr";
if (snapshot.hasData && snapshot.data!.isNotEmpty) {
final incognitoMode =
ref.watch(incognitoModeStateProvider);
@ -68,9 +70,7 @@ class _MangaDetailsViewState extends ConsumerState<MangaDetailsView> {
.toList()
.reversed
.toList();
final isFr =
ref.watch(l10nLocaleStateProvider).languageCode ==
"fr";
if (entries.isNotEmpty && !incognitoMode) {
final chap = entries.first.chapter.value!;
return CustomFloatingActionBtn(
@ -98,7 +98,20 @@ class _MangaDetailsViewState extends ConsumerState<MangaDetailsView> {
width: isFr ? 130 : 90,
);
}
return const ProgressCenter();
return CustomFloatingActionBtn(
isExtended: !isExtended,
label: l10n.read,
onPressed: () {
widget.manga.chapters
.toList()
.reversed
.toList()
.last
.pushToReaderView(context);
},
textWidth: isFr ? 80 : 40,
width: isFr ? 130 : 90,
);
},
)
: Container();

View file

@ -1,6 +1,7 @@
import 'dart:convert';
import 'dart:io';
import 'package:mangayomi/models/page.dart';
import 'package:mangayomi/modules/more/settings/advanced/providers/native_http_client.dart';
import 'package:mangayomi/services/background_downloader/background_downloader.dart';
import 'package:isar/isar.dart';
import 'package:mangayomi/main.dart';
@ -20,8 +21,6 @@ import 'package:path_provider/path_provider.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'download_provider.g.dart';
FileDownloader _mangaFileDownloader = FileDownloader(isAnime: false);
FileDownloader _animeFileDownloader = FileDownloader(isAnime: true);
@riverpod
Future<List<PageUrl>> downloadChapter(
DownloadChapterRef ref, {
@ -251,7 +250,10 @@ Future<List<PageUrl>> downloadChapter(
});
} else {
if (isManga) {
await _mangaFileDownloader.downloadBatch(
await FileDownloader(
useNativeHttpClient:
ref.watch(useNativeHttpClientStateProvider))
.downloadBatch(
tasks,
batchProgressCallback: (succeeded, failed) async {
if (succeeded == tasks.length) {
@ -307,7 +309,7 @@ Future<List<PageUrl>> downloadChapter(
},
);
} else {
await _animeFileDownloader.download(
await FileDownloader(useNativeHttpClient: false).download(
tasks.first,
onProgress: (progress) async {
bool isEmpty = isar.downloads

View file

@ -6,8 +6,8 @@ import 'dart:math';
import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart';
import 'package:logging/logging.dart';
import 'package:mangayomi/services/background_downloader/src/desktop/desktop_downloader.dart';
import 'package:mangayomi/services/background_downloader/src/desktop/desktop_downloader_anime.dart';
import 'package:mangayomi/services/background_downloader/src/desktop/desktop_downloader_http_client.dart';
import 'package:mangayomi/services/background_downloader/src/desktop/desktop_downloader_native_http_client.dart.dart';
import 'database.dart';
import 'exceptions.dart';
import 'models.dart';
@ -100,9 +100,11 @@ abstract base class BaseDownloader {
BaseDownloader();
factory BaseDownloader.instance(
PersistentStorage persistentStorage, Database database, bool isAnime) {
final instance = isAnime ? DesktopDownloaderAnime() : DesktopDownloader();
factory BaseDownloader.instance(PersistentStorage persistentStorage,
Database database, bool useNativeHttpClient) {
final instance = !useNativeHttpClient
? DesktopDownloaderHttpClient()
: DesktopDownloaderNativeHttpClient();
instance._storage = persistentStorage;
instance.database = database;
unawaited(instance.initialize());

View file

@ -2,9 +2,10 @@ import 'dart:async';
import 'dart:isolate';
import 'package:http/http.dart' as http;
import 'package:mangayomi/services/background_downloader/src/desktop/desktop_downloader_http_client.dart';
import 'package:mangayomi/services/background_downloader/src/desktop/desktop_downloader_native_http_client.dart.dart'
as desktop_downloader_native;
import 'package:mangayomi/services/background_downloader/background_downloader.dart';
import 'desktop_downloader.dart';
import 'download_isolate.dart';
import 'isolate.dart';
@ -12,8 +13,11 @@ import 'isolate.dart';
///
/// Sends updates via the [sendPort] and can be commanded to cancel via
/// the [messagesToIsolate] queue
Future<void> doDataTask(DataTask task, SendPort sendPort) async {
final client = DesktopDownloader.httpClient;
Future<void> doDataTask(
DataTask task, SendPort sendPort, bool useNativeHttpClient) async {
final client = useNativeHttpClient
? desktop_downloader_native.DesktopDownloaderNativeHttpClient.httpClient
: DesktopDownloaderHttpClient.httpClient;
var request = http.Request(task.httpRequestMethod, Uri.parse(task.url));
request.headers.addAll(task.headers);
if (task.post is String) {

View file

@ -9,7 +9,6 @@ import 'package:collection/collection.dart';
import 'package:flutter/services.dart';
import 'package:http/io_client.dart';
import 'package:logging/logging.dart';
import 'package:mangayomi/services/http/m_client.dart';
import 'package:path/path.dart' as path;
import 'package:path_provider/path_provider.dart';
import '../base_downloader.dart';
@ -28,26 +27,29 @@ const okResponses = [200, 201, 202, 203, 204, 205, 206];
/// On desktop (MacOS, Linux, Windows) the download and upload are implemented
/// in Dart, as there is no native platform equivalent of URLSession or
/// WorkManager as there is on iOS and Android
final class DesktopDownloaderAnime extends BaseDownloader {
static final _log = Logger('DesktopDownloaderAnime');
final class DesktopDownloaderHttpClient extends BaseDownloader {
static final _log = Logger('DesktopDownloaderHttpClient');
static const unlimited = 1 << 20;
var maxConcurrent = 10;
var maxConcurrentByHost = unlimited;
var maxConcurrentByGroup = unlimited;
static final DesktopDownloaderAnime _singleton = DesktopDownloaderAnime._internal();
static final DesktopDownloaderHttpClient _singleton =
DesktopDownloaderHttpClient._internal();
final _queue = PriorityQueue<Task>();
final _running = Queue<Task>(); // subset that is running
final _resume = <Task>{};
final _isolateSendPorts =
<Task, SendPort?>{}; // isolate SendPort for running task
static var httpClient = MClient.httpClient();
static var httpClient = IOClient(HttpClient()
..badCertificateCallback =
(X509Certificate cert, String host, int port) => true);
static Duration? _requestTimeout;
static var _proxy = <String, dynamic>{}; // 'address' and 'port'
static var _bypassTLSCertificateValidation = false;
factory DesktopDownloaderAnime() => _singleton;
factory DesktopDownloaderHttpClient() => _singleton;
DesktopDownloaderAnime._internal();
DesktopDownloaderHttpClient._internal();
@override
Future<bool> enqueue(Task task) async {
@ -140,7 +142,7 @@ final class DesktopDownloaderAnime extends BaseDownloader {
return;
}
log.finer('${isResume ? "Resuming" : "Starting"} taskId ${task.taskId}');
await Isolate.spawn(doTask, (rootIsolateToken, receivePort.sendPort),
await Isolate.spawn(doTask, (rootIsolateToken, receivePort.sendPort, false),
onError: errorPort.sendPort);
final messagesFromIsolate = StreamQueue<dynamic>(receivePort);
final sendPort = await messagesFromIsolate.next as SendPort;

View file

@ -28,26 +28,27 @@ const okResponses = [200, 201, 202, 203, 204, 205, 206];
/// On desktop (MacOS, Linux, Windows) the download and upload are implemented
/// in Dart, as there is no native platform equivalent of URLSession or
/// WorkManager as there is on iOS and Android
final class DesktopDownloader extends BaseDownloader {
static final _log = Logger('DesktopDownloader');
final class DesktopDownloaderNativeHttpClient extends BaseDownloader {
static final _log = Logger('DesktopDownloaderNativeHttpClient');
static const unlimited = 1 << 20;
var maxConcurrent = 10;
var maxConcurrentByHost = unlimited;
var maxConcurrentByGroup = unlimited;
static final DesktopDownloader _singleton = DesktopDownloader._internal();
static final DesktopDownloaderNativeHttpClient _singleton =
DesktopDownloaderNativeHttpClient._internal();
final _queue = PriorityQueue<Task>();
final _running = Queue<Task>(); // subset that is running
final _resume = <Task>{};
final _isolateSendPorts =
<Task, SendPort?>{}; // isolate SendPort for running task
static var httpClient = MClient.httpClient();
static var httpClient = MClient.nativeHttpClient();
static Duration? _requestTimeout;
static var _proxy = <String, dynamic>{}; // 'address' and 'port'
static var _bypassTLSCertificateValidation = false;
factory DesktopDownloader() => _singleton;
factory DesktopDownloaderNativeHttpClient() => _singleton;
DesktopDownloader._internal();
DesktopDownloaderNativeHttpClient._internal();
@override
Future<bool> enqueue(Task task) async {
@ -140,7 +141,7 @@ final class DesktopDownloader extends BaseDownloader {
return;
}
log.finer('${isResume ? "Resuming" : "Starting"} taskId ${task.taskId}');
await Isolate.spawn(doTask, (rootIsolateToken, receivePort.sendPort),
await Isolate.spawn(doTask, (rootIsolateToken, receivePort.sendPort, true),
onError: errorPort.sendPort);
final messagesFromIsolate = StreamQueue<dynamic>(receivePort);
final sendPort = await messagesFromIsolate.next as SendPort;
@ -582,7 +583,7 @@ final class DesktopDownloader extends BaseDownloader {
(X509Certificate cert, String host, int port) => true;
httpClient = IOClient(client);
} else {
httpClient = MClient.httpClient();
httpClient = MClient.nativeHttpClient();
}
}

View file

@ -6,6 +6,9 @@ import 'dart:isolate';
import 'dart:math';
import 'package:http/http.dart' as http;
import 'package:mangayomi/services/background_downloader/src/desktop/desktop_downloader_http_client.dart';
import 'package:mangayomi/services/background_downloader/src/desktop/desktop_downloader_native_http_client.dart.dart'
as desktop_downloader_native;
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
@ -13,7 +16,6 @@ import '../exceptions.dart';
import '../models.dart';
import '../task.dart';
import '../utils.dart';
import 'desktop_downloader.dart';
import 'isolate.dart';
var taskRangeStartByte = 0; // Start of the Task's download range
@ -30,7 +32,8 @@ Future<void> doDownloadTask(
ResumeData? resumeData,
bool isResume,
Duration requestTimeout,
SendPort sendPort) async {
SendPort sendPort,
bool useNativeHttpClient) async {
// use downloadTask from here on as a 'global' variable in this isolate,
// as we may change the filename of the task
downloadTask = task;
@ -45,7 +48,9 @@ Future<void> doDownloadTask(
final eTag = resumeData?.eTag;
isResume = isResume &&
await determineIfResumeIsPossible(tempFilePath, requiredStartByte);
final client = DesktopDownloader.httpClient;
final client = useNativeHttpClient
? desktop_downloader_native.DesktopDownloaderNativeHttpClient.httpClient
: DesktopDownloaderHttpClient.httpClient;
var request =
http.Request(downloadTask.httpRequestMethod, Uri.parse(downloadTask.url));
request.headers.addAll(downloadTask.headers);

View file

@ -7,6 +7,8 @@ import 'dart:isolate';
import 'dart:math';
import 'package:async/async.dart';
import 'package:mangayomi/services/background_downloader/src/desktop/desktop_downloader_http_client.dart';
import 'package:mangayomi/services/background_downloader/src/desktop/desktop_downloader_native_http_client.dart.dart';
import 'package:mangayomi/services/background_downloader/src/exceptions.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
@ -16,7 +18,6 @@ import 'package:logging/logging.dart';
import '../models.dart';
import '../task.dart';
import 'data_isolate.dart';
import 'desktop_downloader.dart';
import 'download_isolate.dart';
import 'parallel_download_isolate.dart';
import 'upload_isolate.dart';
@ -50,8 +51,8 @@ final log = Logger('FileDownloader');
///
/// The first message sent back is a [ReceivePort] that is the command port
/// for the isolate. The first command must be the arguments: task and filePath.
Future<void> doTask((RootIsolateToken, SendPort) isolateArguments) async {
final (rootIsolateToken, sendPort) = isolateArguments;
Future<void> doTask((RootIsolateToken, SendPort, bool) isolateArguments) async {
final (rootIsolateToken, sendPort, useNativeHttpClient) = isolateArguments;
BackgroundIsolateBinaryMessenger.ensureInitialized(rootIsolateToken);
final receivePort = ReceivePort();
// send the receive port back to the main Isolate
@ -67,8 +68,11 @@ Future<void> doTask((RootIsolateToken, SendPort) isolateArguments) async {
Map<String, dynamic> proxy,
bool bypassTLSCertificateValidation
) = await messagesToIsolate.next;
DesktopDownloader.setHttpClient(
requestTimeout, proxy, bypassTLSCertificateValidation);
useNativeHttpClient
? DesktopDownloaderNativeHttpClient.setHttpClient(
requestTimeout, proxy, bypassTLSCertificateValidation)
: DesktopDownloaderHttpClient.setHttpClient(
requestTimeout, proxy, bypassTLSCertificateValidation);
Logger.root.level = Level.ALL;
Logger.root.onRecord.listen((LogRecord rec) {
if (kDebugMode) {
@ -95,11 +99,18 @@ Future<void> doTask((RootIsolateToken, SendPort) isolateArguments) async {
resumeData,
isResume,
requestTimeout ?? const Duration(seconds: 60),
sendPort),
DownloadTask() => doDownloadTask(task, filePath, resumeData, isResume,
requestTimeout ?? const Duration(seconds: 60), sendPort),
UploadTask() => doUploadTask(task, filePath, sendPort),
DataTask() => doDataTask(task, sendPort)
sendPort,
useNativeHttpClient),
DownloadTask() => doDownloadTask(
task,
filePath,
resumeData,
isResume,
requestTimeout ?? const Duration(seconds: 60),
sendPort,
useNativeHttpClient),
UploadTask() => doUploadTask(task, filePath, sendPort,useNativeHttpClient),
DataTask() => doDataTask(task, sendPort, useNativeHttpClient)
};
}
receivePort.close();

View file

@ -7,13 +7,14 @@ import 'dart:isolate';
import 'dart:math';
import 'package:collection/collection.dart';
import 'package:mangayomi/services/background_downloader/src/desktop/desktop_downloader_http_client.dart';
import 'package:mangayomi/services/background_downloader/src/desktop/desktop_downloader_native_http_client.dart.dart'
as desktop_downloader_native;
import '../chunk.dart';
import '../exceptions.dart';
import '../models.dart';
import '../task.dart';
import '../utils.dart';
import 'desktop_downloader.dart';
import 'download_isolate.dart';
import 'isolate.dart';
@ -64,11 +65,15 @@ Future<void> doParallelDownloadTask(
ResumeData? resumeData,
bool isResume,
Duration requestTimeout,
SendPort sendPort) async {
SendPort sendPort,
bool useNativeHttpClient) async {
parentTask = task;
if (!isResume) {
// start the download by creating [Chunk]s and enqueuing chunk tasks
final response = await DesktopDownloader.httpClient
final response = await (useNativeHttpClient
? desktop_downloader_native
.DesktopDownloaderNativeHttpClient.httpClient
: DesktopDownloaderHttpClient.httpClient)
.head(Uri.parse(task.url), headers: task.headers);
responseHeaders = response.headers;
responseStatusCode = response.statusCode;

View file

@ -11,7 +11,9 @@ import 'package:path/path.dart' as p;
import '../models.dart';
import '../task.dart';
import 'desktop_downloader.dart';
import 'package:mangayomi/services/background_downloader/src/desktop/desktop_downloader_http_client.dart';
import 'package:mangayomi/services/background_downloader/src/desktop/desktop_downloader_native_http_client.dart.dart'
as desktop_downloader_native;
import 'isolate.dart';
const boundary = '-----background_downloader-akjhfw281onqciyhnIk';
@ -21,11 +23,11 @@ const lineFeed = '\r\n';
///
/// Sends updates via the [sendPort] and can be commanded to cancel via
/// the [messagesToIsolate] queue
Future<void> doUploadTask(
UploadTask task, String filePath, SendPort sendPort) async {
Future<void> doUploadTask(UploadTask task, String filePath, SendPort sendPort,
bool useNativeHttpClient) async {
final resultStatus = task.post == 'binary'
? await binaryUpload(task, filePath, sendPort)
: await multipartUpload(task, filePath, sendPort);
? await binaryUpload(task, filePath, sendPort, useNativeHttpClient)
: await multipartUpload(task, filePath, sendPort, useNativeHttpClient);
processStatusUpdateInIsolate(task, resultStatus, sendPort);
}
@ -33,8 +35,8 @@ Future<void> doUploadTask(
///
/// Sends updates via the [sendPort] and can be commanded to cancel via
/// the [messagesToIsolate] queue
Future<TaskStatus> binaryUpload(
UploadTask task, String filePath, SendPort sendPort) async {
Future<TaskStatus> binaryUpload(UploadTask task, String filePath,
SendPort sendPort, bool useNativeHttpClient) async {
final inFile = File(filePath);
if (!inFile.existsSync()) {
logError(task, 'file to upload does not exist: $filePath');
@ -45,7 +47,9 @@ Future<TaskStatus> binaryUpload(
final fileSize = inFile.lengthSync();
var resultStatus = TaskStatus.failed;
try {
final client = DesktopDownloader.httpClient;
final client = useNativeHttpClient
? desktop_downloader_native.DesktopDownloaderNativeHttpClient.httpClient
: DesktopDownloaderHttpClient.httpClient;
final request =
http.StreamedRequest(task.httpRequestMethod, Uri.parse(task.url));
request.headers.addAll(task.headers);
@ -96,8 +100,8 @@ Future<TaskStatus> binaryUpload(
///
/// Sends updates via the [sendPort] and can be commanded to cancel via
/// the [messagesToIsolate] queue
Future<TaskStatus> multipartUpload(
UploadTask task, String filePath, SendPort sendPort) async {
Future<TaskStatus> multipartUpload(UploadTask task, String filePath,
SendPort sendPort, bool useNativeHttpClient) async {
// field portion of the multipart, all in one string
// multiple values should be encoded as '"value1", "value2", ...'
final multiValueRegEx = RegExp(r'^(?:"[^"]+"\s*,\s*)+"[^"]+"$');
@ -152,7 +156,9 @@ Future<TaskStatus> multipartUpload(
var resultStatus = TaskStatus.failed;
try {
// setup the connection
final client = DesktopDownloader.httpClient;
final client = useNativeHttpClient
? desktop_downloader_native.DesktopDownloaderNativeHttpClient.httpClient
: DesktopDownloaderHttpClient.httpClient;
final request =
http.StreamedRequest(task.httpRequestMethod, Uri.parse(task.url));
request.contentLength = contentLength;

View file

@ -6,7 +6,9 @@ import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http;
import 'base_downloader.dart';
import 'localstore/localstore.dart';
import 'desktop/desktop_downloader.dart';
import 'package:mangayomi/services/background_downloader/src/desktop/desktop_downloader_http_client.dart';
import 'package:mangayomi/services/background_downloader/src/desktop/desktop_downloader_native_http_client.dart.dart'
as desktop_downloader_native;
/// Provides access to all functions of the plugin in a single place.
interface class FileDownloader {
@ -32,19 +34,23 @@ interface class FileDownloader {
BaseDownloader get downloaderForTesting => _downloader;
factory FileDownloader(
{PersistentStorage? persistentStorage, bool isAnime = false}) {
{PersistentStorage? persistentStorage,
bool useNativeHttpClient = false}) {
assert(
_singleton == null || persistentStorage == null,
'You can only supply a persistentStorage on the very first call to '
'FileDownloader()');
_singleton ??= FileDownloader._internal(
persistentStorage ?? LocalStorePersistentStorage(), isAnime);
persistentStorage ?? LocalStorePersistentStorage(),
useNativeHttpClient);
return _singleton!;
}
FileDownloader._internal(PersistentStorage persistentStorage, bool isAnime) {
FileDownloader._internal(
PersistentStorage persistentStorage, bool useNativeHttpClient) {
database = Database(persistentStorage);
_downloader = BaseDownloader.instance(persistentStorage, database, isAnime);
_downloader = BaseDownloader.instance(
persistentStorage, database, useNativeHttpClient);
}
/// True when initialization is complete and downloader ready for use
@ -773,12 +779,27 @@ interface class FileDownloader {
/// the downloader. If not set, the default [http.Client] will be used.
/// The request is executed on an Isolate, to ensure minimal interference
/// with the main Isolate
Future<http.Response> request(Request request) => compute(_doRequest, (
Future<http.Response> request(Request request, bool useNativeHttpClient) {
if (useNativeHttpClient) {
return compute(_doRequest, (
request,
DesktopDownloader.requestTimeout,
DesktopDownloader.proxy,
DesktopDownloader.bypassTLSCertificateValidation
desktop_downloader_native
.DesktopDownloaderNativeHttpClient.requestTimeout,
desktop_downloader_native.DesktopDownloaderNativeHttpClient.proxy,
desktop_downloader_native
.DesktopDownloaderNativeHttpClient.bypassTLSCertificateValidation,
true
));
}
return compute(_doRequest, (
request,
DesktopDownloaderHttpClient.requestTimeout,
DesktopDownloaderHttpClient.proxy,
DesktopDownloaderHttpClient.bypassTLSCertificateValidation,
false
));
}
/// Move the file represented by the [task] to a shared storage
/// [destination] and potentially a [directory] within that destination. If
@ -889,13 +910,26 @@ interface class FileDownloader {
/// This function is run on an Isolate to ensure performance on the main
/// Isolate is not affected
Future<http.Response> _doRequest(
(Request, Duration?, Map<String, dynamic>, bool) params) async {
final (request, requestTimeout, proxy, bypassTLSCertificateValidation) =
params;
(Request, Duration?, Map<String, dynamic>, bool, bool) params) async {
final (
request,
requestTimeout,
proxy,
bypassTLSCertificateValidation,
useNativeHttpClient
) = params;
DesktopDownloader.setHttpClient(
requestTimeout, proxy, bypassTLSCertificateValidation);
final client = DesktopDownloader.httpClient;
if (useNativeHttpClient) {
desktop_downloader_native.DesktopDownloaderNativeHttpClient.setHttpClient(
requestTimeout, proxy, bypassTLSCertificateValidation);
} else {
DesktopDownloaderHttpClient.setHttpClient(
requestTimeout, proxy, bypassTLSCertificateValidation);
}
final client = useNativeHttpClient
? desktop_downloader_native.DesktopDownloaderNativeHttpClient.httpClient
: DesktopDownloaderHttpClient.httpClient;
var response = http.Response('', 499,
reasonPhrase: 'Not attempted'); // dummy to start with
while (request.retriesRemaining >= 0) {

View file

@ -8,11 +8,9 @@ import 'dart:typed_data';
import 'package:http/http.dart' as http;
import 'package:logging/logging.dart';
import 'package:mangayomi/services/background_downloader/src/desktop/desktop_downloader.dart';
import 'package:mime/mime.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
import 'file_downloader.dart';
import 'models.dart';
import 'utils.dart';
@ -669,39 +667,6 @@ final class DownloadTask extends Task {
///
/// The suggested filename is obtained by making a HEAD request to the url
/// represented by the [DownloadTask], including urlQueryParameters and headers
Future<DownloadTask> withSuggestedFilename(
{unique = false,
Future<DownloadTask> Function(
DownloadTask task, Map<String, String> headers, bool unique)
taskWithFilenameBuilder = taskWithSuggestedFilename}) async {
try {
final response = await DesktopDownloader.httpClient
.head(Uri.parse(url), headers: headers);
if ([200, 201, 202, 203, 204, 205, 206].contains(response.statusCode)) {
return taskWithFilenameBuilder(this, response.headers, unique);
}
} catch (e) {
_log.finer('Error connecting to server');
}
return taskWithFilenameBuilder(this, {}, unique);
}
/// Return the expected file size for this task, or -1 if unknown
///
/// The expected file size is obtained by making a HEAD request to the url
/// represented by the [DownloadTask], including urlQueryParameters and headers
Future<int> expectedFileSize() async {
try {
final response = await DesktopDownloader.httpClient
.head(Uri.parse(url), headers: headers);
if ([200, 201, 202, 203, 204, 205, 206].contains(response.statusCode)) {
return getContentLength(response.headers, this);
}
} catch (e) {
// no content length available
}
return -1;
}
/// Constant used with `filename` field to indicate server suggestion requested
static const suggestedFilename = '?';

View file

@ -15,24 +15,27 @@ import 'package:mangayomi/utils/log/log.dart';
class MClient {
MClient();
static Client httpClient() {
if ((Platform.isAndroid || Platform.isIOS || Platform.isMacOS) &&
(isar.settings.getSync(227)?.useNativeHttpClient ?? false)) {
if (Platform.isAndroid) {
final engine = CronetEngine.build(
enablePublicKeyPinningBypassForLocalTrustAnchors: true,
enableHttp2: true,
enableBrotli: true,
cacheMode: CacheMode.memory,
cacheMaxSize: 5 * 1024 * 1024);
return CronetClient.fromCronetEngine(engine, closeEngine: true);
}
if (Platform.isIOS || Platform.isMacOS) {
final config = URLSessionConfiguration.ephemeralSessionConfiguration()
..cache = URLCache.withCapacity(memoryCapacity: 5 * 1024 * 1024);
return CupertinoClient.fromSessionConfiguration(config);
}
if (isar.settings.getSync(227)?.useNativeHttpClient ?? false) {
return nativeHttpClient();
}
return IOClient(HttpClient());
}
static Client nativeHttpClient() {
if (Platform.isAndroid) {
final engine = CronetEngine.build(
enablePublicKeyPinningBypassForLocalTrustAnchors: true,
enableHttp2: true,
enableBrotli: true,
cacheMode: CacheMode.memory,
cacheMaxSize: 5 * 1024 * 1024);
return CronetClient.fromCronetEngine(engine, closeEngine: true);
}
if (Platform.isIOS || Platform.isMacOS) {
final config = URLSessionConfiguration.ephemeralSessionConfiguration()
..cache = URLCache.withCapacity(memoryCapacity: 5 * 1024 * 1024);
return CupertinoClient.fromSessionConfiguration(config);
}
return IOClient(HttpClient());
}