feat: task separate proxy (#727)

This commit is contained in:
Levi authored and GitHub committed 2024-09-06 18:35:23 +08:00
1 parent 6538b29b62
commit 0c17093b1a
22 files changed
+447 -67

No files matched your search

+23
View File
@@ -7,11 +7,13 @@ class Request {
String url;
Object? extra;
Map<String, String>? labels = {};
RequestProxy? proxy;
Request({
required this.url,
this.extra,
this.labels,
this.proxy,
});
factory Request.fromJson(Map<String, dynamic> json) =>
@@ -45,3 +47,24 @@ class ReqExtraBt {
Map<String, dynamic> toJson() => _$ReqExtraBtToJson(this);
}
enum RequestProxyMode {
follow,
none,
custom,
}
@JsonSerializable()
class RequestProxy {
RequestProxyMode mode = RequestProxyMode.follow;
String scheme = 'http';
String host = '';
String usr = '';
String pwd = '';
RequestProxy();
factory RequestProxy.fromJson(Map<String, dynamic> json) =>
_$RequestProxyFromJson(json);
Map<String, dynamic> toJson() => _$RequestProxyToJson(this);
}
+26
View File
@@ -12,6 +12,9 @@ Request _$RequestFromJson(Map<String, dynamic> json) => Request(
labels: (json['labels'] as Map<String, dynamic>?)?.map(
(k, e) => MapEntry(k, e as String),
),
proxy: json['proxy'] == null
? null
: RequestProxy.fromJson(json['proxy'] as Map<String, dynamic>),
);
Map<String, dynamic> _$RequestToJson(Request instance) {
@@ -27,6 +30,7 @@ Map<String, dynamic> _$RequestToJson(Request instance) {
writeNotNull('extra', instance.extra);
writeNotNull('labels', instance.labels);
writeNotNull('proxy', instance.proxy?.toJson());
return val;
}
@@ -50,3 +54,25 @@ Map<String, dynamic> _$ReqExtraBtToJson(ReqExtraBt instance) =>
<String, dynamic>{
'trackers': instance.trackers,
};
RequestProxy _$RequestProxyFromJson(Map<String, dynamic> json) => RequestProxy()
..mode = $enumDecode(_$RequestProxyModeEnumMap, json['mode'])
..scheme = json['scheme'] as String
..host = json['host'] as String
..usr = json['usr'] as String
..pwd = json['pwd'] as String;
Map<String, dynamic> _$RequestProxyToJson(RequestProxy instance) =>
<String, dynamic>{
'mode': _$RequestProxyModeEnumMap[instance.mode]!,
'scheme': instance.scheme,
'host': instance.host,
'usr': instance.usr,
'pwd': instance.pwd,
};
const _$RequestProxyModeEnumMap = {
RequestProxyMode.follow: 'follow',
RequestProxyMode.none: 'none',
RequestProxyMode.custom: 'custom',
};
+2 -2
View File
@@ -8,7 +8,7 @@ part of 'resource.dart';
Resource _$ResourceFromJson(Map<String, dynamic> json) => Resource(
name: json['name'] as String? ?? "",
size: json['size'] as int? ?? 0,
size: (json['size'] as num?)?.toInt() ?? 0,
range: json['range'] as bool? ?? false,
files: (json['files'] as List<dynamic>)
.map((e) => FileInfo.fromJson(e as Map<String, dynamic>))
@@ -27,7 +27,7 @@ Map<String, dynamic> _$ResourceToJson(Resource instance) => <String, dynamic>{
FileInfo _$FileInfoFromJson(Map<String, dynamic> json) => FileInfo(
path: json['path'] as String? ?? "",
name: json['name'] as String,
size: json['size'] as int? ?? 0,
size: (json['size'] as num?)?.toInt() ?? 0,
req: json['req'] == null
? null
: Request.fromJson(json['req'] as Map<String, dynamic>),
@@ -3,6 +3,7 @@ import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:gopeed/api/model/request.dart';
class CreateController extends GetxController
with GetSingleTickerProviderStateMixin {
@@ -13,6 +14,7 @@ class CreateController extends GetxController
final isConfirming = false.obs;
final showAdvanced = false.obs;
final directDownload = false.obs;
final proxyConfig = Rx<RequestProxy?>(null);
late TabController advancedTabController;
final oldUrl = "".obs;
final fileDataUri = "".obs;
@@ -4,6 +4,7 @@ import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:gopeed/api/model/downloader_config.dart';
import 'package:path/path.dart' as path;
import 'package:rounded_loading_button_plus/rounded_loading_button.dart';
@@ -32,6 +33,10 @@ class CreateView extends GetView<CreateController> {
final _connectionsController = TextEditingController();
final _pathController = TextEditingController();
final _confirmController = RoundedLoadingButtonController();
final _proxyIpController = TextEditingController();
final _proxyPortController = TextEditingController();
final _proxyUsrController = TextEditingController();
final _proxyPwdController = TextEditingController();
final _httpUaController = TextEditingController();
final _httpCookieController = TextEditingController();
final _httpRefererController = TextEditingController();
@@ -261,7 +266,164 @@ class CreateView extends GetView<CreateController> {
() => Visibility(
visible: controller.showAdvanced.value,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Transform.translate(
offset: const Offset(-40, 0),
child: Row(
children: [
const Icon(
Icons.wifi_2_bar,
color: Colors.grey,
),
const SizedBox(
width: 15,
),
SizedBox(
width: 150,
child: DropdownButton<
RequestProxyMode>(
hint: Text('proxy'.tr),
isExpanded: true,
value: controller
.proxyConfig.value?.mode,
onChanged: (value) async {
if (value != null) {
controller.proxyConfig
.value = RequestProxy()
..mode = value;
}
},
items: [
DropdownMenuItem<
RequestProxyMode>(
value:
RequestProxyMode.follow,
child: Text(
'followSettings'.tr),
),
DropdownMenuItem<
RequestProxyMode>(
value:
RequestProxyMode.none,
child: Text('noProxy'.tr),
),
DropdownMenuItem<
RequestProxyMode>(
value:
RequestProxyMode.custom,
child:
Text('customProxy'.tr),
),
],
))
],
),
),
...(controller.proxyConfig.value?.mode ==
RequestProxyMode.custom
? [
SizedBox(
width: 150,
child: DropdownButtonFormField<
String>(
value: controller
.proxyConfig.value?.scheme,
onChanged: (value) async {
if (value != null) {}
},
items: const [
DropdownMenuItem<String>(
value: 'http',
child: Text('HTTP'),
),
DropdownMenuItem<String>(
value: 'https',
child: Text('HTTPS'),
),
DropdownMenuItem<String>(
value: 'socks5',
child: Text('SOCKS5'),
),
],
),
),
Row(children: [
Flexible(
child: TextFormField(
controller:
_proxyIpController,
decoration: InputDecoration(
labelText: 'server'.tr,
contentPadding:
const EdgeInsets.all(
0.0),
),
),
),
const Padding(
padding: EdgeInsets.only(
left: 10)),
Flexible(
child: TextFormField(
controller:
_proxyPortController,
decoration: InputDecoration(
labelText: 'port'.tr,
contentPadding:
const EdgeInsets.all(
0.0),
),
keyboardType:
TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter
.digitsOnly,
NumericalRangeFormatter(
min: 0, max: 65535),
],
),
),
]),
Row(children: [
Flexible(
child: TextFormField(
controller:
_proxyUsrController,
decoration: InputDecoration(
labelText: 'username'.tr,
contentPadding:
const EdgeInsets.all(
0.0),
),
),
),
const Padding(
padding: EdgeInsets.only(
left: 10)),
Flexible(
child: TextFormField(
controller:
_proxyPwdController,
decoration: InputDecoration(
labelText: 'password'.tr,
contentPadding:
const EdgeInsets.all(
0.0),
),
),
),
])
]
: const []),
],
),
const Divider(),
TabBar(
controller: controller.advancedTabController,
tabs: const [
@@ -416,18 +578,21 @@ class CreateView extends GetView<CreateController> {
if (isDirect) {
await Future.wait(urls.map((url) {
return createTask(CreateTask(
req: Request(url: url, extra: parseReqExtra(url)),
req: Request(
url: url, extra: parseReqExtra(url), proxy: parseProxy()),
opt: Options(
name: isMultiLine ? "" : _renameController.text,
path: _pathController.text,
selectFiles: [],
extra: parseReqOptsExtra())));
name: isMultiLine ? "" : _renameController.text,
path: _pathController.text,
selectFiles: [],
extra: parseReqOptsExtra(),
)));
}));
Get.rootDelegate.offNamed(Routes.TASK);
} else {
final rr = await resolve(Request(
url: submitUrl,
extra: parseReqExtra(_urlController.text),
proxy: parseProxy(),
));
await _showResolveDialog(rr);
}
@@ -440,6 +605,18 @@ class CreateView extends GetView<CreateController> {
}
}
RequestProxy? parseProxy() {
if (controller.proxyConfig.value?.mode == RequestProxyMode.custom) {
return RequestProxy()
..mode = RequestProxyMode.custom
..scheme = _proxyIpController.text
..host = "${_proxyIpController.text}:${_proxyPortController.text}"
..usr = _proxyUsrController.text
..pwd = _proxyPwdController.text;
}
return controller.proxyConfig.value;
}
Object? parseReqExtra(String url) {
Object? reqExtra;
if (controller.showAdvanced.value) {
@@ -543,7 +720,7 @@ class CreateView extends GetView<CreateController> {
controller.selectedIndexes.map((index) {
final file = rr.res.files[index];
return createTask(CreateTask(
req: file.req!,
req: file.req!..proxy = parseProxy(),
opt: Options(
name: file.name,
path: path.join(_pathController.text,
@@ -847,7 +847,7 @@ class SettingView extends GetView<SettingController> {
// advanced config log items start
buildLogsDir() {
return ListTile(
title: Text("日志目录"),
title: Text("logDirectory".tr),
subtitle: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@@ -981,7 +981,7 @@ class SettingView extends GetView<SettingController> {
: null,
]),
)),
const Text('开发者'),
Text('developer'.tr),
Card(
child: Column(
children: _addDivider([
+4 -1
View File
@@ -17,6 +17,7 @@ const enUS = {
'create': 'Create Task',
'directDownload': 'Direct Download',
'advancedOptions': 'Advanced Options',
'followSettings': 'Follow Settings',
'downloadLink': 'Download Link',
'downloadLinkValid': 'Please enter the download link',
'downloadLinkHit':
@@ -60,6 +61,8 @@ const enUS = {
'set': 'SET',
'portInUse': 'Port [@port] is in use, please change the port',
'effectAfterRestart': 'Effect after restart',
'developer': 'Developer',
'logDirectory': 'Log Directory',
'show': 'Show',
'startAll': 'Start All',
'pauseAll': 'Pause All',
@@ -96,7 +99,7 @@ const enUS = {
'browserExtension': 'Browser Extension',
'launchAtStartup': 'Launch at Startup',
'seedConfig': 'Seed Config',
'seedKeep': 'Keep seeding',
'seedKeep': 'Keep seeding until manually stopped',
'seedRatio': 'Seed ratio',
'seedTime': 'Seed time (minutes)',
'taskDetail': 'Task Detail',
+4 -1
View File
@@ -17,6 +17,7 @@ const zhCN = {
'create': '创建任务',
'directDownload': '直接下载',
'advancedOptions': '高级选项',
'followSettings': '跟随设置',
'downloadLink': '下载链接',
'downloadLinkValid': '请输入下载链接',
'downloadLinkHit': '请输入下载链接,支持 HTTP/HTTPS/MAGNET@append',
@@ -58,6 +59,8 @@ const zhCN = {
'set': '已设置',
'portInUse': '端口[@port]已被占用,请更换端口',
'effectAfterRestart': '此配置项将在重启应用后生效',
'developer': '开发者',
'logDirectory': '日志目录',
'show': '显示',
'startAll': '全部开始',
'pauseAll': '全部暂停',
@@ -93,7 +96,7 @@ const zhCN = {
'browserExtension': '浏览器扩展',
'launchAtStartup': '开机自动运行',
'seedConfig': '做种设置',
'seedKeep': '持续做种',
'seedKeep': '持续做种直到手动停止',
'seedRatio': '做种分享率',
'seedTime': '做种时间(分钟)',
'taskDetail': '任务详情',
+4 -7
View File
@@ -17,6 +17,7 @@ const zhTW = {
'create': '建立任務',
'directDownload': '直接下載',
'advancedOptions': '進階選項',
'followSettings': '跟隨設定',
'downloadLink': '下載連結',
'downloadLinkValid': '請輸入下載連結',
'downloadLinkHit': '請輸入下載連結,支援 HTTP/HTTPS/MAGNET@append',
@@ -58,6 +59,8 @@ const zhTW = {
'set': '已設定',
'portInUse': '連接埠 [@port] 已被使用,請更改連接埠',
'effectAfterRestart': '重新啟動後生效',
'developer': '開發者',
'logDirectory': '日誌目錄',
'show': '顯示',
'startAll': '全部開始',
'pauseAll': '全部暫停',
@@ -93,15 +96,9 @@ const zhTW = {
'browserExtension': '瀏覽器擴充功能',
'launchAtStartup': '開機自動執行',
'seedConfig': '做種設定',
'seedKeep': '持續做種',
'seedKeep': '持續做種直到手動停止',
'seedRatio': '做種分享率',
'seedTime': '做種時間(分鐘)',
/*
'taskDetail': '任务详情',
'taskName': '任务名称',
'taskUrl': '任务链接',
'downloadPath': '下载路径',
*/
'taskDetail': '任務詳情',
'taskName': '任務名稱',
'taskUrl': '任務連結',