feat(ui): add nat traversal option

This commit is contained in:
Simon Ding
2025-05-08 16:30:06 +08:00
parent 992fa7ddd0
commit bb0d5d1b58
3 changed files with 46 additions and 26 deletions

View File

@@ -1,8 +1,10 @@
package engine
import (
"fmt"
"net/url"
"polaris/ent/downloadclients"
"polaris/log"
"polaris/pkg/nat"
"polaris/pkg/qbittorrent"
@@ -10,31 +12,33 @@ import (
)
func (s *Engine) stunProxyDownloadClient() error {
downloader, e, err := s.GetDownloadClient()
if err != nil {
return err
}
if !e.UseNatTraversal {
return nil
}
if e.Implementation != downloadclients.ImplementationQbittorrent {
return nil
}
d, ok := downloader.(*qbittorrent.Client)
if !ok {
return nil
}
u, err := url.Parse(d.URL)
if err != nil {
return err
}
downloaders := s.db.GetAllDonloadClients()
for _, d := range downloaders {
if !d.Enable {
continue
}
if d.Implementation != downloadclients.ImplementationQbittorrent { //TODO only support qbittorrent for now
continue
}
qbt, err := qbittorrent.NewClient(d.URL, d.User, d.Password)
if err != nil {
return fmt.Errorf("connect to download client error: %v", d.URL)
}
u, err := url.Parse(d.URL)
if err != nil {
return err
}
log.Infof("start stun proxy for %s", d.Name)
n, err := nat.NewNatTraversal(func(xa stun.XORMappedAddress) error {
return qbt.SetListenPort(xa.Port)
}, u.Hostname())
if err != nil {
return err
}
n.StartProxy()
n, err := nat.NewNatTraversal(func(xa stun.XORMappedAddress) error {
return d.SetListenPort(xa.Port)
}, u.Hostname())
if err != nil {
return err
}
n.StartProxy()
return nil
}

View File

@@ -251,6 +251,7 @@ class DownloadClient {
bool? removeCompletedDownloads;
bool? removeFailedDownloads;
int? priority;
bool useNatTraversal = false;
DownloadClient(
{this.id,
this.enable,
@@ -261,7 +262,7 @@ class DownloadClient {
this.password,
this.removeCompletedDownloads = true,
this.priority = 1,
this.removeFailedDownloads = true});
this.removeFailedDownloads = true, this.useNatTraversal = false});
DownloadClient.fromJson(Map<String, dynamic> json) {
id = json['id'];
@@ -274,6 +275,7 @@ class DownloadClient {
priority = json["priority1"];
removeCompletedDownloads = json["remove_completed_downloads"] ?? false;
removeFailedDownloads = json["remove_failed_downloads"] ?? false;
useNatTraversal = json["use_nat_traversal"]?? false;
}
Map<String, dynamic> toJson() {
@@ -288,6 +290,7 @@ class DownloadClient {
data["priority"] = priority;
data["remove_completed_downloads"] = removeCompletedDownloads;
data["remove_failed_downloads"] = removeFailedDownloads;
data["use_nat_traversal"] = useNatTraversal;
return data;
}

View File

@@ -63,6 +63,7 @@ class _DownloaderState extends ConsumerState<DownloaderSettings> {
"remove_completed_downloads": client.removeCompletedDownloads,
"remove_failed_downloads": client.removeFailedDownloads,
"priority": client.priority.toString(),
"use_nat_traversal": client.useNatTraversal,
},
child: Column(
children: [
@@ -86,6 +87,12 @@ class _DownloaderState extends ConsumerState<DownloaderSettings> {
labelText: "优先级", helperText: "1-50, 1最高优先级50最低优先级"),
validator: FormBuilderValidators.integer(),
autovalidateMode: AutovalidateMode.onUserInteraction),
FormBuilderSwitch(
name: "use_nat_traversal",
enabled: client.implementation == "qbittorrent",
title: const Text("使用内置STUN NAT穿透"),
decoration: InputDecoration(helperText: "内建的NAT穿透功能帮助BT客户端上传(会自动更改下载器的监听地址)"),
),
FormBuilderSwitch(
name: "remove_completed_downloads",
title: const Text("任务完成后删除")),
@@ -150,6 +157,7 @@ class _DownloaderState extends ConsumerState<DownloaderSettings> {
user: _enableAuth ? values["user"] : null,
password: _enableAuth ? values["password"] : null,
priority: int.parse(values["priority"]),
useNatTraversal: values["use_nat_traversal"],
removeCompletedDownloads: values["remove_completed_downloads"],
removeFailedDownloads: values["remove_failed_downloads"]));
} else {
@@ -165,7 +173,12 @@ class _DownloaderState extends ConsumerState<DownloaderSettings> {
}
return showSettingDialog(
context, title, client.idExists() && client.implementation != "buildin", body, onSubmit, onDelete);
context,
title,
client.idExists() && client.implementation != "buildin",
body,
onSubmit,
onDelete);
}
Future<void> showSelections() {