From afbda5a587295a987784c10ae292ec20850a3a70 Mon Sep 17 00:00:00 2001 From: lifegpc Date: Sat, 25 May 2024 15:31:21 +0800 Subject: [PATCH] Add eta support --- lib/api/task.dart | 4 ++-- lib/api/task.g.dart | 5 +++-- lib/components/task.dart | 1 + lib/dialog/task_page.dart | 16 ++++++++++++++-- lib/l10n/app_en.arb | 10 +++++++++- lib/l10n/app_zh.arb | 10 +++++++++- lib/utils/duration.dart | 21 +++++++++++++++++++++ 7 files changed, 59 insertions(+), 8 deletions(-) create mode 100644 lib/utils/duration.dart diff --git a/lib/api/task.dart b/lib/api/task.dart index 3033cbc..7268fa9 100644 --- a/lib/api/task.dart +++ b/lib/api/task.dart @@ -78,8 +78,8 @@ class TaskDownloadSingleProgress { final DateTime started; final int downloaded; final double speed; - @JsonKey(name: 'last_updated') - final int lastUpdated; + @JsonKey(name: 'last_updated', fromJson: _fromJson, toJson: _toJson) + final DateTime lastUpdated; static DateTime _fromJson(int d) => DateTime.fromMillisecondsSinceEpoch(d, isUtc: true); static int _toJson(DateTime d) => d.millisecondsSinceEpoch; diff --git a/lib/api/task.g.dart b/lib/api/task.g.dart index c940804..5480003 100644 --- a/lib/api/task.g.dart +++ b/lib/api/task.g.dart @@ -45,7 +45,8 @@ TaskDownloadSingleProgress _$TaskDownloadSingleProgressFromJson( (json['started'] as num).toInt()), downloaded: (json['downloaded'] as num).toInt(), speed: (json['speed'] as num).toDouble(), - lastUpdated: (json['last_updated'] as num).toInt(), + lastUpdated: TaskDownloadSingleProgress._fromJson( + (json['last_updated'] as num).toInt()), ); Map _$TaskDownloadSingleProgressToJson( @@ -61,7 +62,7 @@ Map _$TaskDownloadSingleProgressToJson( 'started': TaskDownloadSingleProgress._toJson(instance.started), 'downloaded': instance.downloaded, 'speed': instance.speed, - 'last_updated': instance.lastUpdated, + 'last_updated': TaskDownloadSingleProgress._toJson(instance.lastUpdated), }; TaskDownloadProgess _$TaskDownloadProgessFromJson(Map json) => diff --git a/lib/components/task.dart b/lib/components/task.dart index 37a5f0f..596e136 100644 --- a/lib/components/task.dart +++ b/lib/components/task.dart @@ -94,6 +94,7 @@ class _TaskView extends State { onTap: () { context.push("/dialog/task/${widget.task.base.id}"); }, + behavior: HitTestBehavior.opaque, child: Column(children: [ _buildText(context), LinearPercentIndicator( diff --git a/lib/dialog/task_page.dart b/lib/dialog/task_page.dart index bf9d315..88b43ac 100644 --- a/lib/dialog/task_page.dart +++ b/lib/dialog/task_page.dart @@ -4,6 +4,7 @@ import 'package:go_router/go_router.dart'; import 'package:percent_indicator/linear_percent_indicator.dart'; import '../api/task.dart'; import '../globals.dart'; +import '../utils/duration.dart'; import '../utils/filesize.dart'; class _KeyValue extends StatelessWidget { @@ -151,7 +152,8 @@ class _TaskPage extends State { _KeyValue(i18n.totalPages, p.totalPage.toString(), fontSize: 16), _KeyValue(i18n.downloadedSize2, getFileSize(p.downloadedBytes), fontSize: 16), - _KeyValue(i18n.speed, "${getFileSize((speed * 1000).toInt())}/s", fontSize: 16), + _KeyValue(i18n.speed, "${getFileSize((speed * 1000).toInt())}/s", + fontSize: 16), ]); } int now = 0; @@ -199,12 +201,21 @@ class _TaskPage extends State { } final p = task.progress as TaskDownloadProgess; if (p.details.isEmpty) return SliverToBoxAdapter(child: Container()); + final i18n = AppLocalizations.of(context)!; return SliverList.builder( itemCount: p.details.length, itemBuilder: (context, index) { final d = p.details[index]; final percent = d.total == 0 ? 0.0 : d.downloaded / d.total; final percentText = "${(percent * 100).toStringAsFixed(2)}%"; + final avgSpeed = d.started.millisecondsSinceEpoch == 0 + ? 0.0 + : d.downloaded / + (d.lastUpdated.millisecondsSinceEpoch - + d.started.millisecondsSinceEpoch); + final eta = d.total == 0 + ? double.infinity + : (d.total - d.downloaded) / avgSpeed; return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ SelectableText("${d.name}(${d.width}x${d.height})"), LinearPercentIndicator( @@ -223,7 +234,8 @@ class _TaskPage extends State { Expanded( child: Text( "${getFileSize(d.downloaded)}/${getFileSize(d.total)}")), - Text("${getFileSize((d.speed * 1000).toInt())}/s"), + Text( + "${getFileSize((d.speed * 1000).toInt())}/s${i18n.comma}${fmtDuration(context, eta)}"), ]), ]); }, diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 13bbb0f..53824bf 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -181,5 +181,13 @@ }, "comma": ", ", "downloadedSize2": "Downloaded size", - "speed": "Speed" + "speed": "Speed", + "days": "{num} days", + "@days": { + "placeholders": { + "num": { + "type": "int" + } + } + } } diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb index df6111d..2009046 100644 --- a/lib/l10n/app_zh.arb +++ b/lib/l10n/app_zh.arb @@ -181,5 +181,13 @@ }, "comma": ",", "downloadedSize2": "已下载大小", - "speed": "速度" + "speed": "速度", + "days": "{num} 天", + "@days": { + "placeholders": { + "num": { + "type": "int" + } + } + } } diff --git a/lib/utils/duration.dart b/lib/utils/duration.dart new file mode 100644 index 0000000..ec03b01 --- /dev/null +++ b/lib/utils/duration.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; + +String fmtDuration(BuildContext context, double ms) { + if (ms.isInfinite) { + return "∞"; + } + final dur = ms.toInt() ~/ 1000; + String re = ""; + if (dur >= 86400) { + final i18n = AppLocalizations.of(context)!; + re += "${i18n.days(dur ~/ 86400)} "; + } + if (dur >= 3600) { + re += "${(dur ~/ 3600).toString().padLeft(2, '0')}:"; + } + final min = (dur ~/ 60).toString().padLeft(2, '0'); + final secs = (dur % 60).toString().padLeft(2, '0'); + re += "$min:$secs"; + return re; +}