diff --git a/lib/global.dart b/lib/global.dart index f949a9e..e96baa7 100644 --- a/lib/global.dart +++ b/lib/global.dart @@ -18,6 +18,16 @@ void setYandeClient(YandeClient client) { _yandeClient = client; } + +YandeClient get yandeClientForLargeFile => _yandeClientForLargeFile!; + +YandeClient? _yandeClientForLargeFile; + +void setYandeClientForLargeFile(YandeClient client) { + _yandeClientForLargeFile = client; +} + + bool get isDesktop => Platform.isWindows || Platform.isLinux || Platform.isMacOS; bool get isMobile => Platform.isAndroid || Platform.isIOS; diff --git a/lib/pages/downloads/logic.dart b/lib/pages/downloads/logic.dart index dd9b400..37fbd46 100644 --- a/lib/pages/downloads/logic.dart +++ b/lib/pages/downloads/logic.dart @@ -148,7 +148,7 @@ class DownloadTask extends _$DownloadTask { } else { EasyLoading.showToast(i18n.downloads.messages.downloadStartWithId(state.post.id)); } - yandeClient + yandeClientForLargeFile .downloadToMemory( url: state.post.fileUrl ?? state.post.jpegUrl, autoMultiplePart: true, diff --git a/lib/pages/index/index_page.dart b/lib/pages/index/index_page.dart index 12c18a0..9d4c4f5 100644 --- a/lib/pages/index/index_page.dart +++ b/lib/pages/index/index_page.dart @@ -23,11 +23,11 @@ class IndexPage extends StatefulWidget { class _IndexPageState extends State { Map<(IconData, String), WidgetBuilder> get _pages => { - (Icons.list_alt_outlined, i18n.postList.short): (context) => PostListPage(key: ValueKey(widget.language)), - (Icons.search_outlined, i18n.postSearch.title): (context) => PostSearchPage(key: ValueKey(widget.language)), - (Icons.cloud_download_outlined, i18n.downloads.title): (context) => DownloadsPage(key: ValueKey(widget.language)), - (Icons.info_outlined, i18n.about.title): (context) => AboutPage(key: ValueKey(widget.language)), - (Icons.settings, i18n.settings.title): (context) => SettingsPage(key: ValueKey(widget.language)), + (Icons.list_alt_outlined, i18n.postList.short): (context) => PostListPage(key: ValueKey(widget.language)), + (Icons.search_outlined, i18n.postSearch.title): (context) => PostSearchPage(key: ValueKey(widget.language)), + (Icons.cloud_download_outlined, i18n.downloads.title): (context) => DownloadsPage(key: ValueKey(widget.language)), + (Icons.info_outlined, i18n.about.title): (context) => AboutPage(key: ValueKey(widget.language)), + (Icons.settings, i18n.settings.title): (context) => SettingsPage(key: ValueKey(widget.language)), }; final controller = PageController(); @@ -40,16 +40,20 @@ class _IndexPageState extends State { void initState() { Future.delayed(Duration.zero, () async { YandeClient instance; + YandeClient instanceForLargeFile; try { final List? dns = await DnsService.fetchDns(); final ips = dns != null ? StringArray3(dns) : null; - instance = await YandeClient.newInstance(ips: ips); + instance = await YandeClient.newInstance(ips: ips, forLargeFile: false); + instanceForLargeFile = await YandeClient.newInstance(ips: ips, forLargeFile: true); } catch (e) { - instance = await YandeClient.newInstance(ips: null); + instance = await YandeClient.newInstance(ips: null, forLargeFile: false); + instanceForLargeFile = await YandeClient.newInstance(ips: null, forLargeFile: true); } setYandeClient(instance); + setYandeClientForLargeFile(instanceForLargeFile); setState(() { _initialized = true; diff --git a/lib/src/rust/api/yande_client.dart b/lib/src/rust/api/yande_client.dart index 0166a6a..6d3964d 100644 --- a/lib/src/rust/api/yande_client.dart +++ b/lib/src/rust/api/yande_client.dart @@ -22,8 +22,10 @@ abstract class YandeClient implements RustOpaqueInterface { Future getSimilar({required PlatformInt64 postId}); // HINT: Make it `#[frb(sync)]` to let it become the default constructor of Dart class. - static Future newInstance({StringArray3? ips}) => - RustLib.instance.api.crateApiYandeClientYandeClientNew(ips: ips); + static Future newInstance( + {StringArray3? ips, required bool forLargeFile}) => + RustLib.instance.api.crateApiYandeClientYandeClientNew( + ips: ips, forLargeFile: forLargeFile); } class StringArray3 extends NonGrowableListView { diff --git a/lib/src/rust/frb_generated.dart b/lib/src/rust/frb_generated.dart index d7cf402..aa6e0da 100644 --- a/lib/src/rust/frb_generated.dart +++ b/lib/src/rust/frb_generated.dart @@ -97,7 +97,8 @@ abstract class RustLibApi extends BaseApi { Future crateApiYandeClientYandeClientGetSimilar( {required YandeClient that, required PlatformInt64 postId}); - Future crateApiYandeClientYandeClientNew({StringArray3? ips}); + Future crateApiYandeClientYandeClientNew( + {StringArray3? ips, required bool forLargeFile}); RustArcIncrementStrongCountFnType get rust_arc_increment_strong_count_YandeClient; @@ -234,11 +235,13 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); @override - Future crateApiYandeClientYandeClientNew({StringArray3? ips}) { + Future crateApiYandeClientYandeClientNew( + {StringArray3? ips, required bool forLargeFile}) { return handler.executeNormal(NormalTask( callFfi: (port_) { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_opt_String_array_3(ips, serializer); + sse_encode_bool(forLargeFile, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 5, port: port_); }, @@ -248,7 +251,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { decodeErrorData: null, ), constMeta: kCrateApiYandeClientYandeClientNewConstMeta, - argValues: [ips], + argValues: [ips, forLargeFile], apiImpl: this, )); } @@ -256,7 +259,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { TaskConstMeta get kCrateApiYandeClientYandeClientNewConstMeta => const TaskConstMeta( debugName: "YandeClient_new", - argNames: ["ips"], + argNames: ["ips", "forLargeFile"], ); Future Function(int, dynamic, dynamic) diff --git a/rust/src/api/yande_client.rs b/rust/src/api/yande_client.rs index af8e13d..e9b35d3 100644 --- a/rust/src/api/yande_client.rs +++ b/rust/src/api/yande_client.rs @@ -7,9 +7,9 @@ pub struct YandeClient { } impl YandeClient { - pub fn new(ips: Option<[String; 3]>) -> Self { + pub fn new(ips: Option<[String; 3]>, for_large_file: bool) -> Self { Self { - http: HttpClient::new(ips), + http: HttpClient::new(ips, for_large_file), } } } diff --git a/rust/src/frb_generated.rs b/rust/src/frb_generated.rs index 115df81..8871a19 100644 --- a/rust/src/frb_generated.rs +++ b/rust/src/frb_generated.rs @@ -289,11 +289,13 @@ fn wire__crate__api__yande_client__YandeClient_new_impl( let mut deserializer = flutter_rust_bridge::for_generated::SseDeserializer::new(message); let api_ips = >::sse_decode(&mut deserializer); + let api_for_large_file = ::sse_decode(&mut deserializer); deserializer.end(); move |context| { transform_result_sse::<_, ()>((move || { - let output_ok = - Result::<_, ()>::Ok(crate::api::yande_client::YandeClient::new(api_ips))?; + let output_ok = Result::<_, ()>::Ok( + crate::api::yande_client::YandeClient::new(api_ips, api_for_large_file), + )?; Ok(output_ok) })()) } diff --git a/rust/src/yande/http_client.rs b/rust/src/yande/http_client.rs index 6120f00..6ef7de6 100644 --- a/rust/src/yande/http_client.rs +++ b/rust/src/yande/http_client.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use std::time::Duration; use flutter_rust_bridge::DartFnFuture; - +use futures_util::future::join_all; use futures_util::stream::StreamExt; use reqwest::Url; use tokio::sync::Mutex; @@ -14,7 +14,25 @@ pub struct HttpClient { impl HttpClient { - pub fn new(ips: Option<[String; 3]>) -> Self { + pub fn new(ips: Option<[String; 3]>, for_large_file: bool) -> Self { + let mut client_builder = reqwest::ClientBuilder::new(). + http2_prior_knowledge(). + https_only(true). + http2_adaptive_window(true). + http2_keep_alive_interval(Duration::from_secs(30)). + http2_keep_alive_timeout(Duration::from_secs(60)). + http2_keep_alive_while_idle(true); + + if for_large_file { + client_builder = client_builder. + http2_keep_alive_interval(Duration::from_secs(30)). + http2_keep_alive_timeout(Duration::from_secs(120)). + http2_initial_connection_window_size(1024 * 1024). + http2_initial_stream_window_size(1024 * 1024). + http2_max_frame_size(128 * 1024). + http2_keep_alive_while_idle(true) + } + if let Some(ips) = ips { let socket0 = format!("{}:443", ips[0]).parse(); let socket1 = format!("{}:443", ips[1]).parse(); @@ -22,14 +40,8 @@ impl HttpClient { if let (Ok(socket0), Ok(socket1), Ok(socket2)) = (socket0, socket1, socket2) { return Self { - client: reqwest::ClientBuilder::new(). + client: client_builder. tls_sni(false). - http2_prior_knowledge(). - https_only(true). - http2_adaptive_window(true). - http2_keep_alive_interval(Duration::from_secs(30)). - http2_keep_alive_timeout(Duration::from_secs(60)). - http2_keep_alive_while_idle(true). resolve("yande.re", socket0). resolve("files.yande.re", socket1). resolve("assets.yande.re", socket2). @@ -39,14 +51,7 @@ impl HttpClient { } } Self { - client: reqwest::ClientBuilder::new(). - http2_prior_knowledge(). - https_only(true). - http2_adaptive_window(true). - http2_keep_alive_interval(Duration::from_secs(30)). - http2_keep_alive_timeout(Duration::from_secs(60)). - http2_keep_alive_while_idle(true). - build().unwrap(), + client: client_builder.build().unwrap(), } } @@ -150,10 +155,14 @@ impl HttpClient { } // Wait for all parts to be downloaded - for task in tasks { - let part = task.await??; + let results = join_all(tasks).await; + + // Combine all results + for result in results { + let part = result??; bytes.extend_from_slice(&part); } + return Ok(bytes); } }