mirror of
https://github.com/StarCitizenToolBox/app.git
synced 2026-02-06 15:10:20 +00:00
feat: use rust rqbit to replace aria2c
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user