From 4237de585d9042156234d283b1269359ac594d03 Mon Sep 17 00:00:00 2001 From: lifegpc Date: Fri, 24 May 2024 20:56:06 +0800 Subject: [PATCH] Update task manager --- lib/api/eh.dart | 7 +++ lib/components/task.dart | 98 ++++++++++++++++++++++++++++++++++++++++ lib/l10n/app_en.arb | 3 +- lib/l10n/app_zh.arb | 3 +- lib/task.dart | 39 ++++++++++++++++ lib/task_manager.dart | 3 +- pubspec.lock | 8 ++++ pubspec.yaml | 1 + 8 files changed, 159 insertions(+), 3 deletions(-) create mode 100644 lib/components/task.dart diff --git a/lib/api/eh.dart b/lib/api/eh.dart index d5dc643..33e354e 100644 --- a/lib/api/eh.dart +++ b/lib/api/eh.dart @@ -1,5 +1,6 @@ import 'package:json_annotation/json_annotation.dart'; import 'api_result.dart'; +import '../globals.dart'; part 'eh.g.dart'; @@ -75,6 +76,12 @@ class GalleryMetadataSingle { factory GalleryMetadataSingle.fromJson(Map json) => _$GalleryMetadataSingleFromJson(json); Map toJson() => _$GalleryMetadataSingleToJson(this); + + String get preferredTitle => prefs.getBool("useTitleJpn") == true + ? titleJpn.isEmpty + ? title + : titleJpn + : title; } class EHMetaInfo { diff --git a/lib/components/task.dart b/lib/components/task.dart new file mode 100644 index 0000000..30fd2ce --- /dev/null +++ b/lib/components/task.dart @@ -0,0 +1,98 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:percent_indicator/linear_percent_indicator.dart'; +import '../api/task.dart'; +import '../globals.dart'; + +class TaskView extends StatefulWidget { + const TaskView(this.task, {super.key}); + final TaskDetail task; + + @override + State createState() => _TaskView(); +} + +class _TaskView extends State { + @override + void initState() { + listener.on("task_meta_updated", _onStateChanged); + listener.on("task_progress_updated", _onProgressUpdated); + super.initState(); + } + + void _onStateChanged(dynamic _) { + setState(() {}); + } + + void _onProgressUpdated(dynamic arg) { + final id = arg as int; + if (id != widget.task.base.id) return; + setState(() {}); + } + + double get percent { + if (widget.task.status == TaskStatus.finished) return 1; + if (widget.task.status != TaskStatus.running) return 0; + if (widget.task.progress == null) return 0; + switch (widget.task.base.type) { + case TaskType.download: + final progress = widget.task.progress as TaskDownloadProgess; + return progress.downloadedPage / progress.totalPage; + case TaskType.exportZip: + final progress = widget.task.progress as TaskExportZipProgress; + return progress.addedPage / progress.totalPage; + case TaskType.fixGalleryPage: + final progress = widget.task.progress as TaskFixGalleryPageProgress; + return progress.checkedGallery / progress.totalGallery; + case TaskType.updateMeiliSearchData: + final progress = + widget.task.progress as TaskUpdateMeiliSearchDataProgress; + return progress.updatedGallery / progress.totalGallery; + } + } + + String get percentText { + return "${(percent * 100).toStringAsFixed(2)}%"; + } + + @override + void dispose() { + listener.removeEventListener("task_meta_updated", _onStateChanged); + listener.removeEventListener("task_progress_updated", _onProgressUpdated); + super.dispose(); + } + + Widget _buildText(BuildContext context) { + final i18n = AppLocalizations.of(context)!; + if (widget.task.base.type == TaskType.download) { + final gid = widget.task.base.gid; + return Row(children: [ + Padding( + padding: const EdgeInsets.only(right: 8.0), + child: Text(i18n.downloadTask)), + Text(tasks.meta.containsKey(gid) + ? tasks.meta[gid]!.preferredTitle + : gid.toString()), + ]); + } + return Container(); + } + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.all(8.0), + child: Column(children: [ + _buildText(context), + LinearPercentIndicator( + progressColor: Colors.green, + lineHeight: 20.0, + barRadius: const Radius.circular(10), + padding: EdgeInsets.zero, + center: Text(percentText), + percent: percent, + ), + ]), + ); + } +} diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 1c3c89c..0d527d9 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -155,5 +155,6 @@ "createDownloadTask": "Create Download Task", "galleryURL": "Gallery URL", "galleryToken": "Gallery Token", - "randomFileSecret": "The secret of token to access random file without login" + "randomFileSecret": "The secret of token to access random file without login", + "downloadTask": "Download Task" } diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb index f876735..6f20bac 100644 --- a/lib/l10n/app_zh.arb +++ b/lib/l10n/app_zh.arb @@ -155,5 +155,6 @@ "createDownloadTask": "新建下载任务", "galleryURL": "画廊地址", "galleryToken": "画廊令牌", - "randomFileSecret": "生成无需登录即可访问随机文件的令牌的密钥" + "randomFileSecret": "生成无需登录即可访问随机文件的令牌的密钥", + "downloadTask": "下载任务" } diff --git a/lib/task.dart b/lib/task.dart index 11a31a4..78a515d 100644 --- a/lib/task.dart +++ b/lib/task.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:convert'; import 'package:logging/logging.dart'; import 'package:web_socket_channel/web_socket_channel.dart'; +import 'api/eh.dart'; import 'api/task.dart'; import 'globals.dart'; import 'utils/websocket.dart'; @@ -15,6 +16,10 @@ class TaskManager { bool _allowReconnect = true; Timer? _reconnectTimer; List tasksList = []; + Map meta = {}; + bool _isFetching = false; + List peddingGids = []; + List peddingTokens = []; void clear() { tasks.clear(); _channel?.stream.drain(); @@ -22,7 +27,36 @@ class TaskManager { _closed = true; } + void fetchMeta() async { + if (_isFetching) return; + try { + if (peddingGids.isEmpty) return; + _isFetching = true; + final re = (await api.getMetaInfo(peddingGids, peddingTokens)).unwrap(); + for (final e in re.metas.entries) { + if (e.value.ok) { + meta[e.key] = e.value.unwrap(); + final index = peddingGids.indexOf(e.key); + if (index > -1) { + peddingGids.removeAt(index); + peddingTokens.removeAt(index); + } + } else { + _log.warning("Gallery id ${e.key}:", e.value.unwrapErr()); + } + } + listener.tryEmit("task_meta_updated", null); + } catch (e) { + _log.warning("Failed to fetch metadatas:", e); + } + _isFetching = false; + } + void addToTasksList(Task task, TaskStatus status) { + if (task.type == TaskType.download && !meta.containsKey(task.gid)) { + peddingGids.add(task.gid); + peddingTokens.add(task.token); + } if (status == TaskStatus.finished) { tasksList.add(task.id); return; @@ -66,6 +100,7 @@ class TaskManager { addToTasksList(task, status); } listener.tryEmit("task_list_changed", null); + fetchMeta(); } else if (type == "new_task") { final task = Task.fromJson(data["detail"] as Map); tasks[task.id] = TaskDetail( @@ -74,6 +109,7 @@ class TaskManager { ); addToTasksList(task, TaskStatus.wait); listener.tryEmit("task_list_changed", null); + fetchMeta(); } else if (type == "task_started") { final task = Task.fromJson(data["detail"] as Map); tasks.update(task.id, (value) { @@ -83,6 +119,7 @@ class TaskManager { return value; }, ifAbsent: () { addToTasksList(task, TaskStatus.running); + fetchMeta(); return TaskDetail( base: task, status: TaskStatus.running, @@ -99,6 +136,7 @@ class TaskManager { return value; }); listener.tryEmit("task_list_changed", null); + fetchMeta(); } } else if (type == "task_progress") { final task = @@ -108,6 +146,7 @@ class TaskManager { value.progress = task.detail; return value; }); + listener.tryEmit("task_progress_updated", task.taskId); } } else if (type == "task_updated") { final task = Task.fromJson(data["detail"] as Map); diff --git a/lib/task_manager.dart b/lib/task_manager.dart index 95c34b5..96a163f 100644 --- a/lib/task_manager.dart +++ b/lib/task_manager.dart @@ -5,6 +5,7 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:go_router/go_router.dart'; import 'api/task.dart'; +import 'components/task.dart'; import 'globals.dart'; enum TaskStatusFilterFlag with EnumFlag { @@ -98,7 +99,7 @@ class _TaskManagerPage extends State return Padding( padding: const EdgeInsets.all(8), key: ValueKey("task_${task.base.id}"), - child: Text("TODO ${task.base.id}")); + child: TaskView(task)); } Widget _proxyDecorator(Widget child, int index, Animation animation) { diff --git a/pubspec.lock b/pubspec.lock index 2d4571c..2962fcf 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -654,6 +654,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.2.1" + percent_indicator: + dependency: "direct main" + description: + name: percent_indicator + sha256: c37099ad833a883c9d71782321cb65c3a848c21b6939b6185f0ff6640d05814c + url: "https://pub.dev" + source: hosted + version: "4.2.3" petitparser: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 16d7472..eb049dd 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -34,6 +34,7 @@ dependencies: palette_generator: ^0.3.3+3 path: ^1.8.3 path_provider: ^2.1.0 + percent_indicator: ^4.2.3 photo_view: ^0.15.0 retrofit: ^4.0.1 shared_preferences: ^2.2.0