Compare commits

...
Author SHA1 Message Date
liwei c7fefd2b9b fix: test 2024-05-01 18:39:24 +08:00
liwei b8e36b483e fix: test 2024-05-01 18:38:13 +08:00
liwei 91ac841b9d setting ui 2024-05-01 18:35:32 +08:00
liwei 5134446c3f fix: test 2024-05-01 18:35:32 +08:00
liwei 82b6ebd6f1 feat: support proxy from system env 2024-05-01 18:35:32 +08:00
liwei f8ea009fe9 temp 2024-05-01 18:35:32 +08:00
Levi 070db64ca9 fix: http request cookie incremented indefinitely on retries (#514) 2024-05-01 17:45:59 +08:00
Levi 6a8956b04d docs: fix linux download link 2024-05-01 04:16:56 +00:00
Deepansh Singh Arya 057504bb81 feat: updated iOS icons (#512) 2024-05-01 10:48:35 +08:00
madoka773 470b488824 ci: build gopeed with libayatana-appindicator (#508) 2024-04-26 15:41:47 +08:00
madoka773 c8c9df1ae8 ci: build linux with ubuntu latest (#501) 2024-04-23 08:57:23 +08:00
invictus1032 33d787c74e docs: fix Linux download links (#465)
Fix filename discrepancies for .deb and .AppImage
2024-04-12 14:45:09 +08:00
liwei 67338a5e83 docs: add browser extension link 2024-04-03 17:52:37 +08:00
Levi 723697192d fix: command panic before resolve resource (#451) 2024-04-03 16:56:20 +08:00
Zack 4294d07d6a fix: Fix the issue of installing apk files(Android) (#448) (#449) 2024-04-03 09:23:05 +08:00
WINZORT 3936fd786f feat: Update tr_tr.dart (#445) 2024-04-01 09:07:44 +08:00
ilkeiii 45197e9a3b fix: docker entrypoint_sh (#434) 2024-03-30 12:35:00 +08:00
64 changed files with 370 additions and 204 deletions

No files matched your search

+2 -4
View File
@@ -215,7 +215,7 @@ jobs:
overwrite: true overwrite: true
build-linux: build-linux:
if: ${{ github.event.inputs.platform == 'all' || github.event.inputs.platform == 'linux' }} if: ${{ github.event.inputs.platform == 'all' || github.event.inputs.platform == 'linux' }}
runs-on: ubuntu-20.04 runs-on: ubuntu-latest
needs: [ get-release ] needs: [ get-release ]
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
@@ -228,9 +228,7 @@ jobs:
- run: | - run: |
sudo apt update -y sudo apt update -y
# flutter build dependencies # flutter build dependencies
sudo apt install -y ninja-build libgtk-3-dev libappindicator3-dev sudo apt install -y ninja-build libgtk-3-dev libayatana-appindicator3-1 libayatana-appindicator3-dev
# rpm build dependencies
sudo apt install -y rpm patchelf
# appimage build dependencies # appimage build dependencies
sudo apt install -y libfuse2 locate sudo apt install -y libfuse2 locate
wget -O appimagetool "https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage" wget -O appimagetool "https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage"
+6 -2
View File
@@ -53,11 +53,11 @@ Visit ✈ [Official Website](https://gopeed.com) | 📖 [Official Docs](https://
</tr> </tr>
<tr> <tr>
<td><code>DEB</code></td> <td><code>DEB</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux.deb">Link</a></td> <td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-amd64.deb">Link</a></td>
</tr> </tr>
<tr> <tr>
<td><code>AppImage</code></td> <td><code>AppImage</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-amd64.AppImage">Link</a></td> <td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-x86_64.AppImage">Link</a></td>
</tr> </tr>
<tr> <tr>
<td>Android</td> <td>Android</td>
@@ -92,6 +92,10 @@ use `go install`:
go install github.com/GopeedLab/gopeed/cmd/gopeed@latest go install github.com/GopeedLab/gopeed/cmd/gopeed@latest
``` ```
## Browser Extension
Gopeed also provides a browser extension to take over browser downloads, supporting browsers such as Chrome, Edge, Firefox, etc., please refer to: [https://github.com/GopeedLab/browser-extension](https://github.com/GopeedLab/browser-extension)
## Donate ## Donate
If you like this project, please consider [donating](https://docs.gopeed.com/donate.html) to support the development of this project, thank you! If you like this project, please consider [donating](https://docs.gopeed.com/donate.html) to support the development of this project, thank you!
+2 -2
View File
@@ -53,11 +53,11 @@ Gopeed (正式名 Go Speed) は `Golang` + `Flutter` によって開発された
</tr> </tr>
<tr> <tr>
<td><code>DEB</code></td> <td><code>DEB</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux.deb">Link</a></td> <td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-amd64.deb">Link</a></td>
</tr> </tr>
<tr> <tr>
<td><code>AppImage</code></td> <td><code>AppImage</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-amd64.AppImage">Link</a></td> <td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-x86_64.AppImage">Link</a></td>
</tr> </tr>
<tr> <tr>
<td>Android</td> <td>Android</td>
+2 -2
View File
@@ -53,11 +53,11 @@ Truy cập ✈ [Trang web chính thức](https://gopeed.com) | 📖 [Tài liệu
</tr> </tr>
<tr> <tr>
<td><code>DEB</code></td> <td><code>DEB</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux.deb">Liên kết</a></td> <td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-amd64.deb">Liên kết</a></td>
</tr> </tr>
<tr> <tr>
<td><code>AppImage</code></td> <td><code>AppImage</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-amd64.AppImage">Liên kết</a></td> <td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-x86_64.AppImage">Liên kết</a></td>
</tr> </tr>
<tr> <tr>
<td>Android</td> <td>Android</td>
+6 -2
View File
@@ -53,11 +53,11 @@ Gopeed(全称 Go Speed),直译过来中文名叫做`够快下载器`(不
</tr> </tr>
<tr> <tr>
<td><code>DEB</code></td> <td><code>DEB</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux.deb">前往</a></td> <td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-amd64.deb">前往</a></td>
</tr> </tr>
<tr> <tr>
<td><code>AppImage</code></td> <td><code>AppImage</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-amd64.AppImage">前往</a></td> <td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-x86_64.AppImage">前往</a></td>
</tr> </tr>
<tr> <tr>
<td>Android</td> <td>Android</td>
@@ -92,6 +92,10 @@ Gopeed(全称 Go Speed),直译过来中文名叫做`够快下载器`(不
go install github.com/GopeedLab/gopeed/cmd/gopeed@latest go install github.com/GopeedLab/gopeed/cmd/gopeed@latest
``` ```
## 浏览器扩展
Gopeed 还提供了浏览器扩展用于接管浏览器下载,支持 Chrome、Edge、Firefox 等浏览器,具体请参考:[https://github.com/GopeedLab/browser-extension](https://github.com/GopeedLab/browser-extension)
## 赞助 ## 赞助
如果觉得项目对你有帮助,请考虑[赞助](https://docs.gopeed.com/zh/donate)以支持这个项目的发展,非常感谢! 如果觉得项目对你有帮助,请考虑[赞助](https://docs.gopeed.com/zh/donate)以支持这个项目的发展,非常感谢!
+2 -2
View File
@@ -53,11 +53,11 @@ Gopeed(全稱 Go Speed),是一款使用`Golang`+`Flutter`編寫的高速
</tr> </tr>
<tr> <tr>
<td><code>DEB</code></td> <td><code>DEB</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux.deb">前往</a></td> <td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-amd64.deb">前往</a></td>
</tr> </tr>
<tr> <tr>
<td><code>AppImage</code></td> <td><code>AppImage</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-amd64.AppImage">前往</a></td> <td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-x86_64.AppImage">前往</a></td>
</tr> </tr>
<tr> <tr>
<td>Android</td> <td>Android</td>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 309 KiB

+3
View File
@@ -65,6 +65,9 @@ var (
func printProgress(task *download.Task, title string) { func printProgress(task *download.Task, title string) {
var rate float64 var rate float64
if task.Meta.Res == nil {
task = emptyTask
}
if task.Meta.Res.Size <= 0 { if task.Meta.Res.Size <= 0 {
rate = 0 rate = 0
} else { } else {
+1 -1
View File
@@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
chown -R ${PUID}:${PGID} /app/Download chown -R ${PUID}:${PGID} /app
umask ${UMASK} umask ${UMASK}
+1
View File
@@ -69,6 +69,7 @@ require (
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/mattn/go-colorable v0.1.13 // 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/mattn/go-isatty v0.0.20 // indirect
github.com/mschoch/smat v0.2.0 // indirect github.com/mschoch/smat v0.2.0 // indirect
github.com/pion/datachannel v1.5.5 // indirect github.com/pion/datachannel v1.5.5 // indirect
+2
View File
@@ -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/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 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 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.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.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
+3 -3
View File
@@ -1,14 +1,14 @@
package controller package controller
import ( import (
"net/url" "github.com/GopeedLab/gopeed/pkg/base"
"os" "os"
"path/filepath" "path/filepath"
) )
type Controller struct { type Controller struct {
GetConfig func(v any) bool GetConfig func(v any) bool
ProxyUrl *url.URL ProxyConfig *base.DownloaderProxyConfig
FileController FileController
//ContextDialer() (proxy.Dialer, error) //ContextDialer() (proxy.Dialer, error)
} }
+1 -4
View File
@@ -10,7 +10,6 @@ import (
"github.com/GopeedLab/gopeed/pkg/util" "github.com/GopeedLab/gopeed/pkg/util"
"github.com/anacrolix/torrent" "github.com/anacrolix/torrent"
"github.com/anacrolix/torrent/metainfo" "github.com/anacrolix/torrent/metainfo"
"net/http"
"path/filepath" "path/filepath"
"strings" "strings"
"sync" "sync"
@@ -69,9 +68,7 @@ func (f *Fetcher) initClient() (err error) {
cfg.Bep20 = fmt.Sprintf("-GP%s-", parseBep20()) cfg.Bep20 = fmt.Sprintf("-GP%s-", parseBep20())
cfg.ExtendedHandshakeClientVersion = fmt.Sprintf("Gopeed %s", base.Version) cfg.ExtendedHandshakeClientVersion = fmt.Sprintf("Gopeed %s", base.Version)
cfg.ListenPort = f.config.ListenPort cfg.ListenPort = f.config.ListenPort
if f.ctl.ProxyUrl != nil { cfg.HTTPProxy = f.ctl.ProxyConfig.ToHandler()
cfg.HTTPProxy = http.ProxyURL(f.ctl.ProxyUrl)
}
cfg.DefaultStorage = newFileOpts(newFileClientOpts{ cfg.DefaultStorage = newFileOpts(newFileClientOpts{
ClientBaseDir: cfg.DataDir, ClientBaseDir: cfg.DataDir,
HandleFileTorrent: func(infoHash metainfo.Hash, ft *fileTorrentImpl) { HandleFileTorrent: func(infoHash metainfo.Hash, ft *fileTorrentImpl) {
+10 -5
View File
@@ -8,8 +8,6 @@ import (
"github.com/GopeedLab/gopeed/internal/test" "github.com/GopeedLab/gopeed/internal/test"
"github.com/GopeedLab/gopeed/pkg/base" "github.com/GopeedLab/gopeed/pkg/base"
"github.com/GopeedLab/gopeed/pkg/protocol/bt" "github.com/GopeedLab/gopeed/pkg/protocol/bt"
"github.com/GopeedLab/gopeed/pkg/util"
"net/url"
"os" "os"
"reflect" "reflect"
"testing" "testing"
@@ -60,7 +58,14 @@ func TestFetcher_ResolveWithProxy(t *testing.T) {
proxyListener := test.StartSocks5Server(usr, pwd) proxyListener := test.StartSocks5Server(usr, pwd)
defer proxyListener.Close() 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) { func doResolve(t *testing.T, fetcher fetcher.Fetcher) {
@@ -100,7 +105,7 @@ func buildFetcher() fetcher.Fetcher {
return fetcher return fetcher
} }
func buildConfigFetcher(proxyUrl *url.URL) fetcher.Fetcher { func buildConfigFetcher(proxyConfig *base.DownloaderProxyConfig) fetcher.Fetcher {
fetcher := new(FetcherBuilder).Build() fetcher := new(FetcherBuilder).Build()
newController := controller.NewController() newController := controller.NewController()
mockCfg := config{ mockCfg := config{
@@ -114,7 +119,7 @@ func buildConfigFetcher(proxyUrl *url.URL) fetcher.Fetcher {
} }
return true return true
} }
newController.ProxyUrl = proxyUrl newController.ProxyConfig = proxyConfig
fetcher.Setup(newController) fetcher.Setup(newController)
return fetcher return fetcher
} }
+9 -9
View File
@@ -280,10 +280,6 @@ func (f *Fetcher) fetchChunk(index int, ctx context.Context) (err error) {
chunk := f.chunks[index] chunk := f.chunks[index]
chunk.retryTimes = 0 chunk.retryTimes = 0
httpReq, err := f.buildRequest(ctx, f.meta.Req)
if err != nil {
return err
}
var ( var (
client = f.buildClient() client = f.buildClient()
buf = make([]byte, 8192) buf = make([]byte, 8192)
@@ -314,8 +310,13 @@ func (f *Fetcher) fetchChunk(index int, ctx context.Context) (err error) {
} }
var ( var (
resp *http.Response httpReq *http.Request
resp *http.Response
) )
httpReq, err = f.buildRequest(ctx, f.meta.Req)
if err != nil {
return
}
if f.meta.Res.Range { if f.meta.Res.Range {
httpReq.Header.Set(base.HttpHeaderRange, httpReq.Header.Set(base.HttpHeaderRange,
fmt.Sprintf(base.HttpHeaderRangeFormat, chunk.Begin+chunk.Downloaded, chunk.End)) fmt.Sprintf(base.HttpHeaderRangeFormat, chunk.Begin+chunk.Downloaded, chunk.End))
@@ -450,12 +451,11 @@ func (f *Fetcher) splitChunk() (chunks []*chunk) {
} }
func (f *Fetcher) buildClient() *http.Client { func (f *Fetcher) buildClient() *http.Client {
transport := &http.Transport{} transport := &http.Transport{
Proxy: f.ctl.ProxyConfig.ToHandler(),
}
// Cookie handle // Cookie handle
jar, _ := cookiejar.New(nil) jar, _ := cookiejar.New(nil)
if f.ctl.ProxyUrl != nil {
transport.Proxy = http.ProxyURL(f.ctl.ProxyUrl)
}
return &http.Client{ return &http.Client{
Transport: transport, Transport: transport,
Jar: jar, Jar: jar,
+5 -2
View File
@@ -8,7 +8,6 @@ import (
"github.com/GopeedLab/gopeed/internal/test" "github.com/GopeedLab/gopeed/internal/test"
"github.com/GopeedLab/gopeed/pkg/base" "github.com/GopeedLab/gopeed/pkg/base"
"github.com/GopeedLab/gopeed/pkg/protocol/http" "github.com/GopeedLab/gopeed/pkg/protocol/http"
"github.com/GopeedLab/gopeed/pkg/util"
"net" "net"
"testing" "testing"
"time" "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) { func downloadWithProxy(httpListener net.Listener, proxyListener net.Listener, t *testing.T) {
fetcher := downloadReady(httpListener, 4, t) fetcher := downloadReady(httpListener, 4, t)
ctl := controller.NewController() 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) fetcher.Setup(ctl)
err := fetcher.Start() err := fetcher.Start()
if err != nil { if err != nil {
+5
View File
@@ -24,6 +24,11 @@ const (
Dir = "./" Dir = "./"
BuildFile = Dir + BuildName 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" DownloadName = "download.data"
DownloadRename = "download (1).data" DownloadRename = "download (1).data"
DownloadFile = Dir + DownloadName DownloadFile = Dir + DownloadName
+88
View File
@@ -3,7 +3,10 @@ package base
import ( import (
"fmt" "fmt"
"github.com/GopeedLab/gopeed/pkg/util" "github.com/GopeedLab/gopeed/pkg/util"
"github.com/mattn/go-ieproxy"
"golang.org/x/exp/slices" "golang.org/x/exp/slices"
"net/http"
"net/url"
"time" "time"
) )
@@ -128,3 +131,88 @@ func ParseOptsExtra[E any](opts *Options) error {
opts.Extra = &t opts.Extra = &t
return nil 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
}
+5 -5
View File
@@ -110,7 +110,7 @@ func (d *Downloader) Setup() error {
return err return err
} }
// load config from storage // load config from storage
var cfg DownloaderStoreConfig var cfg base.DownloaderStoreConfig
exist, err := d.storage.Get(bucketConfig, "config", &cfg) exist, err := d.storage.Get(bucketConfig, "config", &cfg)
if err != nil { if err != nil {
return err return err
@@ -118,7 +118,7 @@ func (d *Downloader) Setup() error {
if exist { if exist {
d.cfg.DownloaderStoreConfig = &cfg d.cfg.DownloaderStoreConfig = &cfg
} else { } else {
d.cfg.DownloaderStoreConfig = &DownloaderStoreConfig{ d.cfg.DownloaderStoreConfig = &base.DownloaderStoreConfig{
FirstLoad: true, FirstLoad: true,
} }
} }
@@ -225,7 +225,7 @@ func (d *Downloader) setupFetcher(fetcher fetcher.Fetcher) {
ctl.GetConfig = func(v any) bool { ctl.GetConfig = func(v any) bool {
return d.getProtocolConfig(fetcher.Name(), v) return d.getProtocolConfig(fetcher.Name(), v)
} }
ctl.ProxyUrl = d.cfg.ProxyUrl() ctl.ProxyConfig = d.cfg.Proxy
fetcher.Setup(ctl) fetcher.Setup(ctl)
} }
@@ -603,11 +603,11 @@ func (d *Downloader) GetTasksByStatues(statues []base.Status) []*Task {
return tasks return tasks
} }
func (d *Downloader) GetConfig() (*DownloaderStoreConfig, error) { func (d *Downloader) GetConfig() (*base.DownloaderStoreConfig, error) {
return d.cfg.DownloaderStoreConfig, nil return d.cfg.DownloaderStoreConfig, nil
} }
func (d *Downloader) PutConfig(v *DownloaderStoreConfig) error { func (d *Downloader) PutConfig(v *base.DownloaderStoreConfig) error {
d.cfg.DownloaderStoreConfig = v d.cfg.DownloaderStoreConfig = v
return d.storage.Put(bucketConfig, "config", v) return d.storage.Put(bucketConfig, "config", v)
} }
+47 -21
View File
@@ -4,6 +4,7 @@ import (
"github.com/GopeedLab/gopeed/internal/test" "github.com/GopeedLab/gopeed/internal/test"
"github.com/GopeedLab/gopeed/pkg/base" "github.com/GopeedLab/gopeed/pkg/base"
"github.com/GopeedLab/gopeed/pkg/protocol/http" "github.com/GopeedLab/gopeed/pkg/protocol/http"
"os"
"sync" "sync"
"testing" "testing"
"time" "time"
@@ -85,37 +86,58 @@ func TestDownloader_Create(t *testing.T) {
func TestDownloader_CreateWithProxy(t *testing.T) { func TestDownloader_CreateWithProxy(t *testing.T) {
// No proxy // No proxy
doTestDownloaderCreateWithProxy(t, false, func(proxyCfg *DownloaderProxyConfig) *DownloaderProxyConfig { doTestDownloaderCreateWithProxy(t, false, func(proxyCfg *base.DownloaderProxyConfig) *base.DownloaderProxyConfig {
return nil return nil
}) }, nil)
// Disable proxy // Disable proxy
doTestDownloaderCreateWithProxy(t, false, func(proxyCfg *DownloaderProxyConfig) *DownloaderProxyConfig { doTestDownloaderCreateWithProxy(t, false, func(proxyCfg *base.DownloaderProxyConfig) *base.DownloaderProxyConfig {
proxyCfg.Enable = false proxyCfg.Enable = false
return proxyCfg 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 // Invalid proxy scheme
doTestDownloaderCreateWithProxy(t, false, func(proxyCfg *DownloaderProxyConfig) *DownloaderProxyConfig { doTestDownloaderCreateWithProxy(t, false, func(proxyCfg *base.DownloaderProxyConfig) *base.DownloaderProxyConfig {
proxyCfg.Scheme = "" proxyCfg.Scheme = ""
return proxyCfg return proxyCfg
}) }, nil)
// Invalid proxy host // Invalid proxy host
doTestDownloaderCreateWithProxy(t, false, func(proxyCfg *DownloaderProxyConfig) *DownloaderProxyConfig { doTestDownloaderCreateWithProxy(t, false, func(proxyCfg *base.DownloaderProxyConfig) *base.DownloaderProxyConfig {
proxyCfg.Host = "" proxyCfg.Host = ""
return proxyCfg return proxyCfg
}) }, nil)
// Use proxy without auth // Use proxy without auth
doTestDownloaderCreateWithProxy(t, false, func(proxyCfg *DownloaderProxyConfig) *DownloaderProxyConfig { doTestDownloaderCreateWithProxy(t, false, func(proxyCfg *base.DownloaderProxyConfig) *base.DownloaderProxyConfig {
return proxyCfg return proxyCfg
}) }, nil)
// Use proxy with auth // Use proxy with auth
doTestDownloaderCreateWithProxy(t, true, func(proxyCfg *DownloaderProxyConfig) *DownloaderProxyConfig { doTestDownloaderCreateWithProxy(t, true, func(proxyCfg *base.DownloaderProxyConfig) *base.DownloaderProxyConfig {
return proxyCfg return proxyCfg
}) }, nil)
} }
func doTestDownloaderCreateWithProxy(t *testing.T, auth bool, buildProxyConfig func(proxyCfg *DownloaderProxyConfig) *DownloaderProxyConfig) { func doTestDownloaderCreateWithProxy(t *testing.T, auth bool, buildProxyConfig func(proxyCfg *base.DownloaderProxyConfig) *base.DownloaderProxyConfig, errHandler func(err error)) {
httpListener := test.StartTestFileServer()
defer httpListener.Close()
usr, pwd := "", "" usr, pwd := "", ""
if auth { if auth {
usr, pwd = "admin", "123" usr, pwd = "admin", "123"
@@ -128,7 +150,7 @@ func doTestDownloaderCreateWithProxy(t *testing.T, auth bool, buildProxyConfig f
t.Fatal(err) t.Fatal(err)
} }
defer downloader.Clear() defer downloader.Clear()
downloader.cfg.DownloaderStoreConfig.Proxy = buildProxyConfig(&DownloaderProxyConfig{ downloader.cfg.DownloaderStoreConfig.Proxy = buildProxyConfig(&base.DownloaderProxyConfig{
Enable: true, Enable: true,
Scheme: "socks5", Scheme: "socks5",
Host: proxyListener.Addr().String(), Host: proxyListener.Addr().String(),
@@ -137,20 +159,24 @@ func doTestDownloaderCreateWithProxy(t *testing.T, auth bool, buildProxyConfig f
}) })
req := &base.Request{ req := &base.Request{
URL: "http://" + httpListener.Addr().String() + "/" + test.BuildName, URL: test.ExternalDownloadUrl,
} }
rr, err := downloader.Resolve(req) rr, err := downloader.Resolve(req)
if err != nil { if err != nil {
t.Fatal(err) if errHandler == nil {
t.Fatal(err)
}
errHandler(err)
return
} }
want := &base.Resource{ want := &base.Resource{
Size: test.BuildSize, Size: test.ExternalDownloadSize,
Range: true, Range: true,
Files: []*base.FileInfo{ Files: []*base.FileInfo{
{ {
Name: test.BuildName, Name: test.ExternalDownloadName,
Path: "", 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) t.Errorf("getProtocolConfig() got = %v, want %v", exits, false)
} }
storeCfg := &DownloaderStoreConfig{ storeCfg := &base.DownloaderStoreConfig{
DownloadDir: "./downloads", DownloadDir: "./downloads",
ProtocolConfig: map[string]any{ ProtocolConfig: map[string]any{
"http": map[string]any{ "http": map[string]any{
+3 -3
View File
@@ -3,6 +3,7 @@ package engine
import ( import (
_ "embed" _ "embed"
"errors" "errors"
"github.com/GopeedLab/gopeed/pkg/base"
gojaerror "github.com/GopeedLab/gopeed/pkg/download/engine/inject/error" 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/file"
"github.com/GopeedLab/gopeed/pkg/download/engine/inject/formdata" "github.com/GopeedLab/gopeed/pkg/download/engine/inject/formdata"
@@ -11,7 +12,6 @@ import (
"github.com/dop251/goja" "github.com/dop251/goja"
"github.com/dop251/goja_nodejs/eventloop" "github.com/dop251/goja_nodejs/eventloop"
gojaurl "github.com/dop251/goja_nodejs/url" gojaurl "github.com/dop251/goja_nodejs/url"
"net/url"
"time" "time"
) )
@@ -110,7 +110,7 @@ func (e *Engine) Close() {
} }
type Config struct { type Config struct {
ProxyURL *url.URL ProxyConfig *base.DownloaderProxyConfig
} }
func NewEngine(cfg *Config) *Engine { func NewEngine(cfg *Config) *Engine {
@@ -135,7 +135,7 @@ func NewEngine(cfg *Config) *Engine {
if err := formdata.Enable(runtime); err != nil { if err := formdata.Enable(runtime); err != nil {
return return
} }
if err := xhr.Enable(runtime, cfg.ProxyURL); err != nil { if err := xhr.Enable(runtime, cfg.ProxyConfig.ToHandler()); err != nil {
return return
} }
if _, err := runtime.RunString(polyfillScript); err != nil { if _, err := runtime.RunString(polyfillScript); err != nil {
+11 -3
View File
@@ -7,10 +7,10 @@ import (
"errors" "errors"
"fmt" "fmt"
"github.com/GopeedLab/gopeed/internal/test" "github.com/GopeedLab/gopeed/internal/test"
"github.com/GopeedLab/gopeed/pkg/base"
gojaerror "github.com/GopeedLab/gopeed/pkg/download/engine/inject/error" 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/file"
gojautil "github.com/GopeedLab/gopeed/pkg/download/engine/util" gojautil "github.com/GopeedLab/gopeed/pkg/download/engine/util"
"github.com/GopeedLab/gopeed/pkg/util"
"github.com/dop251/goja" "github.com/dop251/goja"
"io" "io"
"net" "net"
@@ -217,8 +217,16 @@ func doTestFetchWithProxy(t *testing.T, usr, pwd string) {
proxyListener := test.StartSocks5Server(usr, pwd) proxyListener := test.StartSocks5Server(usr, pwd)
defer proxyListener.Close() defer proxyListener.Close()
engine := NewEngine(&Config{
engine := NewEngine(&Config{ProxyURL: util.BuildProxyUrl("socks5", proxyListener.Addr().String(), usr, pwd)}) 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 { if _, err := engine.RunString(fmt.Sprintf("var host = 'http://%s';", httpListener.Addr().String())); err != nil {
t.Fatal(err) t.Fatal(err)
+8 -7
View File
@@ -2,6 +2,7 @@ package xhr
import ( import (
"bytes" "bytes"
"errors"
"github.com/GopeedLab/gopeed/pkg/download/engine/inject/file" "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/inject/formdata"
"github.com/GopeedLab/gopeed/pkg/download/engine/util" "github.com/GopeedLab/gopeed/pkg/download/engine/util"
@@ -120,7 +121,7 @@ type XMLHttpRequest struct {
requestHeaders map[string]string requestHeaders map[string]string
responseHeaders map[string]string responseHeaders map[string]string
aborted bool aborted bool
proxyUrl *url.URL proxyHandler func(r *http.Request) (*url.URL, error)
Upload *XMLHttpRequestUpload `json:"upload"` Upload *XMLHttpRequestUpload `json:"upload"`
Timeout int `json:"timeout"` Timeout int `json:"timeout"`
@@ -202,9 +203,8 @@ func (xhr *XMLHttpRequest) Send(data goja.Value) {
for k, v := range xhr.requestHeaders { for k, v := range xhr.requestHeaders {
req.Header.Set(k, v) req.Header.Set(k, v)
} }
transport := &http.Transport{} transport := &http.Transport{
if xhr.proxyUrl != nil { Proxy: xhr.proxyHandler,
transport.Proxy = http.ProxyURL(xhr.proxyUrl)
} }
client := &http.Client{ client := &http.Client{
Transport: transport, Transport: transport,
@@ -213,7 +213,8 @@ func (xhr *XMLHttpRequest) Send(data goja.Value) {
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
// handle timeout error // 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 { if xhr.Timeout > 0 {
xhr.Upload.callOntimeout() xhr.Upload.callOntimeout()
xhr.callOntimeout() xhr.callOntimeout()
@@ -313,7 +314,7 @@ func (xhr *XMLHttpRequest) parseData(data goja.Value) any {
return data.String() 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 { progressEvent := runtime.ToValue(func(call goja.ConstructorCall) *goja.Object {
if len(call.Arguments) < 1 { if len(call.Arguments) < 1 {
util.ThrowTypeError(runtime, "Failed to construct 'ProgressEvent': 1 argument required, but only 0 present.") 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 { xhr := runtime.ToValue(func(call goja.ConstructorCall) *goja.Object {
instance := &XMLHttpRequest{ instance := &XMLHttpRequest{
proxyUrl: proxyUrl, proxyHandler: proxyHandler,
Upload: &XMLHttpRequestUpload{ Upload: &XMLHttpRequestUpload{
EventProp: &EventProp{ EventProp: &EventProp{
eventListeners: make(map[string]func(event *ProgressEvent)), eventListeners: make(map[string]func(event *ProgressEvent)),
+2 -2
View File
@@ -199,7 +199,7 @@ func (d *Downloader) fetchExtensionByGit(url string, handler func(tempExtPath st
return nil, err return nil, err
} }
proxyOptions := transport.ProxyOptions{} proxyOptions := transport.ProxyOptions{}
proxyUrl := d.cfg.ProxyUrl() proxyUrl := d.cfg.DownloaderStoreConfig.Proxy.ToUrl()
if proxyUrl != nil { if proxyUrl != nil {
proxyOptions.URL = proxyUrl.Scheme + "://" + proxyUrl.Host proxyOptions.URL = proxyUrl.Scheme + "://" + proxyUrl.Host
proxyOptions.Username = proxyUrl.User.Username() 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) req.Labels = make(map[string]string)
} }
engine := engine.NewEngine(&engine.Config{ engine := engine.NewEngine(&engine.Config{
ProxyURL: d.cfg.ProxyUrl(), ProxyConfig: d.cfg.Proxy,
}) })
defer engine.Close() defer engine.Close()
err = engine.Runtime.Set("gopeed", gopeed) err = engine.Runtime.Set("gopeed", gopeed)
+1 -42
View File
@@ -7,9 +7,7 @@ import (
"github.com/GopeedLab/gopeed/internal/protocol/http" "github.com/GopeedLab/gopeed/internal/protocol/http"
"github.com/GopeedLab/gopeed/pkg/base" "github.com/GopeedLab/gopeed/pkg/base"
"github.com/GopeedLab/gopeed/pkg/util" "github.com/GopeedLab/gopeed/pkg/util"
gonanoid "github.com/matoous/go-nanoid/v2" gonanoid "github.com/matoous/go-nanoid/v2"
"net/url"
"sync" "sync"
"time" "time"
) )
@@ -87,7 +85,7 @@ type DownloaderConfig struct {
ProductionMode bool ProductionMode bool
*DownloaderStoreConfig *base.DownloaderStoreConfig
} }
func (cfg *DownloaderConfig) Init() *DownloaderConfig { func (cfg *DownloaderConfig) Init() *DownloaderConfig {
@@ -108,42 +106,3 @@ func (cfg *DownloaderConfig) Init() *DownloaderConfig {
} }
return cfg 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"`
}
+2 -2
View File
@@ -142,7 +142,7 @@ func GetConfig(w http.ResponseWriter, r *http.Request) {
} }
func PutConfig(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 ReadJson(r, w, &cfg) {
if err := Downloader.PutConfig(&cfg); err != nil { if err := Downloader.PutConfig(&cfg); err != nil {
WriteJson(w, model.NewErrorResult(err.Error())) WriteJson(w, model.NewErrorResult(err.Error()))
@@ -309,7 +309,7 @@ func writeError(w http.ResponseWriter, msg string) {
w.Write([]byte(msg)) w.Write([]byte(msg))
} }
func getServerConfig() *download.DownloaderStoreConfig { func getServerConfig() *base.DownloaderStoreConfig {
cfg, _ := Downloader.GetConfig() cfg, _ := Downloader.GetConfig()
return cfg return cfg
} }
+2 -2
View File
@@ -324,7 +324,7 @@ func TestGetTasks(t *testing.T) {
func TestGetAndPutConfig(t *testing.T) { func TestGetAndPutConfig(t *testing.T) {
doTest(func() { 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.DownloadDir = "./download"
cfg.Extra = map[string]any{ cfg.Extra = map[string]any{
"serverConfig": &Config{ "serverConfig": &Config{
@@ -335,7 +335,7 @@ func TestGetAndPutConfig(t *testing.T) {
} }
httpRequestCheckOk[any](http.MethodPut, "/api/v1/config", cfg) 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) { if !test.JsonEqual(cfg, newCfg) {
t.Errorf("GetAndPutConfig() got = %v, want %v", test.ToJson(newCfg), test.ToJson(cfg)) t.Errorf("GetAndPutConfig() got = %v, want %v", test.ToJson(newCfg), test.ToJson(cfg))
} }
+12
View File
@@ -2,6 +2,7 @@ package util
import ( import (
"encoding/base64" "encoding/base64"
"net/http"
"net/url" "net/url"
"regexp" "regexp"
"strings" "strings"
@@ -51,3 +52,14 @@ func BuildProxyUrl(scheme, host, usr, pwd string) *url.URL {
Host: host, 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)
}
@@ -2,6 +2,7 @@
package="com.gopeed"> package="com.gopeed">
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<application <application
android:name="${applicationName}" android:name="${applicationName}"
Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 714 B

After

Width:  |  Height:  |  Size: 890 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

@@ -78,6 +78,7 @@ class ExtraConfig {
@JsonSerializable() @JsonSerializable()
class ProxyConfig { class ProxyConfig {
bool enable; bool enable;
bool system;
String scheme; String scheme;
String host; String host;
String usr; String usr;
@@ -85,6 +86,7 @@ class ProxyConfig {
ProxyConfig({ ProxyConfig({
this.enable = false, this.enable = false,
this.system = false,
this.scheme = '', this.scheme = '',
this.host = '', this.host = '',
this.usr = '', this.usr = '',
@@ -9,7 +9,7 @@ part of 'downloader_config.dart';
DownloaderConfig _$DownloaderConfigFromJson(Map<String, dynamic> json) => DownloaderConfig _$DownloaderConfigFromJson(Map<String, dynamic> json) =>
DownloaderConfig( DownloaderConfig(
downloadDir: json['downloadDir'] as String? ?? '', downloadDir: json['downloadDir'] as String? ?? '',
maxRunning: json['maxRunning'] as int? ?? 0, maxRunning: (json['maxRunning'] as num?)?.toInt() ?? 0,
) )
..protocolConfig = ProtocolConfig.fromJson( ..protocolConfig = ProtocolConfig.fromJson(
json['protocolConfig'] as Map<String, dynamic>?) json['protocolConfig'] as Map<String, dynamic>?)
@@ -39,7 +39,7 @@ Map<String, dynamic> _$ProtocolConfigToJson(ProtocolConfig instance) =>
HttpConfig _$HttpConfigFromJson(Map<String, dynamic> json) => HttpConfig( HttpConfig _$HttpConfigFromJson(Map<String, dynamic> json) => HttpConfig(
userAgent: json['userAgent'] as String? ?? 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', '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, useServerCtime: json['useServerCtime'] as bool? ?? false,
); );
@@ -51,7 +51,7 @@ Map<String, dynamic> _$HttpConfigToJson(HttpConfig instance) =>
}; };
BtConfig _$BtConfigFromJson(Map<String, dynamic> json) => BtConfig() BtConfig _$BtConfigFromJson(Map<String, dynamic> json) => BtConfig()
..listenPort = json['listenPort'] as int ..listenPort = (json['listenPort'] as num).toInt()
..trackers = ..trackers =
(json['trackers'] as List<dynamic>).map((e) => e as String).toList(); (json['trackers'] as List<dynamic>).map((e) => e as String).toList();
@@ -74,6 +74,7 @@ Map<String, dynamic> _$ExtraConfigToJson(ExtraConfig instance) =>
ProxyConfig _$ProxyConfigFromJson(Map<String, dynamic> json) => ProxyConfig( ProxyConfig _$ProxyConfigFromJson(Map<String, dynamic> json) => ProxyConfig(
enable: json['enable'] as bool? ?? false, enable: json['enable'] as bool? ?? false,
system: json['system'] as bool? ?? false,
scheme: json['scheme'] as String? ?? '', scheme: json['scheme'] as String? ?? '',
host: json['host'] as String? ?? '', host: json['host'] as String? ?? '',
usr: json['usr'] as String? ?? '', usr: json['usr'] as String? ?? '',
@@ -83,6 +84,7 @@ ProxyConfig _$ProxyConfigFromJson(Map<String, dynamic> json) => ProxyConfig(
Map<String, dynamic> _$ProxyConfigToJson(ProxyConfig instance) => Map<String, dynamic> _$ProxyConfigToJson(ProxyConfig instance) =>
<String, dynamic>{ <String, dynamic>{
'enable': instance.enable, 'enable': instance.enable,
'system': instance.system,
'scheme': instance.scheme, 'scheme': instance.scheme,
'host': instance.host, 'host': instance.host,
'usr': instance.usr, 'usr': instance.usr,
+2 -2
View File
@@ -10,7 +10,7 @@ Options _$OptionsFromJson(Map<String, dynamic> json) => Options(
name: json['name'] as String, name: json['name'] as String,
path: json['path'] as String, path: json['path'] as String,
selectFiles: (json['selectFiles'] as List<dynamic>?) selectFiles: (json['selectFiles'] as List<dynamic>?)
?.map((e) => e as int) ?.map((e) => (e as num).toInt())
.toList() ?? .toList() ??
const [], const [],
extra: json['extra'], extra: json['extra'],
@@ -35,7 +35,7 @@ Map<String, dynamic> _$OptionsToJson(Options instance) {
OptsExtraHttp _$OptsExtraHttpFromJson(Map<String, dynamic> json) => OptsExtraHttp _$OptsExtraHttpFromJson(Map<String, dynamic> json) =>
OptsExtraHttp() OptsExtraHttp()
..connections = json['connections'] as int ..connections = (json['connections'] as num).toInt()
..autoTorrent = json['autoTorrent'] as bool; ..autoTorrent = json['autoTorrent'] as bool;
Map<String, dynamic> _$OptsExtraHttpToJson(OptsExtraHttp instance) => Map<String, dynamic> _$OptsExtraHttpToJson(OptsExtraHttp instance) =>
+2 -2
View File
@@ -8,7 +8,7 @@ part of 'resource.dart';
Resource _$ResourceFromJson(Map<String, dynamic> json) => Resource( Resource _$ResourceFromJson(Map<String, dynamic> json) => Resource(
name: json['name'] as String? ?? "", name: json['name'] as String? ?? "",
size: json['size'] as int? ?? 0, size: (json['size'] as num?)?.toInt() ?? 0,
range: json['range'] as bool? ?? false, range: json['range'] as bool? ?? false,
files: (json['files'] as List<dynamic>) files: (json['files'] as List<dynamic>)
.map((e) => FileInfo.fromJson(e as Map<String, 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( FileInfo _$FileInfoFromJson(Map<String, dynamic> json) => FileInfo(
path: json['path'] as String? ?? "", path: json['path'] as String? ?? "",
name: json['name'] as String, name: json['name'] as String,
size: json['size'] as int? ?? 0, size: (json['size'] as num?)?.toInt() ?? 0,
req: json['req'] == null req: json['req'] == null
? null ? null
: Request.fromJson(json['req'] as Map<String, dynamic>), : Request.fromJson(json['req'] as Map<String, dynamic>),
+1 -1
View File
@@ -11,7 +11,7 @@ Result<T> _$ResultFromJson<T>(
T Function(Object? json) fromJsonT, T Function(Object? json) fromJsonT,
) => ) =>
Result<T>( Result<T>(
code: json['code'] as int, code: (json['code'] as num).toInt(),
msg: json['msg'] as String?, msg: json['msg'] as String?,
data: _$nullableGenericFromJson(json['data'], fromJsonT), data: _$nullableGenericFromJson(json['data'], fromJsonT),
); );
+3 -3
View File
@@ -34,9 +34,9 @@ const _$StatusEnumMap = {
}; };
Progress _$ProgressFromJson(Map<String, dynamic> json) => Progress( Progress _$ProgressFromJson(Map<String, dynamic> json) => Progress(
used: json['used'] as int, used: (json['used'] as num).toInt(),
speed: json['speed'] as int, speed: (json['speed'] as num).toInt(),
downloaded: json['downloaded'] as int, downloaded: (json['downloaded'] as num).toInt(),
); );
Map<String, dynamic> _$ProgressToJson(Progress instance) => <String, dynamic>{ Map<String, dynamic> _$ProgressToJson(Progress instance) => <String, dynamic>{
@@ -7,6 +7,7 @@ import 'package:get/get.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
import '../../../../api/model/downloader_config.dart';
import '../../../../i18n/message.dart'; import '../../../../i18n/message.dart';
import '../../../../util/input_formatter.dart'; import '../../../../util/input_formatter.dart';
import '../../../../util/locale_manager.dart'; import '../../../../util/locale_manager.dart';
@@ -408,29 +409,53 @@ class SettingView extends GetView<SettingController> {
} }
// advanced config proxy items start // advanced config proxy items start
final proxy = downloaderCfg.value.proxy;
final buildProxy = _buildConfigItem( final buildProxy = _buildConfigItem(
'proxy', 'proxy',
() => downloaderCfg.value.proxy.enable () {
? '${downloaderCfg.value.proxy.scheme}://${downloaderCfg.value.proxy.host}' switch (proxy.proxyMode) {
: 'notSet'.tr, 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) { (Key key) {
final proxy = downloaderCfg.value.proxy; final mode = SizedBox(
width: 150,
child: DropdownButtonFormField<ProxyModeEnum>(
value: proxy.proxyMode,
onChanged: (value) async {
if (value != null && value != proxy.proxyMode) {
proxy.proxyMode = value;
downloaderCfg.update((val) {
val!.proxy = proxy;
});
final switcher = Switch( await debounceSave();
value: proxy.enable, }
onChanged: (bool value) async { },
if (value != proxy.enable) { items: [
downloaderCfg.update((val) { DropdownMenuItem<ProxyModeEnum>(
val!.proxy.enable = value; value: ProxyModeEnum.noProxy,
}); child: Text('noProxy'.tr),
),
await debounceSave(); DropdownMenuItem<ProxyModeEnum>(
} value: ProxyModeEnum.systemProxy,
}, child: Text('systemProxy'.tr),
),
DropdownMenuItem<ProxyModeEnum>(
value: ProxyModeEnum.customProxy,
child: Text('customProxy'.tr),
),
],
),
); );
final scheme = SizedBox( final scheme = SizedBox(
width: 100, width: 150,
child: DropdownButtonFormField<String>( child: DropdownButtonFormField<String>(
value: proxy.scheme, value: proxy.scheme,
onChanged: (value) async { onChanged: (value) async {
@@ -478,7 +503,7 @@ class SettingView extends GetView<SettingController> {
ipController.addListener(updateAddress); ipController.addListener(updateAddress);
portController.addListener(updateAddress); portController.addListener(updateAddress);
final server = [ final server = Row(children: [
Flexible( Flexible(
child: TextFormField( child: TextFormField(
controller: ipController, controller: ipController,
@@ -503,12 +528,12 @@ class SettingView extends GetView<SettingController> {
], ],
), ),
), ),
]; ]);
final usrController = TextEditingController(text: proxy.usr); final usrController = TextEditingController(text: proxy.usr);
final pwdController = TextEditingController(text: proxy.pwd); final pwdController = TextEditingController(text: proxy.pwd);
final auth = [ final auth = Row(children: [
Flexible( Flexible(
child: TextFormField( child: TextFormField(
controller: usrController, controller: usrController,
@@ -528,20 +553,21 @@ class SettingView extends GetView<SettingController> {
), ),
), ),
), ),
]; ]);
List<Widget> customView() {
if (proxy.proxyMode != ProxyModeEnum.customProxy) {
return [];
}
return [scheme, server, auth];
}
return Form( return Form(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: _addPadding([ children: _addPadding([
switcher, mode,
scheme, ...customView(),
Row(
children: server,
),
Row(
children: auth,
),
]), ]),
), ),
); );
@@ -857,3 +883,37 @@ class SettingView extends GetView<SettingController> {
} }
} }
} }
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;
}
}
}
@@ -11,7 +11,7 @@ StartConfig _$StartConfigFromJson(Map<String, dynamic> json) => StartConfig()
..address = json['address'] as String ..address = json['address'] as String
..storage = json['storage'] as String ..storage = json['storage'] as String
..storageDir = json['storageDir'] as String ..storageDir = json['storageDir'] as String
..refreshInterval = json['refreshInterval'] as int ..refreshInterval = (json['refreshInterval'] as num).toInt()
..apiToken = json['apiToken'] as String; ..apiToken = json['apiToken'] as String;
Map<String, dynamic> _$StartConfigToJson(StartConfig instance) => Map<String, dynamic> _$StartConfigToJson(StartConfig instance) =>
+3
View File
@@ -82,6 +82,9 @@ const enUS = {
'serviceText': 'Running', 'serviceText': 'Running',
'network': 'Network', 'network': 'Network',
'proxy': 'Proxy', 'proxy': 'Proxy',
'noProxy': 'No Proxy',
'systemProxy': 'System Proxy',
'customProxy': 'Custom Proxy',
'server': 'Server', 'server': 'Server',
'username': 'Username', 'username': 'Username',
'password': 'Password', 'password': 'Password',
+13 -13
View File
@@ -20,7 +20,7 @@ const trTR = {
'downloadLink': 'İndirme bağlantısı', 'downloadLink': 'İndirme bağlantısı',
'downloadLinkValid': 'Lütfen indirme bağlantısını girin', 'downloadLinkValid': 'Lütfen indirme bağlantısını girin',
'downloadLinkHit': 'downloadLinkHit':
'Lütfen indirme bağlantısını girin, HTTP/HTTPS/MAGNET supported@append', 'Lütfen indirme bağlantısını girin, HTTP/HTTPS/MAGNET desteklenir@append',
'downloadLinkHitDesktop': 'downloadLinkHitDesktop':
', veya torrent dosyasını doğrudan buraya sürükleyin', ', veya torrent dosyasını doğrudan buraya sürükleyin',
'download': 'İndir', 'download': 'İndir',
@@ -35,18 +35,18 @@ const trTR = {
'downloadDirValid': 'Lütfen indirme dizinini seçin', 'downloadDirValid': 'Lütfen indirme dizinini seçin',
'connections': 'Bağlantılar', 'connections': 'Bağlantılar',
'useServerCtime': 'Dosya oluşturma için sunucu saatini kullan', 'useServerCtime': 'Dosya oluşturma için sunucu saatini kullan',
'maxRunning': 'Maksimum çalışan görevler', 'maxRunning': 'Maksimum çalışan görev sayısı',
'items': '@count say', 'items': '@count tane',
'subscribeTracker': 'İzleyiciye abone ol', 'subscribeTracker': 'İzleyiciye abone ol',
'subscribeFail': 'subscribeFail':
'Abone olunamadı, lütfen ağı kontrol edin veya daha sonra tekrar deneyin', 'Abone olunamadı, lütfen ağı kontrol edin veya daha sonra tekrar deneyin',
'update': 'Güncelleme', 'update': 'Güncelle',
'updateDaily': 'Günlük güncelleme', 'updateDaily': 'Günlük olarak güncelle',
'lastUpdate': 'Son güncelleme: @time', 'lastUpdate': 'Son güncellenme tarihi: @time',
'addTracker': 'İzleyici ekle', 'addTracker': 'İzleyici ekle',
'addTrackerHit': 'addTrackerHit':
'Lütfen her satıra bir tane olmak üzere izleyici sunucusu bağlantısını girin', 'Lütfen her satıra bir tane olmak üzere izleyici sunucusu bağlantısını girin',
'ui': 'UI', 'ui': 'Arayüz',
'theme': 'Tema', 'theme': 'Tema',
'themeSystem': 'Sistem', 'themeSystem': 'Sistem',
'themeLight': 'Açık', 'themeLight': 'Açık',
@@ -58,9 +58,9 @@ const trTR = {
'protocol': 'Protokol', 'protocol': 'Protokol',
'port': 'Bağlantı noktası', 'port': 'Bağlantı noktası',
'apiToken': 'API anahtarı', 'apiToken': 'API anahtarı',
'notSet': 'AYARLAMA', 'notSet': 'AYARLI',
'set': 'AYARLA', 'set': 'AYARLI DEĞİL',
'effectAfterRestart': 'Yeniden başlatma sonrası etki eder', 'effectAfterRestart': 'Yeniden başlatma sonrası etki edecektir',
'startAll': 'Hepsini başlat', 'startAll': 'Hepsini başlat',
'pauseAll': 'Hepsini durdur', 'pauseAll': 'Hepsini durdur',
'deleteTask': 'Görevleri sil', 'deleteTask': 'Görevleri sil',
@@ -75,8 +75,8 @@ const trTR = {
'extensionUpdateSuccess': 'Başarıyla güncellendi', 'extensionUpdateSuccess': 'Başarıyla güncellendi',
'extensionDelete': 'Uzantıyı sil', 'extensionDelete': 'Uzantıyı sil',
'extensionAlreadyLatest': 'Bu zaten en son sürüm', 'extensionAlreadyLatest': 'Bu zaten en son sürüm',
'extensionFind': 'Uzantıları ara', 'extensionFind': 'Uzantı ara',
'extensionDevelop': 'Uzantıları Geliştirin', 'extensionDevelop': 'Uzantı geliştir',
'history': 'Geçmiş', 'history': 'Geçmiş',
'clearHistory': 'Geçmişi temizle', 'clearHistory': 'Geçmişi temizle',
'noHistoryFound': 'Geçmiş bulunamadı', 'noHistoryFound': 'Geçmiş bulunamadı',
@@ -90,6 +90,6 @@ const trTR = {
'thanks': 'Teşekkürler', 'thanks': 'Teşekkürler',
'thanksDesc': 'thanksDesc':
'Gopeed topluluğunun oluşmasına ve gelişmesine yardımcı olan tüm katılımcılara teşekkürler!', 'Gopeed topluluğunun oluşmasına ve gelişmesine yardımcı olan tüm katılımcılara teşekkürler!',
'browserExtension': 'Tarayıcı Uzantısı', 'browserExtension': 'Tarayıcı uzantısı',
}, },
}; };
+3
View File
@@ -80,6 +80,9 @@ const zhCN = {
'serviceText': '运行中', 'serviceText': '运行中',
'network': '网络', 'network': '网络',
'proxy': '代理', 'proxy': '代理',
'noProxy': '不使用代理',
'systemProxy': '系统代理',
'customProxy': '自定义代理',
'server': '服务器', 'server': '服务器',
'username': '用户名', 'username': '用户名',
'password': '密码', 'password': '密码',
+3
View File
@@ -80,6 +80,9 @@ const zhTW = {
'serviceText': '執行中', 'serviceText': '執行中',
'network': '網路', 'network': '網路',
'proxy': '代理', 'proxy': '代理',
'noProxy': '不使用代理',
'systemProxy': '系統代理',
'customProxy': '自定義代理',
'server': '伺服器', 'server': '伺服器',
'username': '名稱', 'username': '名稱',
'password': '密碼', 'password': '密碼',
@@ -13,8 +13,8 @@ essential: false
icon: assets/icon/icon.svg icon: assets/icon/icon.svg
dependencies: dependencies:
- libappindicator3-1 | libayatana-appindicator3-1 - libayatana-appindicator3-1
- gir1.2-appindicator3-0.1 | gir1.2-ayatanaappindicator3-0.1 - gir1.2-ayatanaappindicator3-0.1
keywords: keywords:
- Application - Application
@@ -1,24 +0,0 @@
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: [email protected]
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