mirror of
https://github.com/GopeedLab/gopeed.git
synced 2026-11-10 02:20:28 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
31228e3edf | ||
|
|
31a32d7e14 | ||
|
|
65fd6fb489 | ||
|
|
953b878a89 | ||
|
|
ae4ca3da58 |
No files matched your search
@@ -306,26 +306,13 @@ func (f *Fetcher) fetchChunk(index int, ctx context.Context) (err error) {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != base.HttpCodeOK && resp.StatusCode != base.HttpCodePartialContent {
|
||||
err = NewRequestError(resp.StatusCode, resp.Status)
|
||||
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
|
||||
}
|
||||
// Http request success, reset retry times
|
||||
chunk.retryTimes = 0
|
||||
err = func() error {
|
||||
defer resp.Body.Close()
|
||||
// Http request success, reset retry times
|
||||
chunk.retryTimes = 0
|
||||
for {
|
||||
n, err := resp.Body.Read(buf)
|
||||
if n > 0 {
|
||||
@@ -342,7 +329,18 @@ func (f *Fetcher) fetchChunk(index int, ctx context.Context) (err error) {
|
||||
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 err == nil {
|
||||
for _, c := range f.chunks {
|
||||
|
||||
@@ -8,6 +8,8 @@ import (
|
||||
"github.com/GopeedLab/gopeed/pkg/base"
|
||||
"github.com/GopeedLab/gopeed/pkg/util"
|
||||
gonanoid "github.com/matoous/go-nanoid/v2"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/pkgerrors"
|
||||
"github.com/virtuald/go-paniclog"
|
||||
"os"
|
||||
"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.ExtensionLogger = logger.NewLogger(cfg.ProductionMode, filepath.Join(cfg.StorageDir, "logs", "extension.log"))
|
||||
if cfg.ProductionMode {
|
||||
|
||||
@@ -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())
|
||||
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.Name = util.ReplaceInvalidFilename(ctx.Res.Name)
|
||||
for _, file := range ctx.Res.Files {
|
||||
file.Name = util.ReplaceInvalidFilename(file.Name)
|
||||
}
|
||||
res = ctx.Res
|
||||
},
|
||||
)
|
||||
|
||||
@@ -3,11 +3,13 @@ package rest
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/GopeedLab/gopeed/pkg/download"
|
||||
"github.com/GopeedLab/gopeed/pkg/rest/model"
|
||||
"github.com/GopeedLab/gopeed/pkg/util"
|
||||
"github.com/gorilla/handlers"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/pkg/errors"
|
||||
"net"
|
||||
"net/http"
|
||||
"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(
|
||||
handlers.AllowedHeaders([]string{"Content-Type", "X-Api-Token", "X-Target-Uri"}),
|
||||
handlers.AllowedMethods([]string{"GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"}),
|
||||
|
||||
@@ -387,35 +387,33 @@ class SettingView extends GetView<SettingController> {
|
||||
},
|
||||
);
|
||||
|
||||
final items = <Widget>[
|
||||
SizedBox(
|
||||
width: 150,
|
||||
child: DropdownButtonFormField<String>(
|
||||
value: proxy.scheme,
|
||||
onChanged: (value) async {
|
||||
if (value != null && value != proxy.scheme) {
|
||||
proxy.scheme = value;
|
||||
final scheme = SizedBox(
|
||||
width: 100,
|
||||
child: DropdownButtonFormField<String>(
|
||||
value: proxy.scheme,
|
||||
onChanged: (value) async {
|
||||
if (value != null && value != proxy.scheme) {
|
||||
proxy.scheme = value;
|
||||
|
||||
await debounceSave();
|
||||
}
|
||||
},
|
||||
items: const [
|
||||
DropdownMenuItem<String>(
|
||||
value: 'http',
|
||||
child: Text('HTTP'),
|
||||
),
|
||||
DropdownMenuItem<String>(
|
||||
value: 'https',
|
||||
child: Text('HTTPS'),
|
||||
),
|
||||
DropdownMenuItem<String>(
|
||||
value: 'socks5',
|
||||
child: Text('SOCKS5'),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
];
|
||||
await debounceSave();
|
||||
}
|
||||
},
|
||||
items: const [
|
||||
DropdownMenuItem<String>(
|
||||
value: 'http',
|
||||
child: Text('HTTP'),
|
||||
),
|
||||
DropdownMenuItem<String>(
|
||||
value: 'https',
|
||||
child: Text('HTTPS'),
|
||||
),
|
||||
DropdownMenuItem<String>(
|
||||
value: 'socks5',
|
||||
child: Text('SOCKS5'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
final arr = proxy.host.split(':');
|
||||
var ip = '';
|
||||
@@ -438,10 +436,8 @@ class SettingView extends GetView<SettingController> {
|
||||
|
||||
ipController.addListener(updateAddress);
|
||||
portController.addListener(updateAddress);
|
||||
items.addAll([
|
||||
const Padding(padding: EdgeInsets.only(left: 20)),
|
||||
SizedBox(
|
||||
width: 150,
|
||||
final host = [
|
||||
Flexible(
|
||||
child: TextFormField(
|
||||
controller: ipController,
|
||||
decoration: const InputDecoration(
|
||||
@@ -455,8 +451,7 @@ class SettingView extends GetView<SettingController> {
|
||||
),
|
||||
),
|
||||
const Padding(padding: EdgeInsets.only(left: 10)),
|
||||
SizedBox(
|
||||
width: 150,
|
||||
Flexible(
|
||||
child: TextFormField(
|
||||
controller: portController,
|
||||
decoration: InputDecoration(
|
||||
@@ -470,14 +465,13 @@ class SettingView extends GetView<SettingController> {
|
||||
],
|
||||
),
|
||||
),
|
||||
]);
|
||||
];
|
||||
|
||||
final usrController = TextEditingController(text: proxy.usr);
|
||||
final pwdController = TextEditingController(text: proxy.pwd);
|
||||
|
||||
final auth = [
|
||||
SizedBox(
|
||||
width: 150,
|
||||
Flexible(
|
||||
child: TextFormField(
|
||||
controller: usrController,
|
||||
decoration: InputDecoration(
|
||||
@@ -491,8 +485,7 @@ class SettingView extends GetView<SettingController> {
|
||||
),
|
||||
),
|
||||
const Padding(padding: EdgeInsets.only(left: 10)),
|
||||
SizedBox(
|
||||
width: 150,
|
||||
Flexible(
|
||||
child: TextFormField(
|
||||
controller: pwdController,
|
||||
decoration: InputDecoration(
|
||||
@@ -513,8 +506,9 @@ class SettingView extends GetView<SettingController> {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: _addPadding([
|
||||
switcher,
|
||||
scheme,
|
||||
Row(
|
||||
children: items,
|
||||
children: host,
|
||||
),
|
||||
Row(
|
||||
children: auth,
|
||||
@@ -534,7 +528,7 @@ class SettingView extends GetView<SettingController> {
|
||||
(Key key) {
|
||||
final items = <Widget>[
|
||||
SizedBox(
|
||||
width: 150,
|
||||
width: 80,
|
||||
child: DropdownButtonFormField<String>(
|
||||
value: startCfg.value.network,
|
||||
onChanged: Util.isDesktop()
|
||||
@@ -585,8 +579,7 @@ class SettingView extends GetView<SettingController> {
|
||||
portController.addListener(updateAddress);
|
||||
items.addAll([
|
||||
const Padding(padding: EdgeInsets.only(left: 20)),
|
||||
SizedBox(
|
||||
width: 150,
|
||||
Flexible(
|
||||
child: TextFormField(
|
||||
controller: ipController,
|
||||
decoration: const InputDecoration(
|
||||
@@ -600,8 +593,7 @@ class SettingView extends GetView<SettingController> {
|
||||
),
|
||||
),
|
||||
const Padding(padding: EdgeInsets.only(left: 10)),
|
||||
SizedBox(
|
||||
width: 150,
|
||||
Flexible(
|
||||
child: TextFormField(
|
||||
controller: portController,
|
||||
decoration: InputDecoration(
|
||||
|
||||
@@ -59,17 +59,17 @@ const zhTW = {
|
||||
'deleteTask': '刪除任務',
|
||||
'deleteTaskTip': '保留已經下載的文件',
|
||||
'delete': '刪除',
|
||||
'newVersionTitle': '發現新版本 @version',
|
||||
'newVersionUpdate': '立刻更新',
|
||||
'newVersionLater': '稍後更新',
|
||||
'extensions': '外掛程序',
|
||||
'newVersionTitle': '現新版本 @version 已發佈',
|
||||
'newVersionUpdate': '下載',
|
||||
'newVersionLater': '取消',
|
||||
'extensions': '擴充程序',
|
||||
'extensionInstallUrl': '安裝URL',
|
||||
'extensionInstallSuccess': '安裝成功',
|
||||
'extensionUpdateSuccess': '更新完成',
|
||||
'extensionDelete': '解除安裝',
|
||||
'extensionAlreadyLatest': '已是最新版本',
|
||||
'extensionFind': '取得外掛',
|
||||
'extensionDevelop': '開發外掛',
|
||||
'extensionFind': '取得擴充程序',
|
||||
'extensionDevelop': '開發擴充程序',
|
||||
'history': '歷史記錄',
|
||||
'clearHistory': '清空記錄',
|
||||
'noHistoryFound': '還沒有記錄',
|
||||
|
||||
@@ -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.
|
||||
# Read more about iOS versioning at
|
||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
version: 1.5.2+1
|
||||
version: 1.5.3+1
|
||||
|
||||
environment:
|
||||
sdk: '>=3.0.0 <4.0.0'
|
||||
|
||||
Reference in new issue
Block a user