Compare commits
14 Commits
Author | SHA1 | Date | |
---|---|---|---|
579f0c06af | |||
b12d92acc9 | |||
e700ce15e5 | |||
7dbef7d559 | |||
7e9cdd8b07 | |||
cee6bc6b5d | |||
cfd23c05b4 | |||
0c1acd72ca | |||
e2ca06dcca | |||
0828fd787d | |||
2e23ea68d4 | |||
4afa822bec | |||
f2ca9b40db | |||
4c2535cb22 |
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@ -21,7 +21,7 @@ body:
|
||||
attributes:
|
||||
label: Alist Version / Alist 版本
|
||||
description: What version of our software are you running?
|
||||
placeholder: v2.0.0
|
||||
placeholder: v3.xx.xx
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
|
2
.github/workflows/issue_question.yml
vendored
2
.github/workflows/issue_question.yml
vendored
@ -10,7 +10,7 @@ jobs:
|
||||
if: github.event.label.name == 'question'
|
||||
steps:
|
||||
- name: Create comment
|
||||
uses: actions-cool/issues-helper@v3.5.0
|
||||
uses: actions-cool/issues-helper@v3.5.1
|
||||
with:
|
||||
actions: 'create-comment'
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
@ -7,7 +7,7 @@
|
||||
Prerequisites:
|
||||
|
||||
- [git](https://git-scm.com)
|
||||
- [Go 1.19+](https://golang.org/doc/install)
|
||||
- [Go 1.20+](https://golang.org/doc/install)
|
||||
- [gcc](https://gcc.gnu.org/)
|
||||
- [nodejs](https://nodejs.org/)
|
||||
|
||||
|
1
build.sh
1
build.sh
@ -98,6 +98,7 @@ BuildRelease() {
|
||||
url="${BASE}${i}.tgz"
|
||||
curl -L -o "${i}.tgz" "${url}"
|
||||
sudo tar xf "${i}.tgz" --strip-components 1 -C /usr/local
|
||||
rm -f "${i}.tgz"
|
||||
done
|
||||
OS_ARCHES=(linux-musl-amd64 linux-musl-arm64 linux-musl-arm linux-musl-mips linux-musl-mips64 linux-musl-mips64le linux-musl-mipsle linux-musl-ppc64le linux-musl-s390x)
|
||||
CGO_ARGS=(x86_64-linux-musl-gcc aarch64-linux-musl-gcc arm-linux-musleabihf-gcc mips-linux-musl-gcc mips64-linux-musl-gcc mips64el-linux-musl-gcc mipsel-linux-musl-gcc powerpc64le-linux-musl-gcc s390x-linux-musl-gcc)
|
||||
|
@ -1,10 +1,11 @@
|
||||
package _115
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
|
||||
"github.com/SheltonZhu/115driver/pkg/driver"
|
||||
"github.com/alist-org/alist/v3/drivers/base"
|
||||
"github.com/alist-org/alist/v3/internal/conf"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
@ -14,9 +15,11 @@ func (d *Pan115) login() error {
|
||||
var err error
|
||||
opts := []driver.Option{
|
||||
driver.UA(UserAgent),
|
||||
func(c *driver.Pan115Client) {
|
||||
c.Client.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: conf.Conf.TlsInsecureSkipVerify})
|
||||
},
|
||||
}
|
||||
d.client = driver.New(opts...)
|
||||
d.client.SetHttpClient(base.HttpClient)
|
||||
cr := &driver.Credential{}
|
||||
if d.Addition.QRCodeToken != "" {
|
||||
s := &driver.QRCodeSession{
|
||||
|
@ -1,14 +1,10 @@
|
||||
package _123
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/alist-org/alist/v3/drivers/base"
|
||||
"github.com/alist-org/alist/v3/pkg/utils"
|
||||
@ -19,9 +15,10 @@ import (
|
||||
// do others that not defined in Driver interface
|
||||
|
||||
const (
|
||||
Api = "https://www.123pan.com/api"
|
||||
AApi = "https://www.123pan.com/a/api"
|
||||
BApi = "https://www.123pan.com/b/api"
|
||||
MainApi = BApi
|
||||
MainApi = Api
|
||||
SignIn = MainApi + "/user/sign_in"
|
||||
Logout = MainApi + "/user/logout"
|
||||
UserInfo = MainApi + "/user/info"
|
||||
@ -37,7 +34,7 @@ const (
|
||||
S3Auth = MainApi + "/file/s3_upload_object/auth"
|
||||
UploadCompleteV2 = MainApi + "/file/upload_complete/v2"
|
||||
S3Complete = MainApi + "/file/s3_complete_multipart_upload"
|
||||
AuthKeySalt = "8-8D$sL8gPjom7bk#cY"
|
||||
//AuthKeySalt = "8-8D$sL8gPjom7bk#cY"
|
||||
)
|
||||
|
||||
func (d *Pan123) login() error {
|
||||
@ -59,9 +56,10 @@ func (d *Pan123) login() error {
|
||||
SetHeaders(map[string]string{
|
||||
"origin": "https://www.123pan.com",
|
||||
"referer": "https://www.123pan.com/",
|
||||
"platform": "web",
|
||||
"app-version": "3",
|
||||
"user-agent": base.UserAgent,
|
||||
"user-agent": "Dart/2.19(dart:io)",
|
||||
"platform": "android",
|
||||
"app-version": "36",
|
||||
//"user-agent": base.UserAgent,
|
||||
}).
|
||||
SetBody(body).Post(SignIn)
|
||||
if err != nil {
|
||||
@ -75,19 +73,19 @@ func (d *Pan123) login() error {
|
||||
return err
|
||||
}
|
||||
|
||||
func authKey(reqUrl string) (*string, error) {
|
||||
reqURL, err := url.Parse(reqUrl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nowUnix := time.Now().Unix()
|
||||
random := rand.Intn(0x989680)
|
||||
|
||||
p4 := fmt.Sprintf("%d|%d|%s|%s|%s|%s", nowUnix, random, reqURL.Path, "web", "3", AuthKeySalt)
|
||||
authKey := fmt.Sprintf("%d-%d-%x", nowUnix, random, md5.Sum([]byte(p4)))
|
||||
return &authKey, nil
|
||||
}
|
||||
//func authKey(reqUrl string) (*string, error) {
|
||||
// reqURL, err := url.Parse(reqUrl)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
//
|
||||
// nowUnix := time.Now().Unix()
|
||||
// random := rand.Intn(0x989680)
|
||||
//
|
||||
// p4 := fmt.Sprintf("%d|%d|%s|%s|%s|%s", nowUnix, random, reqURL.Path, "web", "3", AuthKeySalt)
|
||||
// authKey := fmt.Sprintf("%d-%d-%x", nowUnix, random, md5.Sum([]byte(p4)))
|
||||
// return &authKey, nil
|
||||
//}
|
||||
|
||||
func (d *Pan123) request(url string, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) {
|
||||
req := base.RestyClient.R()
|
||||
@ -95,9 +93,10 @@ func (d *Pan123) request(url string, method string, callback base.ReqCallback, r
|
||||
"origin": "https://www.123pan.com",
|
||||
"referer": "https://www.123pan.com/",
|
||||
"authorization": "Bearer " + d.AccessToken,
|
||||
"platform": "web",
|
||||
"app-version": "3",
|
||||
"user-agent": base.UserAgent,
|
||||
"user-agent": "Dart/2.19(dart:io)",
|
||||
"platform": "android",
|
||||
"app-version": "36",
|
||||
//"user-agent": base.UserAgent,
|
||||
})
|
||||
if callback != nil {
|
||||
callback(req)
|
||||
@ -105,11 +104,11 @@ func (d *Pan123) request(url string, method string, callback base.ReqCallback, r
|
||||
if resp != nil {
|
||||
req.SetResult(resp)
|
||||
}
|
||||
authKey, err := authKey(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.SetQueryParam("auth-key", *authKey)
|
||||
//authKey, err := authKey(url)
|
||||
//if err != nil {
|
||||
// return nil, err
|
||||
//}
|
||||
//req.SetQueryParam("auth-key", *authKey)
|
||||
res, err := req.Execute(method, url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -300,6 +300,9 @@ func (d *Yun139) Put(ctx context.Context, dstDir model.Obj, stream model.FileStr
|
||||
|
||||
var partSize = getPartSize(stream.GetSize())
|
||||
part := (stream.GetSize() + partSize - 1) / partSize
|
||||
if part == 0 {
|
||||
part = 1
|
||||
}
|
||||
for i := int64(0); i < part; i++ {
|
||||
if utils.IsCanceled(ctx) {
|
||||
return ctx.Err()
|
||||
@ -331,13 +334,11 @@ func (d *Yun139) Put(ctx context.Context, dstDir model.Obj, stream model.FileStr
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_ = res.Body.Close()
|
||||
log.Debugf("%+v", res)
|
||||
|
||||
if res.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("unexpected status code: %d", res.StatusCode)
|
||||
}
|
||||
|
||||
res.Body.Close()
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/Xhofe/rateg"
|
||||
"github.com/alist-org/alist/v3/drivers/base"
|
||||
"github.com/alist-org/alist/v3/internal/driver"
|
||||
"github.com/alist-org/alist/v3/internal/errs"
|
||||
@ -39,8 +40,14 @@ func (d *AliyundriveOpen) Init(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
d.DriveId = utils.Json.Get(res, "default_drive_id").ToString()
|
||||
d.limitList = utils.LimitRateCtx(d.list, time.Second/4)
|
||||
d.limitLink = utils.LimitRateCtx(d.link, time.Second)
|
||||
d.limitList = rateg.LimitFnCtx(d.list, rateg.LimitFnOption{
|
||||
Limit: 4,
|
||||
Bucket: 1,
|
||||
})
|
||||
d.limitLink = rateg.LimitFnCtx(d.link, rateg.LimitFnOption{
|
||||
Limit: 1,
|
||||
Bucket: 1,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"github.com/alist-org/alist/v3/internal/op"
|
||||
"github.com/alist-org/alist/v3/pkg/utils"
|
||||
"github.com/go-resty/resty/v2"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// do others that not defined in Driver interface
|
||||
@ -19,9 +20,9 @@ func (d *AliyundriveOpen) refreshToken() error {
|
||||
if d.OauthTokenURL != "" && d.ClientID == "" {
|
||||
url = d.OauthTokenURL
|
||||
}
|
||||
var resp base.TokenResp
|
||||
//var resp base.TokenResp
|
||||
var e ErrResp
|
||||
_, err := base.RestyClient.R().
|
||||
res, err := base.RestyClient.R().
|
||||
ForceContentType("application/json").
|
||||
SetBody(base.Json{
|
||||
"client_id": d.ClientID,
|
||||
@ -29,19 +30,21 @@ func (d *AliyundriveOpen) refreshToken() error {
|
||||
"grant_type": "refresh_token",
|
||||
"refresh_token": d.RefreshToken,
|
||||
}).
|
||||
SetResult(&resp).
|
||||
//SetResult(&resp).
|
||||
SetError(&e).
|
||||
Post(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Debugf("[ali_open] refresh token response: %s", res.String())
|
||||
if e.Code != "" {
|
||||
return fmt.Errorf("failed to refresh token: %s", e.Message)
|
||||
}
|
||||
if resp.RefreshToken == "" {
|
||||
refresh, access := utils.Json.Get(res.Body(), "refresh_token").ToString(), utils.Json.Get(res.Body(), "access_token").ToString()
|
||||
if refresh == "" {
|
||||
return errors.New("failed to refresh token: refresh token is empty")
|
||||
}
|
||||
d.RefreshToken, d.AccessToken = resp.RefreshToken, resp.AccessToken
|
||||
d.RefreshToken, d.AccessToken = refresh, access
|
||||
op.MustSaveDriverStorage(d)
|
||||
return nil
|
||||
}
|
||||
@ -65,6 +68,9 @@ func (d *AliyundriveOpen) requestReturnErrResp(uri, method string, callback base
|
||||
req.SetError(&e)
|
||||
res, err := req.Execute(method, d.base+uri)
|
||||
if err != nil {
|
||||
if res != nil {
|
||||
log.Errorf("[aliyundrive_open] request error: %s", res.String())
|
||||
}
|
||||
return nil, err, nil
|
||||
}
|
||||
isRetry := len(retry) > 0 && retry[0]
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/Xhofe/rateg"
|
||||
"github.com/alist-org/alist/v3/drivers/base"
|
||||
"github.com/alist-org/alist/v3/internal/driver"
|
||||
"github.com/alist-org/alist/v3/internal/errs"
|
||||
@ -52,8 +53,14 @@ func (d *AliyundriveShare) Init(ctx context.Context) error {
|
||||
log.Errorf("%+v", err)
|
||||
}
|
||||
})
|
||||
d.limitList = utils.LimitRateCtx(d.list, time.Second/4)
|
||||
d.limitLink = utils.LimitRateCtx(d.link, time.Second)
|
||||
d.limitList = rateg.LimitFnCtx(d.list, rateg.LimitFnOption{
|
||||
Limit: 4,
|
||||
Bucket: 1,
|
||||
})
|
||||
d.limitLink = rateg.LimitFnCtx(d.link, rateg.LimitFnOption{
|
||||
Limit: 1,
|
||||
Bucket: 1,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
package baidu_netdisk
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
@ -118,7 +117,6 @@ func (d *BaiduNetdisk) Put(ctx context.Context, dstDir model.Obj, stream model.F
|
||||
_ = os.Remove(tempFile.Name())
|
||||
}()
|
||||
var Default int64 = 4 * 1024 * 1024
|
||||
defaultByteData := make([]byte, Default)
|
||||
count := int(math.Ceil(float64(stream.GetSize()) / float64(Default)))
|
||||
var SliceSize int64 = 256 * 1024
|
||||
// cal md5
|
||||
@ -130,20 +128,14 @@ func (d *BaiduNetdisk) Put(ctx context.Context, dstDir model.Obj, stream model.F
|
||||
left := stream.GetSize()
|
||||
for i := 0; i < count; i++ {
|
||||
byteSize := Default
|
||||
var byteData []byte
|
||||
if left < Default {
|
||||
byteSize = left
|
||||
byteData = make([]byte, byteSize)
|
||||
} else {
|
||||
byteData = defaultByteData
|
||||
}
|
||||
left -= byteSize
|
||||
_, err = io.ReadFull(tempFile, byteData)
|
||||
_, err = io.Copy(io.MultiWriter(h1, h2), io.LimitReader(tempFile, byteSize))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h1.Write(byteData)
|
||||
h2.Write(byteData)
|
||||
block_list = append(block_list, fmt.Sprintf("\"%s\"", hex.EncodeToString(h2.Sum(nil))))
|
||||
h2.Reset()
|
||||
}
|
||||
@ -177,6 +169,7 @@ func (d *BaiduNetdisk) Put(ctx context.Context, dstDir model.Obj, stream model.F
|
||||
params := map[string]string{
|
||||
"method": "precreate",
|
||||
}
|
||||
log.Debugf("[baidu_netdisk] precreate data: %s", data)
|
||||
var precreateResp PrecreateResp
|
||||
_, err = d.post("/xpan/file", params, data, &precreateResp)
|
||||
if err != nil {
|
||||
@ -199,24 +192,16 @@ func (d *BaiduNetdisk) Put(ctx context.Context, dstDir model.Obj, stream model.F
|
||||
return ctx.Err()
|
||||
}
|
||||
byteSize := Default
|
||||
var byteData []byte
|
||||
if left < Default {
|
||||
byteSize = left
|
||||
byteData = make([]byte, byteSize)
|
||||
} else {
|
||||
byteData = defaultByteData
|
||||
}
|
||||
left -= byteSize
|
||||
_, err = io.ReadFull(tempFile, byteData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
u := "https://d.pcs.baidu.com/rest/2.0/pcs/superfile2"
|
||||
params["partseq"] = strconv.Itoa(partseq)
|
||||
res, err := base.RestyClient.R().
|
||||
SetContext(ctx).
|
||||
SetQueryParams(params).
|
||||
SetFileReader("file", stream.GetName(), bytes.NewReader(byteData)).
|
||||
SetFileReader("file", stream.GetName(), io.LimitReader(tempFile, byteSize)).
|
||||
Post(u)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -13,6 +13,7 @@ import (
|
||||
"github.com/alist-org/alist/v3/internal/op"
|
||||
"github.com/alist-org/alist/v3/pkg/utils"
|
||||
"github.com/go-resty/resty/v2"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// do others that not defined in Driver interface
|
||||
@ -62,16 +63,17 @@ func (d *BaiduNetdisk) request(furl string, method string, callback base.ReqCall
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Debugf("[baidu_netdisk] req: %s, resp: %s", furl, res.String())
|
||||
errno := utils.Json.Get(res.Body(), "errno").ToInt()
|
||||
if errno != 0 {
|
||||
if errno == -6 {
|
||||
if utils.SliceContains([]int{111, -6}, errno) {
|
||||
err = d.refreshToken()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return d.request(furl, method, callback, resp)
|
||||
}
|
||||
return nil, fmt.Errorf("errno: %d, refer to https://pan.baidu.com/union/doc/", errno)
|
||||
return nil, fmt.Errorf("req: [%s] ,errno: %d, refer to https://pan.baidu.com/union/doc/", furl, errno)
|
||||
}
|
||||
return res.Body(), nil
|
||||
}
|
||||
|
@ -3,15 +3,16 @@ package terbox
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"github.com/alist-org/alist/v3/drivers/base"
|
||||
"github.com/alist-org/alist/v3/internal/model"
|
||||
"github.com/alist-org/alist/v3/pkg/utils"
|
||||
"github.com/go-resty/resty/v2"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/alist-org/alist/v3/drivers/base"
|
||||
"github.com/alist-org/alist/v3/internal/model"
|
||||
"github.com/alist-org/alist/v3/pkg/utils"
|
||||
"github.com/go-resty/resty/v2"
|
||||
)
|
||||
|
||||
func (d *Terabox) request(furl string, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) {
|
||||
@ -139,6 +140,11 @@ func (d *Terabox) linkOfficial(file model.Obj, args model.LinkArgs) (*model.Link
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(resp.Dlink) == 0 {
|
||||
return nil, fmt.Errorf("fid %s no dlink found, errno: %d", file.GetID(), resp.Errno)
|
||||
}
|
||||
|
||||
res, err := base.NoRedirectClient.R().SetHeader("Cookie", d.Cookie).SetHeader("User-Agent", base.UserAgent).Get(resp.Dlink[0].Dlink)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
3
go.mod
3
go.mod
@ -49,6 +49,7 @@ require (
|
||||
require (
|
||||
github.com/BurntSushi/toml v0.3.1 // indirect
|
||||
github.com/RoaringBitmap/roaring v1.2.3 // indirect
|
||||
github.com/Xhofe/rateg v0.0.0-20230728072201-251a4e1adad4 // indirect
|
||||
github.com/aead/ecdh v0.2.0 // indirect
|
||||
github.com/aliyun/aliyun-oss-go-sdk v2.2.5+incompatible // indirect
|
||||
github.com/andreburgaud/crypt2go v1.1.0 // indirect
|
||||
@ -135,7 +136,7 @@ require (
|
||||
golang.org/x/arch v0.3.0 // indirect
|
||||
golang.org/x/sys v0.10.0 // indirect
|
||||
golang.org/x/text v0.11.0 // indirect
|
||||
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/protobuf v1.31.0 // indirect
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
|
||||
|
5
go.sum
5
go.sum
@ -6,6 +6,8 @@ github.com/SheltonZhu/115driver v1.0.14 h1:uW3dl8J9KDMw+3gPxQdhTysoGhw0/uI1484GT
|
||||
github.com/SheltonZhu/115driver v1.0.14/go.mod h1:00ixivHH5HqDj4S7kAWbkuUrjtsJTxc7cGv5RMw3RVs=
|
||||
github.com/Xhofe/go-cache v0.0.0-20220723083548-714439c8af9a h1:RenIAa2q4H8UcS/cqmwdT1WCWIAH5aumP8m8RpbqVsE=
|
||||
github.com/Xhofe/go-cache v0.0.0-20220723083548-714439c8af9a/go.mod h1:sSBbaOg90XwWKtpT56kVujF0bIeVITnPlssLclogS04=
|
||||
github.com/Xhofe/rateg v0.0.0-20230728072201-251a4e1adad4 h1:WnvifFgYyogPz2ZFvaVLk4gI/Co0paF92FmxSR6U1zY=
|
||||
github.com/Xhofe/rateg v0.0.0-20230728072201-251a4e1adad4/go.mod h1:8pWlL2rpusvx7Xa6yYaIWOJ8bR3gPdFBUT7OystyGOY=
|
||||
github.com/Xhofe/wopan-sdk-go v0.1.1 h1:dSrTxNYclqNuo9libjtC+R6C4RCen/inh/dUXd12vpM=
|
||||
github.com/Xhofe/wopan-sdk-go v0.1.1/go.mod h1:xWcUS7PoFLDD9gy2BK2VQfilEsZngLMz2Vkx3oF2zJY=
|
||||
github.com/aead/ecdh v0.2.0 h1:pYop54xVaq/CEREFEcukHRZfTdjiWvYIsZDXXrBapQQ=
|
||||
@ -210,6 +212,7 @@ github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APP
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
|
||||
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ=
|
||||
github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g=
|
||||
github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
@ -394,6 +397,8 @@ golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxb
|
||||
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af h1:Yx9k8YCG3dvF87UAn2tu2HQLf2dt/eR1bXxpLMWeH+Y=
|
||||
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
||||
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
|
@ -36,7 +36,7 @@ func putAsTask(dstDirPath string, file *model.FileStream) error {
|
||||
UploadTaskManager.Submit(task.WithCancelCtx(&task.Task[uint64]{
|
||||
Name: fmt.Sprintf("upload %s to [%s](%s)", file.GetName(), storage.GetStorage().MountPath, dstDirActualPath),
|
||||
Func: func(task *task.Task[uint64]) error {
|
||||
return op.Put(task.Ctx, storage, dstDirActualPath, file, nil, true)
|
||||
return op.Put(task.Ctx, storage, dstDirActualPath, file, task.SetProgress, true)
|
||||
},
|
||||
}))
|
||||
return nil
|
||||
|
@ -25,6 +25,7 @@ type Link struct {
|
||||
Status int // status maybe 200 or 206, etc
|
||||
FilePath *string // local file, return the filepath
|
||||
Expiration *time.Duration // url expiration time
|
||||
IPCacheKey bool // add ip to cache key
|
||||
//Handle func(w http.ResponseWriter, r *http.Request) error `json:"-"` // custom handler
|
||||
Writer WriterFunc `json:"-"` // custom writer
|
||||
}
|
||||
|
@ -243,7 +243,7 @@ func Link(ctx context.Context, storage driver.Driver, path string, args model.Li
|
||||
if file.IsDir() {
|
||||
return nil, nil, errors.WithStack(errs.NotFile)
|
||||
}
|
||||
key := Key(storage, path) + ":" + args.IP
|
||||
key := Key(storage, path)
|
||||
if link, ok := linkCache.Get(key); ok {
|
||||
return link, file, nil
|
||||
}
|
||||
@ -253,6 +253,9 @@ func Link(ctx context.Context, storage driver.Driver, path string, args model.Li
|
||||
return nil, errors.Wrapf(err, "failed get link")
|
||||
}
|
||||
if link.Expiration != nil {
|
||||
if link.IPCacheKey {
|
||||
key = key + ":" + args.IP
|
||||
}
|
||||
linkCache.Set(key, link, cache.WithEx[*model.Link](*link.Expiration))
|
||||
}
|
||||
return link, nil
|
||||
@ -563,6 +566,9 @@ func Put(ctx context.Context, storage driver.Driver, dstDirPath string, file *mo
|
||||
err := Remove(ctx, storage, tempPath)
|
||||
if err != nil {
|
||||
return err
|
||||
} else {
|
||||
key := Key(storage, stdpath.Join(dstDirPath, file.GetName()))
|
||||
linkCache.Del(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,12 +3,13 @@ package qbittorrent
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"github.com/alist-org/alist/v3/pkg/utils"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/http/cookiejar"
|
||||
"net/url"
|
||||
|
||||
"github.com/alist-org/alist/v3/pkg/utils"
|
||||
)
|
||||
|
||||
type Client interface {
|
||||
@ -213,7 +214,7 @@ type TorrentInfo struct {
|
||||
Hash string `json:"hash"` //
|
||||
LastActivity int `json:"last_activity"` // 上次活跃的时间(Unix Epoch)
|
||||
MagnetURI string `json:"magnet_uri"` // 与此 torrent 对应的 Magnet URI
|
||||
MaxRatio int `json:"max_ratio"` // 种子/上传停止种子前的最大共享比率
|
||||
MaxRatio float64 `json:"max_ratio"` // 种子/上传停止种子前的最大共享比率
|
||||
MaxSeedingTime int `json:"max_seeding_time"` // 停止种子种子前的最长种子时间(秒)
|
||||
Name string `json:"name"` //
|
||||
NumComplete int `json:"num_complete"` //
|
||||
|
@ -1,114 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"time"
|
||||
)
|
||||
|
||||
func LimitRateReflect(f interface{}, interval time.Duration) func(...interface{}) []interface{} {
|
||||
// Use closures to save the time of the last function call
|
||||
var lastCall time.Time
|
||||
|
||||
fValue := reflect.ValueOf(f)
|
||||
fType := fValue.Type()
|
||||
|
||||
if fType.Kind() != reflect.Func {
|
||||
panic("f must be a function")
|
||||
}
|
||||
|
||||
//if fType.NumOut() == 0 {
|
||||
// panic("f must have at least one output parameter")
|
||||
//}
|
||||
|
||||
outCount := fType.NumOut()
|
||||
outTypes := make([]reflect.Type, outCount)
|
||||
|
||||
for i := 0; i < outCount; i++ {
|
||||
outTypes[i] = fType.Out(i)
|
||||
}
|
||||
|
||||
// Returns a new function, which is used to limit the function to be called only once at a specified time interval
|
||||
return func(args ...interface{}) []interface{} {
|
||||
// Calculate the time interval since the last function call
|
||||
elapsed := time.Since(lastCall)
|
||||
// If the interval is less than the specified time, wait for the remaining time
|
||||
if elapsed < interval {
|
||||
time.Sleep(interval - elapsed)
|
||||
}
|
||||
// Update the time of the last function call
|
||||
lastCall = time.Now()
|
||||
|
||||
inCount := fType.NumIn()
|
||||
in := make([]reflect.Value, inCount)
|
||||
|
||||
if len(args) != inCount {
|
||||
panic("wrong number of arguments")
|
||||
}
|
||||
|
||||
for i := 0; i < inCount; i++ {
|
||||
in[i] = reflect.ValueOf(args[i])
|
||||
}
|
||||
|
||||
out := fValue.Call(in)
|
||||
|
||||
if len(out) != outCount {
|
||||
panic("function returned wrong number of values")
|
||||
}
|
||||
|
||||
result := make([]interface{}, outCount)
|
||||
|
||||
for i := 0; i < outCount; i++ {
|
||||
result[i] = out[i].Interface()
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
type Fn[T any, R any] func(T) (R, error)
|
||||
type FnCtx[T any, R any] func(context.Context, T) (R, error)
|
||||
|
||||
func LimitRate[T any, R any](f Fn[T, R], interval time.Duration) Fn[T, R] {
|
||||
// Use closures to save the time of the last function call
|
||||
var lastCall time.Time
|
||||
// Returns a new function, which is used to limit the function to be called only once at a specified time interval
|
||||
return func(t T) (R, error) {
|
||||
// Calculate the time interval since the last function call
|
||||
elapsed := time.Since(lastCall)
|
||||
// If the interval is less than the specified time, wait for the remaining time
|
||||
if elapsed < interval {
|
||||
time.Sleep(interval - elapsed)
|
||||
}
|
||||
// Update the time of the last function call
|
||||
lastCall = time.Now()
|
||||
// Execute the function that needs to be limited
|
||||
return f(t)
|
||||
}
|
||||
}
|
||||
|
||||
func LimitRateCtx[T any, R any](f FnCtx[T, R], interval time.Duration) FnCtx[T, R] {
|
||||
// Use closures to save the time of the last function call
|
||||
var lastCall time.Time
|
||||
// Returns a new function, which is used to limit the function to be called only once at a specified time interval
|
||||
return func(ctx context.Context, t T) (R, error) {
|
||||
// Calculate the time interval since the last function call
|
||||
elapsed := time.Since(lastCall)
|
||||
// If the interval is less than the specified time, wait for the remaining time
|
||||
if elapsed < interval {
|
||||
t := time.NewTimer(interval - elapsed)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
t.Stop()
|
||||
var zero R
|
||||
return zero, ctx.Err()
|
||||
case <-t.C:
|
||||
|
||||
}
|
||||
}
|
||||
// Update the time of the last function call
|
||||
lastCall = time.Now()
|
||||
// Execute the function that needs to be limited
|
||||
return f(ctx, t)
|
||||
}
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
package utils_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/alist-org/alist/v3/pkg/utils"
|
||||
)
|
||||
|
||||
func myFunction(a int) (int, error) {
|
||||
// do something
|
||||
return a + 1, nil
|
||||
}
|
||||
|
||||
func TestLimitRate(t *testing.T) {
|
||||
myLimitedFunction := utils.LimitRate(myFunction, time.Second)
|
||||
result, _ := myLimitedFunction(1)
|
||||
t.Log(result) // Output: 2
|
||||
result, _ = myLimitedFunction(2)
|
||||
t.Log(result) // Output: 3
|
||||
}
|
||||
|
||||
type Test struct {
|
||||
limitFn func(string) (string, error)
|
||||
}
|
||||
|
||||
func (t *Test) myFunction(a string) (string, error) {
|
||||
// do something
|
||||
return a + " world", nil
|
||||
}
|
||||
|
||||
func TestLimitRateStruct(t *testing.T) {
|
||||
test := &Test{}
|
||||
test.limitFn = utils.LimitRate(test.myFunction, time.Second)
|
||||
result, _ := test.limitFn("hello")
|
||||
t.Log(result) // Output: hello world
|
||||
result, _ = test.limitFn("hi")
|
||||
t.Log(result) // Output: hi world
|
||||
}
|
||||
|
||||
func myFunctionCtx(ctx context.Context, a int) (int, error) {
|
||||
// do something
|
||||
return a + 1, nil
|
||||
}
|
||||
func TestLimitRateCtx(t *testing.T) {
|
||||
myLimitedFunction := utils.LimitRateCtx(myFunctionCtx, time.Second)
|
||||
result, _ := myLimitedFunction(context.Background(), 1)
|
||||
t.Log(result) // Output: 2
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
go func() {
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
cancel()
|
||||
}()
|
||||
result, err := myLimitedFunction(ctx, 2)
|
||||
t.Log(result, err) // Output: 0 context canceled
|
||||
result, _ = myLimitedFunction(context.Background(), 3)
|
||||
t.Log(result) // Output: 4
|
||||
}
|
Reference in New Issue
Block a user