mirror of
https://github.com/lifegpc/eh_downloader_flutter.git
synced 2026-06-06 05:49:03 +08:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -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<String, dynamic> json) =>
|
||||
_$GalleryMetadataSingleFromJson(json);
|
||||
Map<String, dynamic> toJson() => _$GalleryMetadataSingleToJson(this);
|
||||
|
||||
String get preferredTitle => prefs.getBool("useTitleJpn") == true
|
||||
? titleJpn.isEmpty
|
||||
? title
|
||||
: titleJpn
|
||||
: title;
|
||||
}
|
||||
|
||||
class EHMetaInfo {
|
||||
|
||||
109
lib/components/task.dart
Normal file
109
lib/components/task.dart
Normal file
@@ -0,0 +1,109 @@
|
||||
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, this.index, {super.key});
|
||||
final TaskDetail task;
|
||||
final int index;
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _TaskView();
|
||||
}
|
||||
|
||||
class _TaskView extends State<TaskView> {
|
||||
@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: GestureDetector(
|
||||
onTap: () {},
|
||||
child: Row(children: [
|
||||
Expanded(
|
||||
child: Column(children: [
|
||||
_buildText(context),
|
||||
LinearPercentIndicator(
|
||||
animation: true,
|
||||
animateFromLastPercent: true,
|
||||
progressColor: Colors.green,
|
||||
lineHeight: 20.0,
|
||||
barRadius: const Radius.circular(10),
|
||||
padding: EdgeInsets.zero,
|
||||
center: Text(percentText,
|
||||
style: const TextStyle(color: Colors.black)),
|
||||
percent: percent,
|
||||
),
|
||||
])),
|
||||
ReorderableDragStartListener(
|
||||
index: widget.index, child: const Icon(Icons.reorder)),
|
||||
])),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -155,5 +155,6 @@
|
||||
"createDownloadTask": "新建下载任务",
|
||||
"galleryURL": "画廊地址",
|
||||
"galleryToken": "画廊令牌",
|
||||
"randomFileSecret": "生成无需登录即可访问随机文件的令牌的密钥"
|
||||
"randomFileSecret": "生成无需登录即可访问随机文件的令牌的密钥",
|
||||
"downloadTask": "下载任务"
|
||||
}
|
||||
|
||||
@@ -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<int> tasksList = [];
|
||||
Map<int, GalleryMetadataSingle> meta = {};
|
||||
bool _isFetching = false;
|
||||
List<int> peddingGids = [];
|
||||
List<String> 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<String, dynamic>);
|
||||
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<String, dynamic>);
|
||||
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<String, dynamic>);
|
||||
|
||||
@@ -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<TaskManagerPage>
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
key: ValueKey("task_${task.base.id}"),
|
||||
child: Text("TODO ${task.base.id}"));
|
||||
child: TaskView(task, index));
|
||||
}
|
||||
|
||||
Widget _proxyDecorator(Widget child, int index, Animation<double> animation) {
|
||||
@@ -116,7 +117,15 @@ class _TaskManagerPage extends State<TaskManagerPage>
|
||||
);
|
||||
}
|
||||
|
||||
void _onReorder(int oldIndex, int newIndex) {}
|
||||
void _onReorder(int oldIndex, int newIndex) {
|
||||
setState(() {
|
||||
if (oldIndex < newIndex) {
|
||||
newIndex -= 1;
|
||||
}
|
||||
final task = tasks.tasksList.removeAt(oldIndex);
|
||||
tasks.tasksList.insert(newIndex, task);
|
||||
});
|
||||
}
|
||||
|
||||
Widget _buildList(BuildContext context) {
|
||||
return SliverReorderableList(
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user