Compare commits

...
5 Commits
Author SHA1 Message Date
Levi 31228e3edf release: v1.5.3 2023-12-15 19:23:16 +08:00
Levi 31a32d7e14 fix: handle api panic (#302) 2023-12-15 19:20:56 +08:00
Levi 65fd6fb489 fix: http pause handle (#303) 2023-12-15 19:15:38 +08:00
Levi 953b878a89 fix: adapt mobile proxy settings ui (#301) 2023-12-15 19:09:48 +08:00
Elliot ae4ca3da58 feat: improve zh-TW translation (#300) 2023-12-14 18:06:49 +08:00
7 changed files with 81 additions and 72 deletions

No files matched your search

+14 -16
View File
@@ -306,26 +306,13 @@ func (f *Fetcher) fetchChunk(index int, ctx context.Context) (err error) {
if err != nil { if err != nil {
return err return err
} }
defer resp.Body.Close()
if resp.StatusCode != base.HttpCodeOK && resp.StatusCode != base.HttpCodePartialContent { if resp.StatusCode != base.HttpCodeOK && resp.StatusCode != base.HttpCodePartialContent {
err = NewRequestError(resp.StatusCode, resp.Status) err = NewRequestError(resp.StatusCode, resp.Status)
return err return err
} }
return nil // Http request success, reset retry times
}() chunk.retryTimes = 0
if err != nil {
// If canceled, do not retry
if errors.Is(err, context.Canceled) {
return
}
// retry request after 1 second
chunk.retryTimes = chunk.retryTimes + 1
time.Sleep(time.Second)
continue
}
// Http request success, reset retry times
chunk.retryTimes = 0
err = func() error {
defer resp.Body.Close()
for { for {
n, err := resp.Body.Read(buf) n, err := resp.Body.Read(buf)
if n > 0 { if n > 0 {
@@ -342,7 +329,18 @@ func (f *Fetcher) fetchChunk(index int, ctx context.Context) (err error) {
return err return err
} }
} }
return nil
}() }()
if err != nil {
// If canceled, do not retry
if errors.Is(err, context.Canceled) {
return
}
// retry request after 1 second
chunk.retryTimes = chunk.retryTimes + 1
time.Sleep(time.Second)
continue
}
// if chunk is completed, reset other not complete chunks retry times // if chunk is completed, reset other not complete chunks retry times
if err == nil { if err == nil {
for _, c := range f.chunks { for _, c := range f.chunks {
+3
View File
@@ -8,6 +8,8 @@ import (
"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"
"github.com/rs/zerolog"
"github.com/rs/zerolog/pkgerrors"
"github.com/virtuald/go-paniclog" "github.com/virtuald/go-paniclog"
"os" "os"
"path/filepath" "path/filepath"
@@ -91,6 +93,7 @@ func NewDownloader(cfg *DownloaderConfig) *Downloader {
} }
} }
zerolog.ErrorStackMarshaler = pkgerrors.MarshalStack
d.Logger = logger.NewLogger(cfg.ProductionMode, filepath.Join(cfg.StorageDir, "logs", "core.log")) d.Logger = logger.NewLogger(cfg.ProductionMode, filepath.Join(cfg.StorageDir, "logs", "core.log"))
d.ExtensionLogger = logger.NewLogger(cfg.ProductionMode, filepath.Join(cfg.StorageDir, "logs", "extension.log")) d.ExtensionLogger = logger.NewLogger(cfg.ProductionMode, filepath.Join(cfg.StorageDir, "logs", "extension.log"))
if cfg.ProductionMode { if cfg.ProductionMode {
+4 -4
View File
@@ -254,12 +254,12 @@ func (d *Downloader) triggerOnResolve(req *base.Request) (res *base.Resource, er
gopeed.Logger.logger.Warn().Err(err).Msgf("[%s] resource invalid", ext.buildIdentity()) gopeed.Logger.logger.Warn().Err(err).Msgf("[%s] resource invalid", ext.buildIdentity())
return return
} }
ctx.Res.Name = util.ReplaceInvalidFilename(ctx.Res.Name)
for _, file := range ctx.Res.Files {
file.Name = util.ReplaceInvalidFilename(file.Name)
}
ctx.Res.CalcSize(nil) ctx.Res.CalcSize(nil)
} }
ctx.Res.Name = util.ReplaceInvalidFilename(ctx.Res.Name)
for _, file := range ctx.Res.Files {
file.Name = util.ReplaceInvalidFilename(file.Name)
}
res = ctx.Res res = ctx.Res
}, },
) )
+16
View File
@@ -3,11 +3,13 @@ package rest
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"fmt"
"github.com/GopeedLab/gopeed/pkg/download" "github.com/GopeedLab/gopeed/pkg/download"
"github.com/GopeedLab/gopeed/pkg/rest/model" "github.com/GopeedLab/gopeed/pkg/rest/model"
"github.com/GopeedLab/gopeed/pkg/util" "github.com/GopeedLab/gopeed/pkg/util"
"github.com/gorilla/handlers" "github.com/gorilla/handlers"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/pkg/errors"
"net" "net"
"net/http" "net/http"
"os" "os"
@@ -131,6 +133,20 @@ func BuildServer(startCfg *model.StartConfig) (*http.Server, net.Listener, error
}) })
} }
// recover panic
r.Use(func(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() {
if v := recover(); v != nil {
err := errors.WithStack(fmt.Errorf("%v", v))
Downloader.Logger.Error().Stack().Err(err).Msg("http server panic")
WriteJson(w, model.NewErrorResult(err.Error(), model.CodeError))
}
}()
h.ServeHTTP(w, r)
})
})
srv = &http.Server{Handler: handlers.CORS( srv = &http.Server{Handler: handlers.CORS(
handlers.AllowedHeaders([]string{"Content-Type", "X-Api-Token", "X-Target-Uri"}), handlers.AllowedHeaders([]string{"Content-Type", "X-Api-Token", "X-Target-Uri"}),
handlers.AllowedMethods([]string{"GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"}), handlers.AllowedMethods([]string{"GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"}),
@@ -387,35 +387,33 @@ class SettingView extends GetView<SettingController> {
}, },
); );
final items = <Widget>[ final scheme = SizedBox(
SizedBox( width: 100,
width: 150, child: DropdownButtonFormField<String>(
child: DropdownButtonFormField<String>( value: proxy.scheme,
value: proxy.scheme, onChanged: (value) async {
onChanged: (value) async { if (value != null && value != proxy.scheme) {
if (value != null && value != proxy.scheme) { proxy.scheme = value;
proxy.scheme = value;
await debounceSave(); await debounceSave();
} }
}, },
items: const [ items: const [
DropdownMenuItem<String>( DropdownMenuItem<String>(
value: 'http', value: 'http',
child: Text('HTTP'), child: Text('HTTP'),
), ),
DropdownMenuItem<String>( DropdownMenuItem<String>(
value: 'https', value: 'https',
child: Text('HTTPS'), child: Text('HTTPS'),
), ),
DropdownMenuItem<String>( DropdownMenuItem<String>(
value: 'socks5', value: 'socks5',
child: Text('SOCKS5'), child: Text('SOCKS5'),
), ),
], ],
), ),
) );
];
final arr = proxy.host.split(':'); final arr = proxy.host.split(':');
var ip = ''; var ip = '';
@@ -438,10 +436,8 @@ class SettingView extends GetView<SettingController> {
ipController.addListener(updateAddress); ipController.addListener(updateAddress);
portController.addListener(updateAddress); portController.addListener(updateAddress);
items.addAll([ final host = [
const Padding(padding: EdgeInsets.only(left: 20)), Flexible(
SizedBox(
width: 150,
child: TextFormField( child: TextFormField(
controller: ipController, controller: ipController,
decoration: const InputDecoration( decoration: const InputDecoration(
@@ -455,8 +451,7 @@ class SettingView extends GetView<SettingController> {
), ),
), ),
const Padding(padding: EdgeInsets.only(left: 10)), const Padding(padding: EdgeInsets.only(left: 10)),
SizedBox( Flexible(
width: 150,
child: TextFormField( child: TextFormField(
controller: portController, controller: portController,
decoration: InputDecoration( decoration: InputDecoration(
@@ -470,14 +465,13 @@ 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 = [
SizedBox( Flexible(
width: 150,
child: TextFormField( child: TextFormField(
controller: usrController, controller: usrController,
decoration: InputDecoration( decoration: InputDecoration(
@@ -491,8 +485,7 @@ class SettingView extends GetView<SettingController> {
), ),
), ),
const Padding(padding: EdgeInsets.only(left: 10)), const Padding(padding: EdgeInsets.only(left: 10)),
SizedBox( Flexible(
width: 150,
child: TextFormField( child: TextFormField(
controller: pwdController, controller: pwdController,
decoration: InputDecoration( decoration: InputDecoration(
@@ -513,8 +506,9 @@ class SettingView extends GetView<SettingController> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: _addPadding([ children: _addPadding([
switcher, switcher,
scheme,
Row( Row(
children: items, children: host,
), ),
Row( Row(
children: auth, children: auth,
@@ -534,7 +528,7 @@ class SettingView extends GetView<SettingController> {
(Key key) { (Key key) {
final items = <Widget>[ final items = <Widget>[
SizedBox( SizedBox(
width: 150, width: 80,
child: DropdownButtonFormField<String>( child: DropdownButtonFormField<String>(
value: startCfg.value.network, value: startCfg.value.network,
onChanged: Util.isDesktop() onChanged: Util.isDesktop()
@@ -585,8 +579,7 @@ class SettingView extends GetView<SettingController> {
portController.addListener(updateAddress); portController.addListener(updateAddress);
items.addAll([ items.addAll([
const Padding(padding: EdgeInsets.only(left: 20)), const Padding(padding: EdgeInsets.only(left: 20)),
SizedBox( Flexible(
width: 150,
child: TextFormField( child: TextFormField(
controller: ipController, controller: ipController,
decoration: const InputDecoration( decoration: const InputDecoration(
@@ -600,8 +593,7 @@ class SettingView extends GetView<SettingController> {
), ),
), ),
const Padding(padding: EdgeInsets.only(left: 10)), const Padding(padding: EdgeInsets.only(left: 10)),
SizedBox( Flexible(
width: 150,
child: TextFormField( child: TextFormField(
controller: portController, controller: portController,
decoration: InputDecoration( decoration: InputDecoration(
+6 -6
View File
@@ -59,17 +59,17 @@ const zhTW = {
'deleteTask': '刪除任務', 'deleteTask': '刪除任務',
'deleteTaskTip': '保留已經下載的文件', 'deleteTaskTip': '保留已經下載的文件',
'delete': '刪除', 'delete': '刪除',
'newVersionTitle': '發現新版本 @version', 'newVersionTitle': '現新版本 @version 已發佈',
'newVersionUpdate': '立刻更新', 'newVersionUpdate': '下載',
'newVersionLater': '稍後更新', 'newVersionLater': '取消',
'extensions': '外掛程序', 'extensions': '擴充程序',
'extensionInstallUrl': '安裝URL', 'extensionInstallUrl': '安裝URL',
'extensionInstallSuccess': '安裝成功', 'extensionInstallSuccess': '安裝成功',
'extensionUpdateSuccess': '更新完成', 'extensionUpdateSuccess': '更新完成',
'extensionDelete': '解除安裝', 'extensionDelete': '解除安裝',
'extensionAlreadyLatest': '已是最新版本', 'extensionAlreadyLatest': '已是最新版本',
'extensionFind': '取得外掛', 'extensionFind': '取得擴充程序',
'extensionDevelop': '開發外掛', 'extensionDevelop': '開發擴充程序',
'history': '歷史記錄', 'history': '歷史記錄',
'clearHistory': '清空記錄', 'clearHistory': '清空記錄',
'noHistoryFound': '還沒有記錄', 'noHistoryFound': '還沒有記錄',
+1 -1
View File
@@ -15,7 +15,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at # Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.5.2+1 version: 1.5.3+1
environment: environment:
sdk: '>=3.0.0 <4.0.0' sdk: '>=3.0.0 <4.0.0'