From 27b5d9259f8287c57a61b77a4f876ef4095a0277 Mon Sep 17 00:00:00 2001 From: xkeyC <3334969096@qq.com> Date: Sat, 24 Feb 2024 17:10:28 +0800 Subject: [PATCH] =?UTF-8?q?=E9=99=90=E9=80=9F=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common/io/aria2c.dart | 19 +- lib/ui/home/downloads/downloads_ui.dart | 311 ++++++++++-------- lib/ui/home/downloads/downloads_ui_model.dart | 75 +++++ lib/ui/tools/tools_ui_model.dart | 1 - 4 files changed, 258 insertions(+), 148 deletions(-) diff --git a/lib/common/io/aria2c.dart b/lib/common/io/aria2c.dart index 8c5ad8b..259b8f5 100644 --- a/lib/common/io/aria2c.dart +++ b/lib/common/io/aria2c.dart @@ -62,8 +62,7 @@ class Aria2cManager { "--save-session=${sessionFile.absolute.path.trim()}", "--save-session-interval=60", "--file-allocation=trunc", - // TODO for debug - "--max-overall-download-limit=100k" + "--seed-time=0", ], workingDirectory: _aria2cDir); p.stdout.transform(utf8.decoder).listen((event) { @@ -88,4 +87,20 @@ class Aria2cManager { await Future.delayed(const Duration(milliseconds: 100)); } } + + static int textToByte(String text) { + if (text.length == 1) { + return 0; + } + if (int.tryParse(text) != null) { + return int.parse(text); + } + if (text.endsWith("k")) { + return int.parse(text.substring(0, text.length - 1)) * 1024; + } + if (text.endsWith("m")) { + return int.parse(text.substring(0, text.length - 1)) * 1024 * 1024; + } + return 0; + } } diff --git a/lib/ui/home/downloads/downloads_ui.dart b/lib/ui/home/downloads/downloads_ui.dart index 633a616..ae190f1 100644 --- a/lib/ui/home/downloads/downloads_ui.dart +++ b/lib/ui/home/downloads/downloads_ui.dart @@ -15,9 +15,11 @@ class DownloadsUI extends BaseUI { children: [ const SizedBox(height: 12), Row( - mainAxisAlignment: MainAxisAlignment.end, children: [ + const Spacer(), + const SizedBox(width: 24), for (final item in , String>{ + const MapEntry("settings", FluentIcons.settings): "限速设置", if (model.tasks.isNotEmpty) const MapEntry("pause_all", FluentIcons.pause): "暂停全部" else @@ -42,157 +44,176 @@ class DownloadsUI extends BaseUI { const SizedBox(width: 12), ], ), - Expanded( - child: ListView.builder( - itemBuilder: (BuildContext context, int index) { - final (task, type, isFirstType) = model.getTaskAndType(index); - final nt = model.getTaskTypeAndName(task); - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (isFirstType) - Column( - children: [ - Container( - padding: EdgeInsets.only( - left: 24, - right: 24, - top: index == 0 ? 0 : 12, - bottom: 12), - margin: const EdgeInsets.only(top: 6, bottom: 6), - child: Row( - children: [ - Expanded( - child: Text( - "${model.listHeaderStatusMap[type]}", - style: const TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold), - )), - ], - ), - ), - ], - ), - Container( - padding: const EdgeInsets.only( - left: 12, right: 12, top: 12, bottom: 12), - margin: const EdgeInsets.only( - left: 12, right: 12, top: 6, bottom: 6), - decoration: BoxDecoration( - color: - FluentTheme.of(context).cardColor.withOpacity(.03), - borderRadius: BorderRadius.circular(7), - ), - child: Row( - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - nt.value, - style: const TextStyle( - fontSize: 18, fontWeight: FontWeight.bold), - ), - const SizedBox(height: 6), - Row( + if (model.getTasksLen() == 0) + const Expanded( + child: Center( + child: Text("无下载任务"), + )) + else + Expanded( + child: ListView.builder( + itemBuilder: (BuildContext context, int index) { + final (task, type, isFirstType) = model.getTaskAndType(index); + final nt = model.getTaskTypeAndName(task); + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (isFirstType) + Column( + children: [ + Container( + padding: EdgeInsets.only( + left: 24, + right: 24, + top: index == 0 ? 0 : 12, + bottom: 12), + margin: const EdgeInsets.only(top: 6, bottom: 6), + child: Row( children: [ - Text( - "总大小:${FileSize.getSize(task.totalLength ?? 0)}", - style: const TextStyle(fontSize: 14), - ), - const SizedBox(width: 12), - if (nt.key == "torrent" && - task.verifiedLength != null && - task.verifiedLength != 0) - Text( - "校验中...(${FileSize.getSize(task.verifiedLength)})", - style: const TextStyle(fontSize: 14), - ) - else if (task.status == "active") - Text( - "下载中... (${((task.completedLength ?? 0) / (task.totalLength ?? 1)).toStringAsFixed(4)}%)") - else - Text( - "状态:${model.statusMap[task.status]}", - style: const TextStyle(fontSize: 14), - ), - const SizedBox(width: 24), - if (task.status == "active" && - task.verifiedLength == null) - Text( - "ETA: ${formatter.format(DateTime.now().add(Duration(seconds: model.getETA(task))))}"), + Expanded( + child: Row( + children: [ + Text( + "${model.listHeaderStatusMap[type]}", + style: const TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold), + ), + if (type == "active") + Text( + "下载: ${FileSize.getSize(model.globalStat?.downloadSpeed ?? 0)}/s 上传:${FileSize.getSize(model.globalStat?.uploadSpeed ?? 0)}/s", + style: const TextStyle(fontSize: 13), + ), + ], + )), ], ), - ], - ), - const Spacer(), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - "已上传:${FileSize.getSize(task.uploadLength)}"), - Text( - "已下载:${FileSize.getSize(task.completedLength)}"), - ], - ), - const SizedBox(width: 18), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text("↑:${FileSize.getSize(task.uploadSpeed)}/s"), - Text( - "↓:${FileSize.getSize(task.downloadSpeed)}/s"), - ], - ), - const SizedBox(width: 32), - if (type != "stopped") - DropDownButton( - closeAfterClick: false, - title: const Padding( - padding: EdgeInsets.all(3), - child: Text('选项'), - ), - items: [ - if (task.status == "paused") - MenuFlyoutItem( - leading: const Icon(FluentIcons.download), - text: const Text('继续下载'), - onPressed: () => - model.resumeTask(task.gid)) - else if (task.status == "active") - MenuFlyoutItem( - leading: const Icon(FluentIcons.pause), - text: const Text('暂停下载'), - onPressed: () => - model.pauseTask(task.gid)), - const MenuFlyoutSeparator(), - MenuFlyoutItem( - leading: const Icon( - FluentIcons.chrome_close, - size: 14, + ), + ], + ), + Container( + padding: const EdgeInsets.only( + left: 12, right: 12, top: 12, bottom: 12), + margin: const EdgeInsets.only( + left: 12, right: 12, top: 6, bottom: 6), + decoration: BoxDecoration( + color: FluentTheme.of(context) + .cardColor + .withOpacity(.06), + borderRadius: BorderRadius.circular(7), + ), + child: Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + nt.value, + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold), + ), + const SizedBox(height: 6), + Row( + children: [ + Text( + "总大小:${FileSize.getSize(task.totalLength ?? 0)}", + style: const TextStyle(fontSize: 14), ), - text: const Text('取消下载'), - onPressed: () => - model.cancelTask(task.gid)), - MenuFlyoutItem( - leading: const Icon( - FluentIcons.folder_open, - size: 14, - ), - text: const Text('打开文件夹'), - onPressed: () => model.openFolder(task)), + const SizedBox(width: 12), + if (nt.key == "torrent" && + task.verifiedLength != null && + task.verifiedLength != 0) + Text( + "校验中...(${FileSize.getSize(task.verifiedLength)})", + style: const TextStyle(fontSize: 14), + ) + else if (task.status == "active") + Text( + "下载中... (${((task.completedLength ?? 0) / (task.totalLength ?? 1)).toStringAsFixed(4)}%)") + else + Text( + "状态:${model.statusMap[task.status]}", + style: const TextStyle(fontSize: 14), + ), + const SizedBox(width: 24), + if (task.status == "active" && + task.verifiedLength == null) + Text( + "ETA: ${formatter.format(DateTime.now().add(Duration(seconds: model.getETA(task))))}"), + ], + ), ], ), - const SizedBox(width: 12), - ], + const Spacer(), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "已上传:${FileSize.getSize(task.uploadLength)}"), + Text( + "已下载:${FileSize.getSize(task.completedLength)}"), + ], + ), + const SizedBox(width: 18), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "↑:${FileSize.getSize(task.uploadSpeed)}/s"), + Text( + "↓:${FileSize.getSize(task.downloadSpeed)}/s"), + ], + ), + const SizedBox(width: 32), + if (type != "stopped") + DropDownButton( + closeAfterClick: false, + title: const Padding( + padding: EdgeInsets.all(3), + child: Text('选项'), + ), + items: [ + if (task.status == "paused") + MenuFlyoutItem( + leading: + const Icon(FluentIcons.download), + text: const Text('继续下载'), + onPressed: () => + model.resumeTask(task.gid)) + else if (task.status == "active") + MenuFlyoutItem( + leading: const Icon(FluentIcons.pause), + text: const Text('暂停下载'), + onPressed: () => + model.pauseTask(task.gid)), + const MenuFlyoutSeparator(), + MenuFlyoutItem( + leading: const Icon( + FluentIcons.chrome_close, + size: 14, + ), + text: const Text('取消下载'), + onPressed: () => + model.cancelTask(task.gid)), + MenuFlyoutItem( + leading: const Icon( + FluentIcons.folder_open, + size: 14, + ), + text: const Text('打开文件夹'), + onPressed: () => model.openFolder(task)), + ], + ), + const SizedBox(width: 12), + ], + ), ), - ), - ], - ); - }, - itemCount: model.getTasksLen(), - )) + ], + ); + }, + itemCount: model.getTasksLen(), + )) ], )); } diff --git a/lib/ui/home/downloads/downloads_ui_model.dart b/lib/ui/home/downloads/downloads_ui_model.dart index d540a85..9481af8 100644 --- a/lib/ui/home/downloads/downloads_ui_model.dart +++ b/lib/ui/home/downloads/downloads_ui_model.dart @@ -1,6 +1,8 @@ import 'dart:io'; import 'package:aria2/aria2.dart'; +import 'package:flutter/services.dart'; +import 'package:hive/hive.dart'; import 'package:starcitizen_doctor/base/ui_model.dart'; import 'package:starcitizen_doctor/common/helper/system_helper.dart'; import 'package:starcitizen_doctor/common/io/aria2c.dart'; @@ -9,6 +11,7 @@ class DownloadsUIModel extends BaseUIModel { List tasks = []; List waitingTasks = []; List stoppedTasks = []; + Aria2GlobalStat? globalStat; final statusMap = { "active": "下载中...", @@ -52,6 +55,9 @@ class DownloadsUIModel extends BaseUIModel { } } return; + case "settings": + _showDownloadSpeedSettings(); + return; } } @@ -63,6 +69,7 @@ class DownloadsUIModel extends BaseUIModel { tasks = await Aria2cManager.aria2c.tellActive(); waitingTasks = await Aria2cManager.aria2c.tellWaiting(0, 1000000); stoppedTasks = await Aria2cManager.aria2c.tellStopped(0, 1000000); + globalStat = await Aria2cManager.aria2c.getGlobalStat(); notifyListeners(); await Future.delayed(const Duration(seconds: 1)); } @@ -157,4 +164,72 @@ class DownloadsUIModel extends BaseUIModel { SystemHelper.openDir(File(f.path!).absolute.path.replaceAll("/", "\\")); } } + + Future _showDownloadSpeedSettings() async { + final box = await Hive.openBox("app_conf"); + + final upCtrl = TextEditingController( + text: box.get("downloader_up_limit", defaultValue: "")); + final downCtrl = TextEditingController( + text: box.get("downloader_down_limit", defaultValue: "")); + + final ifr = FilteringTextInputFormatter.allow(RegExp(r'^\d*[km]?$')); + + final ok = await showConfirmDialogs( + context!, + "限速设置", + Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "SC 汉化盒子使用 p2p 网络来加速文件下载,如果您流量有限,可在此处将上传带宽设置为 1(byte)。", + style: TextStyle( + fontSize: 14, + color: Colors.white.withOpacity(.6), + ), + ), + const SizedBox(height: 24), + const Text("请输入下载单位,如:1、100k、10m, 0或留空为不限速。"), + const SizedBox(height: 12), + const Text("上传限速:"), + const SizedBox(height: 6), + TextFormBox( + placeholder: "1、100k、10m、0", + controller: upCtrl, + placeholderStyle: TextStyle(color: Colors.white.withOpacity(.6)), + inputFormatters: [ifr], + ), + const SizedBox(height: 12), + const Text("下载限速:"), + const SizedBox(height: 6), + TextFormBox( + placeholder: "1、100k、10m、0", + controller: downCtrl, + placeholderStyle: TextStyle(color: Colors.white.withOpacity(.6)), + inputFormatters: [ifr], + ), + const SizedBox(height: 24), + Text( + "* P2P 上传仅在下载文件时进行,下载完成后会关闭 p2p 连接。如您想参与做种,请通过关于页面联系我们。", + style: TextStyle( + fontSize: 13, + color: Colors.white.withOpacity(.6), + ), + ) + ], + )); + if (ok == true) { + final upByte = Aria2cManager.textToByte(upCtrl.text.trim()); + final downByte = Aria2cManager.textToByte(downCtrl.text.trim()); + final r = await handleError( + () => Aria2cManager.aria2c.changeGlobalOption(Aria2Option() + ..maxOverallUploadLimit = upByte + ..maxOverallDownloadLimit = downByte)); + if (r != null) { + await box.put('downloader_up_limit', upCtrl.text.trim()); + await box.put('downloader_down_limit', downCtrl.text.trim()); + } + } + } } diff --git a/lib/ui/tools/tools_ui_model.dart b/lib/ui/tools/tools_ui_model.dart index d020277..04c5cce 100644 --- a/lib/ui/tools/tools_ui_model.dart +++ b/lib/ui/tools/tools_ui_model.dart @@ -5,7 +5,6 @@ import 'package:file_picker/file_picker.dart'; import 'package:flutter/foundation.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:starcitizen_doctor/base/ui_model.dart'; -import 'package:starcitizen_doctor/common/conf/app_conf.dart'; import 'package:starcitizen_doctor/common/helper/log_helper.dart'; import 'package:starcitizen_doctor/common/helper/system_helper.dart'; import 'package:starcitizen_doctor/common/io/aria2c.dart';