diff --git a/internal/protocol/bt/fetcher.go b/internal/protocol/bt/fetcher.go index 43a9beb8..1d173ef5 100644 --- a/internal/protocol/bt/fetcher.go +++ b/internal/protocol/bt/fetcher.go @@ -161,7 +161,7 @@ func (f *Fetcher) Wait() (err error) { } } if !selected { - util.SafeRemove(filepath.Join(f.meta.Opts.Path, file.Path())) + util.SafeRemove(filepath.Join(f.meta.Opts.Path, f.meta.Res.Name, file.Path())) } } break @@ -202,7 +202,7 @@ func (f *Fetcher) updateRes() { Size: file.Length(), } } - res.CalcSize() + res.CalcSize(nil) f.meta.Res = res if f.meta.Opts != nil { f.meta.Opts.InitSelectFiles(len(res.Files)) diff --git a/pkg/base/model.go b/pkg/base/model.go index 697df227..8248304c 100644 --- a/pkg/base/model.go +++ b/pkg/base/model.go @@ -3,6 +3,7 @@ package base import ( "fmt" "github.com/GopeedLab/gopeed/pkg/util" + "golang.org/x/exp/slices" ) // Request download request @@ -47,10 +48,12 @@ func (r *Resource) Validate() error { return nil } -func (r *Resource) CalcSize() { +func (r *Resource) CalcSize(selectFiles []int) { var size int64 - for _, file := range r.Files { - size += file.Size + for i, file := range r.Files { + if len(selectFiles) == 0 || slices.Contains(selectFiles, i) { + size += file.Size + } } r.Size = size } diff --git a/pkg/download/downloader.go b/pkg/download/downloader.go index c2380e43..c7bf5c02 100644 --- a/pkg/download/downloader.go +++ b/pkg/download/downloader.go @@ -540,11 +540,17 @@ func (d *Downloader) GetTask(id string) *Task { } func (d *Downloader) GetTasks() []*Task { - // reverse tasks, so that the latest task is in the front tasks := make([]*Task, len(d.tasks)) for i := 0; i < len(d.tasks); i++ { - tasks[i] = d.tasks[len(d.tasks)-i-1] + tasks[i] = d.tasks[i] } + // sort tasks by status, order by Status(if(running,1,0)) desc, CreatedAt desc + sort.Slice(tasks, func(i, j int) bool { + if tasks[i].Status != tasks[j].Status { + return tasks[i].Status == base.DownloadStatusRunning + } + return tasks[i].CreatedAt.After(tasks[j].CreatedAt) + }) return tasks } @@ -579,16 +585,16 @@ func (d *Downloader) watch(task *Task) { d.emit(EventKeyError, task, err) } else { task.Progress.Used = task.timer.Used() - if task.Size == 0 { - task.Size = task.fetcher.Progress().TotalDownloaded() - task.Meta.Res.Size = task.Size + if task.Meta.Res.Size == 0 { + task.Meta.Res.Size = task.fetcher.Progress().TotalDownloaded() } used := task.Progress.Used / int64(time.Second) if used == 0 { used = 1 } - task.Progress.Speed = task.Size / used - task.Progress.Downloaded = task.Size + totalSize := task.Meta.Res.Size + task.Progress.Speed = totalSize / used + task.Progress.Downloaded = totalSize task.Status = base.DownloadStatusDone d.storage.Put(bucketTask, task.ID, task.clone()) d.emit(EventKeyDone, task) @@ -741,6 +747,8 @@ func (d *Downloader) doStart(task *Task) (err error) { } task.Meta.Opts.Name = newName } + + task.Meta.Res.CalcSize(task.Meta.Opts.SelectFiles) } task.Progress.Speed = 0 diff --git a/pkg/download/extension.go b/pkg/download/extension.go index 4a5a5023..f749ba78 100644 --- a/pkg/download/extension.go +++ b/pkg/download/extension.go @@ -243,7 +243,7 @@ func (d *Downloader) triggerOnResolve(req *base.Request) (res *base.Resource) { gopeed.Logger.logger.Warn().Err(err).Msgf("[%s] resource invalid", ext.buildIdentity()) return } - ctx.Res.CalcSize() + ctx.Res.CalcSize(nil) } res = ctx.Res }, diff --git a/pkg/download/model.go b/pkg/download/model.go index 43d066b2..f3aee75f 100644 --- a/pkg/download/model.go +++ b/pkg/download/model.go @@ -22,7 +22,6 @@ type Task struct { Meta *fetcher.FetcherMeta `json:"meta"` Status base.Status `json:"status"` Progress *Progress `json:"progress"` - Size int64 `json:"size"` CreatedAt time.Time `json:"createdAt"` fetcherBuilder fetcher.FetcherBuilder @@ -50,7 +49,6 @@ func (t *Task) clone() *Task { Meta: t.Meta, Status: t.Status, Progress: t.Progress, - Size: t.Size, CreatedAt: t.CreatedAt, } } diff --git a/ui/flutter/assets/locales/en_US.json b/ui/flutter/assets/locales/en_US.json index 042f72c3..7515a079 100644 --- a/ui/flutter/assets/locales/en_US.json +++ b/ui/flutter/assets/locales/en_US.json @@ -12,6 +12,7 @@ "donate": "Donate", "exit": "Exit", "create": "Create Task", + "directDownload": "Direct Download", "advancedOptions": "Advanced Options", "downloadLink": "Download Link", "downloadLinkValid": "Please enter the download link", diff --git a/ui/flutter/assets/locales/zh_CN.json b/ui/flutter/assets/locales/zh_CN.json index 9c2fa81b..bb623ce1 100644 --- a/ui/flutter/assets/locales/zh_CN.json +++ b/ui/flutter/assets/locales/zh_CN.json @@ -12,6 +12,7 @@ "donate": "打赏", "exit": "退出", "create": "创建任务", + "directDownload": "直接下载", "advancedOptions": "高级选项", "downloadLink": "下载链接", "downloadLinkValid": "请输入下载链接", diff --git a/ui/flutter/lib/api/model/task.dart b/ui/flutter/lib/api/model/task.dart index 1b420ed1..67baa0c9 100644 --- a/ui/flutter/lib/api/model/task.dart +++ b/ui/flutter/lib/api/model/task.dart @@ -12,7 +12,6 @@ class Task { Meta meta; Status status; Progress progress; - int size; DateTime createdAt; Task({ @@ -20,7 +19,6 @@ class Task { required this.meta, required this.status, required this.progress, - required this.size, required this.createdAt, }); diff --git a/ui/flutter/lib/api/model/task.g.dart b/ui/flutter/lib/api/model/task.g.dart index 000955e4..43afce32 100644 --- a/ui/flutter/lib/api/model/task.g.dart +++ b/ui/flutter/lib/api/model/task.g.dart @@ -11,7 +11,6 @@ Task _$TaskFromJson(Map json) => Task( meta: Meta.fromJson(json['meta'] as Map), status: $enumDecode(_$StatusEnumMap, json['status']), progress: Progress.fromJson(json['progress'] as Map), - size: json['size'] as int, createdAt: DateTime.parse(json['createdAt'] as String), ); @@ -20,7 +19,6 @@ Map _$TaskToJson(Task instance) => { 'meta': instance.meta.toJson(), 'status': _$StatusEnumMap[instance.status]!, 'progress': instance.progress.toJson(), - 'size': instance.size, 'createdAt': instance.createdAt.toIso8601String(), }; diff --git a/ui/flutter/lib/app/modules/create/controllers/create_controller.dart b/ui/flutter/lib/app/modules/create/controllers/create_controller.dart index 79b08151..f3594de4 100644 --- a/ui/flutter/lib/app/modules/create/controllers/create_controller.dart +++ b/ui/flutter/lib/app/modules/create/controllers/create_controller.dart @@ -10,8 +10,9 @@ class CreateController extends GetxController final RxList fileInfos = [].obs; final RxList openedFolders = [].obs; final selectedIndexes = [].obs; - final isResolving = false.obs; + final isConfirming = false.obs; final showAdvanced = false.obs; + final directDownload = false.obs; late TabController advancedTabController; final oldUrl = "".obs; final fileDataUri = "".obs; diff --git a/ui/flutter/lib/app/modules/create/views/create_view.dart b/ui/flutter/lib/app/modules/create/views/create_view.dart index 726a7f22..0d44e37e 100644 --- a/ui/flutter/lib/app/modules/create/views/create_view.dart +++ b/ui/flutter/lib/app/modules/create/views/create_view.dart @@ -24,9 +24,12 @@ import '../../app/controllers/app_controller.dart'; import '../controllers/create_controller.dart'; class CreateView extends GetView { - final _resolveFormKey = GlobalKey(); + final _confirmFormKey = GlobalKey(); final _urlController = TextEditingController(); + final _renameController = TextEditingController(); + final _connectionsController = TextEditingController(); + final _pathController = TextEditingController(); final _confirmController = RoundedLoadingButtonController(); final _httpUaController = TextEditingController(); final _httpCookieController = TextEditingController(); @@ -40,6 +43,17 @@ class CreateView extends GetView { @override Widget build(BuildContext context) { + final appController = Get.find(); + + if (_connectionsController.text.isEmpty) { + _connectionsController.text = appController + .downloaderConfig.value.protocolConfig.http.connections + .toString(); + } + if (_pathController.text.isEmpty) { + _pathController.text = appController.downloaderConfig.value.downloadDir; + } + final String? filePath = Get.rootDelegate.arguments(); if (_urlController.text.isEmpty) { if (filePath?.isNotEmpty ?? false) { @@ -91,214 +105,253 @@ class CreateView extends GetView { onTap: () { FocusScope.of(context).requestFocus(FocusNode()); }, - child: Padding( - padding: - const EdgeInsets.symmetric(vertical: 16.0, horizontal: 24.0), - child: Form( - key: _resolveFormKey, - autovalidateMode: AutovalidateMode.onUserInteraction, - child: Column( - children: [ - Row(children: [ - Expanded( - child: TextFormField( - autofocus: true, - controller: _urlController, - minLines: 1, - maxLines: 5, - decoration: InputDecoration( - hintText: _hitText(), - hintStyle: const TextStyle(fontSize: 12), - labelText: 'downloadLink'.tr, - icon: const Icon(Icons.link), - suffixIcon: IconButton( - onPressed: () { - _urlController.clear(); - controller.clearFileDataUri(); - }, - icon: const Icon(Icons.clear), + child: SingleChildScrollView( + child: Padding( + padding: + const EdgeInsets.symmetric(vertical: 16.0, horizontal: 24.0), + child: Form( + key: _confirmFormKey, + autovalidateMode: AutovalidateMode.onUserInteraction, + child: Column( + children: [ + Row(children: [ + Expanded( + child: TextFormField( + autofocus: true, + controller: _urlController, + minLines: 1, + maxLines: 5, + decoration: InputDecoration( + hintText: _hitText(), + hintStyle: const TextStyle(fontSize: 12), + labelText: 'downloadLink'.tr, + icon: const Icon(Icons.link), + suffixIcon: IconButton( + onPressed: () { + _urlController.clear(); + controller.clearFileDataUri(); + }, + icon: const Icon(Icons.clear), + ), ), + validator: (v) { + return v!.trim().isNotEmpty + ? null + : 'downloadLinkValid'.tr; + }, + onChanged: (v) async { + controller.clearFileDataUri(); + if (controller.oldUrl.value.isEmpty) { + recognizeMagnetUri(v); + } + controller.oldUrl.value = v; + }, ), - validator: (v) { - return v!.trim().isNotEmpty - ? null - : 'downloadLinkValid'.tr; - }, - onChanged: (v) async { - controller.clearFileDataUri(); - if (controller.oldUrl.value.isEmpty) { - recognizeMagnetUri(v); + ), + IconButton( + icon: const Icon(Icons.folder_open), + onPressed: () async { + var pr = await FilePicker.platform.pickFiles( + type: FileType.custom, + allowedExtensions: ["torrent"]); + if (pr != null) { + if (!Util.isWeb()) { + _urlController.text = pr.files[0].path ?? ""; + return; + } + _urlController.text = pr.files[0].name; + controller.setFileDataUri(pr.files[0].bytes!); } - controller.oldUrl.value = v; }, ), - ), - IconButton( - icon: const Icon(Icons.folder_open), - onPressed: () async { - var pr = await FilePicker.platform.pickFiles( - type: FileType.custom, - allowedExtensions: ["torrent"]); - if (pr != null) { - if (!Util.isWeb()) { - _urlController.text = pr.files[0].path ?? ""; - return; - } - _urlController.text = pr.files[0].name; - controller.setFileDataUri(pr.files[0].bytes!); - } - }, - ), - IconButton( - icon: const Icon(Icons.history_rounded), - onPressed: () async { - List resultOfHistories = - await _historyController.getAllHistory(); - // reversing: display last entered history first - List reverseResultOfHistories = - resultOfHistories.reversed.toList(); - // show dialog box to list history - if (context.mounted) { - showGeneralDialog( - barrierColor: Colors.black.withOpacity(0.5), - transitionBuilder: (context, a1, a2, widget) { - return Transform.scale( - scale: a1.value, - child: Opacity( - opacity: a1.value, - child: HistoryView( - isHistoryListEmpty: - reverseResultOfHistories.isEmpty, - historyList: ListView.builder( - itemCount: reverseResultOfHistories.length, - itemBuilder: (context, index) { - return GestureDetector( - onTap: () { - _urlController.text = - reverseResultOfHistories[index]; - Navigator.pop(context); - }, - child: MouseRegion( - cursor: SystemMouseCursors.click, - child: Container( - padding: - const EdgeInsets.symmetric( - horizontal: 8.0, - vertical: 8.0, - ), - margin: - const EdgeInsets.symmetric( - horizontal: 10.0, - vertical: 8.0, - ), - decoration: BoxDecoration( - color: Theme.of(context) - .colorScheme - .background, - borderRadius: - BorderRadius.circular(10.0), - ), - child: Text( - reverseResultOfHistories[index] - .toString(), + IconButton( + icon: const Icon(Icons.history_rounded), + onPressed: () async { + List resultOfHistories = + await _historyController.getAllHistory(); + // reversing: display last entered history first + List reverseResultOfHistories = + resultOfHistories.reversed.toList(); + // show dialog box to list history + if (context.mounted) { + showGeneralDialog( + barrierColor: Colors.black.withOpacity(0.5), + transitionBuilder: (context, a1, a2, widget) { + return Transform.scale( + scale: a1.value, + child: Opacity( + opacity: a1.value, + child: HistoryView( + isHistoryListEmpty: + reverseResultOfHistories.isEmpty, + historyList: ListView.builder( + itemCount: + reverseResultOfHistories.length, + itemBuilder: (context, index) { + return GestureDetector( + onTap: () { + _urlController.text = + reverseResultOfHistories[ + index]; + Navigator.pop(context); + }, + child: MouseRegion( + cursor: SystemMouseCursors.click, + child: Container( + padding: + const EdgeInsets.symmetric( + horizontal: 8.0, + vertical: 8.0, + ), + margin: + const EdgeInsets.symmetric( + horizontal: 10.0, + vertical: 8.0, + ), + decoration: BoxDecoration( + color: Theme.of(context) + .colorScheme + .background, + borderRadius: + BorderRadius.circular( + 10.0), + ), + child: Text( + reverseResultOfHistories[ + index] + .toString(), + ), ), ), - ), - ); - }, + ); + }, + ), ), ), - ), - ); - }, - transitionDuration: - const Duration(milliseconds: 250), - barrierDismissible: true, - barrierLabel: '', - context: context, - pageBuilder: (context, animation1, animation2) { - return const Text('PAGE BUILDER'); - }, - ); - } - }, - ), - ] - //.where((e) => e != null).map((e) => e!).toList(), + ); + }, + transitionDuration: + const Duration(milliseconds: 250), + barrierDismissible: true, + barrierLabel: '', + context: context, + pageBuilder: (context, animation1, animation2) { + return const Text('PAGE BUILDER'); + }, + ); + } + }, ), - Obx( - () => Visibility( - visible: controller.showAdvanced.value, - child: Padding( - padding: const EdgeInsets.only(left: 40, top: 15), - child: Column( - children: [ - TabBar( - controller: controller.advancedTabController, - tabs: const [ - Tab( - text: 'HTTP', - ), - Tab( - text: 'BitTorrent', - ) - ], - ), - AutoScaleTabBarView( - controller: controller.advancedTabController, - children: [ - Column( - children: [ - TextFormField( - controller: _httpUaController, - decoration: const InputDecoration( - labelText: 'User-Agent', - )), - TextFormField( - controller: _httpCookieController, - decoration: const InputDecoration( - labelText: 'Cookie', - )), - TextFormField( - controller: _httpRefererController, - decoration: const InputDecoration( - labelText: 'Referer', - )), - ], - ), - Column( - children: [ - TextFormField( - controller: _btTrackerController, - maxLines: 5, - decoration: InputDecoration( - labelText: 'Trakers', - hintText: 'addTrackerHit'.tr, - )), - ], - ) - ], - ) + ]), + Padding( + padding: const EdgeInsets.only(left: 40), + child: Column(children: [ + TextField( + controller: _renameController, + decoration: InputDecoration(labelText: 'rename'.tr), + ), + TextField( + controller: _connectionsController, + decoration: InputDecoration( + labelText: 'connections'.tr, + ), + keyboardType: TextInputType.number, + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly, + NumericalRangeFormatter(min: 1, max: 256), ], ), - ), - ), - ), - Center( - child: Padding( - padding: const EdgeInsets.only(top: 15), - child: Column( - children: [ - Padding( - padding: const EdgeInsets.only(right: 10), - child: TextButton( - onPressed: () { - controller.showAdvanced.value = - !controller.showAdvanced.value; - }, - child: Row( - mainAxisSize: MainAxisSize.min, + DirectorySelector( + controller: _pathController, + ), + Obx( + () => Visibility( + visible: controller.showAdvanced.value, + child: Column( + children: [ + TabBar( + controller: controller.advancedTabController, + tabs: const [ + Tab( + text: 'HTTP', + ), + Tab( + text: 'BitTorrent', + ) + ], + ), + AutoScaleTabBarView( + controller: controller.advancedTabController, children: [ + Column( + children: [ + TextFormField( + controller: _httpUaController, + decoration: const InputDecoration( + labelText: 'User-Agent', + )), + TextFormField( + controller: _httpCookieController, + decoration: const InputDecoration( + labelText: 'Cookie', + )), + TextFormField( + controller: _httpRefererController, + decoration: const InputDecoration( + labelText: 'Referer', + )), + ], + ), + Column( + children: [ + TextFormField( + controller: _btTrackerController, + maxLines: 5, + decoration: InputDecoration( + labelText: 'Trakers', + hintText: 'addTrackerHit'.tr, + )), + ], + ) + ], + ) + ], + ).paddingOnly(top: 16), + ), + ), + ]), + ), + Center( + child: Padding( + padding: const EdgeInsets.only(top: 15), + child: Column( + children: [ + Row( + mainAxisSize: MainAxisSize.min, + children: [ + TextButton( + onPressed: () { + controller.directDownload.value = + !controller.directDownload.value; + }, + child: Row(children: [ + Obx(() => Checkbox( + value: + controller.directDownload.value, + onChanged: (bool? value) { + controller.directDownload.value = + value ?? false; + }, + )), + Text('directDownload'.tr), + ]), + ), + TextButton( + onPressed: () { + controller.showAdvanced.value = + !controller.showAdvanced.value; + }, + child: Row(children: [ Obx(() => Checkbox( value: controller.showAdvanced.value, onChanged: (bool? value) { @@ -308,22 +361,24 @@ class CreateView extends GetView { )), Text('advancedOptions'.tr), ]), + ), + ], ), - ), - SizedBox( - width: 150, - child: RoundedLoadingButton( - color: Get.theme.colorScheme.secondary, - onPressed: _doResolve, - controller: _confirmController, - child: Text('confirm'.tr), + SizedBox( + width: 150, + child: RoundedLoadingButton( + color: Get.theme.colorScheme.secondary, + onPressed: _doConfirm, + controller: _confirmController, + child: Text('confirm'.tr), + ), ), - ), - ], + ], + ), ), ), - ), - ], + ], + ), ), ), ), @@ -346,45 +401,54 @@ class CreateView extends GetView { } } - Future _doResolve() async { - if (controller.isResolving.value) { + Future _doConfirm() async { + if (controller.isConfirming.value) { return; } - controller.isResolving.value = true; + controller.isConfirming.value = true; try { _confirmController.start(); - if (_resolveFormKey.currentState!.validate()) { - // check if is multi line urls - final urls = Util.textToLines(_urlController.text); - ResolveResult rr; - bool isMultiLine = false; - if (urls.length > 1) { - isMultiLine = true; - rr = ResolveResult( - res: Resource( - files: urls - .map((u) => FileInfo( - name: u, - req: Request(url: u, extra: parseReqExtra(u)))) - .toList())); + if (_confirmFormKey.currentState!.validate()) { + final submitUrl = Util.isWeb() && controller.fileDataUri.isNotEmpty + ? controller.fileDataUri.value + : _urlController.text; + + final urls = Util.textToLines(submitUrl); + // Add url to the history + for (final url in urls) { + _historyController.addHistory(url); + } + /* + Check if is direct download, there has two ways to direct download + 1. Direct download option is checked + 2. Muli line urls + */ + final isMultiLine = urls.length > 1; + final isDirect = controller.directDownload.value || isMultiLine; + if (isDirect) { + await Future.wait(urls.map((url) { + return createTask(CreateTask( + req: Request(url: url, extra: parseReqExtra(url)), + opt: Options( + name: isMultiLine ? "" : _renameController.text, + path: _pathController.text, + selectFiles: [], + extra: parseReqOpts()))); + })); + Get.rootDelegate.offNamed(Routes.TASK); } else { - final submitUrl = Util.isWeb() && controller.fileDataUri.isNotEmpty - ? controller.fileDataUri.value - : _urlController.text; - // add final submitUrl to the history - _historyController.addHistory(submitUrl); - rr = await resolve(Request( + final rr = await resolve(Request( url: submitUrl, extra: parseReqExtra(_urlController.text), )); + await _showResolveDialog(rr); } - await _showResolveDialog(rr, isMultiLine); } } catch (e) { showErrorMessage(e); } finally { _confirmController.reset(); - controller.isResolving.value = false; + controller.isConfirming.value = false; } } @@ -407,6 +471,13 @@ class CreateView extends GetView { return reqExtra; } + Object? parseReqOpts() { + return _connectionsController.text.isEmpty + ? null + : (OptsExtraHttp() + ..connections = int.parse(_connectionsController.text)); + } + String _hitText() { return 'downloadLinkHit'.trParams({ 'append': @@ -414,14 +485,8 @@ class CreateView extends GetView { }); } - Future _showResolveDialog(ResolveResult rr, bool isMuiltiLine) async { - final appController = Get.find(); - + Future _showResolveDialog(ResolveResult rr) async { final createFormKey = GlobalKey(); - final nameController = TextEditingController(); - final connectionsController = TextEditingController(); - final pathController = TextEditingController( - text: appController.downloaderConfig.value.downloadDir); final downloadController = RoundedLoadingButtonController(); return showDialog( context: Get.context!, @@ -440,31 +505,7 @@ class CreateView extends GetView { child: Form( key: createFormKey, autovalidateMode: AutovalidateMode.always, - child: Column( - children: [ - Expanded(child: FileListView(files: rr.res.files)), - TextFormField( - controller: nameController, - decoration: InputDecoration( - labelText: 'rename'.tr, - ), - ), - TextFormField( - controller: connectionsController, - decoration: InputDecoration( - labelText: 'connections'.tr, - ), - keyboardType: TextInputType.number, - inputFormatters: [ - FilteringTextInputFormatter.digitsOnly, - NumericalRangeFormatter(min: 1, max: 256), - ], - ), - DirectorySelector( - controller: pathController, - ), - ], - )), + child: FileListView(files: rr.res.files)), ); }, ), @@ -500,25 +541,19 @@ class CreateView extends GetView { showMessage('tip'.tr, 'noFileSelected'.tr); return; } - Object? optExtra = connectionsController.text.isEmpty - ? null - : (OptsExtraHttp() - ..connections = - int.parse(connectionsController.text)); + final optExtra = parseReqOpts(); if (createFormKey.currentState!.validate()) { if (rr.id.isEmpty) { - // create task batch, there has two ways to batch create task - // 1. from multi line urls - // 2. from extension resolve result + // from extension resolve result await Future.wait( controller.selectedIndexes.map((index) { final file = rr.res.files[index]; return createTask(CreateTask( req: file.req!, opt: Options( - name: isMuiltiLine ? "" : file.name, + name: file.name, path: path.join( - pathController.text, rr.res.name), + _pathController.text, rr.res.name), selectFiles: [], extra: optExtra))); })); @@ -526,8 +561,8 @@ class CreateView extends GetView { await createTask(CreateTask( rid: rr.id, opt: Options( - name: nameController.text, - path: pathController.text, + name: _renameController.text, + path: _pathController.text, selectFiles: controller.selectedIndexes, extra: optExtra))); } diff --git a/ui/flutter/lib/generated/locales.g.dart b/ui/flutter/lib/generated/locales.g.dart index 292f1784..eab8d648 100644 --- a/ui/flutter/lib/generated/locales.g.dart +++ b/ui/flutter/lib/generated/locales.g.dart @@ -28,6 +28,7 @@ class LocaleKeys { static const donate = 'donate'; static const exit = 'exit'; static const create = 'create'; + static const directDownload = 'directDownload'; static const advancedOptions = 'advancedOptions'; static const downloadLink = 'downloadLink'; static const downloadLinkValid = 'downloadLinkValid'; @@ -106,6 +107,7 @@ class Locales { 'donate': 'Donate', 'exit': 'Exit', 'create': 'Create Task', + 'directDownload': 'Direct Download', 'advancedOptions': 'Advanced Options', 'downloadLink': 'Download Link', 'downloadLinkValid': 'Please enter the download link', @@ -379,6 +381,7 @@ class Locales { 'donate': '打赏', 'exit': '退出', 'create': '创建任务', + 'directDownload': '直接下载', 'advancedOptions': '高级选项', 'downloadLink': '下载链接', 'downloadLinkValid': '请输入下载链接',