diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 6342f5659..0546910ce 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -215,7 +215,7 @@ jobs:
overwrite: true
build-linux:
if: ${{ github.event.inputs.platform == 'all' || github.event.inputs.platform == 'linux' }}
- runs-on: ubuntu-latest
+ runs-on: ubuntu-20.04
needs: [ get-release ]
steps:
- uses: actions/checkout@v3
@@ -228,7 +228,9 @@ jobs:
- run: |
sudo apt update -y
# flutter build dependencies
- sudo apt install -y ninja-build libgtk-3-dev libayatana-appindicator3-1 libayatana-appindicator3-dev
+ sudo apt install -y ninja-build libgtk-3-dev libappindicator3-dev
+ # rpm build dependencies
+ sudo apt install -y rpm patchelf
# appimage build dependencies
sudo apt install -y libfuse2 locate
wget -O appimagetool "https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage"
diff --git a/README.md b/README.md
index a96d73b0d..8c46aabd9 100644
--- a/README.md
+++ b/README.md
@@ -53,11 +53,11 @@ Visit ✈ [Official Website](https://gopeed.com) | 📖 [Official Docs](https://
DEB |
- Link |
+ Link |
AppImage |
- Link |
+ Link |
Android |
diff --git a/README_ja-JP.md b/README_ja-JP.md
index f9873e57c..bc0cc5377 100644
--- a/README_ja-JP.md
+++ b/README_ja-JP.md
@@ -53,11 +53,11 @@ Gopeed (正式名 Go Speed) は `Golang` + `Flutter` によって開発された
DEB |
- Link |
+ Link |
AppImage |
- Link |
+ Link |
Android |
diff --git a/README_vi-VN.md b/README_vi-VN.md
index 7c8ce3690..fa8ecce5c 100644
--- a/README_vi-VN.md
+++ b/README_vi-VN.md
@@ -53,11 +53,11 @@ Truy cập ✈ [Trang web chính thức](https://gopeed.com) | 📖 [Tài liệu
DEB |
- Liên kết |
+ Liên kết |
AppImage |
- Liên kết |
+ Liên kết |
Android |
diff --git a/README_zh-CN.md b/README_zh-CN.md
index 4ec1327b5..2d94ed19c 100644
--- a/README_zh-CN.md
+++ b/README_zh-CN.md
@@ -53,11 +53,11 @@ Gopeed(全称 Go Speed),直译过来中文名叫做`够快下载器`(不
DEB |
- 前往 |
+ 前往 |
AppImage |
- 前往 |
+ 前往 |
Android |
diff --git a/README_zh-TW.md b/README_zh-TW.md
index 147d6004c..7920663db 100644
--- a/README_zh-TW.md
+++ b/README_zh-TW.md
@@ -53,11 +53,11 @@ Gopeed(全稱 Go Speed),是一款使用`Golang`+`Flutter`編寫的高速
DEB |
- 前往 |
+ 前往 |
AppImage |
- 前往 |
+ 前往 |
Android |
diff --git a/_docs/img/ui-demo.png b/_docs/img/ui-demo.png
index c4951fca5..6620a4593 100644
Binary files a/_docs/img/ui-demo.png and b/_docs/img/ui-demo.png differ
diff --git a/go.mod b/go.mod
index 0c25a2b67..c4965679d 100644
--- a/go.mod
+++ b/go.mod
@@ -69,6 +69,7 @@ require (
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
+ github.com/mattn/go-ieproxy v0.0.11 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mschoch/smat v0.2.0 // indirect
github.com/pion/datachannel v1.5.5 // indirect
diff --git a/go.sum b/go.sum
index ae4980b37..66aceab3f 100644
--- a/go.sum
+++ b/go.sum
@@ -270,6 +270,8 @@ github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A=
github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
+github.com/mattn/go-ieproxy v0.0.11 h1:MQ/5BuGSgDAHZOJe6YY80IF2UVCfGkwfo6AeD7HtHYo=
+github.com/mattn/go-ieproxy v0.0.11/go.mod h1:/NsJd+kxZBmjMc5hrJCKMbP57B84rvq9BiDRbtO9AS0=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
diff --git a/internal/controller/controller.go b/internal/controller/controller.go
index ded1ea2de..15eb299d1 100644
--- a/internal/controller/controller.go
+++ b/internal/controller/controller.go
@@ -1,14 +1,14 @@
package controller
import (
- "net/url"
+ "github.com/GopeedLab/gopeed/pkg/base"
"os"
"path/filepath"
)
type Controller struct {
- GetConfig func(v any) bool
- ProxyUrl *url.URL
+ GetConfig func(v any) bool
+ ProxyConfig *base.DownloaderProxyConfig
FileController
//ContextDialer() (proxy.Dialer, error)
}
diff --git a/internal/protocol/bt/fetcher.go b/internal/protocol/bt/fetcher.go
index db1d8df42..75f8e3a08 100644
--- a/internal/protocol/bt/fetcher.go
+++ b/internal/protocol/bt/fetcher.go
@@ -10,7 +10,6 @@ import (
"github.com/GopeedLab/gopeed/pkg/util"
"github.com/anacrolix/torrent"
"github.com/anacrolix/torrent/metainfo"
- "net/http"
"path/filepath"
"strings"
"sync"
@@ -69,9 +68,7 @@ func (f *Fetcher) initClient() (err error) {
cfg.Bep20 = fmt.Sprintf("-GP%s-", parseBep20())
cfg.ExtendedHandshakeClientVersion = fmt.Sprintf("Gopeed %s", base.Version)
cfg.ListenPort = f.config.ListenPort
- if f.ctl.ProxyUrl != nil {
- cfg.HTTPProxy = http.ProxyURL(f.ctl.ProxyUrl)
- }
+ cfg.HTTPProxy = f.ctl.ProxyConfig.ToHandler()
cfg.DefaultStorage = newFileOpts(newFileClientOpts{
ClientBaseDir: cfg.DataDir,
HandleFileTorrent: func(infoHash metainfo.Hash, ft *fileTorrentImpl) {
diff --git a/internal/protocol/bt/fetcher_test.go b/internal/protocol/bt/fetcher_test.go
index b0bff5485..751f9f85b 100644
--- a/internal/protocol/bt/fetcher_test.go
+++ b/internal/protocol/bt/fetcher_test.go
@@ -8,8 +8,6 @@ import (
"github.com/GopeedLab/gopeed/internal/test"
"github.com/GopeedLab/gopeed/pkg/base"
"github.com/GopeedLab/gopeed/pkg/protocol/bt"
- "github.com/GopeedLab/gopeed/pkg/util"
- "net/url"
"os"
"reflect"
"testing"
@@ -60,7 +58,14 @@ func TestFetcher_ResolveWithProxy(t *testing.T) {
proxyListener := test.StartSocks5Server(usr, pwd)
defer proxyListener.Close()
- doResolve(t, buildConfigFetcher(util.BuildProxyUrl("socks5", proxyListener.Addr().String(), usr, pwd)))
+ doResolve(t, buildConfigFetcher(&base.DownloaderProxyConfig{
+ Enable: true,
+ System: false,
+ Scheme: "socks5",
+ Host: proxyListener.Addr().String(),
+ Usr: usr,
+ Pwd: pwd,
+ }))
}
func doResolve(t *testing.T, fetcher fetcher.Fetcher) {
@@ -100,7 +105,7 @@ func buildFetcher() fetcher.Fetcher {
return fetcher
}
-func buildConfigFetcher(proxyUrl *url.URL) fetcher.Fetcher {
+func buildConfigFetcher(proxyConfig *base.DownloaderProxyConfig) fetcher.Fetcher {
fetcher := new(FetcherBuilder).Build()
newController := controller.NewController()
mockCfg := config{
@@ -114,7 +119,7 @@ func buildConfigFetcher(proxyUrl *url.URL) fetcher.Fetcher {
}
return true
}
- newController.ProxyUrl = proxyUrl
+ newController.ProxyConfig = proxyConfig
fetcher.Setup(newController)
return fetcher
}
diff --git a/internal/protocol/http/fetcher.go b/internal/protocol/http/fetcher.go
index e850c0df9..bda1a4574 100644
--- a/internal/protocol/http/fetcher.go
+++ b/internal/protocol/http/fetcher.go
@@ -451,12 +451,11 @@ func (f *Fetcher) splitChunk() (chunks []*chunk) {
}
func (f *Fetcher) buildClient() *http.Client {
- transport := &http.Transport{}
+ transport := &http.Transport{
+ Proxy: f.ctl.ProxyConfig.ToHandler(),
+ }
// Cookie handle
jar, _ := cookiejar.New(nil)
- if f.ctl.ProxyUrl != nil {
- transport.Proxy = http.ProxyURL(f.ctl.ProxyUrl)
- }
return &http.Client{
Transport: transport,
Jar: jar,
diff --git a/internal/protocol/http/fetcher_test.go b/internal/protocol/http/fetcher_test.go
index b65e3a3bd..6c76a0de0 100644
--- a/internal/protocol/http/fetcher_test.go
+++ b/internal/protocol/http/fetcher_test.go
@@ -8,7 +8,6 @@ import (
"github.com/GopeedLab/gopeed/internal/test"
"github.com/GopeedLab/gopeed/pkg/base"
"github.com/GopeedLab/gopeed/pkg/protocol/http"
- "github.com/GopeedLab/gopeed/pkg/util"
"net"
"testing"
"time"
@@ -338,7 +337,11 @@ func downloadResume(listener net.Listener, connections int, t *testing.T) {
func downloadWithProxy(httpListener net.Listener, proxyListener net.Listener, t *testing.T) {
fetcher := downloadReady(httpListener, 4, t)
ctl := controller.NewController()
- ctl.ProxyUrl = util.BuildProxyUrl("socks5", proxyListener.Addr().String(), "", "")
+ ctl.ProxyConfig = &base.DownloaderProxyConfig{
+ Enable: true,
+ Scheme: "socks5",
+ Host: proxyListener.Addr().String(),
+ }
fetcher.Setup(ctl)
err := fetcher.Start()
if err != nil {
diff --git a/internal/test/httptest.go b/internal/test/httptest.go
index 18cc3069b..fb3c38e5d 100644
--- a/internal/test/httptest.go
+++ b/internal/test/httptest.go
@@ -24,6 +24,11 @@ const (
Dir = "./"
BuildFile = Dir + BuildName
+ ExternalDownloadUrl = "https://raw.githubusercontent.com/GopeedLab/gopeed/v1.5.6/_docs/img/banner.png"
+ ExternalDownloadName = "banner.png"
+ ExternalDownloadSize = 26416
+ //ExternalDownloadMd5 = "c67c6e3cae79a95342485676571e8a5c"
+
DownloadName = "download.data"
DownloadRename = "download (1).data"
DownloadFile = Dir + DownloadName
diff --git a/pkg/base/model.go b/pkg/base/model.go
index f0b7ff040..991d6c0ec 100644
--- a/pkg/base/model.go
+++ b/pkg/base/model.go
@@ -3,7 +3,10 @@ package base
import (
"fmt"
"github.com/GopeedLab/gopeed/pkg/util"
+ "github.com/mattn/go-ieproxy"
"golang.org/x/exp/slices"
+ "net/http"
+ "net/url"
"time"
)
@@ -128,3 +131,88 @@ func ParseOptsExtra[E any](opts *Options) error {
opts.Extra = &t
return nil
}
+
+// DownloaderStoreConfig is the config that can restore the downloader.
+type DownloaderStoreConfig struct {
+ FirstLoad bool `json:"-"` // FirstLoad is the flag that the config is first time init and not from store
+
+ DownloadDir string `json:"downloadDir"` // DownloadDir is the default directory to save the downloaded files
+ MaxRunning int `json:"maxRunning"` // MaxRunning is the max running download count
+ ProtocolConfig map[string]any `json:"protocolConfig"` // ProtocolConfig is special config for each protocol
+ Extra map[string]any `json:"extra"`
+ Proxy *DownloaderProxyConfig `json:"proxy"`
+}
+
+func (cfg *DownloaderStoreConfig) Init() *DownloaderStoreConfig {
+ if cfg.MaxRunning == 0 {
+ cfg.MaxRunning = 5
+ }
+ if cfg.Proxy == nil {
+ cfg.Proxy = &DownloaderProxyConfig{}
+ }
+ return cfg
+}
+
+type DownloaderProxyConfig struct {
+ Enable bool `json:"enable"`
+ // System is the flag that use system proxy
+ System bool `json:"system"`
+ Scheme string `json:"scheme"`
+ Host string `json:"host"`
+ Usr string `json:"usr"`
+ Pwd string `json:"pwd"`
+}
+
+func (cfg *DownloaderProxyConfig) ToHandler() func(r *http.Request) (*url.URL, error) {
+ if cfg == nil || cfg.Enable == false {
+ return nil
+ }
+ if cfg.System {
+ ieproxy.ReloadConf()
+ return ieproxy.GetProxyFunc()
+ }
+ if cfg.Scheme == "" || cfg.Host == "" {
+ return nil
+ }
+ return http.ProxyURL(util.BuildProxyUrl(cfg.Scheme, cfg.Host, cfg.Usr, cfg.Pwd))
+}
+
+// ToUrl returns the proxy url, just for git clone
+func (cfg *DownloaderProxyConfig) ToUrl() *url.URL {
+ if cfg == nil || cfg.Enable == false {
+ return nil
+ }
+ if cfg.System {
+ ieproxy.ReloadConf()
+ static := ieproxy.GetConf().Static
+ if static.Active && len(static.Protocols) > 0 {
+ // If only one protocol, use it
+ if len(static.Protocols) == 1 {
+ for _, v := range static.Protocols {
+ return parseUrlSafe(v)
+ }
+ }
+ // Check https
+ if v, ok := static.Protocols["https"]; ok {
+ return parseUrlSafe(v)
+ }
+ // Check http
+ if v, ok := static.Protocols["http"]; ok {
+ return parseUrlSafe(v)
+ }
+ }
+ return nil
+ }
+ if cfg.Scheme == "" || cfg.Host == "" {
+ return nil
+ }
+ return util.BuildProxyUrl(cfg.Scheme, cfg.Host, cfg.Usr, cfg.Pwd)
+}
+
+func parseUrlSafe(rawUrl string) *url.URL {
+ u, err := url.Parse(rawUrl)
+ if err != nil {
+ return nil
+ }
+ return u
+}
diff --git a/pkg/download/downloader.go b/pkg/download/downloader.go
index 5d7faeb97..59dfecdb9 100644
--- a/pkg/download/downloader.go
+++ b/pkg/download/downloader.go
@@ -110,7 +110,7 @@ func (d *Downloader) Setup() error {
return err
}
// load config from storage
- var cfg DownloaderStoreConfig
+ var cfg base.DownloaderStoreConfig
exist, err := d.storage.Get(bucketConfig, "config", &cfg)
if err != nil {
return err
@@ -118,7 +118,7 @@ func (d *Downloader) Setup() error {
if exist {
d.cfg.DownloaderStoreConfig = &cfg
} else {
- d.cfg.DownloaderStoreConfig = &DownloaderStoreConfig{
+ d.cfg.DownloaderStoreConfig = &base.DownloaderStoreConfig{
FirstLoad: true,
}
}
@@ -225,7 +225,7 @@ func (d *Downloader) setupFetcher(fetcher fetcher.Fetcher) {
ctl.GetConfig = func(v any) bool {
return d.getProtocolConfig(fetcher.Name(), v)
}
- ctl.ProxyUrl = d.cfg.ProxyUrl()
+ ctl.ProxyConfig = d.cfg.Proxy
fetcher.Setup(ctl)
}
@@ -603,11 +603,11 @@ func (d *Downloader) GetTasksByStatues(statues []base.Status) []*Task {
return tasks
}
-func (d *Downloader) GetConfig() (*DownloaderStoreConfig, error) {
+func (d *Downloader) GetConfig() (*base.DownloaderStoreConfig, error) {
return d.cfg.DownloaderStoreConfig, nil
}
-func (d *Downloader) PutConfig(v *DownloaderStoreConfig) error {
+func (d *Downloader) PutConfig(v *base.DownloaderStoreConfig) error {
d.cfg.DownloaderStoreConfig = v
return d.storage.Put(bucketConfig, "config", v)
}
diff --git a/pkg/download/downloader_test.go b/pkg/download/downloader_test.go
index 7836baf3b..3d9e3a292 100644
--- a/pkg/download/downloader_test.go
+++ b/pkg/download/downloader_test.go
@@ -4,6 +4,7 @@ import (
"github.com/GopeedLab/gopeed/internal/test"
"github.com/GopeedLab/gopeed/pkg/base"
"github.com/GopeedLab/gopeed/pkg/protocol/http"
+ "os"
"sync"
"testing"
"time"
@@ -85,37 +86,58 @@ func TestDownloader_Create(t *testing.T) {
func TestDownloader_CreateWithProxy(t *testing.T) {
// No proxy
- doTestDownloaderCreateWithProxy(t, false, func(proxyCfg *DownloaderProxyConfig) *DownloaderProxyConfig {
+ doTestDownloaderCreateWithProxy(t, false, func(proxyCfg *base.DownloaderProxyConfig) *base.DownloaderProxyConfig {
return nil
- })
+ }, nil)
// Disable proxy
- doTestDownloaderCreateWithProxy(t, false, func(proxyCfg *DownloaderProxyConfig) *DownloaderProxyConfig {
+ doTestDownloaderCreateWithProxy(t, false, func(proxyCfg *base.DownloaderProxyConfig) *base.DownloaderProxyConfig {
proxyCfg.Enable = false
return proxyCfg
+ }, nil)
+ // Enable system proxy but not set proxy environment variable
+ doTestDownloaderCreateWithProxy(t, false, func(proxyCfg *base.DownloaderProxyConfig) *base.DownloaderProxyConfig {
+ proxyCfg.System = true
+ return proxyCfg
+ }, nil)
+ // Enable proxy but error proxy environment variable
+ doTestDownloaderCreateWithProxy(t, false, func(proxyCfg *base.DownloaderProxyConfig) *base.DownloaderProxyConfig {
+ os.Setenv("HTTP_PROXY", "http://127.0.0.1:1234")
+ os.Setenv("HTTPS_PROXY", "http://127.0.0.1:1234")
+ proxyCfg.System = true
+ return proxyCfg
+ }, func(err error) {
+ if err == nil {
+ t.Fatal("doTestDownloaderCreateWithProxy() got = nil, want error")
+ }
})
+ // Enable system proxy and set proxy environment variable
+ doTestDownloaderCreateWithProxy(t, false, func(proxyCfg *base.DownloaderProxyConfig) *base.DownloaderProxyConfig {
+ os.Setenv("HTTP_PROXY", proxyCfg.ToUrl().String())
+ os.Setenv("HTTPS_PROXY", proxyCfg.ToUrl().String())
+ proxyCfg.System = true
+ return proxyCfg
+ }, nil)
// Invalid proxy scheme
- doTestDownloaderCreateWithProxy(t, false, func(proxyCfg *DownloaderProxyConfig) *DownloaderProxyConfig {
+ doTestDownloaderCreateWithProxy(t, false, func(proxyCfg *base.DownloaderProxyConfig) *base.DownloaderProxyConfig {
proxyCfg.Scheme = ""
return proxyCfg
- })
+ }, nil)
// Invalid proxy host
- doTestDownloaderCreateWithProxy(t, false, func(proxyCfg *DownloaderProxyConfig) *DownloaderProxyConfig {
+ doTestDownloaderCreateWithProxy(t, false, func(proxyCfg *base.DownloaderProxyConfig) *base.DownloaderProxyConfig {
proxyCfg.Host = ""
return proxyCfg
- })
+ }, nil)
// Use proxy without auth
- doTestDownloaderCreateWithProxy(t, false, func(proxyCfg *DownloaderProxyConfig) *DownloaderProxyConfig {
+ doTestDownloaderCreateWithProxy(t, false, func(proxyCfg *base.DownloaderProxyConfig) *base.DownloaderProxyConfig {
return proxyCfg
- })
+ }, nil)
// Use proxy with auth
- doTestDownloaderCreateWithProxy(t, true, func(proxyCfg *DownloaderProxyConfig) *DownloaderProxyConfig {
+ doTestDownloaderCreateWithProxy(t, true, func(proxyCfg *base.DownloaderProxyConfig) *base.DownloaderProxyConfig {
return proxyCfg
- })
+ }, nil)
}
-func doTestDownloaderCreateWithProxy(t *testing.T, auth bool, buildProxyConfig func(proxyCfg *DownloaderProxyConfig) *DownloaderProxyConfig) {
- httpListener := test.StartTestFileServer()
- defer httpListener.Close()
+func doTestDownloaderCreateWithProxy(t *testing.T, auth bool, buildProxyConfig func(proxyCfg *base.DownloaderProxyConfig) *base.DownloaderProxyConfig, errHandler func(err error)) {
usr, pwd := "", ""
if auth {
usr, pwd = "admin", "123"
@@ -128,7 +150,7 @@ func doTestDownloaderCreateWithProxy(t *testing.T, auth bool, buildProxyConfig f
t.Fatal(err)
}
defer downloader.Clear()
- downloader.cfg.DownloaderStoreConfig.Proxy = buildProxyConfig(&DownloaderProxyConfig{
+ downloader.cfg.DownloaderStoreConfig.Proxy = buildProxyConfig(&base.DownloaderProxyConfig{
Enable: true,
Scheme: "socks5",
Host: proxyListener.Addr().String(),
@@ -137,20 +159,24 @@ func doTestDownloaderCreateWithProxy(t *testing.T, auth bool, buildProxyConfig f
})
req := &base.Request{
- URL: "http://" + httpListener.Addr().String() + "/" + test.BuildName,
+ URL: test.ExternalDownloadUrl,
}
rr, err := downloader.Resolve(req)
if err != nil {
- t.Fatal(err)
+ if errHandler == nil {
+ t.Fatal(err)
+ }
+ errHandler(err)
+ return
}
want := &base.Resource{
- Size: test.BuildSize,
+ Size: test.ExternalDownloadSize,
Range: true,
Files: []*base.FileInfo{
{
- Name: test.BuildName,
+ Name: test.ExternalDownloadName,
Path: "",
- Size: test.BuildSize,
+ Size: test.ExternalDownloadSize,
},
},
}
@@ -285,7 +311,7 @@ func TestDownloader_Protocol_Config(t *testing.T) {
t.Errorf("getProtocolConfig() got = %v, want %v", exits, false)
}
- storeCfg := &DownloaderStoreConfig{
+ storeCfg := &base.DownloaderStoreConfig{
DownloadDir: "./downloads",
ProtocolConfig: map[string]any{
"http": map[string]any{
diff --git a/pkg/download/engine/engine.go b/pkg/download/engine/engine.go
index 969a5cefe..ac0db4885 100644
--- a/pkg/download/engine/engine.go
+++ b/pkg/download/engine/engine.go
@@ -3,6 +3,7 @@ package engine
import (
_ "embed"
"errors"
+ "github.com/GopeedLab/gopeed/pkg/base"
gojaerror "github.com/GopeedLab/gopeed/pkg/download/engine/inject/error"
"github.com/GopeedLab/gopeed/pkg/download/engine/inject/file"
"github.com/GopeedLab/gopeed/pkg/download/engine/inject/formdata"
@@ -11,7 +12,6 @@ import (
"github.com/dop251/goja"
"github.com/dop251/goja_nodejs/eventloop"
gojaurl "github.com/dop251/goja_nodejs/url"
- "net/url"
"time"
)
@@ -110,7 +110,7 @@ func (e *Engine) Close() {
}
type Config struct {
- ProxyURL *url.URL
+ ProxyConfig *base.DownloaderProxyConfig
}
func NewEngine(cfg *Config) *Engine {
@@ -135,7 +135,7 @@ func NewEngine(cfg *Config) *Engine {
if err := formdata.Enable(runtime); err != nil {
return
}
- if err := xhr.Enable(runtime, cfg.ProxyURL); err != nil {
+ if err := xhr.Enable(runtime, cfg.ProxyConfig.ToHandler()); err != nil {
return
}
if _, err := runtime.RunString(polyfillScript); err != nil {
diff --git a/pkg/download/engine/engine_test.go b/pkg/download/engine/engine_test.go
index 63e0281eb..61443bd34 100644
--- a/pkg/download/engine/engine_test.go
+++ b/pkg/download/engine/engine_test.go
@@ -7,10 +7,10 @@ import (
"errors"
"fmt"
"github.com/GopeedLab/gopeed/internal/test"
+ "github.com/GopeedLab/gopeed/pkg/base"
gojaerror "github.com/GopeedLab/gopeed/pkg/download/engine/inject/error"
"github.com/GopeedLab/gopeed/pkg/download/engine/inject/file"
gojautil "github.com/GopeedLab/gopeed/pkg/download/engine/util"
- "github.com/GopeedLab/gopeed/pkg/util"
"github.com/dop251/goja"
"io"
"net"
@@ -217,8 +217,16 @@ func doTestFetchWithProxy(t *testing.T, usr, pwd string) {
proxyListener := test.StartSocks5Server(usr, pwd)
defer proxyListener.Close()
-
- engine := NewEngine(&Config{ProxyURL: util.BuildProxyUrl("socks5", proxyListener.Addr().String(), usr, pwd)})
+ engine := NewEngine(&Config{
+ ProxyConfig: &base.DownloaderProxyConfig{
+ Enable: true,
+ System: false,
+ Scheme: "socks5",
+ Host: proxyListener.Addr().String(),
+ Usr: usr,
+ Pwd: pwd,
+ },
+ })
if _, err := engine.RunString(fmt.Sprintf("var host = 'http://%s';", httpListener.Addr().String())); err != nil {
t.Fatal(err)
diff --git a/pkg/download/engine/inject/xhr/module.go b/pkg/download/engine/inject/xhr/module.go
index d91c56ac0..dd6e0a775 100644
--- a/pkg/download/engine/inject/xhr/module.go
+++ b/pkg/download/engine/inject/xhr/module.go
@@ -2,6 +2,7 @@ package xhr
import (
"bytes"
+ "errors"
"github.com/GopeedLab/gopeed/pkg/download/engine/inject/file"
"github.com/GopeedLab/gopeed/pkg/download/engine/inject/formdata"
"github.com/GopeedLab/gopeed/pkg/download/engine/util"
@@ -120,7 +121,7 @@ type XMLHttpRequest struct {
requestHeaders map[string]string
responseHeaders map[string]string
aborted bool
- proxyUrl *url.URL
+ proxyHandler func(r *http.Request) (*url.URL, error)
Upload *XMLHttpRequestUpload `json:"upload"`
Timeout int `json:"timeout"`
@@ -202,9 +203,8 @@ func (xhr *XMLHttpRequest) Send(data goja.Value) {
for k, v := range xhr.requestHeaders {
req.Header.Set(k, v)
}
- transport := &http.Transport{}
- if xhr.proxyUrl != nil {
- transport.Proxy = http.ProxyURL(xhr.proxyUrl)
+ transport := &http.Transport{
+ Proxy: xhr.proxyHandler,
}
client := &http.Client{
Transport: transport,
@@ -213,7 +213,8 @@ func (xhr *XMLHttpRequest) Send(data goja.Value) {
resp, err := client.Do(req)
if err != nil {
// handle timeout error
- if err, ok := err.(net.Error); ok && err.Timeout() {
+ var ne net.Error
+ if errors.As(err, &ne) && ne.Timeout() {
if xhr.Timeout > 0 {
xhr.Upload.callOntimeout()
xhr.callOntimeout()
@@ -313,7 +314,7 @@ func (xhr *XMLHttpRequest) parseData(data goja.Value) any {
return data.String()
}
-func Enable(runtime *goja.Runtime, proxyUrl *url.URL) error {
+func Enable(runtime *goja.Runtime, proxyHandler func(r *http.Request) (*url.URL, error)) error {
progressEvent := runtime.ToValue(func(call goja.ConstructorCall) *goja.Object {
if len(call.Arguments) < 1 {
util.ThrowTypeError(runtime, "Failed to construct 'ProgressEvent': 1 argument required, but only 0 present.")
@@ -327,7 +328,7 @@ func Enable(runtime *goja.Runtime, proxyUrl *url.URL) error {
})
xhr := runtime.ToValue(func(call goja.ConstructorCall) *goja.Object {
instance := &XMLHttpRequest{
- proxyUrl: proxyUrl,
+ proxyHandler: proxyHandler,
Upload: &XMLHttpRequestUpload{
EventProp: &EventProp{
eventListeners: make(map[string]func(event *ProgressEvent)),
diff --git a/pkg/download/extension.go b/pkg/download/extension.go
index 262bc5592..83955aaa6 100644
--- a/pkg/download/extension.go
+++ b/pkg/download/extension.go
@@ -199,7 +199,7 @@ func (d *Downloader) fetchExtensionByGit(url string, handler func(tempExtPath st
return nil, err
}
proxyOptions := transport.ProxyOptions{}
- proxyUrl := d.cfg.ProxyUrl()
+ proxyUrl := d.cfg.DownloaderStoreConfig.Proxy.ToUrl()
if proxyUrl != nil {
proxyOptions.URL = proxyUrl.Scheme + "://" + proxyUrl.Host
proxyOptions.Username = proxyUrl.User.Username()
@@ -341,7 +341,7 @@ func doTrigger[T any](d *Downloader, event ActivationEvent, req *base.Request, c
req.Labels = make(map[string]string)
}
engine := engine.NewEngine(&engine.Config{
- ProxyURL: d.cfg.ProxyUrl(),
+ ProxyConfig: d.cfg.Proxy,
})
defer engine.Close()
err = engine.Runtime.Set("gopeed", gopeed)
diff --git a/pkg/download/model.go b/pkg/download/model.go
index c78521ce3..4fbc5b9d0 100644
--- a/pkg/download/model.go
+++ b/pkg/download/model.go
@@ -7,9 +7,7 @@ import (
"github.com/GopeedLab/gopeed/internal/protocol/http"
"github.com/GopeedLab/gopeed/pkg/base"
"github.com/GopeedLab/gopeed/pkg/util"
-
gonanoid "github.com/matoous/go-nanoid/v2"
- "net/url"
"sync"
"time"
)
@@ -87,7 +85,7 @@ type DownloaderConfig struct {
ProductionMode bool
- *DownloaderStoreConfig
+ *base.DownloaderStoreConfig
}
func (cfg *DownloaderConfig) Init() *DownloaderConfig {
@@ -108,42 +106,3 @@ func (cfg *DownloaderConfig) Init() *DownloaderConfig {
}
return cfg
}
-
-// DownloaderStoreConfig is the config that can restore the downloader.
-type DownloaderStoreConfig struct {
- FirstLoad bool `json:"-"` // FirstLoad is the flag that the config is first time init and not from store
-
- DownloadDir string `json:"downloadDir"` // DownloadDir is the default directory to save the downloaded files
- MaxRunning int `json:"maxRunning"` // MaxRunning is the max running download count
- ProtocolConfig map[string]any `json:"protocolConfig"` // ProtocolConfig is special config for each protocol
- Extra map[string]any `json:"extra"`
- Proxy *DownloaderProxyConfig `json:"proxy"`
-}
-
-func (cfg *DownloaderStoreConfig) Init() *DownloaderStoreConfig {
- if cfg.MaxRunning == 0 {
- cfg.MaxRunning = 5
- }
- if cfg.Proxy == nil {
- cfg.Proxy = &DownloaderProxyConfig{}
- }
- return cfg
-}
-
-func (cfg *DownloaderStoreConfig) ProxyUrl() *url.URL {
- if cfg.Proxy == nil {
- return nil
- }
- if cfg.Proxy.Enable == false || cfg.Proxy.Scheme == "" || cfg.Proxy.Host == "" {
- return nil
- }
- return util.BuildProxyUrl(cfg.Proxy.Scheme, cfg.Proxy.Host, cfg.Proxy.Usr, cfg.Proxy.Pwd)
-}
-
-type DownloaderProxyConfig struct {
- Enable bool `json:"enable"`
- Scheme string `json:"scheme"`
- Host string `json:"host"`
- Usr string `json:"usr"`
- Pwd string `json:"pwd"`
-}
diff --git a/pkg/rest/api.go b/pkg/rest/api.go
index d95964dd3..8a0ae75b9 100644
--- a/pkg/rest/api.go
+++ b/pkg/rest/api.go
@@ -142,7 +142,7 @@ func GetConfig(w http.ResponseWriter, r *http.Request) {
}
func PutConfig(w http.ResponseWriter, r *http.Request) {
- var cfg download.DownloaderStoreConfig
+ var cfg base.DownloaderStoreConfig
if ReadJson(r, w, &cfg) {
if err := Downloader.PutConfig(&cfg); err != nil {
WriteJson(w, model.NewErrorResult(err.Error()))
@@ -309,7 +309,7 @@ func writeError(w http.ResponseWriter, msg string) {
w.Write([]byte(msg))
}
-func getServerConfig() *download.DownloaderStoreConfig {
+func getServerConfig() *base.DownloaderStoreConfig {
cfg, _ := Downloader.GetConfig()
return cfg
}
diff --git a/pkg/rest/server_test.go b/pkg/rest/server_test.go
index 7b001defd..ef6b86053 100644
--- a/pkg/rest/server_test.go
+++ b/pkg/rest/server_test.go
@@ -324,7 +324,7 @@ func TestGetTasks(t *testing.T) {
func TestGetAndPutConfig(t *testing.T) {
doTest(func() {
- cfg := httpRequestCheckOk[*download.DownloaderStoreConfig](http.MethodGet, "/api/v1/config", nil)
+ cfg := httpRequestCheckOk[*base.DownloaderStoreConfig](http.MethodGet, "/api/v1/config", nil)
cfg.DownloadDir = "./download"
cfg.Extra = map[string]any{
"serverConfig": &Config{
@@ -335,7 +335,7 @@ func TestGetAndPutConfig(t *testing.T) {
}
httpRequestCheckOk[any](http.MethodPut, "/api/v1/config", cfg)
- newCfg := httpRequestCheckOk[*download.DownloaderStoreConfig](http.MethodGet, "/api/v1/config", nil)
+ newCfg := httpRequestCheckOk[*base.DownloaderStoreConfig](http.MethodGet, "/api/v1/config", nil)
if !test.JsonEqual(cfg, newCfg) {
t.Errorf("GetAndPutConfig() got = %v, want %v", test.ToJson(newCfg), test.ToJson(cfg))
}
diff --git a/pkg/util/url.go b/pkg/util/url.go
index 0a6ac190b..a54c6780f 100644
--- a/pkg/util/url.go
+++ b/pkg/util/url.go
@@ -2,6 +2,7 @@ package util
import (
"encoding/base64"
+ "net/http"
"net/url"
"regexp"
"strings"
@@ -51,3 +52,14 @@ func BuildProxyUrl(scheme, host, usr, pwd string) *url.URL {
Host: host,
}
}
+
+// ProxyUrlToHandler gets the proxy handler from the proxy url.
+func ProxyUrlToHandler(proxyUrl *url.URL) func(*http.Request) (*url.URL, error) {
+ if proxyUrl == nil {
+ return nil
+ }
+ if proxyUrl.Scheme == "system" {
+ return http.ProxyFromEnvironment
+ }
+ return http.ProxyURL(proxyUrl)
+}
diff --git a/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
index ebb599e91..9f4e06ba9 100644
Binary files a/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png and b/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ
diff --git a/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
index bd992b241..c8caba582 100644
Binary files a/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png and b/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ
diff --git a/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
index 8000e8509..83ee21862 100644
Binary files a/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png and b/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ
diff --git a/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
index 4388230ac..44af22b31 100644
Binary files a/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png and b/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ
diff --git a/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
index 86b1cfd27..707836984 100644
Binary files a/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png and b/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ
diff --git a/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
index 9d5dacad0..1952bfc03 100644
Binary files a/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png and b/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ
diff --git a/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
index d7ad89515..a1e74ca1d 100644
Binary files a/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png and b/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ
diff --git a/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
index 8000e8509..83ee21862 100644
Binary files a/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png and b/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ
diff --git a/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
index 327d490d4..43b67cf69 100644
Binary files a/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png and b/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ
diff --git a/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
index 2e3603dd7..bd7c8ab9a 100644
Binary files a/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png and b/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ
diff --git a/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png b/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png
index 3a680a905..b6e266bfd 100644
Binary files a/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png and b/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png differ
diff --git a/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png b/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png
index d4be832e8..20c49aa99 100644
Binary files a/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png and b/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png differ
diff --git a/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png b/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png
index f3fb629b5..5f0207e74 100644
Binary files a/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png and b/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png differ
diff --git a/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png b/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png
index e63dad432..28178b4b7 100644
Binary files a/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png and b/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png differ
diff --git a/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
index 2e3603dd7..bd7c8ab9a 100644
Binary files a/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png and b/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ
diff --git a/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
index a7bbecdeb..d65ee6bad 100644
Binary files a/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png and b/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ
diff --git a/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png b/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png
index a5666c9fb..c176e16fa 100644
Binary files a/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png and b/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png differ
diff --git a/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png b/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png
index 5da20a9d0..65b6968ef 100644
Binary files a/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png and b/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png differ
diff --git a/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
index 4a2479aaa..1f07d8b28 100644
Binary files a/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png and b/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ
diff --git a/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
index adb453f2d..62e9ec89b 100644
Binary files a/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png and b/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ
diff --git a/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
index 931b4c25b..c19c31bec 100644
Binary files a/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png and b/ui/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ
diff --git a/ui/flutter/lib/api/model/downloader_config.dart b/ui/flutter/lib/api/model/downloader_config.dart
index 01c596cb4..5cab59455 100644
--- a/ui/flutter/lib/api/model/downloader_config.dart
+++ b/ui/flutter/lib/api/model/downloader_config.dart
@@ -78,6 +78,7 @@ class ExtraConfig {
@JsonSerializable()
class ProxyConfig {
bool enable;
+ bool system;
String scheme;
String host;
String usr;
@@ -85,6 +86,7 @@ class ProxyConfig {
ProxyConfig({
this.enable = false,
+ this.system = false,
this.scheme = '',
this.host = '',
this.usr = '',
diff --git a/ui/flutter/lib/api/model/downloader_config.g.dart b/ui/flutter/lib/api/model/downloader_config.g.dart
index 6cd41ae61..662ad4836 100644
--- a/ui/flutter/lib/api/model/downloader_config.g.dart
+++ b/ui/flutter/lib/api/model/downloader_config.g.dart
@@ -9,7 +9,7 @@ part of 'downloader_config.dart';
DownloaderConfig _$DownloaderConfigFromJson(Map json) =>
DownloaderConfig(
downloadDir: json['downloadDir'] as String? ?? '',
- maxRunning: json['maxRunning'] as int? ?? 0,
+ maxRunning: (json['maxRunning'] as num?)?.toInt() ?? 0,
)
..protocolConfig = ProtocolConfig.fromJson(
json['protocolConfig'] as Map?)
@@ -39,7 +39,7 @@ Map _$ProtocolConfigToJson(ProtocolConfig instance) =>
HttpConfig _$HttpConfigFromJson(Map json) => HttpConfig(
userAgent: json['userAgent'] as String? ??
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36',
- connections: json['connections'] as int? ?? 16,
+ connections: (json['connections'] as num?)?.toInt() ?? 16,
useServerCtime: json['useServerCtime'] as bool? ?? false,
);
@@ -51,7 +51,7 @@ Map _$HttpConfigToJson(HttpConfig instance) =>
};
BtConfig _$BtConfigFromJson(Map json) => BtConfig()
- ..listenPort = json['listenPort'] as int
+ ..listenPort = (json['listenPort'] as num).toInt()
..trackers =
(json['trackers'] as List).map((e) => e as String).toList();
@@ -74,6 +74,7 @@ Map _$ExtraConfigToJson(ExtraConfig instance) =>
ProxyConfig _$ProxyConfigFromJson(Map json) => ProxyConfig(
enable: json['enable'] as bool? ?? false,
+ system: json['system'] as bool? ?? false,
scheme: json['scheme'] as String? ?? '',
host: json['host'] as String? ?? '',
usr: json['usr'] as String? ?? '',
@@ -83,6 +84,7 @@ ProxyConfig _$ProxyConfigFromJson(Map json) => ProxyConfig(
Map _$ProxyConfigToJson(ProxyConfig instance) =>
{
'enable': instance.enable,
+ 'system': instance.system,
'scheme': instance.scheme,
'host': instance.host,
'usr': instance.usr,
diff --git a/ui/flutter/lib/api/model/options.g.dart b/ui/flutter/lib/api/model/options.g.dart
index b496e9fc6..40a1a8a4e 100644
--- a/ui/flutter/lib/api/model/options.g.dart
+++ b/ui/flutter/lib/api/model/options.g.dart
@@ -10,7 +10,7 @@ Options _$OptionsFromJson(Map json) => Options(
name: json['name'] as String,
path: json['path'] as String,
selectFiles: (json['selectFiles'] as List?)
- ?.map((e) => e as int)
+ ?.map((e) => (e as num).toInt())
.toList() ??
const [],
extra: json['extra'],
@@ -35,7 +35,7 @@ Map _$OptionsToJson(Options instance) {
OptsExtraHttp _$OptsExtraHttpFromJson(Map json) =>
OptsExtraHttp()
- ..connections = json['connections'] as int
+ ..connections = (json['connections'] as num).toInt()
..autoTorrent = json['autoTorrent'] as bool;
Map _$OptsExtraHttpToJson(OptsExtraHttp instance) =>
diff --git a/ui/flutter/lib/api/model/resource.g.dart b/ui/flutter/lib/api/model/resource.g.dart
index 2feddd86d..0a5e8fc8a 100644
--- a/ui/flutter/lib/api/model/resource.g.dart
+++ b/ui/flutter/lib/api/model/resource.g.dart
@@ -8,7 +8,7 @@ part of 'resource.dart';
Resource _$ResourceFromJson(Map 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)
.map((e) => FileInfo.fromJson(e as Map))
@@ -27,7 +27,7 @@ Map _$ResourceToJson(Resource instance) => {
FileInfo _$FileInfoFromJson(Map 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),
diff --git a/ui/flutter/lib/api/model/result.g.dart b/ui/flutter/lib/api/model/result.g.dart
index 452a7c7ca..03d4f734e 100644
--- a/ui/flutter/lib/api/model/result.g.dart
+++ b/ui/flutter/lib/api/model/result.g.dart
@@ -11,7 +11,7 @@ Result _$ResultFromJson(
T Function(Object? json) fromJsonT,
) =>
Result(
- code: json['code'] as int,
+ code: (json['code'] as num).toInt(),
msg: json['msg'] as String?,
data: _$nullableGenericFromJson(json['data'], fromJsonT),
);
diff --git a/ui/flutter/lib/api/model/task.g.dart b/ui/flutter/lib/api/model/task.g.dart
index b5974a168..c0a77b956 100644
--- a/ui/flutter/lib/api/model/task.g.dart
+++ b/ui/flutter/lib/api/model/task.g.dart
@@ -34,9 +34,9 @@ const _$StatusEnumMap = {
};
Progress _$ProgressFromJson(Map json) => Progress(
- used: json['used'] as int,
- speed: json['speed'] as int,
- downloaded: json['downloaded'] as int,
+ used: (json['used'] as num).toInt(),
+ speed: (json['speed'] as num).toInt(),
+ downloaded: (json['downloaded'] as num).toInt(),
);
Map _$ProgressToJson(Progress instance) => {
diff --git a/ui/flutter/lib/app/modules/setting/views/setting_view.dart b/ui/flutter/lib/app/modules/setting/views/setting_view.dart
index 0340da0ed..02bfbbbd3 100644
--- a/ui/flutter/lib/app/modules/setting/views/setting_view.dart
+++ b/ui/flutter/lib/app/modules/setting/views/setting_view.dart
@@ -7,6 +7,7 @@ import 'package:get/get.dart';
import 'package:intl/intl.dart';
import 'package:url_launcher/url_launcher.dart';
+import '../../../../api/model/downloader_config.dart';
import '../../../../i18n/message.dart';
import '../../../../util/input_formatter.dart';
import '../../../../util/locale_manager.dart';
@@ -408,29 +409,53 @@ class SettingView extends GetView {
}
// advanced config proxy items start
+ final proxy = downloaderCfg.value.proxy;
final buildProxy = _buildConfigItem(
'proxy',
- () => downloaderCfg.value.proxy.enable
- ? '${downloaderCfg.value.proxy.scheme}://${downloaderCfg.value.proxy.host}'
- : 'notSet'.tr,
+ () {
+ switch (proxy.proxyMode) {
+ case ProxyModeEnum.noProxy:
+ return 'noProxy'.tr;
+ case ProxyModeEnum.systemProxy:
+ return 'systemProxy'.tr;
+ case ProxyModeEnum.customProxy:
+ return '${downloaderCfg.value.proxy.scheme}://${downloaderCfg.value.proxy.host}';
+ }
+ },
(Key key) {
- final proxy = downloaderCfg.value.proxy;
-
- final switcher = Switch(
- value: proxy.enable,
- onChanged: (bool value) async {
- if (value != proxy.enable) {
- downloaderCfg.update((val) {
- val!.proxy.enable = value;
- });
+ final mode = SizedBox(
+ width: 150,
+ child: DropdownButtonFormField(
+ value: proxy.proxyMode,
+ onChanged: (value) async {
+ if (value != null && value != proxy.proxyMode) {
+ proxy.proxyMode = value;
+ downloaderCfg.update((val) {
+ val!.proxy = proxy;
+ });
- await debounceSave();
- }
- },
+ await debounceSave();
+ }
+ },
+ items: [
+ DropdownMenuItem(
+ value: ProxyModeEnum.noProxy,
+ child: Text('noProxy'.tr),
+ ),
+ DropdownMenuItem(
+ value: ProxyModeEnum.systemProxy,
+ child: Text('systemProxy'.tr),
+ ),
+ DropdownMenuItem(
+ value: ProxyModeEnum.customProxy,
+ child: Text('customProxy'.tr),
+ ),
+ ],
+ ),
);
final scheme = SizedBox(
- width: 100,
+ width: 150,
child: DropdownButtonFormField(
value: proxy.scheme,
onChanged: (value) async {
@@ -478,7 +503,7 @@ class SettingView extends GetView {
ipController.addListener(updateAddress);
portController.addListener(updateAddress);
- final server = [
+ final server = Row(children: [
Flexible(
child: TextFormField(
controller: ipController,
@@ -503,12 +528,12 @@ class SettingView extends GetView {
],
),
),
- ];
+ ]);
final usrController = TextEditingController(text: proxy.usr);
final pwdController = TextEditingController(text: proxy.pwd);
- final auth = [
+ final auth = Row(children: [
Flexible(
child: TextFormField(
controller: usrController,
@@ -528,20 +553,21 @@ class SettingView extends GetView {
),
),
),
- ];
+ ]);
+
+ List customView() {
+ if (proxy.proxyMode != ProxyModeEnum.customProxy) {
+ return [];
+ }
+ return [scheme, server, auth];
+ }
return Form(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: _addPadding([
- switcher,
- scheme,
- Row(
- children: server,
- ),
- Row(
- children: auth,
- ),
+ mode,
+ ...customView(),
]),
),
);
@@ -857,3 +883,37 @@ class SettingView extends GetView {
}
}
}
+
+enum ProxyModeEnum {
+ noProxy,
+ systemProxy,
+ customProxy,
+}
+
+extension ProxyMode on ProxyConfig {
+ ProxyModeEnum get proxyMode {
+ if (!enable) {
+ return ProxyModeEnum.noProxy;
+ }
+ if (system) {
+ return ProxyModeEnum.systemProxy;
+ }
+ return ProxyModeEnum.customProxy;
+ }
+
+ set proxyMode(ProxyModeEnum value) {
+ switch (value) {
+ case ProxyModeEnum.noProxy:
+ enable = false;
+ break;
+ case ProxyModeEnum.systemProxy:
+ enable = true;
+ system = true;
+ break;
+ case ProxyModeEnum.customProxy:
+ enable = true;
+ system = false;
+ break;
+ }
+ }
+}
diff --git a/ui/flutter/lib/core/common/start_config.g.dart b/ui/flutter/lib/core/common/start_config.g.dart
index 8732accb0..85e67b856 100644
--- a/ui/flutter/lib/core/common/start_config.g.dart
+++ b/ui/flutter/lib/core/common/start_config.g.dart
@@ -11,7 +11,7 @@ StartConfig _$StartConfigFromJson(Map json) => StartConfig()
..address = json['address'] as String
..storage = json['storage'] as String
..storageDir = json['storageDir'] as String
- ..refreshInterval = json['refreshInterval'] as int
+ ..refreshInterval = (json['refreshInterval'] as num).toInt()
..apiToken = json['apiToken'] as String;
Map _$StartConfigToJson(StartConfig instance) =>
diff --git a/ui/flutter/lib/i18n/langs/en_us.dart b/ui/flutter/lib/i18n/langs/en_us.dart
index 0c64beb57..22aa8a75e 100644
--- a/ui/flutter/lib/i18n/langs/en_us.dart
+++ b/ui/flutter/lib/i18n/langs/en_us.dart
@@ -82,6 +82,9 @@ const enUS = {
'serviceText': 'Running',
'network': 'Network',
'proxy': 'Proxy',
+ 'noProxy': 'No Proxy',
+ 'systemProxy': 'System Proxy',
+ 'customProxy': 'Custom Proxy',
'server': 'Server',
'username': 'Username',
'password': 'Password',
diff --git a/ui/flutter/lib/i18n/langs/zh_cn.dart b/ui/flutter/lib/i18n/langs/zh_cn.dart
index a18de6a07..188d3eab6 100644
--- a/ui/flutter/lib/i18n/langs/zh_cn.dart
+++ b/ui/flutter/lib/i18n/langs/zh_cn.dart
@@ -80,6 +80,9 @@ const zhCN = {
'serviceText': '运行中',
'network': '网络',
'proxy': '代理',
+ 'noProxy': '不使用代理',
+ 'systemProxy': '系统代理',
+ 'customProxy': '自定义代理',
'server': '服务器',
'username': '用户名',
'password': '密码',
diff --git a/ui/flutter/lib/i18n/langs/zh_tw.dart b/ui/flutter/lib/i18n/langs/zh_tw.dart
index 30c6ff66c..044227f26 100644
--- a/ui/flutter/lib/i18n/langs/zh_tw.dart
+++ b/ui/flutter/lib/i18n/langs/zh_tw.dart
@@ -80,6 +80,9 @@ const zhTW = {
'serviceText': '執行中',
'network': '網路',
'proxy': '代理',
+ 'noProxy': '不使用代理',
+ 'systemProxy': '系統代理',
+ 'customProxy': '自定義代理',
'server': '伺服器',
'username': '名稱',
'password': '密碼',
diff --git a/ui/flutter/linux/packaging/deb/make_config.yaml b/ui/flutter/linux/packaging/deb/make_config.yaml
index da58a5da9..a753242c0 100644
--- a/ui/flutter/linux/packaging/deb/make_config.yaml
+++ b/ui/flutter/linux/packaging/deb/make_config.yaml
@@ -13,8 +13,8 @@ essential: false
icon: assets/icon/icon.svg
dependencies:
- - libayatana-appindicator3-1
- - gir1.2-ayatanaappindicator3-0.1
+ - libappindicator3-1 | libayatana-appindicator3-1
+ - gir1.2-appindicator3-0.1 | gir1.2-ayatanaappindicator3-0.1
keywords:
- Application
diff --git a/ui/flutter/linux/packaging/rpm/make_config.yaml b/ui/flutter/linux/packaging/rpm/make_config.yaml
new file mode 100644
index 000000000..774e97284
--- /dev/null
+++ b/ui/flutter/linux/packaging/rpm/make_config.yaml
@@ -0,0 +1,24 @@
+display_name: Gopeed
+icon: assets/icon/icon.svg
+summary: A modern download manager that supports all platforms. Built with Golang and Flutter.
+group: Applications/Internet
+vendor: monkeyWie
+packager: madoka773
+packagerEmail: valigarmanda55@gmail.com
+license: GPL-3.0-or-later
+url: https://github.com/GopeedLab/gopeed
+
+description: A modern download manager that supports all platforms. Built with Golang and Flutter.
+keywords:
+ - Application
+ - DownloadManager
+ - Network
+ - Utility
+
+generic_name: Download Manager
+
+categories:
+ - Network
+ - Utility
+
+startup_notify: true