mirror of
https://github.com/StarCitizenToolBox/app.git
synced 2026-01-13 19:50:28 +00:00
feat: use rust rqbit to replace aria2c
This commit is contained in:
parent
c5de9e2252
commit
4315e36cbe
@ -82,7 +82,7 @@ final class AppGlobalModelProvider
|
||||
}
|
||||
}
|
||||
|
||||
String _$appGlobalModelHash() => r'9729c3ffb891e5899abbb3dc7d2d25ef13a442e7';
|
||||
String _$appGlobalModelHash() => r'0e46d72594d94e2beb4d2ccb8616eb37facba288';
|
||||
|
||||
abstract class _$AppGlobalModel extends $Notifier<AppGlobalState> {
|
||||
AppGlobalState build();
|
||||
|
||||
@ -6,7 +6,8 @@ import 'package:flutter/services.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||
|
||||
class BinaryModuleConf {
|
||||
static const _modules = {"aria2c": "0"};
|
||||
// aria2c has been replaced by rqbit (Rust-based torrent library)
|
||||
static const _modules = <String, String>{};
|
||||
|
||||
static Future extractModule(List<String> modules, String workingDir) async {
|
||||
for (var m in _modules.entries) {
|
||||
|
||||
198
lib/common/rust/api/downloader_api.dart
Normal file
198
lib/common/rust/api/downloader_api.dart
Normal file
@ -0,0 +1,198 @@
|
||||
// This file is automatically generated, so please do not edit it.
|
||||
// @generated by `flutter_rust_bridge`@ 2.11.1.
|
||||
|
||||
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
|
||||
|
||||
import '../frb_generated.dart';
|
||||
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
|
||||
|
||||
// These functions are ignored because they are not marked as `pub`: `get_task_status`
|
||||
// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `clone`, `clone`, `clone`, `fmt`, `fmt`, `fmt`
|
||||
|
||||
/// Initialize the download manager session
|
||||
void downloaderInit({required String downloadDir}) => RustLib.instance.api
|
||||
.crateApiDownloaderApiDownloaderInit(downloadDir: downloadDir);
|
||||
|
||||
/// Check if the downloader is initialized
|
||||
bool downloaderIsInitialized() =>
|
||||
RustLib.instance.api.crateApiDownloaderApiDownloaderIsInitialized();
|
||||
|
||||
/// Add a torrent from bytes (e.g., .torrent file content)
|
||||
Future<BigInt> downloaderAddTorrent({
|
||||
required List<int> torrentBytes,
|
||||
String? outputFolder,
|
||||
List<String>? trackers,
|
||||
}) => RustLib.instance.api.crateApiDownloaderApiDownloaderAddTorrent(
|
||||
torrentBytes: torrentBytes,
|
||||
outputFolder: outputFolder,
|
||||
trackers: trackers,
|
||||
);
|
||||
|
||||
/// Add a torrent from a magnet link
|
||||
Future<BigInt> downloaderAddMagnet({
|
||||
required String magnetLink,
|
||||
String? outputFolder,
|
||||
List<String>? trackers,
|
||||
}) => RustLib.instance.api.crateApiDownloaderApiDownloaderAddMagnet(
|
||||
magnetLink: magnetLink,
|
||||
outputFolder: outputFolder,
|
||||
trackers: trackers,
|
||||
);
|
||||
|
||||
/// Add a torrent from URL (HTTP download not supported, only torrent file URLs)
|
||||
Future<BigInt> downloaderAddUrl({
|
||||
required String url,
|
||||
String? outputFolder,
|
||||
List<String>? trackers,
|
||||
}) => RustLib.instance.api.crateApiDownloaderApiDownloaderAddUrl(
|
||||
url: url,
|
||||
outputFolder: outputFolder,
|
||||
trackers: trackers,
|
||||
);
|
||||
|
||||
/// Pause a download task
|
||||
Future<void> downloaderPause({required BigInt taskId}) =>
|
||||
RustLib.instance.api.crateApiDownloaderApiDownloaderPause(taskId: taskId);
|
||||
|
||||
/// Resume a download task
|
||||
Future<void> downloaderResume({required BigInt taskId}) =>
|
||||
RustLib.instance.api.crateApiDownloaderApiDownloaderResume(taskId: taskId);
|
||||
|
||||
/// Remove a download task
|
||||
Future<void> downloaderRemove({
|
||||
required BigInt taskId,
|
||||
required bool deleteFiles,
|
||||
}) => RustLib.instance.api.crateApiDownloaderApiDownloaderRemove(
|
||||
taskId: taskId,
|
||||
deleteFiles: deleteFiles,
|
||||
);
|
||||
|
||||
/// Get information about a specific task
|
||||
Future<DownloadTaskInfo> downloaderGetTaskInfo({required BigInt taskId}) =>
|
||||
RustLib.instance.api.crateApiDownloaderApiDownloaderGetTaskInfo(
|
||||
taskId: taskId,
|
||||
);
|
||||
|
||||
/// Get all tasks
|
||||
Future<List<DownloadTaskInfo>> downloaderGetAllTasks() =>
|
||||
RustLib.instance.api.crateApiDownloaderApiDownloaderGetAllTasks();
|
||||
|
||||
/// Get global statistics
|
||||
Future<DownloadGlobalStat> downloaderGetGlobalStats() =>
|
||||
RustLib.instance.api.crateApiDownloaderApiDownloaderGetGlobalStats();
|
||||
|
||||
/// Check if a task with given name exists
|
||||
Future<bool> downloaderIsNameInTask({required String name}) => RustLib
|
||||
.instance
|
||||
.api
|
||||
.crateApiDownloaderApiDownloaderIsNameInTask(name: name);
|
||||
|
||||
/// Pause all tasks
|
||||
Future<void> downloaderPauseAll() =>
|
||||
RustLib.instance.api.crateApiDownloaderApiDownloaderPauseAll();
|
||||
|
||||
/// Resume all tasks
|
||||
Future<void> downloaderResumeAll() =>
|
||||
RustLib.instance.api.crateApiDownloaderApiDownloaderResumeAll();
|
||||
|
||||
/// Stop the downloader session
|
||||
Future<void> downloaderStop() =>
|
||||
RustLib.instance.api.crateApiDownloaderApiDownloaderStop();
|
||||
|
||||
/// Global statistics
|
||||
class DownloadGlobalStat {
|
||||
final BigInt downloadSpeed;
|
||||
final BigInt uploadSpeed;
|
||||
final BigInt numActive;
|
||||
final BigInt numWaiting;
|
||||
|
||||
const DownloadGlobalStat({
|
||||
required this.downloadSpeed,
|
||||
required this.uploadSpeed,
|
||||
required this.numActive,
|
||||
required this.numWaiting,
|
||||
});
|
||||
|
||||
static Future<DownloadGlobalStat> default_() =>
|
||||
RustLib.instance.api.crateApiDownloaderApiDownloadGlobalStatDefault();
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
downloadSpeed.hashCode ^
|
||||
uploadSpeed.hashCode ^
|
||||
numActive.hashCode ^
|
||||
numWaiting.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is DownloadGlobalStat &&
|
||||
runtimeType == other.runtimeType &&
|
||||
downloadSpeed == other.downloadSpeed &&
|
||||
uploadSpeed == other.uploadSpeed &&
|
||||
numActive == other.numActive &&
|
||||
numWaiting == other.numWaiting;
|
||||
}
|
||||
|
||||
/// Download task information
|
||||
class DownloadTaskInfo {
|
||||
final BigInt id;
|
||||
final String name;
|
||||
final DownloadTaskStatus status;
|
||||
final BigInt totalBytes;
|
||||
final BigInt downloadedBytes;
|
||||
final BigInt uploadedBytes;
|
||||
final BigInt downloadSpeed;
|
||||
final BigInt uploadSpeed;
|
||||
final double progress;
|
||||
final BigInt numPeers;
|
||||
final String outputFolder;
|
||||
|
||||
const DownloadTaskInfo({
|
||||
required this.id,
|
||||
required this.name,
|
||||
required this.status,
|
||||
required this.totalBytes,
|
||||
required this.downloadedBytes,
|
||||
required this.uploadedBytes,
|
||||
required this.downloadSpeed,
|
||||
required this.uploadSpeed,
|
||||
required this.progress,
|
||||
required this.numPeers,
|
||||
required this.outputFolder,
|
||||
});
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
id.hashCode ^
|
||||
name.hashCode ^
|
||||
status.hashCode ^
|
||||
totalBytes.hashCode ^
|
||||
downloadedBytes.hashCode ^
|
||||
uploadedBytes.hashCode ^
|
||||
downloadSpeed.hashCode ^
|
||||
uploadSpeed.hashCode ^
|
||||
progress.hashCode ^
|
||||
numPeers.hashCode ^
|
||||
outputFolder.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is DownloadTaskInfo &&
|
||||
runtimeType == other.runtimeType &&
|
||||
id == other.id &&
|
||||
name == other.name &&
|
||||
status == other.status &&
|
||||
totalBytes == other.totalBytes &&
|
||||
downloadedBytes == other.downloadedBytes &&
|
||||
uploadedBytes == other.uploadedBytes &&
|
||||
downloadSpeed == other.downloadSpeed &&
|
||||
uploadSpeed == other.uploadSpeed &&
|
||||
progress == other.progress &&
|
||||
numPeers == other.numPeers &&
|
||||
outputFolder == other.outputFolder;
|
||||
}
|
||||
|
||||
/// Download task status
|
||||
enum DownloadTaskStatus { initializing, live, paused, error, finished }
|
||||
@ -6,7 +6,6 @@
|
||||
import '../frb_generated.dart';
|
||||
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
|
||||
|
||||
// These functions are ignored because they are not marked as `pub`: `get_process_path`
|
||||
// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `clone`, `clone`, `fmt`, `fmt`
|
||||
|
||||
Future<void> sendNotify({
|
||||
@ -21,27 +20,21 @@ Future<void> sendNotify({
|
||||
appId: appId,
|
||||
);
|
||||
|
||||
/// Get system memory size in GB
|
||||
Future<BigInt> getSystemMemorySizeGb() =>
|
||||
RustLib.instance.api.crateApiWin32ApiGetSystemMemorySizeGb();
|
||||
|
||||
/// Get number of logical processors
|
||||
Future<int> getNumberOfLogicalProcessors() =>
|
||||
RustLib.instance.api.crateApiWin32ApiGetNumberOfLogicalProcessors();
|
||||
|
||||
/// Get all system information at once
|
||||
Future<SystemInfo> getSystemInfo() =>
|
||||
RustLib.instance.api.crateApiWin32ApiGetSystemInfo();
|
||||
|
||||
/// Get GPU info from registry (more accurate VRAM)
|
||||
Future<String> getGpuInfoFromRegistry() =>
|
||||
RustLib.instance.api.crateApiWin32ApiGetGpuInfoFromRegistry();
|
||||
|
||||
/// Resolve shortcut (.lnk) file to get target path
|
||||
Future<String> resolveShortcut({required String lnkPath}) =>
|
||||
RustLib.instance.api.crateApiWin32ApiResolveShortcut(lnkPath: lnkPath);
|
||||
|
||||
/// Open file explorer and select file/folder
|
||||
Future<void> openDirWithExplorer({
|
||||
required String path,
|
||||
required bool isFile,
|
||||
@ -65,19 +58,16 @@ Future<List<ProcessInfo>> getProcessListByName({required String processName}) =>
|
||||
processName: processName,
|
||||
);
|
||||
|
||||
/// Kill processes by name
|
||||
Future<int> killProcessByName({required String processName}) => RustLib
|
||||
.instance
|
||||
.api
|
||||
.crateApiWin32ApiKillProcessByName(processName: processName);
|
||||
|
||||
/// Get disk physical sector size for performance
|
||||
Future<int> getDiskPhysicalSectorSize({required String driveLetter}) => RustLib
|
||||
.instance
|
||||
.api
|
||||
.crateApiWin32ApiGetDiskPhysicalSectorSize(driveLetter: driveLetter);
|
||||
|
||||
/// Create a desktop shortcut
|
||||
Future<void> createDesktopShortcut({
|
||||
required String targetPath,
|
||||
required String shortcutName,
|
||||
@ -86,14 +76,12 @@ Future<void> createDesktopShortcut({
|
||||
shortcutName: shortcutName,
|
||||
);
|
||||
|
||||
/// Run a program with admin privileges (UAC)
|
||||
Future<void> runAsAdmin({required String program, required String args}) =>
|
||||
RustLib.instance.api.crateApiWin32ApiRunAsAdmin(
|
||||
program: program,
|
||||
args: args,
|
||||
);
|
||||
|
||||
/// Start a program (without waiting)
|
||||
Future<void> startProcess({
|
||||
required String program,
|
||||
required List<String> args,
|
||||
@ -102,15 +90,12 @@ Future<void> startProcess({
|
||||
args: args,
|
||||
);
|
||||
|
||||
/// Check if NVME patch is applied
|
||||
Future<bool> checkNvmePatchStatus() =>
|
||||
RustLib.instance.api.crateApiWin32ApiCheckNvmePatchStatus();
|
||||
|
||||
/// Add NVME patch to registry
|
||||
Future<void> addNvmePatch() =>
|
||||
RustLib.instance.api.crateApiWin32ApiAddNvmePatch();
|
||||
|
||||
/// Remove NVME patch from registry
|
||||
Future<void> removeNvmePatch() =>
|
||||
RustLib.instance.api.crateApiWin32ApiRemoveNvmePatch();
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
// ignore_for_file: unused_import, unused_element, unnecessary_import, duplicate_ignore, invalid_use_of_internal_member, annotate_overrides, non_constant_identifier_names, curly_braces_in_flow_control_structures, prefer_const_literals_to_create_immutables, unused_field
|
||||
|
||||
import 'api/asar_api.dart';
|
||||
import 'api/downloader_api.dart';
|
||||
import 'api/http_api.dart';
|
||||
import 'api/ort_api.dart';
|
||||
import 'api/rs_process.dart';
|
||||
@ -71,7 +72,7 @@ class RustLib extends BaseEntrypoint<RustLibApi, RustLibApiImpl, RustLibWire> {
|
||||
String get codegenVersion => '2.11.1';
|
||||
|
||||
@override
|
||||
int get rustContentHash => 1161621087;
|
||||
int get rustContentHash => -1465039096;
|
||||
|
||||
static const kDefaultExternalLibraryLoaderConfig =
|
||||
ExternalLibraryLoaderConfig(
|
||||
@ -97,6 +98,57 @@ abstract class RustLibApi extends BaseApi {
|
||||
|
||||
Future<List<String>> crateApiHttpApiDnsLookupTxt({required String host});
|
||||
|
||||
Future<DownloadGlobalStat> crateApiDownloaderApiDownloadGlobalStatDefault();
|
||||
|
||||
Future<BigInt> crateApiDownloaderApiDownloaderAddMagnet({
|
||||
required String magnetLink,
|
||||
String? outputFolder,
|
||||
List<String>? trackers,
|
||||
});
|
||||
|
||||
Future<BigInt> crateApiDownloaderApiDownloaderAddTorrent({
|
||||
required List<int> torrentBytes,
|
||||
String? outputFolder,
|
||||
List<String>? trackers,
|
||||
});
|
||||
|
||||
Future<BigInt> crateApiDownloaderApiDownloaderAddUrl({
|
||||
required String url,
|
||||
String? outputFolder,
|
||||
List<String>? trackers,
|
||||
});
|
||||
|
||||
Future<List<DownloadTaskInfo>> crateApiDownloaderApiDownloaderGetAllTasks();
|
||||
|
||||
Future<DownloadGlobalStat> crateApiDownloaderApiDownloaderGetGlobalStats();
|
||||
|
||||
Future<DownloadTaskInfo> crateApiDownloaderApiDownloaderGetTaskInfo({
|
||||
required BigInt taskId,
|
||||
});
|
||||
|
||||
void crateApiDownloaderApiDownloaderInit({required String downloadDir});
|
||||
|
||||
bool crateApiDownloaderApiDownloaderIsInitialized();
|
||||
|
||||
Future<bool> crateApiDownloaderApiDownloaderIsNameInTask({
|
||||
required String name,
|
||||
});
|
||||
|
||||
Future<void> crateApiDownloaderApiDownloaderPause({required BigInt taskId});
|
||||
|
||||
Future<void> crateApiDownloaderApiDownloaderPauseAll();
|
||||
|
||||
Future<void> crateApiDownloaderApiDownloaderRemove({
|
||||
required BigInt taskId,
|
||||
required bool deleteFiles,
|
||||
});
|
||||
|
||||
Future<void> crateApiDownloaderApiDownloaderResume({required BigInt taskId});
|
||||
|
||||
Future<void> crateApiDownloaderApiDownloaderResumeAll();
|
||||
|
||||
Future<void> crateApiDownloaderApiDownloaderStop();
|
||||
|
||||
Future<RustHttpResponse> crateApiHttpApiFetch({
|
||||
required MyMethod method,
|
||||
required String url,
|
||||
@ -430,6 +482,451 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
TaskConstMeta get kCrateApiHttpApiDnsLookupTxtConstMeta =>
|
||||
const TaskConstMeta(debugName: "dns_lookup_txt", argNames: ["host"]);
|
||||
|
||||
@override
|
||||
Future<DownloadGlobalStat> crateApiDownloaderApiDownloadGlobalStatDefault() {
|
||||
return handler.executeNormal(
|
||||
NormalTask(
|
||||
callFfi: (port_) {
|
||||
return wire
|
||||
.wire__crate__api__downloader_api__download_global_stat_default(
|
||||
port_,
|
||||
);
|
||||
},
|
||||
codec: DcoCodec(
|
||||
decodeSuccessData: dco_decode_download_global_stat,
|
||||
decodeErrorData: null,
|
||||
),
|
||||
constMeta: kCrateApiDownloaderApiDownloadGlobalStatDefaultConstMeta,
|
||||
argValues: [],
|
||||
apiImpl: this,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
TaskConstMeta get kCrateApiDownloaderApiDownloadGlobalStatDefaultConstMeta =>
|
||||
const TaskConstMeta(
|
||||
debugName: "download_global_stat_default",
|
||||
argNames: [],
|
||||
);
|
||||
|
||||
@override
|
||||
Future<BigInt> crateApiDownloaderApiDownloaderAddMagnet({
|
||||
required String magnetLink,
|
||||
String? outputFolder,
|
||||
List<String>? trackers,
|
||||
}) {
|
||||
return handler.executeNormal(
|
||||
NormalTask(
|
||||
callFfi: (port_) {
|
||||
var arg0 = cst_encode_String(magnetLink);
|
||||
var arg1 = cst_encode_opt_String(outputFolder);
|
||||
var arg2 = cst_encode_opt_list_String(trackers);
|
||||
return wire.wire__crate__api__downloader_api__downloader_add_magnet(
|
||||
port_,
|
||||
arg0,
|
||||
arg1,
|
||||
arg2,
|
||||
);
|
||||
},
|
||||
codec: DcoCodec(
|
||||
decodeSuccessData: dco_decode_usize,
|
||||
decodeErrorData: dco_decode_AnyhowException,
|
||||
),
|
||||
constMeta: kCrateApiDownloaderApiDownloaderAddMagnetConstMeta,
|
||||
argValues: [magnetLink, outputFolder, trackers],
|
||||
apiImpl: this,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
TaskConstMeta get kCrateApiDownloaderApiDownloaderAddMagnetConstMeta =>
|
||||
const TaskConstMeta(
|
||||
debugName: "downloader_add_magnet",
|
||||
argNames: ["magnetLink", "outputFolder", "trackers"],
|
||||
);
|
||||
|
||||
@override
|
||||
Future<BigInt> crateApiDownloaderApiDownloaderAddTorrent({
|
||||
required List<int> torrentBytes,
|
||||
String? outputFolder,
|
||||
List<String>? trackers,
|
||||
}) {
|
||||
return handler.executeNormal(
|
||||
NormalTask(
|
||||
callFfi: (port_) {
|
||||
var arg0 = cst_encode_list_prim_u_8_loose(torrentBytes);
|
||||
var arg1 = cst_encode_opt_String(outputFolder);
|
||||
var arg2 = cst_encode_opt_list_String(trackers);
|
||||
return wire.wire__crate__api__downloader_api__downloader_add_torrent(
|
||||
port_,
|
||||
arg0,
|
||||
arg1,
|
||||
arg2,
|
||||
);
|
||||
},
|
||||
codec: DcoCodec(
|
||||
decodeSuccessData: dco_decode_usize,
|
||||
decodeErrorData: dco_decode_AnyhowException,
|
||||
),
|
||||
constMeta: kCrateApiDownloaderApiDownloaderAddTorrentConstMeta,
|
||||
argValues: [torrentBytes, outputFolder, trackers],
|
||||
apiImpl: this,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
TaskConstMeta get kCrateApiDownloaderApiDownloaderAddTorrentConstMeta =>
|
||||
const TaskConstMeta(
|
||||
debugName: "downloader_add_torrent",
|
||||
argNames: ["torrentBytes", "outputFolder", "trackers"],
|
||||
);
|
||||
|
||||
@override
|
||||
Future<BigInt> crateApiDownloaderApiDownloaderAddUrl({
|
||||
required String url,
|
||||
String? outputFolder,
|
||||
List<String>? trackers,
|
||||
}) {
|
||||
return handler.executeNormal(
|
||||
NormalTask(
|
||||
callFfi: (port_) {
|
||||
var arg0 = cst_encode_String(url);
|
||||
var arg1 = cst_encode_opt_String(outputFolder);
|
||||
var arg2 = cst_encode_opt_list_String(trackers);
|
||||
return wire.wire__crate__api__downloader_api__downloader_add_url(
|
||||
port_,
|
||||
arg0,
|
||||
arg1,
|
||||
arg2,
|
||||
);
|
||||
},
|
||||
codec: DcoCodec(
|
||||
decodeSuccessData: dco_decode_usize,
|
||||
decodeErrorData: dco_decode_AnyhowException,
|
||||
),
|
||||
constMeta: kCrateApiDownloaderApiDownloaderAddUrlConstMeta,
|
||||
argValues: [url, outputFolder, trackers],
|
||||
apiImpl: this,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
TaskConstMeta get kCrateApiDownloaderApiDownloaderAddUrlConstMeta =>
|
||||
const TaskConstMeta(
|
||||
debugName: "downloader_add_url",
|
||||
argNames: ["url", "outputFolder", "trackers"],
|
||||
);
|
||||
|
||||
@override
|
||||
Future<List<DownloadTaskInfo>> crateApiDownloaderApiDownloaderGetAllTasks() {
|
||||
return handler.executeNormal(
|
||||
NormalTask(
|
||||
callFfi: (port_) {
|
||||
return wire
|
||||
.wire__crate__api__downloader_api__downloader_get_all_tasks(
|
||||
port_,
|
||||
);
|
||||
},
|
||||
codec: DcoCodec(
|
||||
decodeSuccessData: dco_decode_list_download_task_info,
|
||||
decodeErrorData: dco_decode_AnyhowException,
|
||||
),
|
||||
constMeta: kCrateApiDownloaderApiDownloaderGetAllTasksConstMeta,
|
||||
argValues: [],
|
||||
apiImpl: this,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
TaskConstMeta get kCrateApiDownloaderApiDownloaderGetAllTasksConstMeta =>
|
||||
const TaskConstMeta(debugName: "downloader_get_all_tasks", argNames: []);
|
||||
|
||||
@override
|
||||
Future<DownloadGlobalStat> crateApiDownloaderApiDownloaderGetGlobalStats() {
|
||||
return handler.executeNormal(
|
||||
NormalTask(
|
||||
callFfi: (port_) {
|
||||
return wire
|
||||
.wire__crate__api__downloader_api__downloader_get_global_stats(
|
||||
port_,
|
||||
);
|
||||
},
|
||||
codec: DcoCodec(
|
||||
decodeSuccessData: dco_decode_download_global_stat,
|
||||
decodeErrorData: dco_decode_AnyhowException,
|
||||
),
|
||||
constMeta: kCrateApiDownloaderApiDownloaderGetGlobalStatsConstMeta,
|
||||
argValues: [],
|
||||
apiImpl: this,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
TaskConstMeta get kCrateApiDownloaderApiDownloaderGetGlobalStatsConstMeta =>
|
||||
const TaskConstMeta(
|
||||
debugName: "downloader_get_global_stats",
|
||||
argNames: [],
|
||||
);
|
||||
|
||||
@override
|
||||
Future<DownloadTaskInfo> crateApiDownloaderApiDownloaderGetTaskInfo({
|
||||
required BigInt taskId,
|
||||
}) {
|
||||
return handler.executeNormal(
|
||||
NormalTask(
|
||||
callFfi: (port_) {
|
||||
var arg0 = cst_encode_usize(taskId);
|
||||
return wire
|
||||
.wire__crate__api__downloader_api__downloader_get_task_info(
|
||||
port_,
|
||||
arg0,
|
||||
);
|
||||
},
|
||||
codec: DcoCodec(
|
||||
decodeSuccessData: dco_decode_download_task_info,
|
||||
decodeErrorData: dco_decode_AnyhowException,
|
||||
),
|
||||
constMeta: kCrateApiDownloaderApiDownloaderGetTaskInfoConstMeta,
|
||||
argValues: [taskId],
|
||||
apiImpl: this,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
TaskConstMeta get kCrateApiDownloaderApiDownloaderGetTaskInfoConstMeta =>
|
||||
const TaskConstMeta(
|
||||
debugName: "downloader_get_task_info",
|
||||
argNames: ["taskId"],
|
||||
);
|
||||
|
||||
@override
|
||||
void crateApiDownloaderApiDownloaderInit({required String downloadDir}) {
|
||||
return handler.executeSync(
|
||||
SyncTask(
|
||||
callFfi: () {
|
||||
var arg0 = cst_encode_String(downloadDir);
|
||||
return wire.wire__crate__api__downloader_api__downloader_init(arg0);
|
||||
},
|
||||
codec: DcoCodec(
|
||||
decodeSuccessData: dco_decode_unit,
|
||||
decodeErrorData: dco_decode_AnyhowException,
|
||||
),
|
||||
constMeta: kCrateApiDownloaderApiDownloaderInitConstMeta,
|
||||
argValues: [downloadDir],
|
||||
apiImpl: this,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
TaskConstMeta get kCrateApiDownloaderApiDownloaderInitConstMeta =>
|
||||
const TaskConstMeta(
|
||||
debugName: "downloader_init",
|
||||
argNames: ["downloadDir"],
|
||||
);
|
||||
|
||||
@override
|
||||
bool crateApiDownloaderApiDownloaderIsInitialized() {
|
||||
return handler.executeSync(
|
||||
SyncTask(
|
||||
callFfi: () {
|
||||
return wire
|
||||
.wire__crate__api__downloader_api__downloader_is_initialized();
|
||||
},
|
||||
codec: DcoCodec(
|
||||
decodeSuccessData: dco_decode_bool,
|
||||
decodeErrorData: null,
|
||||
),
|
||||
constMeta: kCrateApiDownloaderApiDownloaderIsInitializedConstMeta,
|
||||
argValues: [],
|
||||
apiImpl: this,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
TaskConstMeta get kCrateApiDownloaderApiDownloaderIsInitializedConstMeta =>
|
||||
const TaskConstMeta(debugName: "downloader_is_initialized", argNames: []);
|
||||
|
||||
@override
|
||||
Future<bool> crateApiDownloaderApiDownloaderIsNameInTask({
|
||||
required String name,
|
||||
}) {
|
||||
return handler.executeNormal(
|
||||
NormalTask(
|
||||
callFfi: (port_) {
|
||||
var arg0 = cst_encode_String(name);
|
||||
return wire
|
||||
.wire__crate__api__downloader_api__downloader_is_name_in_task(
|
||||
port_,
|
||||
arg0,
|
||||
);
|
||||
},
|
||||
codec: DcoCodec(
|
||||
decodeSuccessData: dco_decode_bool,
|
||||
decodeErrorData: null,
|
||||
),
|
||||
constMeta: kCrateApiDownloaderApiDownloaderIsNameInTaskConstMeta,
|
||||
argValues: [name],
|
||||
apiImpl: this,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
TaskConstMeta get kCrateApiDownloaderApiDownloaderIsNameInTaskConstMeta =>
|
||||
const TaskConstMeta(
|
||||
debugName: "downloader_is_name_in_task",
|
||||
argNames: ["name"],
|
||||
);
|
||||
|
||||
@override
|
||||
Future<void> crateApiDownloaderApiDownloaderPause({required BigInt taskId}) {
|
||||
return handler.executeNormal(
|
||||
NormalTask(
|
||||
callFfi: (port_) {
|
||||
var arg0 = cst_encode_usize(taskId);
|
||||
return wire.wire__crate__api__downloader_api__downloader_pause(
|
||||
port_,
|
||||
arg0,
|
||||
);
|
||||
},
|
||||
codec: DcoCodec(
|
||||
decodeSuccessData: dco_decode_unit,
|
||||
decodeErrorData: dco_decode_AnyhowException,
|
||||
),
|
||||
constMeta: kCrateApiDownloaderApiDownloaderPauseConstMeta,
|
||||
argValues: [taskId],
|
||||
apiImpl: this,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
TaskConstMeta get kCrateApiDownloaderApiDownloaderPauseConstMeta =>
|
||||
const TaskConstMeta(debugName: "downloader_pause", argNames: ["taskId"]);
|
||||
|
||||
@override
|
||||
Future<void> crateApiDownloaderApiDownloaderPauseAll() {
|
||||
return handler.executeNormal(
|
||||
NormalTask(
|
||||
callFfi: (port_) {
|
||||
return wire.wire__crate__api__downloader_api__downloader_pause_all(
|
||||
port_,
|
||||
);
|
||||
},
|
||||
codec: DcoCodec(
|
||||
decodeSuccessData: dco_decode_unit,
|
||||
decodeErrorData: dco_decode_AnyhowException,
|
||||
),
|
||||
constMeta: kCrateApiDownloaderApiDownloaderPauseAllConstMeta,
|
||||
argValues: [],
|
||||
apiImpl: this,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
TaskConstMeta get kCrateApiDownloaderApiDownloaderPauseAllConstMeta =>
|
||||
const TaskConstMeta(debugName: "downloader_pause_all", argNames: []);
|
||||
|
||||
@override
|
||||
Future<void> crateApiDownloaderApiDownloaderRemove({
|
||||
required BigInt taskId,
|
||||
required bool deleteFiles,
|
||||
}) {
|
||||
return handler.executeNormal(
|
||||
NormalTask(
|
||||
callFfi: (port_) {
|
||||
var arg0 = cst_encode_usize(taskId);
|
||||
var arg1 = cst_encode_bool(deleteFiles);
|
||||
return wire.wire__crate__api__downloader_api__downloader_remove(
|
||||
port_,
|
||||
arg0,
|
||||
arg1,
|
||||
);
|
||||
},
|
||||
codec: DcoCodec(
|
||||
decodeSuccessData: dco_decode_unit,
|
||||
decodeErrorData: dco_decode_AnyhowException,
|
||||
),
|
||||
constMeta: kCrateApiDownloaderApiDownloaderRemoveConstMeta,
|
||||
argValues: [taskId, deleteFiles],
|
||||
apiImpl: this,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
TaskConstMeta get kCrateApiDownloaderApiDownloaderRemoveConstMeta =>
|
||||
const TaskConstMeta(
|
||||
debugName: "downloader_remove",
|
||||
argNames: ["taskId", "deleteFiles"],
|
||||
);
|
||||
|
||||
@override
|
||||
Future<void> crateApiDownloaderApiDownloaderResume({required BigInt taskId}) {
|
||||
return handler.executeNormal(
|
||||
NormalTask(
|
||||
callFfi: (port_) {
|
||||
var arg0 = cst_encode_usize(taskId);
|
||||
return wire.wire__crate__api__downloader_api__downloader_resume(
|
||||
port_,
|
||||
arg0,
|
||||
);
|
||||
},
|
||||
codec: DcoCodec(
|
||||
decodeSuccessData: dco_decode_unit,
|
||||
decodeErrorData: dco_decode_AnyhowException,
|
||||
),
|
||||
constMeta: kCrateApiDownloaderApiDownloaderResumeConstMeta,
|
||||
argValues: [taskId],
|
||||
apiImpl: this,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
TaskConstMeta get kCrateApiDownloaderApiDownloaderResumeConstMeta =>
|
||||
const TaskConstMeta(debugName: "downloader_resume", argNames: ["taskId"]);
|
||||
|
||||
@override
|
||||
Future<void> crateApiDownloaderApiDownloaderResumeAll() {
|
||||
return handler.executeNormal(
|
||||
NormalTask(
|
||||
callFfi: (port_) {
|
||||
return wire.wire__crate__api__downloader_api__downloader_resume_all(
|
||||
port_,
|
||||
);
|
||||
},
|
||||
codec: DcoCodec(
|
||||
decodeSuccessData: dco_decode_unit,
|
||||
decodeErrorData: dco_decode_AnyhowException,
|
||||
),
|
||||
constMeta: kCrateApiDownloaderApiDownloaderResumeAllConstMeta,
|
||||
argValues: [],
|
||||
apiImpl: this,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
TaskConstMeta get kCrateApiDownloaderApiDownloaderResumeAllConstMeta =>
|
||||
const TaskConstMeta(debugName: "downloader_resume_all", argNames: []);
|
||||
|
||||
@override
|
||||
Future<void> crateApiDownloaderApiDownloaderStop() {
|
||||
return handler.executeNormal(
|
||||
NormalTask(
|
||||
callFfi: (port_) {
|
||||
return wire.wire__crate__api__downloader_api__downloader_stop(port_);
|
||||
},
|
||||
codec: DcoCodec(
|
||||
decodeSuccessData: dco_decode_unit,
|
||||
decodeErrorData: dco_decode_AnyhowException,
|
||||
),
|
||||
constMeta: kCrateApiDownloaderApiDownloaderStopConstMeta,
|
||||
argValues: [],
|
||||
apiImpl: this,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
TaskConstMeta get kCrateApiDownloaderApiDownloaderStopConstMeta =>
|
||||
const TaskConstMeta(debugName: "downloader_stop", argNames: []);
|
||||
|
||||
@override
|
||||
Future<RustHttpResponse> crateApiHttpApiFetch({
|
||||
required MyMethod method,
|
||||
@ -1903,6 +2400,53 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
return dco_decode_web_view_configuration(raw);
|
||||
}
|
||||
|
||||
@protected
|
||||
DownloadGlobalStat dco_decode_download_global_stat(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
final arr = raw as List<dynamic>;
|
||||
if (arr.length != 4)
|
||||
throw Exception('unexpected arr length: expect 4 but see ${arr.length}');
|
||||
return DownloadGlobalStat(
|
||||
downloadSpeed: dco_decode_u_64(arr[0]),
|
||||
uploadSpeed: dco_decode_u_64(arr[1]),
|
||||
numActive: dco_decode_usize(arr[2]),
|
||||
numWaiting: dco_decode_usize(arr[3]),
|
||||
);
|
||||
}
|
||||
|
||||
@protected
|
||||
DownloadTaskInfo dco_decode_download_task_info(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
final arr = raw as List<dynamic>;
|
||||
if (arr.length != 11)
|
||||
throw Exception('unexpected arr length: expect 11 but see ${arr.length}');
|
||||
return DownloadTaskInfo(
|
||||
id: dco_decode_usize(arr[0]),
|
||||
name: dco_decode_String(arr[1]),
|
||||
status: dco_decode_download_task_status(arr[2]),
|
||||
totalBytes: dco_decode_u_64(arr[3]),
|
||||
downloadedBytes: dco_decode_u_64(arr[4]),
|
||||
uploadedBytes: dco_decode_u_64(arr[5]),
|
||||
downloadSpeed: dco_decode_u_64(arr[6]),
|
||||
uploadSpeed: dco_decode_u_64(arr[7]),
|
||||
progress: dco_decode_f_64(arr[8]),
|
||||
numPeers: dco_decode_usize(arr[9]),
|
||||
outputFolder: dco_decode_String(arr[10]),
|
||||
);
|
||||
}
|
||||
|
||||
@protected
|
||||
DownloadTaskStatus dco_decode_download_task_status(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
return DownloadTaskStatus.values[raw as int];
|
||||
}
|
||||
|
||||
@protected
|
||||
double dco_decode_f_64(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
return raw as double;
|
||||
}
|
||||
|
||||
@protected
|
||||
int dco_decode_i_32(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
@ -1921,6 +2465,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
return (raw as List<dynamic>).map(dco_decode_String).toList();
|
||||
}
|
||||
|
||||
@protected
|
||||
List<DownloadTaskInfo> dco_decode_list_download_task_info(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
return (raw as List<dynamic>).map(dco_decode_download_task_info).toList();
|
||||
}
|
||||
|
||||
@protected
|
||||
List<P4kFileItem> dco_decode_list_p_4_k_file_item(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
@ -1993,6 +2543,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
return raw == null ? null : dco_decode_box_autoadd_u_64(raw);
|
||||
}
|
||||
|
||||
@protected
|
||||
List<String>? dco_decode_opt_list_String(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
return raw == null ? null : dco_decode_list_String(raw);
|
||||
}
|
||||
|
||||
@protected
|
||||
Uint8List? dco_decode_opt_list_prim_u_8_strict(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
@ -2255,6 +2811,67 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
return (sse_decode_web_view_configuration(deserializer));
|
||||
}
|
||||
|
||||
@protected
|
||||
DownloadGlobalStat sse_decode_download_global_stat(
|
||||
SseDeserializer deserializer,
|
||||
) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
var var_downloadSpeed = sse_decode_u_64(deserializer);
|
||||
var var_uploadSpeed = sse_decode_u_64(deserializer);
|
||||
var var_numActive = sse_decode_usize(deserializer);
|
||||
var var_numWaiting = sse_decode_usize(deserializer);
|
||||
return DownloadGlobalStat(
|
||||
downloadSpeed: var_downloadSpeed,
|
||||
uploadSpeed: var_uploadSpeed,
|
||||
numActive: var_numActive,
|
||||
numWaiting: var_numWaiting,
|
||||
);
|
||||
}
|
||||
|
||||
@protected
|
||||
DownloadTaskInfo sse_decode_download_task_info(SseDeserializer deserializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
var var_id = sse_decode_usize(deserializer);
|
||||
var var_name = sse_decode_String(deserializer);
|
||||
var var_status = sse_decode_download_task_status(deserializer);
|
||||
var var_totalBytes = sse_decode_u_64(deserializer);
|
||||
var var_downloadedBytes = sse_decode_u_64(deserializer);
|
||||
var var_uploadedBytes = sse_decode_u_64(deserializer);
|
||||
var var_downloadSpeed = sse_decode_u_64(deserializer);
|
||||
var var_uploadSpeed = sse_decode_u_64(deserializer);
|
||||
var var_progress = sse_decode_f_64(deserializer);
|
||||
var var_numPeers = sse_decode_usize(deserializer);
|
||||
var var_outputFolder = sse_decode_String(deserializer);
|
||||
return DownloadTaskInfo(
|
||||
id: var_id,
|
||||
name: var_name,
|
||||
status: var_status,
|
||||
totalBytes: var_totalBytes,
|
||||
downloadedBytes: var_downloadedBytes,
|
||||
uploadedBytes: var_uploadedBytes,
|
||||
downloadSpeed: var_downloadSpeed,
|
||||
uploadSpeed: var_uploadSpeed,
|
||||
progress: var_progress,
|
||||
numPeers: var_numPeers,
|
||||
outputFolder: var_outputFolder,
|
||||
);
|
||||
}
|
||||
|
||||
@protected
|
||||
DownloadTaskStatus sse_decode_download_task_status(
|
||||
SseDeserializer deserializer,
|
||||
) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
var inner = sse_decode_i_32(deserializer);
|
||||
return DownloadTaskStatus.values[inner];
|
||||
}
|
||||
|
||||
@protected
|
||||
double sse_decode_f_64(SseDeserializer deserializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
return deserializer.buffer.getFloat64();
|
||||
}
|
||||
|
||||
@protected
|
||||
int sse_decode_i_32(SseDeserializer deserializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
@ -2279,6 +2896,20 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
return ans_;
|
||||
}
|
||||
|
||||
@protected
|
||||
List<DownloadTaskInfo> sse_decode_list_download_task_info(
|
||||
SseDeserializer deserializer,
|
||||
) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
|
||||
var len_ = sse_decode_i_32(deserializer);
|
||||
var ans_ = <DownloadTaskInfo>[];
|
||||
for (var idx_ = 0; idx_ < len_; ++idx_) {
|
||||
ans_.add(sse_decode_download_task_info(deserializer));
|
||||
}
|
||||
return ans_;
|
||||
}
|
||||
|
||||
@protected
|
||||
List<P4kFileItem> sse_decode_list_p_4_k_file_item(
|
||||
SseDeserializer deserializer,
|
||||
@ -2407,6 +3038,17 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
}
|
||||
}
|
||||
|
||||
@protected
|
||||
List<String>? sse_decode_opt_list_String(SseDeserializer deserializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
|
||||
if (sse_decode_bool(deserializer)) {
|
||||
return (sse_decode_list_String(deserializer));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@protected
|
||||
Uint8List? sse_decode_opt_list_prim_u_8_strict(SseDeserializer deserializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
@ -2640,6 +3282,18 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
return raw;
|
||||
}
|
||||
|
||||
@protected
|
||||
int cst_encode_download_task_status(DownloadTaskStatus raw) {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
return cst_encode_i_32(raw.index);
|
||||
}
|
||||
|
||||
@protected
|
||||
double cst_encode_f_64(double raw) {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
return raw;
|
||||
}
|
||||
|
||||
@protected
|
||||
int cst_encode_i_32(int raw) {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
@ -2768,6 +3422,52 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
sse_encode_web_view_configuration(self, serializer);
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_download_global_stat(
|
||||
DownloadGlobalStat self,
|
||||
SseSerializer serializer,
|
||||
) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
sse_encode_u_64(self.downloadSpeed, serializer);
|
||||
sse_encode_u_64(self.uploadSpeed, serializer);
|
||||
sse_encode_usize(self.numActive, serializer);
|
||||
sse_encode_usize(self.numWaiting, serializer);
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_download_task_info(
|
||||
DownloadTaskInfo self,
|
||||
SseSerializer serializer,
|
||||
) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
sse_encode_usize(self.id, serializer);
|
||||
sse_encode_String(self.name, serializer);
|
||||
sse_encode_download_task_status(self.status, serializer);
|
||||
sse_encode_u_64(self.totalBytes, serializer);
|
||||
sse_encode_u_64(self.downloadedBytes, serializer);
|
||||
sse_encode_u_64(self.uploadedBytes, serializer);
|
||||
sse_encode_u_64(self.downloadSpeed, serializer);
|
||||
sse_encode_u_64(self.uploadSpeed, serializer);
|
||||
sse_encode_f_64(self.progress, serializer);
|
||||
sse_encode_usize(self.numPeers, serializer);
|
||||
sse_encode_String(self.outputFolder, serializer);
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_download_task_status(
|
||||
DownloadTaskStatus self,
|
||||
SseSerializer serializer,
|
||||
) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
sse_encode_i_32(self.index, serializer);
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_f_64(double self, SseSerializer serializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
serializer.buffer.putFloat64(self);
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_i_32(int self, SseSerializer serializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
@ -2789,6 +3489,18 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
}
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_list_download_task_info(
|
||||
List<DownloadTaskInfo> self,
|
||||
SseSerializer serializer,
|
||||
) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
sse_encode_i_32(self.length, serializer);
|
||||
for (final item in self) {
|
||||
sse_encode_download_task_info(item, serializer);
|
||||
}
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_list_p_4_k_file_item(
|
||||
List<P4kFileItem> self,
|
||||
@ -2917,6 +3629,19 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
}
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_opt_list_String(
|
||||
List<String>? self,
|
||||
SseSerializer serializer,
|
||||
) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
|
||||
sse_encode_bool(self != null, serializer);
|
||||
if (self != null) {
|
||||
sse_encode_list_String(self, serializer);
|
||||
}
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_opt_list_prim_u_8_strict(
|
||||
Uint8List? self,
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
// ignore_for_file: unused_import, unused_element, unnecessary_import, duplicate_ignore, invalid_use_of_internal_member, annotate_overrides, non_constant_identifier_names, curly_braces_in_flow_control_structures, prefer_const_literals_to_create_immutables, unused_field
|
||||
|
||||
import 'api/asar_api.dart';
|
||||
import 'api/downloader_api.dart';
|
||||
import 'api/http_api.dart';
|
||||
import 'api/ort_api.dart';
|
||||
import 'api/rs_process.dart';
|
||||
@ -57,6 +58,18 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
dynamic raw,
|
||||
);
|
||||
|
||||
@protected
|
||||
DownloadGlobalStat dco_decode_download_global_stat(dynamic raw);
|
||||
|
||||
@protected
|
||||
DownloadTaskInfo dco_decode_download_task_info(dynamic raw);
|
||||
|
||||
@protected
|
||||
DownloadTaskStatus dco_decode_download_task_status(dynamic raw);
|
||||
|
||||
@protected
|
||||
double dco_decode_f_64(dynamic raw);
|
||||
|
||||
@protected
|
||||
int dco_decode_i_32(dynamic raw);
|
||||
|
||||
@ -66,6 +79,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
List<String> dco_decode_list_String(dynamic raw);
|
||||
|
||||
@protected
|
||||
List<DownloadTaskInfo> dco_decode_list_download_task_info(dynamic raw);
|
||||
|
||||
@protected
|
||||
List<P4kFileItem> dco_decode_list_p_4_k_file_item(dynamic raw);
|
||||
|
||||
@ -102,6 +118,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
BigInt? dco_decode_opt_box_autoadd_u_64(dynamic raw);
|
||||
|
||||
@protected
|
||||
List<String>? dco_decode_opt_list_String(dynamic raw);
|
||||
|
||||
@protected
|
||||
Uint8List? dco_decode_opt_list_prim_u_8_strict(dynamic raw);
|
||||
|
||||
@ -192,6 +211,22 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
DownloadGlobalStat sse_decode_download_global_stat(
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
DownloadTaskInfo sse_decode_download_task_info(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
DownloadTaskStatus sse_decode_download_task_status(
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
double sse_decode_f_64(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
int sse_decode_i_32(SseDeserializer deserializer);
|
||||
|
||||
@ -201,6 +236,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
List<String> sse_decode_list_String(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
List<DownloadTaskInfo> sse_decode_list_download_task_info(
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
List<P4kFileItem> sse_decode_list_p_4_k_file_item(
|
||||
SseDeserializer deserializer,
|
||||
@ -245,6 +285,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
BigInt? sse_decode_opt_box_autoadd_u_64(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
List<String>? sse_decode_opt_list_String(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
Uint8List? sse_decode_opt_list_prim_u_8_strict(SseDeserializer deserializer);
|
||||
|
||||
@ -396,6 +439,17 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
return ans;
|
||||
}
|
||||
|
||||
@protected
|
||||
ffi.Pointer<wire_cst_list_download_task_info>
|
||||
cst_encode_list_download_task_info(List<DownloadTaskInfo> raw) {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
final ans = wire.cst_new_list_download_task_info(raw.length);
|
||||
for (var i = 0; i < raw.length; ++i) {
|
||||
cst_api_fill_to_wire_download_task_info(raw[i], ans.ref.ptr[i]);
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
|
||||
@protected
|
||||
ffi.Pointer<wire_cst_list_p_4_k_file_item> cst_encode_list_p_4_k_file_item(
|
||||
List<P4kFileItem> raw,
|
||||
@ -490,6 +544,14 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
return raw == null ? ffi.nullptr : cst_encode_box_autoadd_u_64(raw);
|
||||
}
|
||||
|
||||
@protected
|
||||
ffi.Pointer<wire_cst_list_String> cst_encode_opt_list_String(
|
||||
List<String>? raw,
|
||||
) {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
return raw == null ? ffi.nullptr : cst_encode_list_String(raw);
|
||||
}
|
||||
|
||||
@protected
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>
|
||||
cst_encode_opt_list_prim_u_8_strict(Uint8List? raw) {
|
||||
@ -525,6 +587,35 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
cst_api_fill_to_wire_web_view_configuration(apiObj, wireObj.ref);
|
||||
}
|
||||
|
||||
@protected
|
||||
void cst_api_fill_to_wire_download_global_stat(
|
||||
DownloadGlobalStat apiObj,
|
||||
wire_cst_download_global_stat wireObj,
|
||||
) {
|
||||
wireObj.download_speed = cst_encode_u_64(apiObj.downloadSpeed);
|
||||
wireObj.upload_speed = cst_encode_u_64(apiObj.uploadSpeed);
|
||||
wireObj.num_active = cst_encode_usize(apiObj.numActive);
|
||||
wireObj.num_waiting = cst_encode_usize(apiObj.numWaiting);
|
||||
}
|
||||
|
||||
@protected
|
||||
void cst_api_fill_to_wire_download_task_info(
|
||||
DownloadTaskInfo apiObj,
|
||||
wire_cst_download_task_info wireObj,
|
||||
) {
|
||||
wireObj.id = cst_encode_usize(apiObj.id);
|
||||
wireObj.name = cst_encode_String(apiObj.name);
|
||||
wireObj.status = cst_encode_download_task_status(apiObj.status);
|
||||
wireObj.total_bytes = cst_encode_u_64(apiObj.totalBytes);
|
||||
wireObj.downloaded_bytes = cst_encode_u_64(apiObj.downloadedBytes);
|
||||
wireObj.uploaded_bytes = cst_encode_u_64(apiObj.uploadedBytes);
|
||||
wireObj.download_speed = cst_encode_u_64(apiObj.downloadSpeed);
|
||||
wireObj.upload_speed = cst_encode_u_64(apiObj.uploadSpeed);
|
||||
wireObj.progress = cst_encode_f_64(apiObj.progress);
|
||||
wireObj.num_peers = cst_encode_usize(apiObj.numPeers);
|
||||
wireObj.output_folder = cst_encode_String(apiObj.outputFolder);
|
||||
}
|
||||
|
||||
@protected
|
||||
void cst_api_fill_to_wire_p_4_k_file_item(
|
||||
P4kFileItem apiObj,
|
||||
@ -675,6 +766,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
bool cst_encode_bool(bool raw);
|
||||
|
||||
@protected
|
||||
int cst_encode_download_task_status(DownloadTaskStatus raw);
|
||||
|
||||
@protected
|
||||
double cst_encode_f_64(double raw);
|
||||
|
||||
@protected
|
||||
int cst_encode_i_32(int raw);
|
||||
|
||||
@ -741,6 +838,27 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_download_global_stat(
|
||||
DownloadGlobalStat self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_download_task_info(
|
||||
DownloadTaskInfo self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_download_task_status(
|
||||
DownloadTaskStatus self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_f_64(double self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_i_32(int self, SseSerializer serializer);
|
||||
|
||||
@ -750,6 +868,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
void sse_encode_list_String(List<String> self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_list_download_task_info(
|
||||
List<DownloadTaskInfo> self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_list_p_4_k_file_item(
|
||||
List<P4kFileItem> self,
|
||||
@ -804,6 +928,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
void sse_encode_opt_box_autoadd_u_64(BigInt? self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_opt_list_String(List<String>? self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_opt_list_prim_u_8_strict(
|
||||
Uint8List? self,
|
||||
@ -1034,6 +1161,334 @@ class RustLibWire implements BaseWire {
|
||||
void Function(int, ffi.Pointer<wire_cst_list_prim_u_8_strict>)
|
||||
>();
|
||||
|
||||
void wire__crate__api__downloader_api__download_global_stat_default(
|
||||
int port_,
|
||||
) {
|
||||
return _wire__crate__api__downloader_api__download_global_stat_default(
|
||||
port_,
|
||||
);
|
||||
}
|
||||
|
||||
late final _wire__crate__api__downloader_api__download_global_stat_defaultPtr =
|
||||
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Int64)>>(
|
||||
'frbgen_starcitizen_doctor_wire__crate__api__downloader_api__download_global_stat_default',
|
||||
);
|
||||
late final _wire__crate__api__downloader_api__download_global_stat_default =
|
||||
_wire__crate__api__downloader_api__download_global_stat_defaultPtr
|
||||
.asFunction<void Function(int)>();
|
||||
|
||||
void wire__crate__api__downloader_api__downloader_add_magnet(
|
||||
int port_,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> magnet_link,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> output_folder,
|
||||
ffi.Pointer<wire_cst_list_String> trackers,
|
||||
) {
|
||||
return _wire__crate__api__downloader_api__downloader_add_magnet(
|
||||
port_,
|
||||
magnet_link,
|
||||
output_folder,
|
||||
trackers,
|
||||
);
|
||||
}
|
||||
|
||||
late final _wire__crate__api__downloader_api__downloader_add_magnetPtr =
|
||||
_lookup<
|
||||
ffi.NativeFunction<
|
||||
ffi.Void Function(
|
||||
ffi.Int64,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
|
||||
ffi.Pointer<wire_cst_list_String>,
|
||||
)
|
||||
>
|
||||
>(
|
||||
'frbgen_starcitizen_doctor_wire__crate__api__downloader_api__downloader_add_magnet',
|
||||
);
|
||||
late final _wire__crate__api__downloader_api__downloader_add_magnet =
|
||||
_wire__crate__api__downloader_api__downloader_add_magnetPtr
|
||||
.asFunction<
|
||||
void Function(
|
||||
int,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
|
||||
ffi.Pointer<wire_cst_list_String>,
|
||||
)
|
||||
>();
|
||||
|
||||
void wire__crate__api__downloader_api__downloader_add_torrent(
|
||||
int port_,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_loose> torrent_bytes,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> output_folder,
|
||||
ffi.Pointer<wire_cst_list_String> trackers,
|
||||
) {
|
||||
return _wire__crate__api__downloader_api__downloader_add_torrent(
|
||||
port_,
|
||||
torrent_bytes,
|
||||
output_folder,
|
||||
trackers,
|
||||
);
|
||||
}
|
||||
|
||||
late final _wire__crate__api__downloader_api__downloader_add_torrentPtr =
|
||||
_lookup<
|
||||
ffi.NativeFunction<
|
||||
ffi.Void Function(
|
||||
ffi.Int64,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_loose>,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
|
||||
ffi.Pointer<wire_cst_list_String>,
|
||||
)
|
||||
>
|
||||
>(
|
||||
'frbgen_starcitizen_doctor_wire__crate__api__downloader_api__downloader_add_torrent',
|
||||
);
|
||||
late final _wire__crate__api__downloader_api__downloader_add_torrent =
|
||||
_wire__crate__api__downloader_api__downloader_add_torrentPtr
|
||||
.asFunction<
|
||||
void Function(
|
||||
int,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_loose>,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
|
||||
ffi.Pointer<wire_cst_list_String>,
|
||||
)
|
||||
>();
|
||||
|
||||
void wire__crate__api__downloader_api__downloader_add_url(
|
||||
int port_,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> url,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> output_folder,
|
||||
ffi.Pointer<wire_cst_list_String> trackers,
|
||||
) {
|
||||
return _wire__crate__api__downloader_api__downloader_add_url(
|
||||
port_,
|
||||
url,
|
||||
output_folder,
|
||||
trackers,
|
||||
);
|
||||
}
|
||||
|
||||
late final _wire__crate__api__downloader_api__downloader_add_urlPtr =
|
||||
_lookup<
|
||||
ffi.NativeFunction<
|
||||
ffi.Void Function(
|
||||
ffi.Int64,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
|
||||
ffi.Pointer<wire_cst_list_String>,
|
||||
)
|
||||
>
|
||||
>(
|
||||
'frbgen_starcitizen_doctor_wire__crate__api__downloader_api__downloader_add_url',
|
||||
);
|
||||
late final _wire__crate__api__downloader_api__downloader_add_url =
|
||||
_wire__crate__api__downloader_api__downloader_add_urlPtr
|
||||
.asFunction<
|
||||
void Function(
|
||||
int,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
|
||||
ffi.Pointer<wire_cst_list_String>,
|
||||
)
|
||||
>();
|
||||
|
||||
void wire__crate__api__downloader_api__downloader_get_all_tasks(int port_) {
|
||||
return _wire__crate__api__downloader_api__downloader_get_all_tasks(port_);
|
||||
}
|
||||
|
||||
late final _wire__crate__api__downloader_api__downloader_get_all_tasksPtr =
|
||||
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Int64)>>(
|
||||
'frbgen_starcitizen_doctor_wire__crate__api__downloader_api__downloader_get_all_tasks',
|
||||
);
|
||||
late final _wire__crate__api__downloader_api__downloader_get_all_tasks =
|
||||
_wire__crate__api__downloader_api__downloader_get_all_tasksPtr
|
||||
.asFunction<void Function(int)>();
|
||||
|
||||
void wire__crate__api__downloader_api__downloader_get_global_stats(
|
||||
int port_,
|
||||
) {
|
||||
return _wire__crate__api__downloader_api__downloader_get_global_stats(
|
||||
port_,
|
||||
);
|
||||
}
|
||||
|
||||
late final _wire__crate__api__downloader_api__downloader_get_global_statsPtr =
|
||||
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Int64)>>(
|
||||
'frbgen_starcitizen_doctor_wire__crate__api__downloader_api__downloader_get_global_stats',
|
||||
);
|
||||
late final _wire__crate__api__downloader_api__downloader_get_global_stats =
|
||||
_wire__crate__api__downloader_api__downloader_get_global_statsPtr
|
||||
.asFunction<void Function(int)>();
|
||||
|
||||
void wire__crate__api__downloader_api__downloader_get_task_info(
|
||||
int port_,
|
||||
int task_id,
|
||||
) {
|
||||
return _wire__crate__api__downloader_api__downloader_get_task_info(
|
||||
port_,
|
||||
task_id,
|
||||
);
|
||||
}
|
||||
|
||||
late final _wire__crate__api__downloader_api__downloader_get_task_infoPtr =
|
||||
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Int64, ffi.UintPtr)>>(
|
||||
'frbgen_starcitizen_doctor_wire__crate__api__downloader_api__downloader_get_task_info',
|
||||
);
|
||||
late final _wire__crate__api__downloader_api__downloader_get_task_info =
|
||||
_wire__crate__api__downloader_api__downloader_get_task_infoPtr
|
||||
.asFunction<void Function(int, int)>();
|
||||
|
||||
WireSyncRust2DartDco wire__crate__api__downloader_api__downloader_init(
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> download_dir,
|
||||
) {
|
||||
return _wire__crate__api__downloader_api__downloader_init(download_dir);
|
||||
}
|
||||
|
||||
late final _wire__crate__api__downloader_api__downloader_initPtr =
|
||||
_lookup<
|
||||
ffi.NativeFunction<
|
||||
WireSyncRust2DartDco Function(
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
|
||||
)
|
||||
>
|
||||
>(
|
||||
'frbgen_starcitizen_doctor_wire__crate__api__downloader_api__downloader_init',
|
||||
);
|
||||
late final _wire__crate__api__downloader_api__downloader_init =
|
||||
_wire__crate__api__downloader_api__downloader_initPtr
|
||||
.asFunction<
|
||||
WireSyncRust2DartDco Function(
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
|
||||
)
|
||||
>();
|
||||
|
||||
WireSyncRust2DartDco
|
||||
wire__crate__api__downloader_api__downloader_is_initialized() {
|
||||
return _wire__crate__api__downloader_api__downloader_is_initialized();
|
||||
}
|
||||
|
||||
late final _wire__crate__api__downloader_api__downloader_is_initializedPtr =
|
||||
_lookup<ffi.NativeFunction<WireSyncRust2DartDco Function()>>(
|
||||
'frbgen_starcitizen_doctor_wire__crate__api__downloader_api__downloader_is_initialized',
|
||||
);
|
||||
late final _wire__crate__api__downloader_api__downloader_is_initialized =
|
||||
_wire__crate__api__downloader_api__downloader_is_initializedPtr
|
||||
.asFunction<WireSyncRust2DartDco Function()>();
|
||||
|
||||
void wire__crate__api__downloader_api__downloader_is_name_in_task(
|
||||
int port_,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> name,
|
||||
) {
|
||||
return _wire__crate__api__downloader_api__downloader_is_name_in_task(
|
||||
port_,
|
||||
name,
|
||||
);
|
||||
}
|
||||
|
||||
late final _wire__crate__api__downloader_api__downloader_is_name_in_taskPtr =
|
||||
_lookup<
|
||||
ffi.NativeFunction<
|
||||
ffi.Void Function(
|
||||
ffi.Int64,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
|
||||
)
|
||||
>
|
||||
>(
|
||||
'frbgen_starcitizen_doctor_wire__crate__api__downloader_api__downloader_is_name_in_task',
|
||||
);
|
||||
late final _wire__crate__api__downloader_api__downloader_is_name_in_task =
|
||||
_wire__crate__api__downloader_api__downloader_is_name_in_taskPtr
|
||||
.asFunction<
|
||||
void Function(int, ffi.Pointer<wire_cst_list_prim_u_8_strict>)
|
||||
>();
|
||||
|
||||
void wire__crate__api__downloader_api__downloader_pause(
|
||||
int port_,
|
||||
int task_id,
|
||||
) {
|
||||
return _wire__crate__api__downloader_api__downloader_pause(port_, task_id);
|
||||
}
|
||||
|
||||
late final _wire__crate__api__downloader_api__downloader_pausePtr =
|
||||
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Int64, ffi.UintPtr)>>(
|
||||
'frbgen_starcitizen_doctor_wire__crate__api__downloader_api__downloader_pause',
|
||||
);
|
||||
late final _wire__crate__api__downloader_api__downloader_pause =
|
||||
_wire__crate__api__downloader_api__downloader_pausePtr
|
||||
.asFunction<void Function(int, int)>();
|
||||
|
||||
void wire__crate__api__downloader_api__downloader_pause_all(int port_) {
|
||||
return _wire__crate__api__downloader_api__downloader_pause_all(port_);
|
||||
}
|
||||
|
||||
late final _wire__crate__api__downloader_api__downloader_pause_allPtr =
|
||||
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Int64)>>(
|
||||
'frbgen_starcitizen_doctor_wire__crate__api__downloader_api__downloader_pause_all',
|
||||
);
|
||||
late final _wire__crate__api__downloader_api__downloader_pause_all =
|
||||
_wire__crate__api__downloader_api__downloader_pause_allPtr
|
||||
.asFunction<void Function(int)>();
|
||||
|
||||
void wire__crate__api__downloader_api__downloader_remove(
|
||||
int port_,
|
||||
int task_id,
|
||||
bool delete_files,
|
||||
) {
|
||||
return _wire__crate__api__downloader_api__downloader_remove(
|
||||
port_,
|
||||
task_id,
|
||||
delete_files,
|
||||
);
|
||||
}
|
||||
|
||||
late final _wire__crate__api__downloader_api__downloader_removePtr =
|
||||
_lookup<
|
||||
ffi.NativeFunction<ffi.Void Function(ffi.Int64, ffi.UintPtr, ffi.Bool)>
|
||||
>(
|
||||
'frbgen_starcitizen_doctor_wire__crate__api__downloader_api__downloader_remove',
|
||||
);
|
||||
late final _wire__crate__api__downloader_api__downloader_remove =
|
||||
_wire__crate__api__downloader_api__downloader_removePtr
|
||||
.asFunction<void Function(int, int, bool)>();
|
||||
|
||||
void wire__crate__api__downloader_api__downloader_resume(
|
||||
int port_,
|
||||
int task_id,
|
||||
) {
|
||||
return _wire__crate__api__downloader_api__downloader_resume(port_, task_id);
|
||||
}
|
||||
|
||||
late final _wire__crate__api__downloader_api__downloader_resumePtr =
|
||||
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Int64, ffi.UintPtr)>>(
|
||||
'frbgen_starcitizen_doctor_wire__crate__api__downloader_api__downloader_resume',
|
||||
);
|
||||
late final _wire__crate__api__downloader_api__downloader_resume =
|
||||
_wire__crate__api__downloader_api__downloader_resumePtr
|
||||
.asFunction<void Function(int, int)>();
|
||||
|
||||
void wire__crate__api__downloader_api__downloader_resume_all(int port_) {
|
||||
return _wire__crate__api__downloader_api__downloader_resume_all(port_);
|
||||
}
|
||||
|
||||
late final _wire__crate__api__downloader_api__downloader_resume_allPtr =
|
||||
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Int64)>>(
|
||||
'frbgen_starcitizen_doctor_wire__crate__api__downloader_api__downloader_resume_all',
|
||||
);
|
||||
late final _wire__crate__api__downloader_api__downloader_resume_all =
|
||||
_wire__crate__api__downloader_api__downloader_resume_allPtr
|
||||
.asFunction<void Function(int)>();
|
||||
|
||||
void wire__crate__api__downloader_api__downloader_stop(int port_) {
|
||||
return _wire__crate__api__downloader_api__downloader_stop(port_);
|
||||
}
|
||||
|
||||
late final _wire__crate__api__downloader_api__downloader_stopPtr =
|
||||
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Int64)>>(
|
||||
'frbgen_starcitizen_doctor_wire__crate__api__downloader_api__downloader_stop',
|
||||
);
|
||||
late final _wire__crate__api__downloader_api__downloader_stop =
|
||||
_wire__crate__api__downloader_api__downloader_stopPtr
|
||||
.asFunction<void Function(int)>();
|
||||
|
||||
void wire__crate__api__http_api__fetch(
|
||||
int port_,
|
||||
int method,
|
||||
@ -1506,9 +1961,9 @@ class RustLibWire implements BaseWire {
|
||||
|
||||
void wire__crate__api__win32_api__resolve_shortcut(
|
||||
int port_,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> lnk_path,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> _lnk_path,
|
||||
) {
|
||||
return _wire__crate__api__win32_api__resolve_shortcut(port_, lnk_path);
|
||||
return _wire__crate__api__win32_api__resolve_shortcut(port_, _lnk_path);
|
||||
}
|
||||
|
||||
late final _wire__crate__api__win32_api__resolve_shortcutPtr =
|
||||
@ -2341,6 +2796,24 @@ class RustLibWire implements BaseWire {
|
||||
late final _cst_new_list_String = _cst_new_list_StringPtr
|
||||
.asFunction<ffi.Pointer<wire_cst_list_String> Function(int)>();
|
||||
|
||||
ffi.Pointer<wire_cst_list_download_task_info> cst_new_list_download_task_info(
|
||||
int len,
|
||||
) {
|
||||
return _cst_new_list_download_task_info(len);
|
||||
}
|
||||
|
||||
late final _cst_new_list_download_task_infoPtr =
|
||||
_lookup<
|
||||
ffi.NativeFunction<
|
||||
ffi.Pointer<wire_cst_list_download_task_info> Function(ffi.Int32)
|
||||
>
|
||||
>('frbgen_starcitizen_doctor_cst_new_list_download_task_info');
|
||||
late final _cst_new_list_download_task_info =
|
||||
_cst_new_list_download_task_infoPtr
|
||||
.asFunction<
|
||||
ffi.Pointer<wire_cst_list_download_task_info> Function(int)
|
||||
>();
|
||||
|
||||
ffi.Pointer<wire_cst_list_p_4_k_file_item> cst_new_list_p_4_k_file_item(
|
||||
int len,
|
||||
) {
|
||||
@ -2459,6 +2932,20 @@ final class wire_cst_list_prim_u_8_strict extends ffi.Struct {
|
||||
external int len;
|
||||
}
|
||||
|
||||
final class wire_cst_list_String extends ffi.Struct {
|
||||
external ffi.Pointer<ffi.Pointer<wire_cst_list_prim_u_8_strict>> ptr;
|
||||
|
||||
@ffi.Int32()
|
||||
external int len;
|
||||
}
|
||||
|
||||
final class wire_cst_list_prim_u_8_loose extends ffi.Struct {
|
||||
external ffi.Pointer<ffi.Uint8> ptr;
|
||||
|
||||
@ffi.Int32()
|
||||
external int len;
|
||||
}
|
||||
|
||||
final class wire_cst_record_string_string extends ffi.Struct {
|
||||
external ffi.Pointer<wire_cst_list_prim_u_8_strict> field0;
|
||||
|
||||
@ -2472,13 +2959,6 @@ final class wire_cst_list_record_string_string extends ffi.Struct {
|
||||
external int len;
|
||||
}
|
||||
|
||||
final class wire_cst_list_String extends ffi.Struct {
|
||||
external ffi.Pointer<ffi.Pointer<wire_cst_list_prim_u_8_strict>> ptr;
|
||||
|
||||
@ffi.Int32()
|
||||
external int len;
|
||||
}
|
||||
|
||||
final class wire_cst_rsi_launcher_asar_data extends ffi.Struct {
|
||||
external ffi.Pointer<wire_cst_list_prim_u_8_strict> asar_path;
|
||||
|
||||
@ -2487,13 +2967,6 @@ final class wire_cst_rsi_launcher_asar_data extends ffi.Struct {
|
||||
external ffi.Pointer<wire_cst_list_prim_u_8_strict> main_js_content;
|
||||
}
|
||||
|
||||
final class wire_cst_list_prim_u_8_loose extends ffi.Struct {
|
||||
external ffi.Pointer<ffi.Uint8> ptr;
|
||||
|
||||
@ffi.Int32()
|
||||
external int len;
|
||||
}
|
||||
|
||||
final class wire_cst_web_view_configuration extends ffi.Struct {
|
||||
external ffi.Pointer<wire_cst_list_prim_u_8_strict> title;
|
||||
|
||||
@ -2514,6 +2987,46 @@ final class wire_cst_web_view_configuration extends ffi.Struct {
|
||||
external ffi.Pointer<wire_cst_list_prim_u_8_strict> user_agent;
|
||||
}
|
||||
|
||||
final class wire_cst_download_task_info extends ffi.Struct {
|
||||
@ffi.UintPtr()
|
||||
external int id;
|
||||
|
||||
external ffi.Pointer<wire_cst_list_prim_u_8_strict> name;
|
||||
|
||||
@ffi.Int32()
|
||||
external int status;
|
||||
|
||||
@ffi.Uint64()
|
||||
external int total_bytes;
|
||||
|
||||
@ffi.Uint64()
|
||||
external int downloaded_bytes;
|
||||
|
||||
@ffi.Uint64()
|
||||
external int uploaded_bytes;
|
||||
|
||||
@ffi.Uint64()
|
||||
external int download_speed;
|
||||
|
||||
@ffi.Uint64()
|
||||
external int upload_speed;
|
||||
|
||||
@ffi.Double()
|
||||
external double progress;
|
||||
|
||||
@ffi.UintPtr()
|
||||
external int num_peers;
|
||||
|
||||
external ffi.Pointer<wire_cst_list_prim_u_8_strict> output_folder;
|
||||
}
|
||||
|
||||
final class wire_cst_list_download_task_info extends ffi.Struct {
|
||||
external ffi.Pointer<wire_cst_download_task_info> ptr;
|
||||
|
||||
@ffi.Int32()
|
||||
external int len;
|
||||
}
|
||||
|
||||
final class wire_cst_p_4_k_file_item extends ffi.Struct {
|
||||
external ffi.Pointer<wire_cst_list_prim_u_8_strict> name;
|
||||
|
||||
@ -2599,6 +3112,20 @@ final class wire_cst_list_web_view_event extends ffi.Struct {
|
||||
external int len;
|
||||
}
|
||||
|
||||
final class wire_cst_download_global_stat extends ffi.Struct {
|
||||
@ffi.Uint64()
|
||||
external int download_speed;
|
||||
|
||||
@ffi.Uint64()
|
||||
external int upload_speed;
|
||||
|
||||
@ffi.UintPtr()
|
||||
external int num_active;
|
||||
|
||||
@ffi.UintPtr()
|
||||
external int num_waiting;
|
||||
}
|
||||
|
||||
final class wire_cst_rs_process_stream_data extends ffi.Struct {
|
||||
@ffi.Int32()
|
||||
external int data_type;
|
||||
|
||||
@ -1,222 +0,0 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:starcitizen_doctor/common/conf/binary_conf.dart';
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
import 'package:aria2/aria2.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:hive_ce/hive.dart';
|
||||
import 'package:starcitizen_doctor/api/api.dart';
|
||||
import 'package:starcitizen_doctor/common/helper/system_helper.dart';
|
||||
import 'package:starcitizen_doctor/common/rust/api/rs_process.dart' as rs_process;
|
||||
|
||||
import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/provider.dart';
|
||||
import 'package:starcitizen_doctor/ui/home/downloader/home_downloader_ui_model.dart';
|
||||
|
||||
part 'aria2c.g.dart';
|
||||
|
||||
part 'aria2c.freezed.dart';
|
||||
|
||||
@freezed
|
||||
abstract class Aria2cModelState with _$Aria2cModelState {
|
||||
const factory Aria2cModelState({required String aria2cDir, Aria2c? aria2c, Aria2GlobalStat? aria2globalStat}) =
|
||||
_Aria2cModelState;
|
||||
}
|
||||
|
||||
extension Aria2cModelExt on Aria2cModelState {
|
||||
bool get isRunning => aria2c != null;
|
||||
|
||||
bool get hasDownloadTask => aria2globalStat != null && aria2TotalTaskNum > 0;
|
||||
|
||||
int get aria2TotalTaskNum =>
|
||||
aria2globalStat == null ? 0 : ((aria2globalStat!.numActive ?? 0) + (aria2globalStat!.numWaiting ?? 0));
|
||||
}
|
||||
|
||||
@riverpod
|
||||
class Aria2cModel extends _$Aria2cModel {
|
||||
bool _disposed = false;
|
||||
|
||||
@override
|
||||
Aria2cModelState build() {
|
||||
if (appGlobalState.applicationBinaryModuleDir == null) {
|
||||
throw Exception("applicationBinaryModuleDir is null");
|
||||
}
|
||||
ref.onDispose(() {
|
||||
_disposed = true;
|
||||
});
|
||||
ref.keepAlive();
|
||||
final aria2cDir = "${appGlobalState.applicationBinaryModuleDir}\\aria2c";
|
||||
// LazyLoad init
|
||||
() async {
|
||||
try {
|
||||
final sessionFile = File("$aria2cDir\\aria2.session");
|
||||
// 有下载任务则第一时间初始化
|
||||
if (await sessionFile.exists ()
|
||||
&& (await sessionFile.readAsString()).trim().isNotEmpty) {
|
||||
dPrint("launch Aria2c daemon");
|
||||
await launchDaemon(appGlobalState.applicationBinaryModuleDir!);
|
||||
} else {
|
||||
dPrint("LazyLoad Aria2c daemon");
|
||||
}
|
||||
} catch (e) {
|
||||
dPrint("Aria2cManager.checkLazyLoad Error:$e");
|
||||
}
|
||||
}();
|
||||
|
||||
return Aria2cModelState(aria2cDir: aria2cDir);
|
||||
}
|
||||
|
||||
Future launchDaemon(String applicationBinaryModuleDir) async {
|
||||
if (state.aria2c != null) return;
|
||||
await BinaryModuleConf.extractModule(["aria2c"], applicationBinaryModuleDir);
|
||||
|
||||
/// skip for debug hot reload
|
||||
if (kDebugMode) {
|
||||
if ((await SystemHelper.getPID("aria2c")).isNotEmpty) {
|
||||
dPrint("[Aria2cManager] debug skip for hot reload");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final sessionFile = File("${state.aria2cDir}\\aria2.session");
|
||||
if (!await sessionFile.exists()) {
|
||||
await sessionFile.create(recursive: true);
|
||||
}
|
||||
|
||||
final exePath = "${state.aria2cDir}\\aria2c.exe";
|
||||
final port = await getFreePort();
|
||||
final pwd = generateRandomPassword(16);
|
||||
dPrint("pwd === $pwd");
|
||||
final trackerList = await Api.getTorrentTrackerList();
|
||||
dPrint("trackerList === $trackerList");
|
||||
dPrint("Aria2cManager .----- aria2c start $port------");
|
||||
|
||||
final stream = rs_process.start(
|
||||
executable: exePath,
|
||||
arguments: [
|
||||
"-V",
|
||||
"-c",
|
||||
"-x 16",
|
||||
"--dir=${state.aria2cDir}\\downloads",
|
||||
"--disable-ipv6",
|
||||
"--enable-rpc",
|
||||
"--pause",
|
||||
"--rpc-listen-port=$port",
|
||||
"--rpc-secret=$pwd",
|
||||
"--input-file=${sessionFile.absolute.path.trim()}",
|
||||
"--save-session=${sessionFile.absolute.path.trim()}",
|
||||
"--save-session-interval=60",
|
||||
"--file-allocation=trunc",
|
||||
"--seed-time=0",
|
||||
],
|
||||
workingDirectory: state.aria2cDir,
|
||||
);
|
||||
|
||||
String launchError = "";
|
||||
|
||||
stream.listen((event) {
|
||||
dPrint("Aria2cManager.rs_process event === [${event.rsPid}] ${event.dataType} >> ${event.data}");
|
||||
switch (event.dataType) {
|
||||
case rs_process.RsProcessStreamDataType.output:
|
||||
if (event.data.contains("IPv4 RPC: listening on TCP port")) {
|
||||
_onLaunch(port, pwd, trackerList);
|
||||
}
|
||||
break;
|
||||
case rs_process.RsProcessStreamDataType.error:
|
||||
launchError = event.data;
|
||||
state = state.copyWith(aria2c: null);
|
||||
break;
|
||||
case rs_process.RsProcessStreamDataType.exit:
|
||||
launchError = event.data;
|
||||
state = state.copyWith(aria2c: null);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
while (true) {
|
||||
if (state.aria2c != null) return;
|
||||
if (launchError.isNotEmpty) throw launchError;
|
||||
await Future.delayed(const Duration(milliseconds: 100));
|
||||
}
|
||||
}
|
||||
|
||||
Future<int> getFreePort() async {
|
||||
final serverSocket = await ServerSocket.bind("127.0.0.1", 0);
|
||||
final port = serverSocket.port;
|
||||
await serverSocket.close();
|
||||
return port;
|
||||
}
|
||||
|
||||
String generateRandomPassword(int length) {
|
||||
const String charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
Random random = Random();
|
||||
StringBuffer buffer = StringBuffer();
|
||||
for (int i = 0; i < length; i++) {
|
||||
int randomIndex = random.nextInt(charset.length);
|
||||
buffer.write(charset[randomIndex]);
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
Future<void> _onLaunch(int port, String pwd, String trackerList) async {
|
||||
final aria2c = Aria2c("ws://127.0.0.1:$port/jsonrpc", "websocket", pwd);
|
||||
state = state.copyWith(aria2c: aria2c);
|
||||
aria2c.getVersion().then((value) {
|
||||
dPrint("Aria2cManager.connected! version == ${value.version}");
|
||||
_listenState(aria2c);
|
||||
});
|
||||
final box = await Hive.openBox("app_conf");
|
||||
aria2c.changeGlobalOption(
|
||||
Aria2Option()
|
||||
..maxOverallUploadLimit = textToByte(box.get("downloader_up_limit", defaultValue: "0"))
|
||||
..maxOverallDownloadLimit = textToByte(box.get("downloader_down_limit", defaultValue: "0"))
|
||||
..btTracker = trackerList,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _listenState(Aria2c aria2c) async {
|
||||
dPrint("Aria2cModel._listenState start");
|
||||
while (true) {
|
||||
if (_disposed || state.aria2c == null) {
|
||||
dPrint("Aria2cModel._listenState end");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
final aria2globalStat = await aria2c.getGlobalStat();
|
||||
state = state.copyWith(aria2globalStat: aria2globalStat);
|
||||
} catch (e) {
|
||||
dPrint("aria2globalStat update error:$e");
|
||||
}
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> isNameInTask(String name) async {
|
||||
final aria2c = state.aria2c;
|
||||
if (aria2c == null) return false;
|
||||
for (var value in [...await aria2c.tellActive(), ...await aria2c.tellWaiting(0, 100000)]) {
|
||||
final t = HomeDownloaderUIModel.getTaskTypeAndName(value);
|
||||
if (t.key == "torrent" && t.value.contains(name)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -1,289 +0,0 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'aria2c.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// dart format off
|
||||
T _$identity<T>(T value) => value;
|
||||
/// @nodoc
|
||||
mixin _$Aria2cModelState implements DiagnosticableTreeMixin {
|
||||
|
||||
String get aria2cDir; Aria2c? get aria2c; Aria2GlobalStat? get aria2globalStat;
|
||||
/// Create a copy of Aria2cModelState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$Aria2cModelStateCopyWith<Aria2cModelState> get copyWith => _$Aria2cModelStateCopyWithImpl<Aria2cModelState>(this as Aria2cModelState, _$identity);
|
||||
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
properties
|
||||
..add(DiagnosticsProperty('type', 'Aria2cModelState'))
|
||||
..add(DiagnosticsProperty('aria2cDir', aria2cDir))..add(DiagnosticsProperty('aria2c', aria2c))..add(DiagnosticsProperty('aria2globalStat', aria2globalStat));
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is Aria2cModelState&&(identical(other.aria2cDir, aria2cDir) || other.aria2cDir == aria2cDir)&&(identical(other.aria2c, aria2c) || other.aria2c == aria2c)&&(identical(other.aria2globalStat, aria2globalStat) || other.aria2globalStat == aria2globalStat));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,aria2cDir,aria2c,aria2globalStat);
|
||||
|
||||
@override
|
||||
String toString({ DiagnosticLevel minLevel = DiagnosticLevel.info }) {
|
||||
return 'Aria2cModelState(aria2cDir: $aria2cDir, aria2c: $aria2c, aria2globalStat: $aria2globalStat)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $Aria2cModelStateCopyWith<$Res> {
|
||||
factory $Aria2cModelStateCopyWith(Aria2cModelState value, $Res Function(Aria2cModelState) _then) = _$Aria2cModelStateCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
String aria2cDir, Aria2c? aria2c, Aria2GlobalStat? aria2globalStat
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class _$Aria2cModelStateCopyWithImpl<$Res>
|
||||
implements $Aria2cModelStateCopyWith<$Res> {
|
||||
_$Aria2cModelStateCopyWithImpl(this._self, this._then);
|
||||
|
||||
final Aria2cModelState _self;
|
||||
final $Res Function(Aria2cModelState) _then;
|
||||
|
||||
/// Create a copy of Aria2cModelState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? aria2cDir = null,Object? aria2c = freezed,Object? aria2globalStat = freezed,}) {
|
||||
return _then(_self.copyWith(
|
||||
aria2cDir: null == aria2cDir ? _self.aria2cDir : aria2cDir // ignore: cast_nullable_to_non_nullable
|
||||
as String,aria2c: freezed == aria2c ? _self.aria2c : aria2c // ignore: cast_nullable_to_non_nullable
|
||||
as Aria2c?,aria2globalStat: freezed == aria2globalStat ? _self.aria2globalStat : aria2globalStat // ignore: cast_nullable_to_non_nullable
|
||||
as Aria2GlobalStat?,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// Adds pattern-matching-related methods to [Aria2cModelState].
|
||||
extension Aria2cModelStatePatterns on Aria2cModelState {
|
||||
/// A variant of `map` that fallback to returning `orElse`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _Aria2cModelState value)? $default,{required TResult orElse(),}){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _Aria2cModelState() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// Callbacks receives the raw object, upcasted.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case final Subclass2 value:
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _Aria2cModelState value) $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _Aria2cModelState():
|
||||
return $default(_that);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `map` that fallback to returning `null`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _Aria2cModelState value)? $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _Aria2cModelState() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to an `orElse` callback.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String aria2cDir, Aria2c? aria2c, Aria2GlobalStat? aria2globalStat)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _Aria2cModelState() when $default != null:
|
||||
return $default(_that.aria2cDir,_that.aria2c,_that.aria2globalStat);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// As opposed to `map`, this offers destructuring.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case Subclass2(:final field2):
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String aria2cDir, Aria2c? aria2c, Aria2GlobalStat? aria2globalStat) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _Aria2cModelState():
|
||||
return $default(_that.aria2cDir,_that.aria2c,_that.aria2globalStat);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to returning `null`
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String aria2cDir, Aria2c? aria2c, Aria2GlobalStat? aria2globalStat)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _Aria2cModelState() when $default != null:
|
||||
return $default(_that.aria2cDir,_that.aria2c,_that.aria2globalStat);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
|
||||
class _Aria2cModelState with DiagnosticableTreeMixin implements Aria2cModelState {
|
||||
const _Aria2cModelState({required this.aria2cDir, this.aria2c, this.aria2globalStat});
|
||||
|
||||
|
||||
@override final String aria2cDir;
|
||||
@override final Aria2c? aria2c;
|
||||
@override final Aria2GlobalStat? aria2globalStat;
|
||||
|
||||
/// Create a copy of Aria2cModelState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$Aria2cModelStateCopyWith<_Aria2cModelState> get copyWith => __$Aria2cModelStateCopyWithImpl<_Aria2cModelState>(this, _$identity);
|
||||
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
properties
|
||||
..add(DiagnosticsProperty('type', 'Aria2cModelState'))
|
||||
..add(DiagnosticsProperty('aria2cDir', aria2cDir))..add(DiagnosticsProperty('aria2c', aria2c))..add(DiagnosticsProperty('aria2globalStat', aria2globalStat));
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _Aria2cModelState&&(identical(other.aria2cDir, aria2cDir) || other.aria2cDir == aria2cDir)&&(identical(other.aria2c, aria2c) || other.aria2c == aria2c)&&(identical(other.aria2globalStat, aria2globalStat) || other.aria2globalStat == aria2globalStat));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,aria2cDir,aria2c,aria2globalStat);
|
||||
|
||||
@override
|
||||
String toString({ DiagnosticLevel minLevel = DiagnosticLevel.info }) {
|
||||
return 'Aria2cModelState(aria2cDir: $aria2cDir, aria2c: $aria2c, aria2globalStat: $aria2globalStat)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$Aria2cModelStateCopyWith<$Res> implements $Aria2cModelStateCopyWith<$Res> {
|
||||
factory _$Aria2cModelStateCopyWith(_Aria2cModelState value, $Res Function(_Aria2cModelState) _then) = __$Aria2cModelStateCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
String aria2cDir, Aria2c? aria2c, Aria2GlobalStat? aria2globalStat
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class __$Aria2cModelStateCopyWithImpl<$Res>
|
||||
implements _$Aria2cModelStateCopyWith<$Res> {
|
||||
__$Aria2cModelStateCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _Aria2cModelState _self;
|
||||
final $Res Function(_Aria2cModelState) _then;
|
||||
|
||||
/// Create a copy of Aria2cModelState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? aria2cDir = null,Object? aria2c = freezed,Object? aria2globalStat = freezed,}) {
|
||||
return _then(_Aria2cModelState(
|
||||
aria2cDir: null == aria2cDir ? _self.aria2cDir : aria2cDir // ignore: cast_nullable_to_non_nullable
|
||||
as String,aria2c: freezed == aria2c ? _self.aria2c : aria2c // ignore: cast_nullable_to_non_nullable
|
||||
as Aria2c?,aria2globalStat: freezed == aria2globalStat ? _self.aria2globalStat : aria2globalStat // ignore: cast_nullable_to_non_nullable
|
||||
as Aria2GlobalStat?,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// dart format on
|
||||
@ -1,63 +0,0 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'aria2c.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint, type=warning
|
||||
|
||||
@ProviderFor(Aria2cModel)
|
||||
const aria2cModelProvider = Aria2cModelProvider._();
|
||||
|
||||
final class Aria2cModelProvider
|
||||
extends $NotifierProvider<Aria2cModel, Aria2cModelState> {
|
||||
const Aria2cModelProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'aria2cModelProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$aria2cModelHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
Aria2cModel create() => Aria2cModel();
|
||||
|
||||
/// {@macro riverpod.override_with_value}
|
||||
Override overrideWithValue(Aria2cModelState value) {
|
||||
return $ProviderOverride(
|
||||
origin: this,
|
||||
providerOverride: $SyncValueProvider<Aria2cModelState>(value),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _$aria2cModelHash() => r'17956c60a79c68ae13b8b8e700ebbafb70e93194';
|
||||
|
||||
abstract class _$Aria2cModel extends $Notifier<Aria2cModelState> {
|
||||
Aria2cModelState build();
|
||||
@$mustCallSuper
|
||||
@override
|
||||
void runBuild() {
|
||||
final created = build();
|
||||
final ref = this.ref as $Ref<Aria2cModelState, Aria2cModelState>;
|
||||
final element =
|
||||
ref.element
|
||||
as $ClassProviderElement<
|
||||
AnyNotifier<Aria2cModelState, Aria2cModelState>,
|
||||
Aria2cModelState,
|
||||
Object?,
|
||||
Object?
|
||||
>;
|
||||
element.handleValue(ref, created);
|
||||
}
|
||||
}
|
||||
198
lib/provider/download_manager.dart
Normal file
198
lib/provider/download_manager.dart
Normal file
@ -0,0 +1,198 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:starcitizen_doctor/common/rust/api/downloader_api.dart' as downloader_api;
|
||||
import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/provider.dart';
|
||||
|
||||
part 'download_manager.g.dart';
|
||||
|
||||
part 'download_manager.freezed.dart';
|
||||
|
||||
@freezed
|
||||
abstract class DownloadManagerState with _$DownloadManagerState {
|
||||
const factory DownloadManagerState({
|
||||
required String downloadDir,
|
||||
@Default(false) bool isInitialized,
|
||||
downloader_api.DownloadGlobalStat? globalStat,
|
||||
}) = _DownloadManagerState;
|
||||
}
|
||||
|
||||
extension DownloadManagerStateExt on DownloadManagerState {
|
||||
bool get isRunning => isInitialized;
|
||||
|
||||
bool get hasDownloadTask => globalStat != null && (globalStat!.numActive + globalStat!.numWaiting) > BigInt.zero;
|
||||
|
||||
int get totalTaskNum => globalStat == null ? 0 : (globalStat!.numActive + globalStat!.numWaiting).toInt();
|
||||
}
|
||||
|
||||
@riverpod
|
||||
class DownloadManager extends _$DownloadManager {
|
||||
bool _disposed = false;
|
||||
|
||||
@override
|
||||
DownloadManagerState build() {
|
||||
if (appGlobalState.applicationBinaryModuleDir == null) {
|
||||
throw Exception("applicationBinaryModuleDir is null");
|
||||
}
|
||||
ref.onDispose(() {
|
||||
_disposed = true;
|
||||
});
|
||||
ref.keepAlive();
|
||||
|
||||
final downloadDir = "${appGlobalState.applicationBinaryModuleDir}${Platform.pathSeparator}downloads";
|
||||
|
||||
// Lazy load init
|
||||
() async {
|
||||
try {
|
||||
// Check if there are existing tasks
|
||||
final dir = Directory(downloadDir);
|
||||
if (await dir.exists()) {
|
||||
dPrint("Launch download manager");
|
||||
await initDownloader();
|
||||
} else {
|
||||
dPrint("LazyLoad download manager");
|
||||
}
|
||||
} catch (e) {
|
||||
dPrint("DownloadManager.checkLazyLoad Error:$e");
|
||||
}
|
||||
}();
|
||||
|
||||
return DownloadManagerState(downloadDir: downloadDir);
|
||||
}
|
||||
|
||||
Future<void> initDownloader() async {
|
||||
if (state.isInitialized) return;
|
||||
|
||||
try {
|
||||
// Create download directory if it doesn't exist
|
||||
final dir = Directory(state.downloadDir);
|
||||
if (!await dir.exists()) {
|
||||
await dir.create(recursive: true);
|
||||
}
|
||||
|
||||
// Initialize the Rust downloader
|
||||
downloader_api.downloaderInit(downloadDir: state.downloadDir);
|
||||
|
||||
state = state.copyWith(isInitialized: true);
|
||||
|
||||
// Start listening to state updates
|
||||
_listenState();
|
||||
|
||||
dPrint("DownloadManager initialized");
|
||||
} catch (e) {
|
||||
dPrint("DownloadManager.initDownloader Error: $e");
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _listenState() async {
|
||||
dPrint("DownloadManager._listenState start");
|
||||
while (true) {
|
||||
if (_disposed || !state.isInitialized) {
|
||||
dPrint("DownloadManager._listenState end");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
final globalStat = await downloader_api.downloaderGetGlobalStats();
|
||||
state = state.copyWith(globalStat: globalStat);
|
||||
} catch (e) {
|
||||
dPrint("globalStat update error:$e");
|
||||
}
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a torrent from base64 encoded bytes
|
||||
Future<int> addTorrent(List<int> torrentBytes, {String? outputFolder, List<String>? trackers}) async {
|
||||
await initDownloader();
|
||||
final taskId = await downloader_api.downloaderAddTorrent(
|
||||
torrentBytes: torrentBytes,
|
||||
outputFolder: outputFolder,
|
||||
trackers: trackers,
|
||||
);
|
||||
return taskId.toInt();
|
||||
}
|
||||
|
||||
/// Add a torrent from magnet link
|
||||
Future<int> addMagnet(String magnetLink, {String? outputFolder, List<String>? trackers}) async {
|
||||
await initDownloader();
|
||||
final taskId = await downloader_api.downloaderAddMagnet(
|
||||
magnetLink: magnetLink,
|
||||
outputFolder: outputFolder,
|
||||
trackers: trackers,
|
||||
);
|
||||
return taskId.toInt();
|
||||
}
|
||||
|
||||
/// Add a torrent from URL (only .torrent file URLs are supported)
|
||||
/// HTTP downloads are NOT supported - will throw an exception
|
||||
Future<int> addUrl(String url, {String? outputFolder, List<String>? trackers}) async {
|
||||
await initDownloader();
|
||||
final taskId = await downloader_api.downloaderAddUrl(url: url, outputFolder: outputFolder, trackers: trackers);
|
||||
return taskId.toInt();
|
||||
}
|
||||
|
||||
Future<void> pauseTask(int taskId) async {
|
||||
await downloader_api.downloaderPause(taskId: BigInt.from(taskId));
|
||||
}
|
||||
|
||||
Future<void> resumeTask(int taskId) async {
|
||||
await downloader_api.downloaderResume(taskId: BigInt.from(taskId));
|
||||
}
|
||||
|
||||
Future<void> removeTask(int taskId, {bool deleteFiles = false}) async {
|
||||
await downloader_api.downloaderRemove(taskId: BigInt.from(taskId), deleteFiles: deleteFiles);
|
||||
}
|
||||
|
||||
Future<downloader_api.DownloadTaskInfo> getTaskInfo(int taskId) async {
|
||||
return await downloader_api.downloaderGetTaskInfo(taskId: BigInt.from(taskId));
|
||||
}
|
||||
|
||||
Future<List<downloader_api.DownloadTaskInfo>> getAllTasks() async {
|
||||
if (!state.isInitialized) {
|
||||
return [];
|
||||
}
|
||||
return await downloader_api.downloaderGetAllTasks();
|
||||
}
|
||||
|
||||
Future<bool> isNameInTask(String name) async {
|
||||
if (!state.isInitialized) {
|
||||
return false;
|
||||
}
|
||||
return await downloader_api.downloaderIsNameInTask(name: name);
|
||||
}
|
||||
|
||||
Future<void> pauseAll() async {
|
||||
await downloader_api.downloaderPauseAll();
|
||||
}
|
||||
|
||||
Future<void> resumeAll() async {
|
||||
await downloader_api.downloaderResumeAll();
|
||||
}
|
||||
|
||||
Future<void> stop() async {
|
||||
await downloader_api.downloaderStop();
|
||||
state = state.copyWith(isInitialized: false, globalStat: null);
|
||||
}
|
||||
|
||||
/// Convert speed limit text to bytes per second
|
||||
/// Supports formats like: "1", "100k", "10m", "0"
|
||||
int textToByte(String text) {
|
||||
if (text.isEmpty || text == "0") {
|
||||
return 0;
|
||||
}
|
||||
final trimmed = text.trim().toLowerCase();
|
||||
if (int.tryParse(trimmed) != null) {
|
||||
return int.parse(trimmed);
|
||||
}
|
||||
if (trimmed.endsWith("k")) {
|
||||
return int.parse(trimmed.substring(0, trimmed.length - 1)) * 1024;
|
||||
}
|
||||
if (trimmed.endsWith("m")) {
|
||||
return int.parse(trimmed.substring(0, trimmed.length - 1)) * 1024 * 1024;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
277
lib/provider/download_manager.freezed.dart
Normal file
277
lib/provider/download_manager.freezed.dart
Normal file
@ -0,0 +1,277 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'download_manager.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// dart format off
|
||||
T _$identity<T>(T value) => value;
|
||||
/// @nodoc
|
||||
mixin _$DownloadManagerState {
|
||||
|
||||
String get downloadDir; bool get isInitialized; downloader_api.DownloadGlobalStat? get globalStat;
|
||||
/// Create a copy of DownloadManagerState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$DownloadManagerStateCopyWith<DownloadManagerState> get copyWith => _$DownloadManagerStateCopyWithImpl<DownloadManagerState>(this as DownloadManagerState, _$identity);
|
||||
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is DownloadManagerState&&(identical(other.downloadDir, downloadDir) || other.downloadDir == downloadDir)&&(identical(other.isInitialized, isInitialized) || other.isInitialized == isInitialized)&&(identical(other.globalStat, globalStat) || other.globalStat == globalStat));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,downloadDir,isInitialized,globalStat);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'DownloadManagerState(downloadDir: $downloadDir, isInitialized: $isInitialized, globalStat: $globalStat)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $DownloadManagerStateCopyWith<$Res> {
|
||||
factory $DownloadManagerStateCopyWith(DownloadManagerState value, $Res Function(DownloadManagerState) _then) = _$DownloadManagerStateCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
String downloadDir, bool isInitialized, downloader_api.DownloadGlobalStat? globalStat
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class _$DownloadManagerStateCopyWithImpl<$Res>
|
||||
implements $DownloadManagerStateCopyWith<$Res> {
|
||||
_$DownloadManagerStateCopyWithImpl(this._self, this._then);
|
||||
|
||||
final DownloadManagerState _self;
|
||||
final $Res Function(DownloadManagerState) _then;
|
||||
|
||||
/// Create a copy of DownloadManagerState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? downloadDir = null,Object? isInitialized = null,Object? globalStat = freezed,}) {
|
||||
return _then(_self.copyWith(
|
||||
downloadDir: null == downloadDir ? _self.downloadDir : downloadDir // ignore: cast_nullable_to_non_nullable
|
||||
as String,isInitialized: null == isInitialized ? _self.isInitialized : isInitialized // ignore: cast_nullable_to_non_nullable
|
||||
as bool,globalStat: freezed == globalStat ? _self.globalStat : globalStat // ignore: cast_nullable_to_non_nullable
|
||||
as downloader_api.DownloadGlobalStat?,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// Adds pattern-matching-related methods to [DownloadManagerState].
|
||||
extension DownloadManagerStatePatterns on DownloadManagerState {
|
||||
/// A variant of `map` that fallback to returning `orElse`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _DownloadManagerState value)? $default,{required TResult orElse(),}){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _DownloadManagerState() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// Callbacks receives the raw object, upcasted.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case final Subclass2 value:
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _DownloadManagerState value) $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _DownloadManagerState():
|
||||
return $default(_that);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `map` that fallback to returning `null`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _DownloadManagerState value)? $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _DownloadManagerState() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to an `orElse` callback.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String downloadDir, bool isInitialized, downloader_api.DownloadGlobalStat? globalStat)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _DownloadManagerState() when $default != null:
|
||||
return $default(_that.downloadDir,_that.isInitialized,_that.globalStat);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// As opposed to `map`, this offers destructuring.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case Subclass2(:final field2):
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String downloadDir, bool isInitialized, downloader_api.DownloadGlobalStat? globalStat) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _DownloadManagerState():
|
||||
return $default(_that.downloadDir,_that.isInitialized,_that.globalStat);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to returning `null`
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String downloadDir, bool isInitialized, downloader_api.DownloadGlobalStat? globalStat)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _DownloadManagerState() when $default != null:
|
||||
return $default(_that.downloadDir,_that.isInitialized,_that.globalStat);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
|
||||
class _DownloadManagerState implements DownloadManagerState {
|
||||
const _DownloadManagerState({required this.downloadDir, this.isInitialized = false, this.globalStat});
|
||||
|
||||
|
||||
@override final String downloadDir;
|
||||
@override@JsonKey() final bool isInitialized;
|
||||
@override final downloader_api.DownloadGlobalStat? globalStat;
|
||||
|
||||
/// Create a copy of DownloadManagerState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$DownloadManagerStateCopyWith<_DownloadManagerState> get copyWith => __$DownloadManagerStateCopyWithImpl<_DownloadManagerState>(this, _$identity);
|
||||
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _DownloadManagerState&&(identical(other.downloadDir, downloadDir) || other.downloadDir == downloadDir)&&(identical(other.isInitialized, isInitialized) || other.isInitialized == isInitialized)&&(identical(other.globalStat, globalStat) || other.globalStat == globalStat));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,downloadDir,isInitialized,globalStat);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'DownloadManagerState(downloadDir: $downloadDir, isInitialized: $isInitialized, globalStat: $globalStat)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$DownloadManagerStateCopyWith<$Res> implements $DownloadManagerStateCopyWith<$Res> {
|
||||
factory _$DownloadManagerStateCopyWith(_DownloadManagerState value, $Res Function(_DownloadManagerState) _then) = __$DownloadManagerStateCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
String downloadDir, bool isInitialized, downloader_api.DownloadGlobalStat? globalStat
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class __$DownloadManagerStateCopyWithImpl<$Res>
|
||||
implements _$DownloadManagerStateCopyWith<$Res> {
|
||||
__$DownloadManagerStateCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _DownloadManagerState _self;
|
||||
final $Res Function(_DownloadManagerState) _then;
|
||||
|
||||
/// Create a copy of DownloadManagerState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? downloadDir = null,Object? isInitialized = null,Object? globalStat = freezed,}) {
|
||||
return _then(_DownloadManagerState(
|
||||
downloadDir: null == downloadDir ? _self.downloadDir : downloadDir // ignore: cast_nullable_to_non_nullable
|
||||
as String,isInitialized: null == isInitialized ? _self.isInitialized : isInitialized // ignore: cast_nullable_to_non_nullable
|
||||
as bool,globalStat: freezed == globalStat ? _self.globalStat : globalStat // ignore: cast_nullable_to_non_nullable
|
||||
as downloader_api.DownloadGlobalStat?,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// dart format on
|
||||
63
lib/provider/download_manager.g.dart
Normal file
63
lib/provider/download_manager.g.dart
Normal file
@ -0,0 +1,63 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'download_manager.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint, type=warning
|
||||
|
||||
@ProviderFor(DownloadManager)
|
||||
const downloadManagerProvider = DownloadManagerProvider._();
|
||||
|
||||
final class DownloadManagerProvider
|
||||
extends $NotifierProvider<DownloadManager, DownloadManagerState> {
|
||||
const DownloadManagerProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'downloadManagerProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$downloadManagerHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
DownloadManager create() => DownloadManager();
|
||||
|
||||
/// {@macro riverpod.override_with_value}
|
||||
Override overrideWithValue(DownloadManagerState value) {
|
||||
return $ProviderOverride(
|
||||
origin: this,
|
||||
providerOverride: $SyncValueProvider<DownloadManagerState>(value),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _$downloadManagerHash() => r'adc9a147522afbfcfc8a2e16310649220a75d6a3';
|
||||
|
||||
abstract class _$DownloadManager extends $Notifier<DownloadManagerState> {
|
||||
DownloadManagerState build();
|
||||
@$mustCallSuper
|
||||
@override
|
||||
void runBuild() {
|
||||
final created = build();
|
||||
final ref = this.ref as $Ref<DownloadManagerState, DownloadManagerState>;
|
||||
final element =
|
||||
ref.element
|
||||
as $ClassProviderElement<
|
||||
AnyNotifier<DownloadManagerState, DownloadManagerState>,
|
||||
DownloadManagerState,
|
||||
Object?,
|
||||
Object?
|
||||
>;
|
||||
element.handleValue(ref, created);
|
||||
}
|
||||
}
|
||||
@ -2,6 +2,7 @@ import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:starcitizen_doctor/widgets/widgets.dart';
|
||||
import 'package:file_sizes/file_sizes.dart';
|
||||
import 'package:starcitizen_doctor/common/rust/api/downloader_api.dart';
|
||||
|
||||
import 'home_downloader_ui_model.dart';
|
||||
|
||||
@ -13,59 +14,51 @@ class HomeDownloaderUI extends HookConsumerWidget {
|
||||
final state = ref.watch(homeDownloaderUIModelProvider);
|
||||
final model = ref.read(homeDownloaderUIModelProvider.notifier);
|
||||
|
||||
return makeDefaultPage(context,
|
||||
content: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
children: [
|
||||
const Spacer(),
|
||||
const SizedBox(width: 24),
|
||||
const SizedBox(width: 12),
|
||||
for (final item in <MapEntry<String, IconData>, String>{
|
||||
const MapEntry("settings", FluentIcons.settings):
|
||||
S.current.downloader_speed_limit_settings,
|
||||
if (state.tasks.isNotEmpty)
|
||||
const MapEntry("pause_all", FluentIcons.pause):
|
||||
S.current.downloader_action_pause_all,
|
||||
if (state.waitingTasks.isNotEmpty)
|
||||
const MapEntry("resume_all", FluentIcons.download):
|
||||
S.current.downloader_action_resume_all,
|
||||
if (state.tasks.isNotEmpty || state.waitingTasks.isNotEmpty)
|
||||
const MapEntry("cancel_all", FluentIcons.cancel):
|
||||
S.current.downloader_action_cancel_all,
|
||||
}.entries)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 6, right: 6),
|
||||
child: Button(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(item.key.value),
|
||||
const SizedBox(width: 6),
|
||||
Text(item.value),
|
||||
],
|
||||
),
|
||||
),
|
||||
onPressed: () =>
|
||||
model.onTapButton(context, item.key.key)),
|
||||
return makeDefaultPage(
|
||||
context,
|
||||
content: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
children: [
|
||||
const Spacer(),
|
||||
const SizedBox(width: 24),
|
||||
const SizedBox(width: 12),
|
||||
for (final item in <MapEntry<String, IconData>, String>{
|
||||
const MapEntry("settings", FluentIcons.settings): S.current.downloader_speed_limit_settings,
|
||||
if (state.activeTasks.isNotEmpty)
|
||||
const MapEntry("pause_all", FluentIcons.pause): S.current.downloader_action_pause_all,
|
||||
if (state.waitingTasks.isNotEmpty)
|
||||
const MapEntry("resume_all", FluentIcons.download): S.current.downloader_action_resume_all,
|
||||
if (state.activeTasks.isNotEmpty || state.waitingTasks.isNotEmpty)
|
||||
const MapEntry("cancel_all", FluentIcons.cancel): S.current.downloader_action_cancel_all,
|
||||
}.entries)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 6, right: 6),
|
||||
child: Button(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Row(children: [Icon(item.key.value), const SizedBox(width: 6), Text(item.value)]),
|
||||
),
|
||||
onPressed: () => model.onTapButton(context, item.key.key),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
],
|
||||
),
|
||||
if (model.getTasksLen() == 0)
|
||||
Expanded(
|
||||
child: Center(
|
||||
child: Text(S.current.downloader_info_no_download_tasks),
|
||||
))
|
||||
else
|
||||
Expanded(
|
||||
child: ListView.builder(
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
],
|
||||
),
|
||||
if (model.getTasksLen() == 0)
|
||||
Expanded(child: Center(child: Text(S.current.downloader_info_no_download_tasks)))
|
||||
else
|
||||
Expanded(
|
||||
child: ListView.builder(
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
final (task, type, isFirstType) = model.getTaskAndType(index);
|
||||
final nt = HomeDownloaderUIModel.getTaskTypeAndName(task);
|
||||
final statusStr = model.getStatusString(task.status);
|
||||
final isActive = task.status == DownloadTaskStatus.live;
|
||||
final isPaused = task.status == DownloadTaskStatus.paused;
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@ -73,39 +66,30 @@ class HomeDownloaderUI extends HookConsumerWidget {
|
||||
Column(
|
||||
children: [
|
||||
Container(
|
||||
padding: EdgeInsets.only(
|
||||
left: 24,
|
||||
right: 24,
|
||||
top: index == 0 ? 0 : 12,
|
||||
bottom: 12),
|
||||
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: Row(
|
||||
children: [
|
||||
Text(
|
||||
"${model.listHeaderStatusMap[type]}",
|
||||
style: const TextStyle(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
],
|
||||
)),
|
||||
child: Row(
|
||||
children: [
|
||||
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),
|
||||
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
|
||||
.withValues(alpha: .06),
|
||||
color: FluentTheme.of(context).cardColor.withValues(alpha: .06),
|
||||
borderRadius: BorderRadius.circular(7),
|
||||
),
|
||||
child: Row(
|
||||
@ -113,47 +97,26 @@ class HomeDownloaderUI extends HookConsumerWidget {
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
nt.value,
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
Text(nt.value, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
|
||||
const SizedBox(height: 6),
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
S.current.downloader_info_total_size(
|
||||
FileSize.getSize(
|
||||
task.totalLength ?? 0)),
|
||||
S.current.downloader_info_total_size(FileSize.getSize(task.totalBytes.toInt())),
|
||||
style: const TextStyle(fontSize: 14),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
if (nt.key == "torrent" &&
|
||||
task.verifiedLength != null &&
|
||||
task.verifiedLength != 0)
|
||||
if (isActive)
|
||||
Text(
|
||||
S.current.downloader_info_verifying(
|
||||
FileSize.getSize(
|
||||
task.verifiedLength)),
|
||||
style: const TextStyle(fontSize: 14),
|
||||
S.current.downloader_info_downloading((task.progress * 100).toStringAsFixed(2)),
|
||||
)
|
||||
else if (task.status == "active")
|
||||
Text(S.current
|
||||
.downloader_info_downloading(
|
||||
((task.completedLength ?? 0) *
|
||||
100 /
|
||||
(task.totalLength ?? 1))
|
||||
.toStringAsFixed(4)))
|
||||
else
|
||||
Text(S.current.downloader_info_status(
|
||||
model.statusMap[task.status] ??
|
||||
"Unknown")),
|
||||
Text(S.current.downloader_info_status(model.statusMap[statusStr] ?? "Unknown")),
|
||||
const SizedBox(width: 24),
|
||||
if (task.status == "active" &&
|
||||
task.verifiedLength == null)
|
||||
if (isActive)
|
||||
Text(
|
||||
"ETA: ${model.formatter.format(DateTime.now().add(Duration(seconds: model.getETA(task))))}"),
|
||||
"ETA: ${model.formatter.format(DateTime.now().add(Duration(seconds: model.getETA(task))))}",
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
@ -162,20 +125,18 @@ class HomeDownloaderUI extends HookConsumerWidget {
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(S.current.downloader_info_uploaded(
|
||||
FileSize.getSize(task.uploadLength))),
|
||||
Text(S.current.downloader_info_downloaded(
|
||||
FileSize.getSize(task.completedLength))),
|
||||
Text(S.current.downloader_info_uploaded(FileSize.getSize(task.uploadedBytes.toInt()))),
|
||||
Text(
|
||||
S.current.downloader_info_downloaded(FileSize.getSize(task.downloadedBytes.toInt())),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(width: 18),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"↑:${FileSize.getSize(task.uploadSpeed)}/s"),
|
||||
Text(
|
||||
"↓:${FileSize.getSize(task.downloadSpeed)}/s"),
|
||||
Text("↑:${FileSize.getSize(task.uploadSpeed.toInt())}/s"),
|
||||
Text("↓:${FileSize.getSize(task.downloadSpeed.toInt())}/s"),
|
||||
],
|
||||
),
|
||||
const SizedBox(width: 32),
|
||||
@ -184,42 +145,32 @@ class HomeDownloaderUI extends HookConsumerWidget {
|
||||
closeAfterClick: false,
|
||||
title: Padding(
|
||||
padding: const EdgeInsets.all(3),
|
||||
child:
|
||||
Text(S.current.downloader_action_options),
|
||||
child: Text(S.current.downloader_action_options),
|
||||
),
|
||||
items: [
|
||||
if (task.status == "paused")
|
||||
if (isPaused)
|
||||
MenuFlyoutItem(
|
||||
leading:
|
||||
const Icon(FluentIcons.download),
|
||||
text: Text(S.current
|
||||
.downloader_action_continue_download),
|
||||
onPressed: () =>
|
||||
model.resumeTask(task.gid))
|
||||
else if (task.status == "active")
|
||||
leading: const Icon(FluentIcons.download),
|
||||
text: Text(S.current.downloader_action_continue_download),
|
||||
onPressed: () => model.resumeTask(task.id.toInt()),
|
||||
)
|
||||
else if (isActive)
|
||||
MenuFlyoutItem(
|
||||
leading: const Icon(FluentIcons.pause),
|
||||
text: Text(S.current
|
||||
.downloader_action_pause_download),
|
||||
onPressed: () =>
|
||||
model.pauseTask(task.gid)),
|
||||
leading: const Icon(FluentIcons.pause),
|
||||
text: Text(S.current.downloader_action_pause_download),
|
||||
onPressed: () => model.pauseTask(task.id.toInt()),
|
||||
),
|
||||
const MenuFlyoutSeparator(),
|
||||
MenuFlyoutItem(
|
||||
leading: const Icon(
|
||||
FluentIcons.chrome_close,
|
||||
size: 14,
|
||||
),
|
||||
text: Text(S.current
|
||||
.downloader_action_cancel_download),
|
||||
onPressed: () =>
|
||||
model.cancelTask(context, task.gid)),
|
||||
leading: const Icon(FluentIcons.chrome_close, size: 14),
|
||||
text: Text(S.current.downloader_action_cancel_download),
|
||||
onPressed: () => model.cancelTask(context, task.id.toInt()),
|
||||
),
|
||||
MenuFlyoutItem(
|
||||
leading: const Icon(
|
||||
FluentIcons.folder_open,
|
||||
size: 14,
|
||||
),
|
||||
text: Text(S.current.action_open_folder),
|
||||
onPressed: () => model.openFolder(task)),
|
||||
leading: const Icon(FluentIcons.folder_open, size: 14),
|
||||
text: Text(S.current.action_open_folder),
|
||||
onPressed: () => model.openFolder(task),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
@ -230,31 +181,36 @@ class HomeDownloaderUI extends HookConsumerWidget {
|
||||
);
|
||||
},
|
||||
itemCount: model.getTasksLen(),
|
||||
)),
|
||||
Container(
|
||||
color: FluentTheme.of(context).cardColor.withValues(alpha: .06),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 12, bottom: 3, top: 3),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 8,
|
||||
height: 8,
|
||||
decoration: BoxDecoration(
|
||||
color: state.isAvailable ? Colors.green : Colors.white,
|
||||
borderRadius: BorderRadius.circular(1000),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(S.current.downloader_info_download_upload_speed(
|
||||
FileSize.getSize(state.globalStat?.downloadSpeed ?? 0),
|
||||
FileSize.getSize(state.globalStat?.uploadSpeed ?? 0)))
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
useBodyContainer: true);
|
||||
Container(
|
||||
color: FluentTheme.of(context).cardColor.withValues(alpha: .06),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 12, bottom: 3, top: 3),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 8,
|
||||
height: 8,
|
||||
decoration: BoxDecoration(
|
||||
color: state.isAvailable ? Colors.green : Colors.white,
|
||||
borderRadius: BorderRadius.circular(1000),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
S.current.downloader_info_download_upload_speed(
|
||||
FileSize.getSize((state.globalStat?.downloadSpeed ?? BigInt.zero).toInt()),
|
||||
FileSize.getSize((state.globalStat?.uploadSpeed ?? BigInt.zero).toInt()),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
useBodyContainer: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
// ignore_for_file: avoid_build_context_in_providers, avoid_public_notifier_properties
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:aria2/aria2.dart';
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
@ -9,9 +7,9 @@ import 'package:hive_ce/hive.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:starcitizen_doctor/common/helper/system_helper.dart';
|
||||
import 'package:starcitizen_doctor/common/rust/api/downloader_api.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/provider.dart';
|
||||
import 'package:starcitizen_doctor/provider/aria2c.dart';
|
||||
import 'package:starcitizen_doctor/provider/download_manager.dart';
|
||||
|
||||
import '../../../widgets/widgets.dart';
|
||||
|
||||
@ -22,10 +20,10 @@ part 'home_downloader_ui_model.freezed.dart';
|
||||
@freezed
|
||||
abstract class HomeDownloaderUIState with _$HomeDownloaderUIState {
|
||||
factory HomeDownloaderUIState({
|
||||
@Default([]) List<Aria2Task> tasks,
|
||||
@Default([]) List<Aria2Task> waitingTasks,
|
||||
@Default([]) List<Aria2Task> stoppedTasks,
|
||||
Aria2GlobalStat? globalStat,
|
||||
@Default([]) List<DownloadTaskInfo> activeTasks,
|
||||
@Default([]) List<DownloadTaskInfo> waitingTasks,
|
||||
@Default([]) List<DownloadTaskInfo> stoppedTasks,
|
||||
DownloadGlobalStat? globalStat,
|
||||
}) = _HomeDownloaderUIState;
|
||||
}
|
||||
|
||||
@ -40,12 +38,11 @@ class HomeDownloaderUIModel extends _$HomeDownloaderUIModel {
|
||||
bool _disposed = false;
|
||||
|
||||
final statusMap = {
|
||||
"active": S.current.downloader_info_downloading_status,
|
||||
"waiting": S.current.downloader_info_waiting,
|
||||
"live": S.current.downloader_info_downloading_status,
|
||||
"initializing": S.current.downloader_info_waiting,
|
||||
"paused": S.current.downloader_info_paused,
|
||||
"error": S.current.downloader_info_download_failed,
|
||||
"complete": S.current.downloader_info_download_completed,
|
||||
"removed": S.current.downloader_info_deleted,
|
||||
"finished": S.current.downloader_info_download_completed,
|
||||
};
|
||||
|
||||
final listHeaderStatusMap = {
|
||||
@ -65,17 +62,17 @@ class HomeDownloaderUIModel extends _$HomeDownloaderUIModel {
|
||||
}
|
||||
|
||||
Future<void> onTapButton(BuildContext context, String key) async {
|
||||
final aria2cState = ref.read(aria2cModelProvider);
|
||||
final downloadManagerState = ref.read(downloadManagerProvider);
|
||||
final downloadManager = ref.read(downloadManagerProvider.notifier);
|
||||
|
||||
switch (key) {
|
||||
case "pause_all":
|
||||
if (!aria2cState.isRunning) return;
|
||||
await aria2cState.aria2c?.pauseAll();
|
||||
await aria2cState.aria2c?.saveSession();
|
||||
if (!downloadManagerState.isRunning) return;
|
||||
await downloadManager.pauseAll();
|
||||
return;
|
||||
case "resume_all":
|
||||
if (!aria2cState.isRunning) return;
|
||||
await aria2cState.aria2c?.unpauseAll();
|
||||
await aria2cState.aria2c?.saveSession();
|
||||
if (!downloadManagerState.isRunning) return;
|
||||
await downloadManager.resumeAll();
|
||||
return;
|
||||
case "cancel_all":
|
||||
final userOK = await showConfirmDialogs(
|
||||
@ -84,12 +81,12 @@ class HomeDownloaderUIModel extends _$HomeDownloaderUIModel {
|
||||
Text(S.current.downloader_info_manual_file_deletion_note),
|
||||
);
|
||||
if (userOK == true) {
|
||||
if (!aria2cState.isRunning) return;
|
||||
if (!downloadManagerState.isRunning) return;
|
||||
try {
|
||||
for (var value in [...state.tasks, ...state.waitingTasks]) {
|
||||
await aria2cState.aria2c?.remove(value.gid!);
|
||||
final allTasks = [...state.activeTasks, ...state.waitingTasks];
|
||||
for (var task in allTasks) {
|
||||
await downloadManager.removeTask(task.id.toInt(), deleteFiles: false);
|
||||
}
|
||||
await aria2cState.aria2c?.saveSession();
|
||||
} catch (e) {
|
||||
dPrint("DownloadsUIModel cancel_all Error: $e");
|
||||
}
|
||||
@ -102,91 +99,77 @@ class HomeDownloaderUIModel extends _$HomeDownloaderUIModel {
|
||||
}
|
||||
|
||||
int getTasksLen() {
|
||||
return state.tasks.length + state.waitingTasks.length + state.stoppedTasks.length;
|
||||
return state.activeTasks.length + state.waitingTasks.length + state.stoppedTasks.length;
|
||||
}
|
||||
|
||||
(Aria2Task, String, bool) getTaskAndType(int index) {
|
||||
final tempList = <Aria2Task>[...state.tasks, ...state.waitingTasks, ...state.stoppedTasks];
|
||||
if (index >= 0 && index < state.tasks.length) {
|
||||
(DownloadTaskInfo, String, bool) getTaskAndType(int index) {
|
||||
final tempList = <DownloadTaskInfo>[...state.activeTasks, ...state.waitingTasks, ...state.stoppedTasks];
|
||||
if (index >= 0 && index < state.activeTasks.length) {
|
||||
return (tempList[index], "active", index == 0);
|
||||
}
|
||||
if (index >= state.tasks.length && index < state.tasks.length + state.waitingTasks.length) {
|
||||
return (tempList[index], "waiting", index == state.tasks.length);
|
||||
if (index >= state.activeTasks.length && index < state.activeTasks.length + state.waitingTasks.length) {
|
||||
return (tempList[index], "waiting", index == state.activeTasks.length);
|
||||
}
|
||||
if (index >= state.tasks.length + state.waitingTasks.length && index < tempList.length) {
|
||||
return (tempList[index], "stopped", index == state.tasks.length + state.waitingTasks.length);
|
||||
if (index >= state.activeTasks.length + state.waitingTasks.length && index < tempList.length) {
|
||||
return (tempList[index], "stopped", index == state.activeTasks.length + state.waitingTasks.length);
|
||||
}
|
||||
throw Exception("Index out of range or element is null");
|
||||
}
|
||||
|
||||
static MapEntry<String, String> getTaskTypeAndName(Aria2Task task) {
|
||||
if (task.bittorrent == null) {
|
||||
String uri = task.files?[0]['uris'][0]['uri'] as String;
|
||||
return MapEntry("url", uri.split('/').last);
|
||||
} else if (task.bittorrent != null) {
|
||||
if (task.bittorrent!.containsKey('info')) {
|
||||
var btName = task.bittorrent?["info"]["name"];
|
||||
return MapEntry("torrent", btName ?? 'torrent');
|
||||
} else {
|
||||
return MapEntry("magnet", '[METADATA]${task.infoHash}');
|
||||
}
|
||||
} else {
|
||||
return const MapEntry("metaLink", '==========metaLink============');
|
||||
static MapEntry<String, String> getTaskTypeAndName(DownloadTaskInfo task) {
|
||||
// All tasks in rqbit are torrent-based
|
||||
return MapEntry("torrent", task.name);
|
||||
}
|
||||
|
||||
int getETA(DownloadTaskInfo task) {
|
||||
if (task.downloadSpeed == BigInt.zero) return 0;
|
||||
final remainingBytes = task.totalBytes - task.downloadedBytes;
|
||||
return (remainingBytes ~/ task.downloadSpeed).toInt();
|
||||
}
|
||||
|
||||
String getStatusString(DownloadTaskStatus status) {
|
||||
switch (status) {
|
||||
case DownloadTaskStatus.live:
|
||||
return "live";
|
||||
case DownloadTaskStatus.initializing:
|
||||
return "initializing";
|
||||
case DownloadTaskStatus.paused:
|
||||
return "paused";
|
||||
case DownloadTaskStatus.error:
|
||||
return "error";
|
||||
case DownloadTaskStatus.finished:
|
||||
return "finished";
|
||||
}
|
||||
}
|
||||
|
||||
int getETA(Aria2Task task) {
|
||||
if (task.downloadSpeed == null || task.downloadSpeed == 0) return 0;
|
||||
final remainingBytes = (task.totalLength ?? 0) - (task.completedLength ?? 0);
|
||||
return remainingBytes ~/ (task.downloadSpeed!);
|
||||
Future<void> resumeTask(int taskId) async {
|
||||
final downloadManager = ref.read(downloadManagerProvider.notifier);
|
||||
await downloadManager.resumeTask(taskId);
|
||||
}
|
||||
|
||||
Future<void> resumeTask(String? gid) async {
|
||||
final aria2c = ref.read(aria2cModelProvider).aria2c;
|
||||
if (gid != null) {
|
||||
await aria2c?.unpause(gid);
|
||||
}
|
||||
Future<void> pauseTask(int taskId) async {
|
||||
final downloadManager = ref.read(downloadManagerProvider.notifier);
|
||||
await downloadManager.pauseTask(taskId);
|
||||
}
|
||||
|
||||
Future<void> pauseTask(String? gid) async {
|
||||
final aria2c = ref.read(aria2cModelProvider).aria2c;
|
||||
if (gid != null) {
|
||||
await aria2c?.pause(gid);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> cancelTask(BuildContext context, String? gid) async {
|
||||
Future<void> cancelTask(BuildContext context, int taskId) async {
|
||||
await Future.delayed(const Duration(milliseconds: 300));
|
||||
if (gid != null) {
|
||||
if (!context.mounted) return;
|
||||
final ok = await showConfirmDialogs(
|
||||
context,
|
||||
S.current.downloader_action_confirm_cancel_download,
|
||||
Text(S.current.downloader_info_manual_file_deletion_note),
|
||||
);
|
||||
if (ok == true) {
|
||||
final aria2c = ref.read(aria2cModelProvider).aria2c;
|
||||
await aria2c?.remove(gid);
|
||||
await aria2c?.saveSession();
|
||||
}
|
||||
if (!context.mounted) return;
|
||||
final ok = await showConfirmDialogs(
|
||||
context,
|
||||
S.current.downloader_action_confirm_cancel_download,
|
||||
Text(S.current.downloader_info_manual_file_deletion_note),
|
||||
);
|
||||
if (ok == true) {
|
||||
final downloadManager = ref.read(downloadManagerProvider.notifier);
|
||||
await downloadManager.removeTask(taskId, deleteFiles: false);
|
||||
}
|
||||
}
|
||||
|
||||
List<Aria2File> getFilesFormTask(Aria2Task task) {
|
||||
List<Aria2File> l = [];
|
||||
if (task.files != null) {
|
||||
for (var element in task.files!) {
|
||||
final f = Aria2File.fromJson(element);
|
||||
l.add(f);
|
||||
}
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
void openFolder(Aria2Task task) {
|
||||
final f = getFilesFormTask(task).firstOrNull;
|
||||
if (f != null) {
|
||||
SystemHelper.openDir(File(f.path!).absolute.path.replaceAll("/", "\\"));
|
||||
void openFolder(DownloadTaskInfo task) {
|
||||
final outputFolder = task.outputFolder;
|
||||
if (outputFolder.isNotEmpty) {
|
||||
SystemHelper.openDir(outputFolder.replaceAll("/", "\\"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -194,21 +177,39 @@ class HomeDownloaderUIModel extends _$HomeDownloaderUIModel {
|
||||
try {
|
||||
while (true) {
|
||||
if (_disposed) return;
|
||||
final aria2cState = ref.read(aria2cModelProvider);
|
||||
if (aria2cState.isRunning) {
|
||||
final aria2c = aria2cState.aria2c!;
|
||||
final tasks = await aria2c.tellActive();
|
||||
final waitingTasks = await aria2c.tellWaiting(0, 1000000);
|
||||
final stoppedTasks = await aria2c.tellStopped(0, 1000000);
|
||||
final globalStat = await aria2c.getGlobalStat();
|
||||
final downloadManagerState = ref.read(downloadManagerProvider);
|
||||
if (downloadManagerState.isRunning) {
|
||||
final downloadManager = ref.read(downloadManagerProvider.notifier);
|
||||
final allTasks = await downloadManager.getAllTasks();
|
||||
|
||||
final activeTasks = <DownloadTaskInfo>[];
|
||||
final waitingTasks = <DownloadTaskInfo>[];
|
||||
final stoppedTasks = <DownloadTaskInfo>[];
|
||||
|
||||
for (var task in allTasks) {
|
||||
switch (task.status) {
|
||||
case DownloadTaskStatus.live:
|
||||
activeTasks.add(task);
|
||||
break;
|
||||
case DownloadTaskStatus.initializing:
|
||||
case DownloadTaskStatus.paused:
|
||||
waitingTasks.add(task);
|
||||
break;
|
||||
case DownloadTaskStatus.finished:
|
||||
case DownloadTaskStatus.error:
|
||||
stoppedTasks.add(task);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
state = state.copyWith(
|
||||
tasks: tasks,
|
||||
activeTasks: activeTasks,
|
||||
waitingTasks: waitingTasks,
|
||||
stoppedTasks: stoppedTasks,
|
||||
globalStat: globalStat,
|
||||
globalStat: downloadManagerState.globalStat,
|
||||
);
|
||||
} else {
|
||||
state = state.copyWith(tasks: [], waitingTasks: [], stoppedTasks: [], globalStat: null);
|
||||
state = state.copyWith(activeTasks: [], waitingTasks: [], stoppedTasks: [], globalStat: null);
|
||||
}
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
}
|
||||
@ -266,22 +267,14 @@ class HomeDownloaderUIModel extends _$HomeDownloaderUIModel {
|
||||
),
|
||||
);
|
||||
if (ok == true) {
|
||||
final aria2cState = ref.read(aria2cModelProvider);
|
||||
final aria2cModel = ref.read(aria2cModelProvider.notifier);
|
||||
await aria2cModel.launchDaemon(appGlobalState.applicationBinaryModuleDir!);
|
||||
final aria2c = aria2cState.aria2c!;
|
||||
final upByte = aria2cModel.textToByte(upCtrl.text.trim());
|
||||
final downByte = aria2cModel.textToByte(downCtrl.text.trim());
|
||||
final r = await aria2c
|
||||
.changeGlobalOption(
|
||||
Aria2Option()
|
||||
..maxOverallUploadLimit = upByte
|
||||
..maxOverallDownloadLimit = downByte,
|
||||
)
|
||||
.unwrap();
|
||||
if (r != null) {
|
||||
await box.put('downloader_up_limit', upCtrl.text.trim());
|
||||
await box.put('downloader_down_limit', downCtrl.text.trim());
|
||||
// Note: rqbit doesn't support dynamic speed limit changes yet
|
||||
// Just save the settings for now
|
||||
await box.put('downloader_up_limit', upCtrl.text.trim());
|
||||
await box.put('downloader_down_limit', downCtrl.text.trim());
|
||||
|
||||
// Show info that speed limits will apply on next restart
|
||||
if (context.mounted) {
|
||||
showToast(context, "Speed limit settings saved. Will apply on next download.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,7 +14,7 @@ T _$identity<T>(T value) => value;
|
||||
/// @nodoc
|
||||
mixin _$HomeDownloaderUIState {
|
||||
|
||||
List<Aria2Task> get tasks; List<Aria2Task> get waitingTasks; List<Aria2Task> get stoppedTasks; Aria2GlobalStat? get globalStat;
|
||||
List<DownloadTaskInfo> get activeTasks; List<DownloadTaskInfo> get waitingTasks; List<DownloadTaskInfo> get stoppedTasks; DownloadGlobalStat? get globalStat;
|
||||
/// Create a copy of HomeDownloaderUIState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@ -25,16 +25,16 @@ $HomeDownloaderUIStateCopyWith<HomeDownloaderUIState> get copyWith => _$HomeDown
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is HomeDownloaderUIState&&const DeepCollectionEquality().equals(other.tasks, tasks)&&const DeepCollectionEquality().equals(other.waitingTasks, waitingTasks)&&const DeepCollectionEquality().equals(other.stoppedTasks, stoppedTasks)&&(identical(other.globalStat, globalStat) || other.globalStat == globalStat));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is HomeDownloaderUIState&&const DeepCollectionEquality().equals(other.activeTasks, activeTasks)&&const DeepCollectionEquality().equals(other.waitingTasks, waitingTasks)&&const DeepCollectionEquality().equals(other.stoppedTasks, stoppedTasks)&&(identical(other.globalStat, globalStat) || other.globalStat == globalStat));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(tasks),const DeepCollectionEquality().hash(waitingTasks),const DeepCollectionEquality().hash(stoppedTasks),globalStat);
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(activeTasks),const DeepCollectionEquality().hash(waitingTasks),const DeepCollectionEquality().hash(stoppedTasks),globalStat);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'HomeDownloaderUIState(tasks: $tasks, waitingTasks: $waitingTasks, stoppedTasks: $stoppedTasks, globalStat: $globalStat)';
|
||||
return 'HomeDownloaderUIState(activeTasks: $activeTasks, waitingTasks: $waitingTasks, stoppedTasks: $stoppedTasks, globalStat: $globalStat)';
|
||||
}
|
||||
|
||||
|
||||
@ -45,7 +45,7 @@ abstract mixin class $HomeDownloaderUIStateCopyWith<$Res> {
|
||||
factory $HomeDownloaderUIStateCopyWith(HomeDownloaderUIState value, $Res Function(HomeDownloaderUIState) _then) = _$HomeDownloaderUIStateCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
List<Aria2Task> tasks, List<Aria2Task> waitingTasks, List<Aria2Task> stoppedTasks, Aria2GlobalStat? globalStat
|
||||
List<DownloadTaskInfo> activeTasks, List<DownloadTaskInfo> waitingTasks, List<DownloadTaskInfo> stoppedTasks, DownloadGlobalStat? globalStat
|
||||
});
|
||||
|
||||
|
||||
@ -62,13 +62,13 @@ class _$HomeDownloaderUIStateCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of HomeDownloaderUIState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? tasks = null,Object? waitingTasks = null,Object? stoppedTasks = null,Object? globalStat = freezed,}) {
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? activeTasks = null,Object? waitingTasks = null,Object? stoppedTasks = null,Object? globalStat = freezed,}) {
|
||||
return _then(_self.copyWith(
|
||||
tasks: null == tasks ? _self.tasks : tasks // ignore: cast_nullable_to_non_nullable
|
||||
as List<Aria2Task>,waitingTasks: null == waitingTasks ? _self.waitingTasks : waitingTasks // ignore: cast_nullable_to_non_nullable
|
||||
as List<Aria2Task>,stoppedTasks: null == stoppedTasks ? _self.stoppedTasks : stoppedTasks // ignore: cast_nullable_to_non_nullable
|
||||
as List<Aria2Task>,globalStat: freezed == globalStat ? _self.globalStat : globalStat // ignore: cast_nullable_to_non_nullable
|
||||
as Aria2GlobalStat?,
|
||||
activeTasks: null == activeTasks ? _self.activeTasks : activeTasks // ignore: cast_nullable_to_non_nullable
|
||||
as List<DownloadTaskInfo>,waitingTasks: null == waitingTasks ? _self.waitingTasks : waitingTasks // ignore: cast_nullable_to_non_nullable
|
||||
as List<DownloadTaskInfo>,stoppedTasks: null == stoppedTasks ? _self.stoppedTasks : stoppedTasks // ignore: cast_nullable_to_non_nullable
|
||||
as List<DownloadTaskInfo>,globalStat: freezed == globalStat ? _self.globalStat : globalStat // ignore: cast_nullable_to_non_nullable
|
||||
as DownloadGlobalStat?,
|
||||
));
|
||||
}
|
||||
|
||||
@ -153,10 +153,10 @@ return $default(_that);case _:
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( List<Aria2Task> tasks, List<Aria2Task> waitingTasks, List<Aria2Task> stoppedTasks, Aria2GlobalStat? globalStat)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( List<DownloadTaskInfo> activeTasks, List<DownloadTaskInfo> waitingTasks, List<DownloadTaskInfo> stoppedTasks, DownloadGlobalStat? globalStat)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _HomeDownloaderUIState() when $default != null:
|
||||
return $default(_that.tasks,_that.waitingTasks,_that.stoppedTasks,_that.globalStat);case _:
|
||||
return $default(_that.activeTasks,_that.waitingTasks,_that.stoppedTasks,_that.globalStat);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
@ -174,10 +174,10 @@ return $default(_that.tasks,_that.waitingTasks,_that.stoppedTasks,_that.globalSt
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( List<Aria2Task> tasks, List<Aria2Task> waitingTasks, List<Aria2Task> stoppedTasks, Aria2GlobalStat? globalStat) $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( List<DownloadTaskInfo> activeTasks, List<DownloadTaskInfo> waitingTasks, List<DownloadTaskInfo> stoppedTasks, DownloadGlobalStat? globalStat) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _HomeDownloaderUIState():
|
||||
return $default(_that.tasks,_that.waitingTasks,_that.stoppedTasks,_that.globalStat);case _:
|
||||
return $default(_that.activeTasks,_that.waitingTasks,_that.stoppedTasks,_that.globalStat);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
@ -194,10 +194,10 @@ return $default(_that.tasks,_that.waitingTasks,_that.stoppedTasks,_that.globalSt
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( List<Aria2Task> tasks, List<Aria2Task> waitingTasks, List<Aria2Task> stoppedTasks, Aria2GlobalStat? globalStat)? $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( List<DownloadTaskInfo> activeTasks, List<DownloadTaskInfo> waitingTasks, List<DownloadTaskInfo> stoppedTasks, DownloadGlobalStat? globalStat)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _HomeDownloaderUIState() when $default != null:
|
||||
return $default(_that.tasks,_that.waitingTasks,_that.stoppedTasks,_that.globalStat);case _:
|
||||
return $default(_that.activeTasks,_that.waitingTasks,_that.stoppedTasks,_that.globalStat);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
@ -209,31 +209,31 @@ return $default(_that.tasks,_that.waitingTasks,_that.stoppedTasks,_that.globalSt
|
||||
|
||||
|
||||
class _HomeDownloaderUIState implements HomeDownloaderUIState {
|
||||
_HomeDownloaderUIState({final List<Aria2Task> tasks = const [], final List<Aria2Task> waitingTasks = const [], final List<Aria2Task> stoppedTasks = const [], this.globalStat}): _tasks = tasks,_waitingTasks = waitingTasks,_stoppedTasks = stoppedTasks;
|
||||
_HomeDownloaderUIState({final List<DownloadTaskInfo> activeTasks = const [], final List<DownloadTaskInfo> waitingTasks = const [], final List<DownloadTaskInfo> stoppedTasks = const [], this.globalStat}): _activeTasks = activeTasks,_waitingTasks = waitingTasks,_stoppedTasks = stoppedTasks;
|
||||
|
||||
|
||||
final List<Aria2Task> _tasks;
|
||||
@override@JsonKey() List<Aria2Task> get tasks {
|
||||
if (_tasks is EqualUnmodifiableListView) return _tasks;
|
||||
final List<DownloadTaskInfo> _activeTasks;
|
||||
@override@JsonKey() List<DownloadTaskInfo> get activeTasks {
|
||||
if (_activeTasks is EqualUnmodifiableListView) return _activeTasks;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_tasks);
|
||||
return EqualUnmodifiableListView(_activeTasks);
|
||||
}
|
||||
|
||||
final List<Aria2Task> _waitingTasks;
|
||||
@override@JsonKey() List<Aria2Task> get waitingTasks {
|
||||
final List<DownloadTaskInfo> _waitingTasks;
|
||||
@override@JsonKey() List<DownloadTaskInfo> get waitingTasks {
|
||||
if (_waitingTasks is EqualUnmodifiableListView) return _waitingTasks;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_waitingTasks);
|
||||
}
|
||||
|
||||
final List<Aria2Task> _stoppedTasks;
|
||||
@override@JsonKey() List<Aria2Task> get stoppedTasks {
|
||||
final List<DownloadTaskInfo> _stoppedTasks;
|
||||
@override@JsonKey() List<DownloadTaskInfo> get stoppedTasks {
|
||||
if (_stoppedTasks is EqualUnmodifiableListView) return _stoppedTasks;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_stoppedTasks);
|
||||
}
|
||||
|
||||
@override final Aria2GlobalStat? globalStat;
|
||||
@override final DownloadGlobalStat? globalStat;
|
||||
|
||||
/// Create a copy of HomeDownloaderUIState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@ -245,16 +245,16 @@ _$HomeDownloaderUIStateCopyWith<_HomeDownloaderUIState> get copyWith => __$HomeD
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _HomeDownloaderUIState&&const DeepCollectionEquality().equals(other._tasks, _tasks)&&const DeepCollectionEquality().equals(other._waitingTasks, _waitingTasks)&&const DeepCollectionEquality().equals(other._stoppedTasks, _stoppedTasks)&&(identical(other.globalStat, globalStat) || other.globalStat == globalStat));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _HomeDownloaderUIState&&const DeepCollectionEquality().equals(other._activeTasks, _activeTasks)&&const DeepCollectionEquality().equals(other._waitingTasks, _waitingTasks)&&const DeepCollectionEquality().equals(other._stoppedTasks, _stoppedTasks)&&(identical(other.globalStat, globalStat) || other.globalStat == globalStat));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_tasks),const DeepCollectionEquality().hash(_waitingTasks),const DeepCollectionEquality().hash(_stoppedTasks),globalStat);
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_activeTasks),const DeepCollectionEquality().hash(_waitingTasks),const DeepCollectionEquality().hash(_stoppedTasks),globalStat);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'HomeDownloaderUIState(tasks: $tasks, waitingTasks: $waitingTasks, stoppedTasks: $stoppedTasks, globalStat: $globalStat)';
|
||||
return 'HomeDownloaderUIState(activeTasks: $activeTasks, waitingTasks: $waitingTasks, stoppedTasks: $stoppedTasks, globalStat: $globalStat)';
|
||||
}
|
||||
|
||||
|
||||
@ -265,7 +265,7 @@ abstract mixin class _$HomeDownloaderUIStateCopyWith<$Res> implements $HomeDownl
|
||||
factory _$HomeDownloaderUIStateCopyWith(_HomeDownloaderUIState value, $Res Function(_HomeDownloaderUIState) _then) = __$HomeDownloaderUIStateCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
List<Aria2Task> tasks, List<Aria2Task> waitingTasks, List<Aria2Task> stoppedTasks, Aria2GlobalStat? globalStat
|
||||
List<DownloadTaskInfo> activeTasks, List<DownloadTaskInfo> waitingTasks, List<DownloadTaskInfo> stoppedTasks, DownloadGlobalStat? globalStat
|
||||
});
|
||||
|
||||
|
||||
@ -282,13 +282,13 @@ class __$HomeDownloaderUIStateCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of HomeDownloaderUIState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? tasks = null,Object? waitingTasks = null,Object? stoppedTasks = null,Object? globalStat = freezed,}) {
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? activeTasks = null,Object? waitingTasks = null,Object? stoppedTasks = null,Object? globalStat = freezed,}) {
|
||||
return _then(_HomeDownloaderUIState(
|
||||
tasks: null == tasks ? _self._tasks : tasks // ignore: cast_nullable_to_non_nullable
|
||||
as List<Aria2Task>,waitingTasks: null == waitingTasks ? _self._waitingTasks : waitingTasks // ignore: cast_nullable_to_non_nullable
|
||||
as List<Aria2Task>,stoppedTasks: null == stoppedTasks ? _self._stoppedTasks : stoppedTasks // ignore: cast_nullable_to_non_nullable
|
||||
as List<Aria2Task>,globalStat: freezed == globalStat ? _self.globalStat : globalStat // ignore: cast_nullable_to_non_nullable
|
||||
as Aria2GlobalStat?,
|
||||
activeTasks: null == activeTasks ? _self._activeTasks : activeTasks // ignore: cast_nullable_to_non_nullable
|
||||
as List<DownloadTaskInfo>,waitingTasks: null == waitingTasks ? _self._waitingTasks : waitingTasks // ignore: cast_nullable_to_non_nullable
|
||||
as List<DownloadTaskInfo>,stoppedTasks: null == stoppedTasks ? _self._stoppedTasks : stoppedTasks // ignore: cast_nullable_to_non_nullable
|
||||
as List<DownloadTaskInfo>,globalStat: freezed == globalStat ? _self.globalStat : globalStat // ignore: cast_nullable_to_non_nullable
|
||||
as DownloadGlobalStat?,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@ -42,7 +42,7 @@ final class HomeDownloaderUIModelProvider
|
||||
}
|
||||
|
||||
String _$homeDownloaderUIModelHash() =>
|
||||
r'cb5d0973d56bbf40673afc2a734b49f5d034ab98';
|
||||
r'27e2e4b7a5103eee9d489a347410131edef46be4';
|
||||
|
||||
abstract class _$HomeDownloaderUIModel
|
||||
extends $Notifier<HomeDownloaderUIState> {
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
// ignore_for_file: avoid_build_context_in_providers, use_build_context_synchronously
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
@ -14,7 +13,7 @@ import 'package:starcitizen_doctor/common/utils/async.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/base_utils.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/provider.dart';
|
||||
import 'package:starcitizen_doctor/provider/aria2c.dart';
|
||||
import 'package:starcitizen_doctor/provider/download_manager.dart';
|
||||
import 'package:starcitizen_doctor/ui/home/localization/localization_ui_model.dart';
|
||||
import 'package:starcitizen_doctor/common/rust/api/ort_api.dart' as ort;
|
||||
|
||||
@ -240,11 +239,10 @@ class InputMethodDialogUIModel extends _$InputMethodDialogUIModel {
|
||||
Future<String> doDownloadTranslateModel() async {
|
||||
state = state.copyWith(isAutoTranslateWorking: true);
|
||||
try {
|
||||
final aria2cManager = ref.read(aria2cModelProvider.notifier);
|
||||
await aria2cManager.launchDaemon(appGlobalState.applicationBinaryModuleDir!);
|
||||
final aria2c = ref.read(aria2cModelProvider).aria2c!;
|
||||
final downloadManager = ref.read(downloadManagerProvider.notifier);
|
||||
await downloadManager.initDownloader();
|
||||
|
||||
if (await aria2cManager.isNameInTask(_localTranslateModelName)) {
|
||||
if (await downloadManager.isNameInTask(_localTranslateModelName)) {
|
||||
throw Exception("Model is already downloading");
|
||||
}
|
||||
|
||||
@ -259,9 +257,8 @@ class InputMethodDialogUIModel extends _$InputMethodDialogUIModel {
|
||||
}
|
||||
// get torrent Data
|
||||
final data = await RSHttp.get(torrentUrl!);
|
||||
final b64Str = base64Encode(data.data!);
|
||||
final gid = await aria2c.addTorrent(b64Str, extraParams: {"dir": _localTranslateModelDir});
|
||||
return gid;
|
||||
final taskId = await downloadManager.addTorrent(data.data!, outputFolder: _localTranslateModelDir);
|
||||
return taskId.toString();
|
||||
} catch (e) {
|
||||
dPrint("[InputMethodDialogUIModel] doDownloadTranslateModel error: $e");
|
||||
rethrow;
|
||||
@ -330,8 +327,8 @@ class InputMethodDialogUIModel extends _$InputMethodDialogUIModel {
|
||||
}
|
||||
|
||||
Future<bool> isTranslateModelDownloading() async {
|
||||
final aria2cManager = ref.read(aria2cModelProvider.notifier);
|
||||
return await aria2cManager.isNameInTask(_localTranslateModelName);
|
||||
final downloadManager = ref.read(downloadManagerProvider.notifier);
|
||||
return await downloadManager.isNameInTask(_localTranslateModelName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -43,7 +43,7 @@ final class InputMethodDialogUIModelProvider
|
||||
}
|
||||
|
||||
String _$inputMethodDialogUIModelHash() =>
|
||||
r'bd96c85ef2073d80de6eba71748b41adb8861e1c';
|
||||
r'5c2989faf94d43bb814e5b80e10d68416c8241ec';
|
||||
|
||||
abstract class _$InputMethodDialogUIModel
|
||||
extends $Notifier<InputMethodDialogUIState> {
|
||||
|
||||
@ -4,7 +4,7 @@ import 'package:go_router/go_router.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:starcitizen_doctor/app.dart';
|
||||
import 'package:starcitizen_doctor/common/conf/conf.dart';
|
||||
import 'package:starcitizen_doctor/provider/aria2c.dart';
|
||||
import 'package:starcitizen_doctor/provider/download_manager.dart';
|
||||
import 'package:starcitizen_doctor/ui/home/home_ui_model.dart';
|
||||
import 'package:starcitizen_doctor/ui/party_room/party_room_ui.dart';
|
||||
import 'package:starcitizen_doctor/ui/settings/settings_ui_model.dart';
|
||||
@ -61,7 +61,7 @@ class IndexUI extends HookConsumerWidget {
|
||||
padding: const EdgeInsets.all(6),
|
||||
child: Icon(FluentIcons.installation, size: 22, color: Colors.white.withValues(alpha: .6)),
|
||||
),
|
||||
_makeAria2TaskNumWidget(),
|
||||
_makeDownloadTaskNumWidget(),
|
||||
],
|
||||
),
|
||||
onPressed: () => _goDownloader(context),
|
||||
@ -124,11 +124,11 @@ class IndexUI extends HookConsumerWidget {
|
||||
curIndexState.value = pageIndex;
|
||||
}
|
||||
|
||||
Widget _makeAria2TaskNumWidget() {
|
||||
Widget _makeDownloadTaskNumWidget() {
|
||||
return Consumer(
|
||||
builder: (BuildContext context, WidgetRef ref, Widget? child) {
|
||||
final aria2cState = ref.watch(aria2cModelProvider);
|
||||
if (!aria2cState.hasDownloadTask) {
|
||||
final downloadState = ref.watch(downloadManagerProvider);
|
||||
if (!downloadState.hasDownloadTask) {
|
||||
return const SizedBox();
|
||||
}
|
||||
return Positioned(
|
||||
@ -137,7 +137,7 @@ class IndexUI extends HookConsumerWidget {
|
||||
child: Container(
|
||||
decoration: BoxDecoration(color: Colors.red, borderRadius: BorderRadius.circular(12)),
|
||||
padding: const EdgeInsets.only(left: 6, right: 6, bottom: 1.5, top: 1.5),
|
||||
child: Text("${aria2cState.aria2TotalTaskNum}", style: const TextStyle(fontSize: 8, color: Colors.white)),
|
||||
child: Text("${downloadState.totalTaskNum}", style: const TextStyle(fontSize: 8, color: Colors.white)),
|
||||
),
|
||||
);
|
||||
},
|
||||
|
||||
@ -13,7 +13,7 @@ import 'package:starcitizen_doctor/app.dart';
|
||||
import 'package:starcitizen_doctor/common/conf/conf.dart';
|
||||
import 'package:starcitizen_doctor/common/conf/url_conf.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||
import 'package:starcitizen_doctor/provider/aria2c.dart';
|
||||
import 'package:starcitizen_doctor/provider/download_manager.dart';
|
||||
import 'package:starcitizen_doctor/widgets/widgets.dart';
|
||||
|
||||
class SplashUI extends HookConsumerWidget {
|
||||
@ -42,36 +42,36 @@ class SplashUI extends HookConsumerWidget {
|
||||
child: diagnosticMode.value
|
||||
? _buildDiagnosticView(diagnosticLogs, step, context)
|
||||
: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
final now = DateTime.now();
|
||||
final lastClick = lastClickTime.value;
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
final now = DateTime.now();
|
||||
final lastClick = lastClickTime.value;
|
||||
|
||||
// 重置计数器如果距离上次点击超过2秒
|
||||
if (lastClick != null && now.difference(lastClick).inSeconds > 2) {
|
||||
clickCount.value = 0;
|
||||
}
|
||||
// 重置计数器如果距离上次点击超过2秒
|
||||
if (lastClick != null && now.difference(lastClick).inSeconds > 2) {
|
||||
clickCount.value = 0;
|
||||
}
|
||||
|
||||
lastClickTime.value = now;
|
||||
clickCount.value++;
|
||||
lastClickTime.value = now;
|
||||
clickCount.value++;
|
||||
|
||||
if (clickCount.value >= 10) {
|
||||
diagnosticMode.value = true;
|
||||
clickCount.value = 0;
|
||||
}
|
||||
},
|
||||
child: Image.asset("assets/app_logo.png", width: 192, height: 192),
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
const ProgressRing(),
|
||||
const SizedBox(height: 32),
|
||||
if (step == 0) Text(S.current.app_splash_checking_availability),
|
||||
if (step == 1) Text(S.current.app_splash_checking_for_updates),
|
||||
if (step == 2) Text(S.current.app_splash_almost_done)
|
||||
],
|
||||
),
|
||||
if (clickCount.value >= 10) {
|
||||
diagnosticMode.value = true;
|
||||
clickCount.value = 0;
|
||||
}
|
||||
},
|
||||
child: Image.asset("assets/app_logo.png", width: 192, height: 192),
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
const ProgressRing(),
|
||||
const SizedBox(height: 32),
|
||||
if (step == 0) Text(S.current.app_splash_checking_availability),
|
||||
if (step == 1) Text(S.current.app_splash_checking_for_updates),
|
||||
if (step == 2) Text(S.current.app_splash_almost_done),
|
||||
],
|
||||
),
|
||||
),
|
||||
automaticallyImplyLeading: false,
|
||||
titleRow: Align(
|
||||
@ -124,26 +124,28 @@ class SplashUI extends HookConsumerWidget {
|
||||
child: logs.isEmpty
|
||||
? Center(child: Text(S.current.splash_waiting_log))
|
||||
: ListView.builder(
|
||||
itemCount: logs.length,
|
||||
itemBuilder: (context, index) {
|
||||
final log = logs[index];
|
||||
Color textColor = Colors.white;
|
||||
if (log.contains('✓')) {
|
||||
textColor = Colors.green;
|
||||
} else if (log.contains('✗') || log.contains(S.current.splash_timeout) || log.contains(S.current.splash_error)) {
|
||||
textColor = Colors.red;
|
||||
} else if (log.contains('⚠')) {
|
||||
textColor = Colors.orange;
|
||||
}
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 2),
|
||||
child: Text(
|
||||
log,
|
||||
style: TextStyle(fontFamily: 'Consolas', fontSize: 12, color: textColor),
|
||||
itemCount: logs.length,
|
||||
itemBuilder: (context, index) {
|
||||
final log = logs[index];
|
||||
Color textColor = Colors.white;
|
||||
if (log.contains('✓')) {
|
||||
textColor = Colors.green;
|
||||
} else if (log.contains('✗') ||
|
||||
log.contains(S.current.splash_timeout) ||
|
||||
log.contains(S.current.splash_error)) {
|
||||
textColor = Colors.red;
|
||||
} else if (log.contains('⚠')) {
|
||||
textColor = Colors.orange;
|
||||
}
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 2),
|
||||
child: Text(
|
||||
log,
|
||||
style: TextStyle(fontFamily: 'Consolas', fontSize: 12, color: textColor),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
@ -154,12 +156,12 @@ class SplashUI extends HookConsumerWidget {
|
||||
}
|
||||
|
||||
void _initApp(
|
||||
BuildContext context,
|
||||
AppGlobalModel appModel,
|
||||
ValueNotifier<int> stepState,
|
||||
WidgetRef ref,
|
||||
ValueNotifier<List<String>> diagnosticLogs,
|
||||
) async {
|
||||
BuildContext context,
|
||||
AppGlobalModel appModel,
|
||||
ValueNotifier<int> stepState,
|
||||
WidgetRef ref,
|
||||
ValueNotifier<List<String>> diagnosticLogs,
|
||||
) async {
|
||||
void addLog(String message) {
|
||||
final logMessage = '[${DateTime.now().toString().substring(11, 23)}] $message';
|
||||
diagnosticLogs.value = [...diagnosticLogs.value, logMessage];
|
||||
@ -263,12 +265,12 @@ class SplashUI extends HookConsumerWidget {
|
||||
await appModel
|
||||
.checkUpdate(context)
|
||||
.timeout(
|
||||
const Duration(seconds: 10),
|
||||
onTimeout: () {
|
||||
addLog(S.current.splash_check_update_timeout);
|
||||
return false;
|
||||
},
|
||||
);
|
||||
const Duration(seconds: 10),
|
||||
onTimeout: () {
|
||||
addLog(S.current.splash_check_update_timeout);
|
||||
return false;
|
||||
},
|
||||
);
|
||||
addLog(S.current.splash_check_update_done);
|
||||
} catch (e) {
|
||||
addLog('⚠ appModel.checkUpdate() 错误: $e - 继续执行');
|
||||
@ -277,14 +279,14 @@ class SplashUI extends HookConsumerWidget {
|
||||
addLog(S.current.splash_step1_done);
|
||||
stepState.value = 2;
|
||||
|
||||
// Step 2: Initialize aria2c
|
||||
// Step 2: Initialize download manager
|
||||
addLog(S.current.splash_init_aria2c);
|
||||
dPrint("_initApp aria2cModelProvider");
|
||||
dPrint("_initApp downloadManagerProvider");
|
||||
try {
|
||||
ref.read(aria2cModelProvider);
|
||||
ref.read(downloadManagerProvider);
|
||||
addLog(S.current.splash_aria2c_done);
|
||||
} catch (e) {
|
||||
addLog('⚠ aria2cModelProvider 初始化错误: $e');
|
||||
addLog('⚠ downloadManagerProvider 初始化错误: $e');
|
||||
}
|
||||
|
||||
if (!context.mounted) {
|
||||
@ -385,4 +387,4 @@ class SplashUI extends HookConsumerWidget {
|
||||
dPrint(S.current.splash_reset_db_failed(e.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,7 +18,7 @@ import 'package:starcitizen_doctor/common/io/rs_http.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/multi_window_manager.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/provider.dart';
|
||||
import 'package:starcitizen_doctor/provider/aria2c.dart';
|
||||
import 'package:starcitizen_doctor/provider/download_manager.dart';
|
||||
import 'package:starcitizen_doctor/widgets/widgets.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
import 'package:xml/xml.dart';
|
||||
@ -27,7 +27,6 @@ import 'dialogs/hosts_booster_dialog_ui.dart';
|
||||
import 'dialogs/rsi_launcher_enhance_dialog_ui.dart';
|
||||
|
||||
part 'tools_ui_model.g.dart';
|
||||
|
||||
part 'tools_ui_model.freezed.dart';
|
||||
|
||||
class ToolsItemData {
|
||||
@ -577,12 +576,11 @@ class ToolsUIModel extends _$ToolsUIModel {
|
||||
if (!ok) return;
|
||||
try {
|
||||
state = state.copyWith(working: true);
|
||||
final aria2cManager = ref.read(aria2cModelProvider.notifier);
|
||||
await aria2cManager.launchDaemon(appGlobalState.applicationBinaryModuleDir!);
|
||||
final aria2c = ref.read(aria2cModelProvider).aria2c!;
|
||||
final downloadManager = ref.read(downloadManagerProvider.notifier);
|
||||
await downloadManager.initDownloader();
|
||||
|
||||
// check download task list
|
||||
if (await aria2cManager.isNameInTask("Data.p4k")) {
|
||||
if (await downloadManager.isNameInTask("Data.p4k")) {
|
||||
if (!context.mounted) return;
|
||||
showToast(context, S.current.tools_action_info_p4k_download_in_progress);
|
||||
state = state.copyWith(working: false);
|
||||
@ -619,12 +617,10 @@ class ToolsUIModel extends _$ToolsUIModel {
|
||||
state = state.copyWith(working: false);
|
||||
return;
|
||||
}
|
||||
final b64Str = base64Encode(btData.data!);
|
||||
|
||||
final gid = await aria2c.addTorrent(b64Str, extraParams: {"dir": savePath});
|
||||
final taskId = await downloadManager.addTorrent(btData.data!, outputFolder: savePath);
|
||||
state = state.copyWith(working: false);
|
||||
dPrint("Aria2cManager.aria2c.addUri resp === $gid");
|
||||
await aria2c.saveSession();
|
||||
dPrint("DownloadManager.addTorrent resp === $taskId");
|
||||
AnalyticsApi.touch("p4k_download");
|
||||
if (!context.mounted) return;
|
||||
context.push("/index/downloader");
|
||||
|
||||
@ -41,7 +41,7 @@ final class ToolsUIModelProvider
|
||||
}
|
||||
}
|
||||
|
||||
String _$toolsUIModelHash() => r'ee1de3d555443f72b4fbb395a5728b2de1e8aaf4';
|
||||
String _$toolsUIModelHash() => r'17890d6d16d8e9d98c42d06d9e6c6165965bcee0';
|
||||
|
||||
abstract class _$ToolsUIModel extends $Notifier<ToolsUIState> {
|
||||
ToolsUIState build();
|
||||
|
||||
17
pubspec.lock
17
pubspec.lock
@ -57,15 +57,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.7.0"
|
||||
aria2:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "."
|
||||
ref: HEAD
|
||||
resolved-ref: b274c4c25e7ab49940b43d20e2fd335cfeef1c37
|
||||
url: "https://github.com/xkeyC/dart_aria2_rpc.git"
|
||||
source: git
|
||||
version: "0.1.3"
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -774,14 +765,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.9.0"
|
||||
json_rpc_2:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: json_rpc_2
|
||||
sha256: "246b321532f0e8e2ba474b4d757eaa558ae4fdd0688fdbc1e1ca9705f9b8ca0e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.3"
|
||||
json_serializable:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
|
||||
@ -55,9 +55,7 @@ dependencies:
|
||||
fixnum: ^1.1.1
|
||||
rust_builder:
|
||||
path: rust_builder
|
||||
aria2:
|
||||
git: https://github.com/xkeyC/dart_aria2_rpc.git
|
||||
# path: ../../xkeyC/dart_aria2_rpc
|
||||
# aria2 has been replaced by rqbit (Rust-based torrent library)
|
||||
intl: ^0.20.2
|
||||
synchronized: ^3.4.0
|
||||
super_sliver_list: ^0.4.1
|
||||
|
||||
986
rust/Cargo.lock
generated
986
rust/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -35,6 +35,10 @@ unp4k_rs = { git = "https://github.com/StarCitizenToolBox/unp4k_rs", tag = "V0.0
|
||||
uuid = { version = "1.19.0", features = ["v4"] }
|
||||
parking_lot = "0.12.5"
|
||||
crossbeam-channel = "0.5.15"
|
||||
librqbit = { git = "https://github.com/StarCitizenToolBox/rqbit", tag = "webseed-v0.0.1" }
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
bytes = "1.10"
|
||||
|
||||
# WebView
|
||||
[target.'cfg(not(target_os = "macos"))'.dependencies]
|
||||
|
||||
513
rust/src/api/downloader_api.rs
Normal file
513
rust/src/api/downloader_api.rs
Normal file
@ -0,0 +1,513 @@
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::{bail, Context, Result};
|
||||
use bytes::Bytes;
|
||||
use flutter_rust_bridge::frb;
|
||||
use librqbit::{
|
||||
AddTorrent, AddTorrentOptions, AddTorrentResponse, Session, SessionOptions,
|
||||
TorrentStats, ManagedTorrent, TorrentStatsState,
|
||||
};
|
||||
use once_cell::sync::OnceCell;
|
||||
use parking_lot::RwLock;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
// Type alias for ManagedTorrentHandle
|
||||
type ManagedTorrentHandle = Arc<ManagedTorrent>;
|
||||
|
||||
// Global session instance
|
||||
static SESSION: OnceCell<Arc<Session>> = OnceCell::new();
|
||||
static SESSION_INIT_LOCK: once_cell::sync::Lazy<Mutex<()>> =
|
||||
once_cell::sync::Lazy::new(|| Mutex::new(()));
|
||||
|
||||
// Store torrent handles
|
||||
static TORRENT_HANDLES: once_cell::sync::Lazy<RwLock<HashMap<usize, ManagedTorrentHandle>>> =
|
||||
once_cell::sync::Lazy::new(|| RwLock::new(HashMap::new()));
|
||||
|
||||
// Store output folders for each task
|
||||
static TASK_OUTPUT_FOLDERS: once_cell::sync::Lazy<RwLock<HashMap<usize, String>>> =
|
||||
once_cell::sync::Lazy::new(|| RwLock::new(HashMap::new()));
|
||||
|
||||
/// Download task status
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum DownloadTaskStatus {
|
||||
Initializing,
|
||||
Live,
|
||||
Paused,
|
||||
Error,
|
||||
Finished,
|
||||
}
|
||||
|
||||
/// Download task information
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DownloadTaskInfo {
|
||||
pub id: usize,
|
||||
pub name: String,
|
||||
pub status: DownloadTaskStatus,
|
||||
pub total_bytes: u64,
|
||||
pub downloaded_bytes: u64,
|
||||
pub uploaded_bytes: u64,
|
||||
pub download_speed: u64,
|
||||
pub upload_speed: u64,
|
||||
pub progress: f64,
|
||||
pub num_peers: usize,
|
||||
pub output_folder: String,
|
||||
}
|
||||
|
||||
/// Global statistics
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct DownloadGlobalStat {
|
||||
pub download_speed: u64,
|
||||
pub upload_speed: u64,
|
||||
pub num_active: usize,
|
||||
pub num_waiting: usize,
|
||||
}
|
||||
|
||||
/// Initialize the download manager session
|
||||
#[frb(sync)]
|
||||
pub fn downloader_init(download_dir: String) -> Result<()> {
|
||||
// Already initialized
|
||||
if SESSION.get().is_some() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let rt = tokio::runtime::Handle::current();
|
||||
rt.block_on(async {
|
||||
let _lock = SESSION_INIT_LOCK.lock().await;
|
||||
|
||||
// Double check after acquiring lock
|
||||
if SESSION.get().is_some() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let output_folder = PathBuf::from(&download_dir);
|
||||
std::fs::create_dir_all(&output_folder)?;
|
||||
|
||||
let session = Session::new_with_opts(
|
||||
output_folder,
|
||||
SessionOptions {
|
||||
disable_dht: false,
|
||||
disable_dht_persistence: true,
|
||||
persistence: None,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
.context("Failed to create rqbit session")?;
|
||||
|
||||
SESSION
|
||||
.set(session)
|
||||
.map_err(|_| anyhow::anyhow!("Session already initialized"))?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
/// Check if the downloader is initialized
|
||||
#[frb(sync)]
|
||||
pub fn downloader_is_initialized() -> bool {
|
||||
SESSION.get().is_some()
|
||||
}
|
||||
|
||||
/// Add a torrent from bytes (e.g., .torrent file content)
|
||||
pub async fn downloader_add_torrent(
|
||||
torrent_bytes: Vec<u8>,
|
||||
output_folder: Option<String>,
|
||||
trackers: Option<Vec<String>>,
|
||||
) -> Result<usize> {
|
||||
let session = SESSION
|
||||
.get()
|
||||
.context("Downloader not initialized. Call downloader_init first.")?;
|
||||
|
||||
let bytes = Bytes::from(torrent_bytes);
|
||||
let add_torrent = AddTorrent::from_bytes(bytes);
|
||||
|
||||
let mut opts = AddTorrentOptions {
|
||||
overwrite: true,
|
||||
paused: false,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
if let Some(ref folder) = output_folder {
|
||||
opts.output_folder = Some(folder.clone());
|
||||
}
|
||||
|
||||
if let Some(tracker_list) = trackers {
|
||||
opts.trackers = Some(tracker_list);
|
||||
}
|
||||
|
||||
let response = session
|
||||
.add_torrent(add_torrent, Some(opts))
|
||||
.await
|
||||
.context("Failed to add torrent")?;
|
||||
|
||||
match response {
|
||||
AddTorrentResponse::Added(id, handle) => {
|
||||
// Store output folder
|
||||
if let Some(folder) = output_folder.clone() {
|
||||
TASK_OUTPUT_FOLDERS.write().insert(id, folder);
|
||||
}
|
||||
TORRENT_HANDLES.write().insert(id, handle);
|
||||
Ok(id)
|
||||
}
|
||||
AddTorrentResponse::AlreadyManaged(id, handle) => {
|
||||
if let Some(folder) = output_folder.clone() {
|
||||
TASK_OUTPUT_FOLDERS.write().insert(id, folder);
|
||||
}
|
||||
TORRENT_HANDLES.write().insert(id, handle);
|
||||
Ok(id)
|
||||
}
|
||||
AddTorrentResponse::ListOnly(_) => {
|
||||
bail!("Torrent was only listed, not added")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a torrent from a magnet link
|
||||
pub async fn downloader_add_magnet(
|
||||
magnet_link: String,
|
||||
output_folder: Option<String>,
|
||||
trackers: Option<Vec<String>>,
|
||||
) -> Result<usize> {
|
||||
let session = SESSION
|
||||
.get()
|
||||
.context("Downloader not initialized. Call downloader_init first.")?;
|
||||
|
||||
// Check if it's a magnet link
|
||||
if !magnet_link.starts_with("magnet:") {
|
||||
bail!("Invalid magnet link. Must start with 'magnet:'");
|
||||
}
|
||||
|
||||
let add_torrent = AddTorrent::from_url(magnet_link);
|
||||
|
||||
let mut opts = AddTorrentOptions {
|
||||
overwrite: true,
|
||||
paused: false,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
if let Some(ref folder) = output_folder {
|
||||
opts.output_folder = Some(folder.clone());
|
||||
}
|
||||
|
||||
if let Some(tracker_list) = trackers {
|
||||
opts.trackers = Some(tracker_list);
|
||||
}
|
||||
|
||||
let response = session
|
||||
.add_torrent(add_torrent, Some(opts))
|
||||
.await
|
||||
.context("Failed to add magnet")?;
|
||||
|
||||
match response {
|
||||
AddTorrentResponse::Added(id, handle) => {
|
||||
if let Some(folder) = output_folder.clone() {
|
||||
TASK_OUTPUT_FOLDERS.write().insert(id, folder);
|
||||
}
|
||||
TORRENT_HANDLES.write().insert(id, handle);
|
||||
Ok(id)
|
||||
}
|
||||
AddTorrentResponse::AlreadyManaged(id, handle) => {
|
||||
if let Some(folder) = output_folder.clone() {
|
||||
TASK_OUTPUT_FOLDERS.write().insert(id, folder);
|
||||
}
|
||||
TORRENT_HANDLES.write().insert(id, handle);
|
||||
Ok(id)
|
||||
}
|
||||
AddTorrentResponse::ListOnly(_) => {
|
||||
bail!("Magnet was only listed, not added")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a torrent from URL (HTTP download not supported, only torrent file URLs)
|
||||
pub async fn downloader_add_url(
|
||||
url: String,
|
||||
output_folder: Option<String>,
|
||||
trackers: Option<Vec<String>>,
|
||||
) -> Result<usize> {
|
||||
// Check if it's a magnet link
|
||||
if url.starts_with("magnet:") {
|
||||
return downloader_add_magnet(url, output_folder, trackers).await;
|
||||
}
|
||||
|
||||
// Check if it's a torrent file URL
|
||||
if url.starts_with("http://") || url.starts_with("https://") {
|
||||
// Download the torrent file first
|
||||
let client = reqwest::Client::new();
|
||||
let response = client
|
||||
.get(&url)
|
||||
.send()
|
||||
.await
|
||||
.context("Failed to download torrent file")?;
|
||||
|
||||
if !response.status().is_success() {
|
||||
bail!("Failed to download torrent file: HTTP {}", response.status());
|
||||
}
|
||||
|
||||
let bytes = response
|
||||
.bytes()
|
||||
.await
|
||||
.context("Failed to read torrent file content")?;
|
||||
|
||||
return downloader_add_torrent(bytes.to_vec(), output_folder, trackers).await;
|
||||
}
|
||||
|
||||
bail!("HTTP downloads are not supported. Only BitTorrent (magnet links and .torrent files) are supported.")
|
||||
}
|
||||
|
||||
/// Pause a download task
|
||||
pub async fn downloader_pause(task_id: usize) -> Result<()> {
|
||||
let session = SESSION
|
||||
.get()
|
||||
.context("Downloader not initialized")?;
|
||||
|
||||
let handle = {
|
||||
let handles = TORRENT_HANDLES.read();
|
||||
handles.get(&task_id).cloned()
|
||||
};
|
||||
|
||||
if let Some(handle) = handle {
|
||||
session.pause(&handle).await.context("Failed to pause torrent")?;
|
||||
Ok(())
|
||||
} else {
|
||||
bail!("Task not found: {}", task_id)
|
||||
}
|
||||
}
|
||||
|
||||
/// Resume a download task
|
||||
pub async fn downloader_resume(task_id: usize) -> Result<()> {
|
||||
let session = SESSION
|
||||
.get()
|
||||
.context("Downloader not initialized")?;
|
||||
|
||||
let handle = {
|
||||
let handles = TORRENT_HANDLES.read();
|
||||
handles.get(&task_id).cloned()
|
||||
};
|
||||
|
||||
if let Some(handle) = handle {
|
||||
session.unpause(&handle).await.context("Failed to resume torrent")?;
|
||||
Ok(())
|
||||
} else {
|
||||
bail!("Task not found: {}", task_id)
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove a download task
|
||||
pub async fn downloader_remove(task_id: usize, delete_files: bool) -> Result<()> {
|
||||
let session = SESSION
|
||||
.get()
|
||||
.context("Downloader not initialized")?;
|
||||
|
||||
session
|
||||
.delete(librqbit::api::TorrentIdOrHash::Id(task_id), delete_files)
|
||||
.await
|
||||
.context("Failed to remove torrent")?;
|
||||
|
||||
TORRENT_HANDLES.write().remove(&task_id);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get information about a specific task
|
||||
pub async fn downloader_get_task_info(task_id: usize) -> Result<DownloadTaskInfo> {
|
||||
let handle = {
|
||||
let handles = TORRENT_HANDLES.read();
|
||||
handles.get(&task_id).cloned()
|
||||
};
|
||||
|
||||
if let Some(handle) = handle {
|
||||
let stats = handle.stats();
|
||||
let name = handle.name().unwrap_or_else(|| format!("Task {}", task_id));
|
||||
let output_folder = TASK_OUTPUT_FOLDERS
|
||||
.read()
|
||||
.get(&task_id)
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
let status = get_task_status(&stats);
|
||||
let progress = if stats.total_bytes > 0 {
|
||||
stats.progress_bytes as f64 / stats.total_bytes as f64
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
// Get speed from live stats
|
||||
let (download_speed, upload_speed, num_peers) = if let Some(live) = &stats.live {
|
||||
let down = (live.download_speed.mbps * 1024.0 * 1024.0 / 8.0) as u64;
|
||||
let up = (live.upload_speed.mbps * 1024.0 * 1024.0 / 8.0) as u64;
|
||||
let peers = (live.snapshot.peer_stats.queued + live.snapshot.peer_stats.connecting + live.snapshot.peer_stats.live) as usize;
|
||||
(down, up, peers)
|
||||
} else {
|
||||
(0, 0, 0)
|
||||
};
|
||||
|
||||
Ok(DownloadTaskInfo {
|
||||
id: task_id,
|
||||
name,
|
||||
status,
|
||||
total_bytes: stats.total_bytes,
|
||||
downloaded_bytes: stats.progress_bytes,
|
||||
uploaded_bytes: stats.uploaded_bytes,
|
||||
download_speed,
|
||||
upload_speed,
|
||||
progress,
|
||||
num_peers,
|
||||
output_folder,
|
||||
})
|
||||
} else {
|
||||
bail!("Task not found: {}", task_id)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_task_status(stats: &TorrentStats) -> DownloadTaskStatus {
|
||||
if stats.error.is_some() {
|
||||
return DownloadTaskStatus::Error;
|
||||
}
|
||||
|
||||
if stats.finished {
|
||||
return DownloadTaskStatus::Finished;
|
||||
}
|
||||
|
||||
match stats.state {
|
||||
TorrentStatsState::Initializing => DownloadTaskStatus::Initializing,
|
||||
TorrentStatsState::Live => DownloadTaskStatus::Live,
|
||||
TorrentStatsState::Paused => DownloadTaskStatus::Paused,
|
||||
TorrentStatsState::Error => DownloadTaskStatus::Error,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get all tasks
|
||||
pub async fn downloader_get_all_tasks() -> Result<Vec<DownloadTaskInfo>> {
|
||||
let session = SESSION.get();
|
||||
if session.is_none() {
|
||||
return Ok(vec![]);
|
||||
}
|
||||
let session = session.unwrap();
|
||||
|
||||
// Use RwLock to collect tasks since with_torrents takes Fn (not FnMut)
|
||||
let tasks: RwLock<Vec<DownloadTaskInfo>> = RwLock::new(Vec::new());
|
||||
|
||||
session.with_torrents(|torrents| {
|
||||
for (id, handle) in torrents {
|
||||
let stats = handle.stats();
|
||||
let name = handle.name().unwrap_or_else(|| format!("Task {}", id));
|
||||
let output_folder = TASK_OUTPUT_FOLDERS
|
||||
.read()
|
||||
.get(&id)
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
let status = get_task_status(&stats);
|
||||
let progress = if stats.total_bytes > 0 {
|
||||
stats.progress_bytes as f64 / stats.total_bytes as f64
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
// Get speed from live stats
|
||||
let (download_speed, upload_speed, num_peers) = if let Some(live) = &stats.live {
|
||||
let down = (live.download_speed.mbps * 1024.0 * 1024.0 / 8.0) as u64;
|
||||
let up = (live.upload_speed.mbps * 1024.0 * 1024.0 / 8.0) as u64;
|
||||
let peers = (live.snapshot.peer_stats.queued + live.snapshot.peer_stats.connecting + live.snapshot.peer_stats.live) as usize;
|
||||
(down, up, peers)
|
||||
} else {
|
||||
(0, 0, 0)
|
||||
};
|
||||
|
||||
tasks.write().push(DownloadTaskInfo {
|
||||
id,
|
||||
name,
|
||||
status,
|
||||
total_bytes: stats.total_bytes,
|
||||
downloaded_bytes: stats.progress_bytes,
|
||||
uploaded_bytes: stats.uploaded_bytes,
|
||||
download_speed,
|
||||
upload_speed,
|
||||
progress,
|
||||
num_peers,
|
||||
output_folder,
|
||||
});
|
||||
|
||||
// Update handles cache
|
||||
TORRENT_HANDLES.write().insert(id, handle.clone());
|
||||
}
|
||||
});
|
||||
|
||||
Ok(tasks.into_inner())
|
||||
}
|
||||
|
||||
/// Get global statistics
|
||||
pub async fn downloader_get_global_stats() -> Result<DownloadGlobalStat> {
|
||||
let tasks = downloader_get_all_tasks().await?;
|
||||
|
||||
let mut stat = DownloadGlobalStat::default();
|
||||
|
||||
for task in &tasks {
|
||||
stat.download_speed += task.download_speed;
|
||||
stat.upload_speed += task.upload_speed;
|
||||
|
||||
match task.status {
|
||||
DownloadTaskStatus::Live => stat.num_active += 1,
|
||||
DownloadTaskStatus::Paused | DownloadTaskStatus::Initializing => stat.num_waiting += 1,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(stat)
|
||||
}
|
||||
|
||||
/// Check if a task with given name exists
|
||||
pub async fn downloader_is_name_in_task(name: String) -> bool {
|
||||
if let Ok(tasks) = downloader_get_all_tasks().await {
|
||||
for task in tasks {
|
||||
if task.name.contains(&name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Pause all tasks
|
||||
pub async fn downloader_pause_all() -> Result<()> {
|
||||
let session = SESSION
|
||||
.get()
|
||||
.context("Downloader not initialized")?;
|
||||
|
||||
let handles: Vec<_> = TORRENT_HANDLES.read().values().cloned().collect();
|
||||
|
||||
for handle in handles {
|
||||
let _ = session.pause(&handle).await;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Resume all tasks
|
||||
pub async fn downloader_resume_all() -> Result<()> {
|
||||
let session = SESSION
|
||||
.get()
|
||||
.context("Downloader not initialized")?;
|
||||
|
||||
let handles: Vec<_> = TORRENT_HANDLES.read().values().cloned().collect();
|
||||
|
||||
for handle in handles {
|
||||
let _ = session.unpause(&handle).await;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Stop the downloader session
|
||||
pub async fn downloader_stop() -> Result<()> {
|
||||
if let Some(session) = SESSION.get() {
|
||||
session.stop().await;
|
||||
}
|
||||
TORRENT_HANDLES.write().clear();
|
||||
TASK_OUTPUT_FOLDERS.write().clear();
|
||||
Ok(())
|
||||
}
|
||||
@ -8,3 +8,4 @@ pub mod asar_api;
|
||||
pub mod ort_api;
|
||||
pub mod unp4k_api;
|
||||
pub mod webview_api;
|
||||
pub mod downloader_api;
|
||||
|
||||
@ -302,7 +302,7 @@ pub fn get_gpu_info_from_registry() -> anyhow::Result<String> {
|
||||
|
||||
/// Resolve shortcut (.lnk) file to get target path
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn resolve_shortcut(lnk_path: &str) -> anyhow::Result<String> {
|
||||
pub fn resolve_shortcut(lnk_path: String) -> anyhow::Result<String> {
|
||||
use windows::core::{HSTRING, Interface};
|
||||
use windows::Win32::System::Com::{
|
||||
CoCreateInstance, CoInitializeEx, CoUninitialize,
|
||||
@ -327,7 +327,7 @@ pub fn resolve_shortcut(lnk_path: &str) -> anyhow::Result<String> {
|
||||
let persist_file: IPersistFile = shell_link.cast()?;
|
||||
|
||||
// Load the shortcut file
|
||||
let lnk_path_w = HSTRING::from(lnk_path);
|
||||
let lnk_path_w = HSTRING::from(&lnk_path);
|
||||
persist_file.Load(windows::core::PCWSTR(lnk_path_w.as_ptr()), STGM_READ)?;
|
||||
|
||||
// Get target path
|
||||
@ -351,7 +351,7 @@ pub fn resolve_shortcut(lnk_path: &str) -> anyhow::Result<String> {
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub fn resolve_shortcut(_: &str) -> anyhow::Result<String> {
|
||||
pub fn resolve_shortcut(_lnk_path: String) -> anyhow::Result<String> {
|
||||
Ok(String::new())
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user