Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
c3e43ff605 | |||
5f19d73fcc | |||
bdf4b52885 | |||
6106a2d4cc | |||
b6451451b1 | |||
f06d2c0348 |
1
build.sh
1
build.sh
@ -8,6 +8,7 @@ if [ "$1" = "dev" ]; then
|
|||||||
version="dev"
|
version="dev"
|
||||||
webVersion="dev"
|
webVersion="dev"
|
||||||
else
|
else
|
||||||
|
git tag -d beta
|
||||||
version=$(git describe --abbrev=0 --tags)
|
version=$(git describe --abbrev=0 --tags)
|
||||||
webVersion=$(wget -qO- -t1 -T2 "https://api.github.com/repos/alist-org/alist-web/releases/latest" | grep "tag_name" | head -n 1 | awk -F ":" '{print $2}' | sed 's/\"//g;s/,//g;s/ //g')
|
webVersion=$(wget -qO- -t1 -T2 "https://api.github.com/repos/alist-org/alist-web/releases/latest" | grep "tag_name" | head -n 1 | awk -F ":" '{print $2}' | sed 's/\"//g;s/,//g;s/ //g')
|
||||||
fi
|
fi
|
||||||
|
43
drivers/115/appver.go
Normal file
43
drivers/115/appver.go
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package _115
|
||||||
|
|
||||||
|
import (
|
||||||
|
driver115 "github.com/SheltonZhu/115driver/pkg/driver"
|
||||||
|
"github.com/alist-org/alist/v3/drivers/base"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
md5Salt = "Qclm8MGWUv59TnrR0XPg"
|
||||||
|
appVer = "27.0.5.7"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (d *Pan115) getAppVersion() ([]driver115.AppVersion, error) {
|
||||||
|
result := driver115.VersionResp{}
|
||||||
|
resp, err := base.RestyClient.R().Get(driver115.ApiGetVersion)
|
||||||
|
|
||||||
|
err = driver115.CheckErr(err, &result, resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.Data.GetAppVersions(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Pan115) getAppVer() string {
|
||||||
|
// todo add some cache?
|
||||||
|
vers, err := d.getAppVersion()
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("[115] get app version failed: %v", err)
|
||||||
|
return appVer
|
||||||
|
}
|
||||||
|
for _, ver := range vers {
|
||||||
|
if ver.AppName == "win" {
|
||||||
|
return ver.Version
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return appVer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Pan115) initAppVer() {
|
||||||
|
appVer = d.getAppVer()
|
||||||
|
}
|
@ -3,6 +3,7 @@ package _115
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
driver115 "github.com/SheltonZhu/115driver/pkg/driver"
|
driver115 "github.com/SheltonZhu/115driver/pkg/driver"
|
||||||
"github.com/alist-org/alist/v3/internal/driver"
|
"github.com/alist-org/alist/v3/internal/driver"
|
||||||
@ -16,8 +17,9 @@ import (
|
|||||||
type Pan115 struct {
|
type Pan115 struct {
|
||||||
model.Storage
|
model.Storage
|
||||||
Addition
|
Addition
|
||||||
client *driver115.Pan115Client
|
client *driver115.Pan115Client
|
||||||
limiter *rate.Limiter
|
limiter *rate.Limiter
|
||||||
|
appVerOnce sync.Once
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Pan115) Config() driver.Config {
|
func (d *Pan115) Config() driver.Config {
|
||||||
@ -29,6 +31,7 @@ func (d *Pan115) GetAddition() driver.Additional {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Pan115) Init(ctx context.Context) error {
|
func (d *Pan115) Init(ctx context.Context) error {
|
||||||
|
d.appVerOnce.Do(d.initAppVer)
|
||||||
if d.LimitRate > 0 {
|
if d.LimitRate > 0 {
|
||||||
d.limiter = rate.NewLimiter(rate.Limit(d.LimitRate), 1)
|
d.limiter = rate.NewLimiter(rate.Limit(d.LimitRate), 1)
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,9 @@ package _115
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto/md5"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@ -26,12 +28,12 @@ import (
|
|||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
var UserAgent = driver115.UA115Desktop
|
//var UserAgent = driver115.UA115Browser
|
||||||
|
|
||||||
func (d *Pan115) login() error {
|
func (d *Pan115) login() error {
|
||||||
var err error
|
var err error
|
||||||
opts := []driver115.Option{
|
opts := []driver115.Option{
|
||||||
driver115.UA(UserAgent),
|
driver115.UA(d.getUA()),
|
||||||
func(c *driver115.Pan115Client) {
|
func(c *driver115.Pan115Client) {
|
||||||
c.Client.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: conf.Conf.TlsInsecureSkipVerify})
|
c.Client.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: conf.Conf.TlsInsecureSkipVerify})
|
||||||
},
|
},
|
||||||
@ -73,11 +75,11 @@ func (d *Pan115) getFiles(fileId string) ([]FileObj, error) {
|
|||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
func (d *Pan115) getUA() string {
|
||||||
appVer = "2.0.3.6"
|
return fmt.Sprintf("Mozilla/5.0 115Browser/%s", appVer)
|
||||||
)
|
}
|
||||||
|
|
||||||
func (c *Pan115) DownloadWithUA(pickCode, ua string) (*driver115.DownloadInfo, error) {
|
func (d *Pan115) DownloadWithUA(pickCode, ua string) (*driver115.DownloadInfo, error) {
|
||||||
key := crypto.GenerateKey()
|
key := crypto.GenerateKey()
|
||||||
result := driver115.DownloadResp{}
|
result := driver115.DownloadResp{}
|
||||||
params, err := utils.Json.Marshal(map[string]string{"pickcode": pickCode})
|
params, err := utils.Json.Marshal(map[string]string{"pickcode": pickCode})
|
||||||
@ -91,10 +93,10 @@ func (c *Pan115) DownloadWithUA(pickCode, ua string) (*driver115.DownloadInfo, e
|
|||||||
reqUrl := fmt.Sprintf("%s?t=%s", driver115.ApiDownloadGetUrl, driver115.Now().String())
|
reqUrl := fmt.Sprintf("%s?t=%s", driver115.ApiDownloadGetUrl, driver115.Now().String())
|
||||||
req, _ := http.NewRequest(http.MethodPost, reqUrl, bodyReader)
|
req, _ := http.NewRequest(http.MethodPost, reqUrl, bodyReader)
|
||||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||||
req.Header.Set("Cookie", c.Cookie)
|
req.Header.Set("Cookie", d.Cookie)
|
||||||
req.Header.Set("User-Agent", ua)
|
req.Header.Set("User-Agent", ua)
|
||||||
|
|
||||||
resp, err := c.client.Client.GetClient().Do(req)
|
resp, err := d.client.Client.GetClient().Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -132,6 +134,13 @@ func (c *Pan115) DownloadWithUA(pickCode, ua string) (*driver115.DownloadInfo, e
|
|||||||
return nil, driver115.ErrUnexpected
|
return nil, driver115.ErrUnexpected
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Pan115) GenerateToken(fileID, preID, timeStamp, fileSize, signKey, signVal string) string {
|
||||||
|
userID := strconv.FormatInt(c.client.UserID, 10)
|
||||||
|
userIDMd5 := md5.Sum([]byte(userID))
|
||||||
|
tokenMd5 := md5.Sum([]byte(md5Salt + fileID + fileSize + signKey + signVal + userID + timeStamp + hex.EncodeToString(userIDMd5[:]) + appVer))
|
||||||
|
return hex.EncodeToString(tokenMd5[:])
|
||||||
|
}
|
||||||
|
|
||||||
func (d *Pan115) rapidUpload(fileSize int64, fileName, dirID, preID, fileID string, stream model.FileStreamer) (*driver115.UploadInitResp, error) {
|
func (d *Pan115) rapidUpload(fileSize int64, fileName, dirID, preID, fileID string, stream model.FileStreamer) (*driver115.UploadInitResp, error) {
|
||||||
var (
|
var (
|
||||||
ecdhCipher *cipher.EcdhCipher
|
ecdhCipher *cipher.EcdhCipher
|
||||||
@ -161,7 +170,7 @@ func (d *Pan115) rapidUpload(fileSize int64, fileName, dirID, preID, fileID stri
|
|||||||
|
|
||||||
signKey, signVal := "", ""
|
signKey, signVal := "", ""
|
||||||
for retry := true; retry; {
|
for retry := true; retry; {
|
||||||
t := driver115.Now()
|
t := driver115.NowMilli()
|
||||||
|
|
||||||
if encodedToken, err = ecdhCipher.EncodeToken(t.ToInt64()); err != nil {
|
if encodedToken, err = ecdhCipher.EncodeToken(t.ToInt64()); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -172,7 +181,7 @@ func (d *Pan115) rapidUpload(fileSize int64, fileName, dirID, preID, fileID stri
|
|||||||
}
|
}
|
||||||
|
|
||||||
form.Set("t", t.String())
|
form.Set("t", t.String())
|
||||||
form.Set("token", d.client.GenerateToken(fileID, preID, t.String(), fileSizeStr, signKey, signVal))
|
form.Set("token", d.GenerateToken(fileID, preID, t.String(), fileSizeStr, signKey, signVal))
|
||||||
if signKey != "" && signVal != "" {
|
if signKey != "" && signVal != "" {
|
||||||
form.Set("sign_key", signKey)
|
form.Set("sign_key", signKey)
|
||||||
form.Set("sign_val", signVal)
|
form.Set("sign_val", signVal)
|
||||||
@ -225,6 +234,9 @@ func UploadDigestRange(stream model.FileStreamer, rangeSpec string) (result stri
|
|||||||
|
|
||||||
length := end - start + 1
|
length := end - start + 1
|
||||||
reader, err := stream.RangeRead(http_range.Range{Start: start, Length: length})
|
reader, err := stream.RangeRead(http_range.Range{Start: start, Length: length})
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
hashStr, err := utils.HashReader(utils.SHA1, reader)
|
hashStr, err := utils.HashReader(utils.SHA1, reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
stdpath "path"
|
stdpath "path"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/alist-org/alist/v3/drivers/base"
|
"github.com/alist-org/alist/v3/drivers/base"
|
||||||
"github.com/alist-org/alist/v3/pkg/utils"
|
"github.com/alist-org/alist/v3/pkg/utils"
|
||||||
@ -23,7 +24,9 @@ import (
|
|||||||
type Terabox struct {
|
type Terabox struct {
|
||||||
model.Storage
|
model.Storage
|
||||||
Addition
|
Addition
|
||||||
JsToken string
|
JsToken string
|
||||||
|
url_domain_prefix string
|
||||||
|
base_url string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Terabox) Config() driver.Config {
|
func (d *Terabox) Config() driver.Config {
|
||||||
@ -36,6 +39,8 @@ func (d *Terabox) GetAddition() driver.Additional {
|
|||||||
|
|
||||||
func (d *Terabox) Init(ctx context.Context) error {
|
func (d *Terabox) Init(ctx context.Context) error {
|
||||||
var resp CheckLoginResp
|
var resp CheckLoginResp
|
||||||
|
d.base_url = "https://www.terabox.com"
|
||||||
|
d.url_domain_prefix = "jp"
|
||||||
_, err := d.get("/api/check/login", nil, &resp)
|
_, err := d.get("/api/check/login", nil, &resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -71,7 +76,16 @@ func (d *Terabox) Link(ctx context.Context, file model.Obj, args model.LinkArgs)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Terabox) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
|
func (d *Terabox) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
|
||||||
_, err := d.create(stdpath.Join(parentDir.GetPath(), dirName), 0, 1, "", "")
|
params := map[string]string{
|
||||||
|
"a": "commit",
|
||||||
|
}
|
||||||
|
data := map[string]string{
|
||||||
|
"path": stdpath.Join(parentDir.GetPath(), dirName),
|
||||||
|
"isdir": "1",
|
||||||
|
"block_list": "[]",
|
||||||
|
}
|
||||||
|
res, err := d.post_form("/api/create", params, data, nil)
|
||||||
|
log.Debugln(string(res))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,6 +131,20 @@ func (d *Terabox) Remove(ctx context.Context, obj model.Obj) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Terabox) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
|
func (d *Terabox) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
|
||||||
|
resp, err := base.RestyClient.R().
|
||||||
|
SetContext(ctx).
|
||||||
|
Get("https://" + d.url_domain_prefix + "-data.terabox.com/rest/2.0/pcs/file?method=locateupload")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var locateupload_resp LocateUploadResp
|
||||||
|
err = utils.Json.Unmarshal(resp.Body(), &locateupload_resp)
|
||||||
|
if err != nil {
|
||||||
|
log.Debugln(resp)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Debugln(locateupload_resp)
|
||||||
|
|
||||||
tempFile, err := stream.CacheFullInTempFile()
|
tempFile, err := stream.CacheFullInTempFile()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -157,23 +185,28 @@ func (d *Terabox) Put(ctx context.Context, dstDir model.Obj, stream model.FileSt
|
|||||||
rawPath := stdpath.Join(dstDir.GetPath(), stream.GetName())
|
rawPath := stdpath.Join(dstDir.GetPath(), stream.GetName())
|
||||||
path := encodeURIComponent(rawPath)
|
path := encodeURIComponent(rawPath)
|
||||||
block_list_str := fmt.Sprintf("[%s]", strings.Join(block_list, ","))
|
block_list_str := fmt.Sprintf("[%s]", strings.Join(block_list, ","))
|
||||||
data := fmt.Sprintf("path=%s&size=%d&isdir=0&autoinit=1&block_list=%s",
|
data := map[string]string{
|
||||||
path, stream.GetSize(),
|
"path": rawPath,
|
||||||
block_list_str)
|
"autoinit": "1",
|
||||||
params := map[string]string{}
|
"target_path": dstDir.GetPath(),
|
||||||
|
"block_list": block_list_str,
|
||||||
|
"local_mtime": strconv.FormatInt(time.Now().Unix(), 10),
|
||||||
|
}
|
||||||
var precreateResp PrecreateResp
|
var precreateResp PrecreateResp
|
||||||
_, err = d.post("/api/precreate", params, data, &precreateResp)
|
log.Debugln(data)
|
||||||
|
res, err := d.post_form("/api/precreate", nil, data, &precreateResp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.Debugf("%+v", precreateResp)
|
log.Debugf("%+v", precreateResp)
|
||||||
if precreateResp.Errno != 0 {
|
if precreateResp.Errno != 0 {
|
||||||
|
log.Debugln(string(res))
|
||||||
return fmt.Errorf("[terabox] failed to precreate file, errno: %d", precreateResp.Errno)
|
return fmt.Errorf("[terabox] failed to precreate file, errno: %d", precreateResp.Errno)
|
||||||
}
|
}
|
||||||
if precreateResp.ReturnType == 2 {
|
if precreateResp.ReturnType == 2 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
params = map[string]string{
|
params := map[string]string{
|
||||||
"method": "upload",
|
"method": "upload",
|
||||||
"path": path,
|
"path": path,
|
||||||
"uploadid": precreateResp.Uploadid,
|
"uploadid": precreateResp.Uploadid,
|
||||||
@ -200,7 +233,7 @@ func (d *Terabox) Put(ctx context.Context, dstDir model.Obj, stream model.FileSt
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
u := "https://c-jp.terabox.com/rest/2.0/pcs/superfile2"
|
u := "https://" + locateupload_resp.Host + "/rest/2.0/pcs/superfile2"
|
||||||
params["partseq"] = strconv.Itoa(partseq)
|
params["partseq"] = strconv.Itoa(partseq)
|
||||||
res, err := base.RestyClient.R().
|
res, err := base.RestyClient.R().
|
||||||
SetContext(ctx).
|
SetContext(ctx).
|
||||||
@ -216,7 +249,20 @@ func (d *Terabox) Put(ctx context.Context, dstDir model.Obj, stream model.FileSt
|
|||||||
up(float64(i) * 100 / float64(len(precreateResp.BlockList)))
|
up(float64(i) * 100 / float64(len(precreateResp.BlockList)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_, err = d.create(rawPath, stream.GetSize(), 0, precreateResp.Uploadid, block_list_str)
|
params = map[string]string{
|
||||||
|
"isdir": "0",
|
||||||
|
"rtype": "1",
|
||||||
|
}
|
||||||
|
data = map[string]string{
|
||||||
|
"path": rawPath,
|
||||||
|
"size": strconv.FormatInt(stream.GetSize(), 10),
|
||||||
|
"uploadid": precreateResp.Uploadid,
|
||||||
|
"target_path": dstDir.GetPath(),
|
||||||
|
"block_list": block_list_str,
|
||||||
|
"local_mtime": strconv.FormatInt(time.Now().Unix(), 10),
|
||||||
|
}
|
||||||
|
res, err = d.post_form("/api/create", params, data, nil)
|
||||||
|
log.Debugln(string(res))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,3 +95,7 @@ type PrecreateResp struct {
|
|||||||
type CheckLoginResp struct {
|
type CheckLoginResp struct {
|
||||||
Errno int `json:"errno"`
|
Errno int `json:"errno"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type LocateUploadResp struct {
|
||||||
|
Host string `json:"host"`
|
||||||
|
}
|
||||||
|
@ -14,6 +14,7 @@ import (
|
|||||||
"github.com/alist-org/alist/v3/internal/model"
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
"github.com/alist-org/alist/v3/pkg/utils"
|
"github.com/alist-org/alist/v3/pkg/utils"
|
||||||
"github.com/go-resty/resty/v2"
|
"github.com/go-resty/resty/v2"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getStrBetween(raw, start, end string) string {
|
func getStrBetween(raw, start, end string) string {
|
||||||
@ -28,11 +29,11 @@ func getStrBetween(raw, start, end string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Terabox) resetJsToken() error {
|
func (d *Terabox) resetJsToken() error {
|
||||||
u := "https://www.terabox.com/main"
|
u := d.base_url
|
||||||
res, err := base.RestyClient.R().SetHeaders(map[string]string{
|
res, err := base.RestyClient.R().SetHeaders(map[string]string{
|
||||||
"Cookie": d.Cookie,
|
"Cookie": d.Cookie,
|
||||||
"Accept": "application/json, text/plain, */*",
|
"Accept": "application/json, text/plain, */*",
|
||||||
"Referer": "https://www.terabox.com/",
|
"Referer": d.base_url,
|
||||||
"User-Agent": base.UserAgent,
|
"User-Agent": base.UserAgent,
|
||||||
"X-Requested-With": "XMLHttpRequest",
|
"X-Requested-With": "XMLHttpRequest",
|
||||||
}).Get(u)
|
}).Get(u)
|
||||||
@ -48,12 +49,12 @@ func (d *Terabox) resetJsToken() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Terabox) request(furl string, method string, callback base.ReqCallback, resp interface{}, noRetry ...bool) ([]byte, error) {
|
func (d *Terabox) request(rurl string, method string, callback base.ReqCallback, resp interface{}, noRetry ...bool) ([]byte, error) {
|
||||||
req := base.RestyClient.R()
|
req := base.RestyClient.R()
|
||||||
req.SetHeaders(map[string]string{
|
req.SetHeaders(map[string]string{
|
||||||
"Cookie": d.Cookie,
|
"Cookie": d.Cookie,
|
||||||
"Accept": "application/json, text/plain, */*",
|
"Accept": "application/json, text/plain, */*",
|
||||||
"Referer": "https://www.terabox.com/",
|
"Referer": d.base_url,
|
||||||
"User-Agent": base.UserAgent,
|
"User-Agent": base.UserAgent,
|
||||||
"X-Requested-With": "XMLHttpRequest",
|
"X-Requested-With": "XMLHttpRequest",
|
||||||
})
|
})
|
||||||
@ -70,7 +71,7 @@ func (d *Terabox) request(furl string, method string, callback base.ReqCallback,
|
|||||||
if resp != nil {
|
if resp != nil {
|
||||||
req.SetResult(resp)
|
req.SetResult(resp)
|
||||||
}
|
}
|
||||||
res, err := req.Execute(method, furl)
|
res, err := req.Execute(method, d.base_url+rurl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -82,14 +83,20 @@ func (d *Terabox) request(furl string, method string, callback base.ReqCallback,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if !utils.IsBool(noRetry...) {
|
if !utils.IsBool(noRetry...) {
|
||||||
return d.request(furl, method, callback, resp, true)
|
return d.request(rurl, method, callback, resp, true)
|
||||||
}
|
}
|
||||||
|
} else if errno == -6 {
|
||||||
|
log.Debugln(res.Header())
|
||||||
|
d.url_domain_prefix = res.Header()["Url-Domain-Prefix"][0]
|
||||||
|
d.base_url = "https://" + d.url_domain_prefix + ".terabox.com"
|
||||||
|
log.Debugln("Redirect base_url to", d.base_url)
|
||||||
|
return d.request(rurl, method, callback, resp, noRetry...)
|
||||||
}
|
}
|
||||||
return res.Body(), nil
|
return res.Body(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Terabox) get(pathname string, params map[string]string, resp interface{}) ([]byte, error) {
|
func (d *Terabox) get(pathname string, params map[string]string, resp interface{}) ([]byte, error) {
|
||||||
return d.request("https://www.terabox.com"+pathname, http.MethodGet, func(req *resty.Request) {
|
return d.request(pathname, http.MethodGet, func(req *resty.Request) {
|
||||||
if params != nil {
|
if params != nil {
|
||||||
req.SetQueryParams(params)
|
req.SetQueryParams(params)
|
||||||
}
|
}
|
||||||
@ -97,7 +104,7 @@ func (d *Terabox) get(pathname string, params map[string]string, resp interface{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Terabox) post(pathname string, params map[string]string, data interface{}, resp interface{}) ([]byte, error) {
|
func (d *Terabox) post(pathname string, params map[string]string, data interface{}, resp interface{}) ([]byte, error) {
|
||||||
return d.request("https://www.terabox.com"+pathname, http.MethodPost, func(req *resty.Request) {
|
return d.request(pathname, http.MethodPost, func(req *resty.Request) {
|
||||||
if params != nil {
|
if params != nil {
|
||||||
req.SetQueryParams(params)
|
req.SetQueryParams(params)
|
||||||
}
|
}
|
||||||
@ -105,6 +112,15 @@ func (d *Terabox) post(pathname string, params map[string]string, data interface
|
|||||||
}, resp)
|
}, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *Terabox) post_form(pathname string, params map[string]string, data map[string]string, resp interface{}) ([]byte, error) {
|
||||||
|
return d.request(pathname, http.MethodPost, func(req *resty.Request) {
|
||||||
|
if params != nil {
|
||||||
|
req.SetQueryParams(params)
|
||||||
|
}
|
||||||
|
req.SetFormData(data)
|
||||||
|
}, resp)
|
||||||
|
}
|
||||||
|
|
||||||
func (d *Terabox) getFiles(dir string) ([]File, error) {
|
func (d *Terabox) getFiles(dir string) ([]File, error) {
|
||||||
page := 1
|
page := 1
|
||||||
num := 100
|
num := 100
|
||||||
@ -237,15 +253,6 @@ func (d *Terabox) manage(opera string, filelist interface{}) ([]byte, error) {
|
|||||||
return d.post("/api/filemanager", params, data, nil)
|
return d.post("/api/filemanager", params, data, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Terabox) create(path string, size int64, isdir int, uploadid, block_list string) ([]byte, error) {
|
|
||||||
params := map[string]string{}
|
|
||||||
data := fmt.Sprintf("path=%s&size=%d&isdir=%d", encodeURIComponent(path), size, isdir)
|
|
||||||
if uploadid != "" {
|
|
||||||
data += fmt.Sprintf("&uploadid=%s&block_list=%s", uploadid, block_list)
|
|
||||||
}
|
|
||||||
return d.post("/api/create", params, data, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func encodeURIComponent(str string) string {
|
func encodeURIComponent(str string) string {
|
||||||
r := url.QueryEscape(str)
|
r := url.QueryEscape(str)
|
||||||
r = strings.ReplaceAll(r, "+", "%20")
|
r = strings.ReplaceAll(r, "+", "%20")
|
||||||
|
5
go.mod
5
go.mod
@ -3,7 +3,7 @@ module github.com/alist-org/alist/v3
|
|||||||
go 1.22.4
|
go 1.22.4
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/SheltonZhu/115driver v1.0.27
|
github.com/SheltonZhu/115driver v1.0.29
|
||||||
github.com/Xhofe/go-cache v0.0.0-20240804043513-b1a71927bc21
|
github.com/Xhofe/go-cache v0.0.0-20240804043513-b1a71927bc21
|
||||||
github.com/Xhofe/rateg v0.0.0-20230728072201-251a4e1adad4
|
github.com/Xhofe/rateg v0.0.0-20230728072201-251a4e1adad4
|
||||||
github.com/alist-org/gofakes3 v0.0.7
|
github.com/alist-org/gofakes3 v0.0.7
|
||||||
@ -33,6 +33,7 @@ require (
|
|||||||
github.com/golang-jwt/jwt/v4 v4.5.0
|
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
github.com/gorilla/websocket v1.5.3
|
github.com/gorilla/websocket v1.5.3
|
||||||
|
github.com/hekmon/transmissionrpc/v3 v3.0.0
|
||||||
github.com/hirochachacha/go-smb2 v1.1.0
|
github.com/hirochachacha/go-smb2 v1.1.0
|
||||||
github.com/ipfs/go-ipfs-api v0.7.0
|
github.com/ipfs/go-ipfs-api v0.7.0
|
||||||
github.com/jlaffaye/ftp v0.2.0
|
github.com/jlaffaye/ftp v0.2.0
|
||||||
@ -82,6 +83,8 @@ require (
|
|||||||
github.com/cloudwego/base64x v0.1.4 // indirect
|
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||||
github.com/cloudwego/iasm v0.2.0 // indirect
|
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
|
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
|
||||||
|
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||||
|
github.com/hekmon/cunits/v2 v2.1.0 // indirect
|
||||||
github.com/ipfs/boxo v0.12.0 // indirect
|
github.com/ipfs/boxo v0.12.0 // indirect
|
||||||
github.com/jackc/puddle/v2 v2.2.1 // indirect
|
github.com/jackc/puddle/v2 v2.2.1 // indirect
|
||||||
)
|
)
|
||||||
|
20
go.sum
20
go.sum
@ -7,8 +7,8 @@ github.com/Max-Sum/base32768 v0.0.0-20230304063302-18e6ce5945fd h1:nzE1YQBdx1bq9
|
|||||||
github.com/Max-Sum/base32768 v0.0.0-20230304063302-18e6ce5945fd/go.mod h1:C8yoIfvESpM3GD07OCHU7fqI7lhwyZ2Td1rbNbTAhnc=
|
github.com/Max-Sum/base32768 v0.0.0-20230304063302-18e6ce5945fd/go.mod h1:C8yoIfvESpM3GD07OCHU7fqI7lhwyZ2Td1rbNbTAhnc=
|
||||||
github.com/RoaringBitmap/roaring v1.9.3 h1:t4EbC5qQwnisr5PrP9nt0IRhRTb9gMUgQF4t4S2OByM=
|
github.com/RoaringBitmap/roaring v1.9.3 h1:t4EbC5qQwnisr5PrP9nt0IRhRTb9gMUgQF4t4S2OByM=
|
||||||
github.com/RoaringBitmap/roaring v1.9.3/go.mod h1:6AXUsoIEzDTFFQCe1RbGA6uFONMhvejWj5rqITANK90=
|
github.com/RoaringBitmap/roaring v1.9.3/go.mod h1:6AXUsoIEzDTFFQCe1RbGA6uFONMhvejWj5rqITANK90=
|
||||||
github.com/SheltonZhu/115driver v1.0.27 h1:Ya1HYHYXFmi7JnqQ/+Vy6xZvq3leto+E+PxTm6UChj8=
|
github.com/SheltonZhu/115driver v1.0.29 h1:yFBqFDYJyADo3eG2RjJgSovnFd1OrpGHmsHBi6j0+r4=
|
||||||
github.com/SheltonZhu/115driver v1.0.27/go.mod h1:e3fPOBANbH/FsTya8FquJwOR3ErhCQgEab3q6CVY2k4=
|
github.com/SheltonZhu/115driver v1.0.29/go.mod h1:e3fPOBANbH/FsTya8FquJwOR3ErhCQgEab3q6CVY2k4=
|
||||||
github.com/Unknwon/goconfig v1.0.0 h1:9IAu/BYbSLQi8puFjUQApZTxIHqSwrj5d8vpP8vTq4A=
|
github.com/Unknwon/goconfig v1.0.0 h1:9IAu/BYbSLQi8puFjUQApZTxIHqSwrj5d8vpP8vTq4A=
|
||||||
github.com/Unknwon/goconfig v1.0.0/go.mod h1:wngxua9XCNjvHjDiTiV26DaKDT+0c63QR6H5hjVUUxw=
|
github.com/Unknwon/goconfig v1.0.0/go.mod h1:wngxua9XCNjvHjDiTiV26DaKDT+0c63QR6H5hjVUUxw=
|
||||||
github.com/Xhofe/go-cache v0.0.0-20240804043513-b1a71927bc21 h1:h6q5E9aMBhhdqouW81LozVPI1I+Pu6IxL2EKpfm5OjY=
|
github.com/Xhofe/go-cache v0.0.0-20240804043513-b1a71927bc21 h1:h6q5E9aMBhhdqouW81LozVPI1I+Pu6IxL2EKpfm5OjY=
|
||||||
@ -96,8 +96,6 @@ github.com/caarlos0/env/v9 v9.0.0 h1:SI6JNsOA+y5gj9njpgybykATIylrRMklbs5ch6wO6pc
|
|||||||
github.com/caarlos0/env/v9 v9.0.0/go.mod h1:ye5mlCVMYh6tZ+vCgrs/B95sj88cg5Tlnc0XIzgZ020=
|
github.com/caarlos0/env/v9 v9.0.0/go.mod h1:ye5mlCVMYh6tZ+vCgrs/B95sj88cg5Tlnc0XIzgZ020=
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/charmbracelet/bubbles v0.19.0 h1:gKZkKXPP6GlDk6EcfujDK19PCQqRjaJZQ7QRERx1UF0=
|
|
||||||
github.com/charmbracelet/bubbles v0.19.0/go.mod h1:WILteEqZ+krG5c3ntGEMeG99nCupcuIk7V0/zOP0tOA=
|
|
||||||
github.com/charmbracelet/bubbles v0.20.0 h1:jSZu6qD8cRQ6k9OMfR1WlM+ruM8fkPWkHvQWD9LIutE=
|
github.com/charmbracelet/bubbles v0.20.0 h1:jSZu6qD8cRQ6k9OMfR1WlM+ruM8fkPWkHvQWD9LIutE=
|
||||||
github.com/charmbracelet/bubbles v0.20.0/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU=
|
github.com/charmbracelet/bubbles v0.20.0/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU=
|
||||||
github.com/charmbracelet/bubbletea v1.1.0 h1:FjAl9eAL3HBCHenhz/ZPjkKdScmaS5SK69JAK2YJK9c=
|
github.com/charmbracelet/bubbletea v1.1.0 h1:FjAl9eAL3HBCHenhz/ZPjkKdScmaS5SK69JAK2YJK9c=
|
||||||
@ -242,11 +240,17 @@ github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad
|
|||||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
|
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||||
|
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||||
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
|
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
|
||||||
github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||||
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||||
|
github.com/hekmon/cunits/v2 v2.1.0 h1:k6wIjc4PlacNOHwKEMBgWV2/c8jyD4eRMs5mR1BBhI0=
|
||||||
|
github.com/hekmon/cunits/v2 v2.1.0/go.mod h1:9r1TycXYXaTmEWlAIfFV8JT+Xo59U96yUJAYHxzii2M=
|
||||||
|
github.com/hekmon/transmissionrpc/v3 v3.0.0 h1:0Fb11qE0IBh4V4GlOwHNYpqpjcYDp5GouolwrpmcUDQ=
|
||||||
|
github.com/hekmon/transmissionrpc/v3 v3.0.0/go.mod h1:38SlNhFzinVUuY87wGj3acOmRxeYZAZfrj6Re7UgCDg=
|
||||||
github.com/hirochachacha/go-smb2 v1.1.0 h1:b6hs9qKIql9eVXAiN0M2wSFY5xnhbHAQoCwRKbaRTZI=
|
github.com/hirochachacha/go-smb2 v1.1.0 h1:b6hs9qKIql9eVXAiN0M2wSFY5xnhbHAQoCwRKbaRTZI=
|
||||||
github.com/hirochachacha/go-smb2 v1.1.0/go.mod h1:8F1A4d5EZzrGu5R7PU163UcMRDJQl4FtcxjBfsY8TZE=
|
github.com/hirochachacha/go-smb2 v1.1.0/go.mod h1:8F1A4d5EZzrGu5R7PU163UcMRDJQl4FtcxjBfsY8TZE=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
@ -548,8 +552,6 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY
|
|||||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||||
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
|
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
|
||||||
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
|
|
||||||
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
|
|
||||||
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
|
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
|
||||||
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
|
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
|
||||||
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e h1:I88y4caeGeuDQxgdoFPUq097j7kNfw6uvuiNxUBfcBk=
|
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e h1:I88y4caeGeuDQxgdoFPUq097j7kNfw6uvuiNxUBfcBk=
|
||||||
@ -626,8 +628,6 @@ golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
|||||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
|
|
||||||
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
|
||||||
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
||||||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||||
@ -640,8 +640,6 @@ golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
|||||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||||
golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
|
golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
|
||||||
golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU=
|
|
||||||
golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk=
|
|
||||||
golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM=
|
golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM=
|
||||||
golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
|
golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
@ -657,8 +655,6 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
|||||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||||
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
|
|
||||||
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
|
||||||
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
|
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
|
||||||
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
@ -54,11 +54,15 @@ const (
|
|||||||
Aria2Uri = "aria2_uri"
|
Aria2Uri = "aria2_uri"
|
||||||
Aria2Secret = "aria2_secret"
|
Aria2Secret = "aria2_secret"
|
||||||
|
|
||||||
|
// transmission
|
||||||
|
TransmissionUri = "transmission_uri"
|
||||||
|
TransmissionSeedtime = "transmission_seedtime"
|
||||||
|
|
||||||
// single
|
// single
|
||||||
Token = "token"
|
Token = "token"
|
||||||
IndexProgress = "index_progress"
|
IndexProgress = "index_progress"
|
||||||
|
|
||||||
//SSO
|
// SSO
|
||||||
SSOClientId = "sso_client_id"
|
SSOClientId = "sso_client_id"
|
||||||
SSOClientSecret = "sso_client_secret"
|
SSOClientSecret = "sso_client_secret"
|
||||||
SSOLoginEnabled = "sso_login_enabled"
|
SSOLoginEnabled = "sso_login_enabled"
|
||||||
@ -73,7 +77,7 @@ const (
|
|||||||
SSODefaultPermission = "sso_default_permission"
|
SSODefaultPermission = "sso_default_permission"
|
||||||
SSOCompatibilityMode = "sso_compatibility_mode"
|
SSOCompatibilityMode = "sso_compatibility_mode"
|
||||||
|
|
||||||
//ldap
|
// ldap
|
||||||
LdapLoginEnabled = "ldap_login_enabled"
|
LdapLoginEnabled = "ldap_login_enabled"
|
||||||
LdapServer = "ldap_server"
|
LdapServer = "ldap_server"
|
||||||
LdapManagerDN = "ldap_manager_dn"
|
LdapManagerDN = "ldap_manager_dn"
|
||||||
@ -84,7 +88,7 @@ const (
|
|||||||
LdapDefaultDir = "ldap_default_dir"
|
LdapDefaultDir = "ldap_default_dir"
|
||||||
LdapLoginTips = "ldap_login_tips"
|
LdapLoginTips = "ldap_login_tips"
|
||||||
|
|
||||||
//s3
|
// s3
|
||||||
S3Buckets = "s3_buckets"
|
S3Buckets = "s3_buckets"
|
||||||
S3AccessKeyId = "s3_access_key_id"
|
S3AccessKeyId = "s3_access_key_id"
|
||||||
S3SecretAccessKey = "s3_secret_access_key"
|
S3SecretAccessKey = "s3_secret_access_key"
|
||||||
@ -97,7 +101,7 @@ const (
|
|||||||
const (
|
const (
|
||||||
UNKNOWN = iota
|
UNKNOWN = iota
|
||||||
FOLDER
|
FOLDER
|
||||||
//OFFICE
|
// OFFICE
|
||||||
VIDEO
|
VIDEO
|
||||||
AUDIO
|
AUDIO
|
||||||
TEXT
|
TEXT
|
||||||
|
@ -6,4 +6,5 @@ import (
|
|||||||
_ "github.com/alist-org/alist/v3/internal/offline_download/http"
|
_ "github.com/alist-org/alist/v3/internal/offline_download/http"
|
||||||
_ "github.com/alist-org/alist/v3/internal/offline_download/pikpak"
|
_ "github.com/alist-org/alist/v3/internal/offline_download/pikpak"
|
||||||
_ "github.com/alist-org/alist/v3/internal/offline_download/qbit"
|
_ "github.com/alist-org/alist/v3/internal/offline_download/qbit"
|
||||||
|
_ "github.com/alist-org/alist/v3/internal/offline_download/transmission"
|
||||||
)
|
)
|
||||||
|
@ -101,6 +101,19 @@ outer:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if t.tool.Name() == "transmission" {
|
||||||
|
// hack for transmission
|
||||||
|
seedTime := setting.GetInt(conf.TransmissionSeedtime, 0)
|
||||||
|
if seedTime >= 0 {
|
||||||
|
t.Status = "offline download completed, waiting for seeding"
|
||||||
|
<-time.After(time.Minute * time.Duration(seedTime))
|
||||||
|
err := t.tool.Remove(t)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorln(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
176
internal/offline_download/transmission/client.go
Normal file
176
internal/offline_download/transmission/client.go
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
package transmission
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/alist-org/alist/v3/internal/conf"
|
||||||
|
"github.com/alist-org/alist/v3/internal/errs"
|
||||||
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
|
"github.com/alist-org/alist/v3/internal/offline_download/tool"
|
||||||
|
"github.com/alist-org/alist/v3/internal/setting"
|
||||||
|
"github.com/hekmon/transmissionrpc/v3"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Transmission struct {
|
||||||
|
client *transmissionrpc.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Transmission) Run(task *tool.DownloadTask) error {
|
||||||
|
return errs.NotSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Transmission) Name() string {
|
||||||
|
return "transmission"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Transmission) Items() []model.SettingItem {
|
||||||
|
// transmission settings
|
||||||
|
return []model.SettingItem{
|
||||||
|
{Key: conf.TransmissionUri, Value: "http://localhost:9091/transmission/rpc", Type: conf.TypeString, Group: model.OFFLINE_DOWNLOAD, Flag: model.PRIVATE},
|
||||||
|
{Key: conf.TransmissionSeedtime, Value: "0", Type: conf.TypeNumber, Group: model.OFFLINE_DOWNLOAD, Flag: model.PRIVATE},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Transmission) Init() (string, error) {
|
||||||
|
t.client = nil
|
||||||
|
uri := setting.GetStr(conf.TransmissionUri)
|
||||||
|
endpoint, err := url.Parse(uri)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Wrap(err, "failed to init transmission client")
|
||||||
|
}
|
||||||
|
c, err := transmissionrpc.New(endpoint, nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Wrap(err, "failed to init transmission client")
|
||||||
|
}
|
||||||
|
|
||||||
|
ok, serverVersion, serverMinimumVersion, err := c.RPCVersion(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Wrapf(err, "failed get transmission version")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("remote transmission RPC version (v%d) is incompatible with the transmission library (v%d): remote needs at least v%d",
|
||||||
|
serverVersion, transmissionrpc.RPCVersion, serverMinimumVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.client = c
|
||||||
|
log.Infof("remote transmission RPC version (v%d) is compatible with our transmissionrpc library (v%d)\n",
|
||||||
|
serverVersion, transmissionrpc.RPCVersion)
|
||||||
|
log.Infof("using transmission version: %d", serverVersion)
|
||||||
|
return fmt.Sprintf("transmission version: %d", serverVersion), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Transmission) IsReady() bool {
|
||||||
|
return t.client != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Transmission) AddURL(args *tool.AddUrlArgs) (string, error) {
|
||||||
|
endpoint, err := url.Parse(args.Url)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Wrap(err, "failed to parse transmission uri")
|
||||||
|
}
|
||||||
|
|
||||||
|
rpcPayload := transmissionrpc.TorrentAddPayload{
|
||||||
|
DownloadDir: &args.TempDir,
|
||||||
|
}
|
||||||
|
// http url for .torrent file
|
||||||
|
if endpoint.Scheme == "http" || endpoint.Scheme == "https" {
|
||||||
|
resp, err := http.Get(args.Url)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Wrap(err, "failed to get .torrent file")
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
buffer := new(bytes.Buffer)
|
||||||
|
encoder := base64.NewEncoder(base64.StdEncoding, buffer)
|
||||||
|
// Stream file to the encoder
|
||||||
|
if _, err = io.Copy(encoder, resp.Body); err != nil {
|
||||||
|
return "", errors.Wrap(err, "can't copy file content into the base64 encoder")
|
||||||
|
}
|
||||||
|
// Flush last bytes
|
||||||
|
if err = encoder.Close(); err != nil {
|
||||||
|
return "", errors.Wrap(err, "can't flush last bytes of the base64 encoder")
|
||||||
|
}
|
||||||
|
// Get the string form
|
||||||
|
b64 := buffer.String()
|
||||||
|
rpcPayload.MetaInfo = &b64
|
||||||
|
} else { // magnet uri
|
||||||
|
rpcPayload.Filename = &args.Url
|
||||||
|
}
|
||||||
|
|
||||||
|
torrent, err := t.client.TorrentAdd(context.TODO(), rpcPayload)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if torrent.ID == nil {
|
||||||
|
return "", fmt.Errorf("failed get torrent ID")
|
||||||
|
}
|
||||||
|
gid := strconv.FormatInt(*torrent.ID, 10)
|
||||||
|
return gid, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Transmission) Remove(task *tool.DownloadTask) error {
|
||||||
|
gid, err := strconv.ParseInt(task.GID, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = t.client.TorrentRemove(context.TODO(), transmissionrpc.TorrentRemovePayload{
|
||||||
|
IDs: []int64{gid},
|
||||||
|
DeleteLocalData: false,
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Transmission) Status(task *tool.DownloadTask) (*tool.Status, error) {
|
||||||
|
gid, err := strconv.ParseInt(task.GID, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
infos, err := t.client.TorrentGetAllFor(context.TODO(), []int64{gid})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(infos) < 1 {
|
||||||
|
return nil, fmt.Errorf("failed get status, wrong gid: %s", task.GID)
|
||||||
|
}
|
||||||
|
info := infos[0]
|
||||||
|
|
||||||
|
s := &tool.Status{
|
||||||
|
Completed: *info.IsFinished,
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
s.Progress = *info.PercentDone * 100
|
||||||
|
|
||||||
|
switch *info.Status {
|
||||||
|
case transmissionrpc.TorrentStatusCheckWait,
|
||||||
|
transmissionrpc.TorrentStatusDownloadWait,
|
||||||
|
transmissionrpc.TorrentStatusCheck,
|
||||||
|
transmissionrpc.TorrentStatusDownload,
|
||||||
|
transmissionrpc.TorrentStatusIsolated:
|
||||||
|
s.Status = "[transmission] " + info.Status.String()
|
||||||
|
case transmissionrpc.TorrentStatusSeedWait,
|
||||||
|
transmissionrpc.TorrentStatusSeed:
|
||||||
|
s.Completed = true
|
||||||
|
case transmissionrpc.TorrentStatusStopped:
|
||||||
|
s.Err = errors.Errorf("[transmission] failed to download %s, status: %s, error: %s", task.GID, info.Status.String(), *info.ErrorString)
|
||||||
|
default:
|
||||||
|
s.Err = errors.Errorf("[transmission] unknown status occurred downloading %s, err: %s", task.GID, *info.ErrorString)
|
||||||
|
}
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ tool.Tool = (*Transmission)(nil)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
tool.Tools.Add(&Transmission{})
|
||||||
|
}
|
@ -30,6 +30,10 @@ func SetAria2(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
_tool, err := tool.Tools.Get("aria2")
|
_tool, err := tool.Tools.Get("aria2")
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResp(c, err, 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
version, err := _tool.Init()
|
version, err := _tool.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.ErrorResp(c, err, 500)
|
common.ErrorResp(c, err, 500)
|
||||||
@ -74,6 +78,37 @@ func OfflineDownloadTools(c *gin.Context) {
|
|||||||
common.SuccessResp(c, tools)
|
common.SuccessResp(c, tools)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SetTransmissionReq struct {
|
||||||
|
Uri string `json:"uri" form:"uri"`
|
||||||
|
Seedtime string `json:"seedtime" form:"seedtime"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetTransmission(c *gin.Context) {
|
||||||
|
var req SetTransmissionReq
|
||||||
|
if err := c.ShouldBind(&req); err != nil {
|
||||||
|
common.ErrorResp(c, err, 400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
items := []model.SettingItem{
|
||||||
|
{Key: conf.TransmissionUri, Value: req.Uri, Type: conf.TypeString, Group: model.OFFLINE_DOWNLOAD, Flag: model.PRIVATE},
|
||||||
|
{Key: conf.TransmissionSeedtime, Value: req.Seedtime, Type: conf.TypeNumber, Group: model.OFFLINE_DOWNLOAD, Flag: model.PRIVATE},
|
||||||
|
}
|
||||||
|
if err := op.SaveSettingItems(items); err != nil {
|
||||||
|
common.ErrorResp(c, err, 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_tool, err := tool.Tools.Get("transmission")
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResp(c, err, 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, err := _tool.Init(); err != nil {
|
||||||
|
common.ErrorResp(c, err, 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.SuccessResp(c, "ok")
|
||||||
|
}
|
||||||
|
|
||||||
type AddOfflineDownloadReq struct {
|
type AddOfflineDownloadReq struct {
|
||||||
Urls []string `json:"urls"`
|
Urls []string `json:"urls"`
|
||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
|
@ -62,7 +62,7 @@ func Init(e *gin.Engine) {
|
|||||||
api.GET("/auth/get_sso_id", handles.SSOLoginCallback)
|
api.GET("/auth/get_sso_id", handles.SSOLoginCallback)
|
||||||
api.GET("/auth/sso_get_token", handles.SSOLoginCallback)
|
api.GET("/auth/sso_get_token", handles.SSOLoginCallback)
|
||||||
|
|
||||||
//webauthn
|
// webauthn
|
||||||
webauthn.GET("/webauthn_begin_registration", handles.BeginAuthnRegistration)
|
webauthn.GET("/webauthn_begin_registration", handles.BeginAuthnRegistration)
|
||||||
webauthn.POST("/webauthn_finish_registration", handles.FinishAuthnRegistration)
|
webauthn.POST("/webauthn_finish_registration", handles.FinishAuthnRegistration)
|
||||||
webauthn.GET("/webauthn_begin_login", handles.BeginAuthnLogin)
|
webauthn.GET("/webauthn_begin_login", handles.BeginAuthnLogin)
|
||||||
@ -125,6 +125,7 @@ func admin(g *gin.RouterGroup) {
|
|||||||
setting.POST("/reset_token", handles.ResetToken)
|
setting.POST("/reset_token", handles.ResetToken)
|
||||||
setting.POST("/set_aria2", handles.SetAria2)
|
setting.POST("/set_aria2", handles.SetAria2)
|
||||||
setting.POST("/set_qbit", handles.SetQbittorrent)
|
setting.POST("/set_qbit", handles.SetQbittorrent)
|
||||||
|
setting.POST("/set_transmission", handles.SetTransmission)
|
||||||
|
|
||||||
task := g.Group("/task")
|
task := g.Group("/task")
|
||||||
handles.SetupTaskRoute(task)
|
handles.SetupTaskRoute(task)
|
||||||
@ -159,14 +160,15 @@ func _fs(g *gin.RouterGroup) {
|
|||||||
g.PUT("/put", middlewares.FsUp, handles.FsStream)
|
g.PUT("/put", middlewares.FsUp, handles.FsStream)
|
||||||
g.PUT("/form", middlewares.FsUp, handles.FsForm)
|
g.PUT("/form", middlewares.FsUp, handles.FsForm)
|
||||||
g.POST("/link", middlewares.AuthAdmin, handles.Link)
|
g.POST("/link", middlewares.AuthAdmin, handles.Link)
|
||||||
//g.POST("/add_aria2", handles.AddOfflineDownload)
|
// g.POST("/add_aria2", handles.AddOfflineDownload)
|
||||||
//g.POST("/add_qbit", handles.AddQbittorrent)
|
// g.POST("/add_qbit", handles.AddQbittorrent)
|
||||||
|
// g.POST("/add_transmission", handles.SetTransmission)
|
||||||
g.POST("/add_offline_download", handles.AddOfflineDownload)
|
g.POST("/add_offline_download", handles.AddOfflineDownload)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Cors(r *gin.Engine) {
|
func Cors(r *gin.Engine) {
|
||||||
config := cors.DefaultConfig()
|
config := cors.DefaultConfig()
|
||||||
//config.AllowAllOrigins = true
|
// config.AllowAllOrigins = true
|
||||||
config.AllowOrigins = conf.Conf.Cors.AllowOrigins
|
config.AllowOrigins = conf.Conf.Cors.AllowOrigins
|
||||||
config.AllowHeaders = conf.Conf.Cors.AllowHeaders
|
config.AllowHeaders = conf.Conf.Cors.AllowHeaders
|
||||||
config.AllowMethods = conf.Conf.Cors.AllowMethods
|
config.AllowMethods = conf.Conf.Cors.AllowMethods
|
||||||
|
Reference in New Issue
Block a user