mirror of https://github.com/Xhofe/alist
253 lines
7.0 KiB
Go
253 lines
7.0 KiB
Go
package quark
|
|
|
|
import (
|
|
"context"
|
|
"crypto/md5"
|
|
"encoding/base64"
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"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/internal/op"
|
|
"github.com/alist-org/alist/v3/pkg/cookie"
|
|
"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
|
|
|
|
func (d *QuarkOrUC) request(pathname string, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) {
|
|
u := d.conf.api + pathname
|
|
req := base.RestyClient.R()
|
|
req.SetHeaders(map[string]string{
|
|
"Cookie": d.Cookie,
|
|
"Accept": "application/json, text/plain, */*",
|
|
"Referer": d.conf.referer,
|
|
})
|
|
req.SetQueryParam("pr", d.conf.pr)
|
|
req.SetQueryParam("fr", "pc")
|
|
if callback != nil {
|
|
callback(req)
|
|
}
|
|
if resp != nil {
|
|
req.SetResult(resp)
|
|
}
|
|
var e Resp
|
|
req.SetError(&e)
|
|
res, err := req.Execute(method, u)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
__puus := cookie.GetCookie(res.Cookies(), "__puus")
|
|
if __puus != nil {
|
|
d.Cookie = cookie.SetStr(d.Cookie, "__puus", __puus.Value)
|
|
op.MustSaveDriverStorage(d)
|
|
}
|
|
if e.Status >= 400 || e.Code != 0 {
|
|
return nil, errors.New(e.Message)
|
|
}
|
|
return res.Body(), nil
|
|
}
|
|
|
|
func (d *QuarkOrUC) GetFiles(parent string) ([]File, error) {
|
|
files := make([]File, 0)
|
|
page := 1
|
|
size := 100
|
|
query := map[string]string{
|
|
"pdir_fid": parent,
|
|
"_size": strconv.Itoa(size),
|
|
"_fetch_total": "1",
|
|
}
|
|
if d.OrderBy != "none" {
|
|
query["_sort"] = "file_type:asc," + d.OrderBy + ":" + d.OrderDirection
|
|
}
|
|
for {
|
|
query["_page"] = strconv.Itoa(page)
|
|
var resp SortResp
|
|
_, err := d.request("/file/sort", http.MethodGet, func(req *resty.Request) {
|
|
req.SetQueryParams(query)
|
|
}, &resp)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
files = append(files, resp.Data.List...)
|
|
if page*size >= resp.Metadata.Total {
|
|
break
|
|
}
|
|
page++
|
|
}
|
|
return files, nil
|
|
}
|
|
|
|
func (d *QuarkOrUC) upPre(file model.FileStreamer, parentId string) (UpPreResp, error) {
|
|
now := time.Now()
|
|
data := base.Json{
|
|
"ccp_hash_update": true,
|
|
"dir_name": "",
|
|
"file_name": file.GetName(),
|
|
"format_type": file.GetMimetype(),
|
|
"l_created_at": now.UnixMilli(),
|
|
"l_updated_at": now.UnixMilli(),
|
|
"pdir_fid": parentId,
|
|
"size": file.GetSize(),
|
|
//"same_path_reuse": true,
|
|
}
|
|
var resp UpPreResp
|
|
_, err := d.request("/file/upload/pre", http.MethodPost, func(req *resty.Request) {
|
|
req.SetBody(data)
|
|
}, &resp)
|
|
return resp, err
|
|
}
|
|
|
|
func (d *QuarkOrUC) upHash(md5, sha1, taskId string) (bool, error) {
|
|
data := base.Json{
|
|
"md5": md5,
|
|
"sha1": sha1,
|
|
"task_id": taskId,
|
|
}
|
|
log.Debugf("hash: %+v", data)
|
|
var resp HashResp
|
|
_, err := d.request("/file/update/hash", http.MethodPost, func(req *resty.Request) {
|
|
req.SetBody(data)
|
|
}, &resp)
|
|
return resp.Data.Finish, err
|
|
}
|
|
|
|
func (d *QuarkOrUC) upPart(ctx context.Context, pre UpPreResp, mineType string, partNumber int, bytes []byte) (string, error) {
|
|
//func (driver QuarkOrUC) UpPart(pre UpPreResp, mineType string, partNumber int, bytes []byte, account *model.Account, md5Str, sha1Str string) (string, error) {
|
|
timeStr := time.Now().UTC().Format(http.TimeFormat)
|
|
data := base.Json{
|
|
"auth_info": pre.Data.AuthInfo,
|
|
"auth_meta": fmt.Sprintf(`PUT
|
|
|
|
%s
|
|
%s
|
|
x-oss-date:%s
|
|
x-oss-user-agent:aliyun-sdk-js/6.6.1 Chrome 98.0.4758.80 on Windows 10 64-bit
|
|
/%s/%s?partNumber=%d&uploadId=%s`,
|
|
mineType, timeStr, timeStr, pre.Data.Bucket, pre.Data.ObjKey, partNumber, pre.Data.UploadId),
|
|
"task_id": pre.Data.TaskId,
|
|
}
|
|
var resp UpAuthResp
|
|
_, err := d.request("/file/upload/auth", http.MethodPost, func(req *resty.Request) {
|
|
req.SetBody(data).SetContext(ctx)
|
|
}, &resp)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
//if partNumber == 1 {
|
|
// finish, err := driver.UpHash(md5Str, sha1Str, pre.Data.TaskId, account)
|
|
// if err != nil {
|
|
// return "", err
|
|
// }
|
|
// if finish {
|
|
// return "finish", nil
|
|
// }
|
|
//}
|
|
u := fmt.Sprintf("https://%s.%s/%s", pre.Data.Bucket, pre.Data.UploadUrl[7:], pre.Data.ObjKey)
|
|
res, err := base.RestyClient.R().SetContext(ctx).
|
|
SetHeaders(map[string]string{
|
|
"Authorization": resp.Data.AuthKey,
|
|
"Content-Type": mineType,
|
|
"Referer": "https://pan.quark.cn/",
|
|
"x-oss-date": timeStr,
|
|
"x-oss-user-agent": "aliyun-sdk-js/6.6.1 Chrome 98.0.4758.80 on Windows 10 64-bit",
|
|
}).
|
|
SetQueryParams(map[string]string{
|
|
"partNumber": strconv.Itoa(partNumber),
|
|
"uploadId": pre.Data.UploadId,
|
|
}).SetBody(bytes).Put(u)
|
|
if res.StatusCode() != 200 {
|
|
return "", fmt.Errorf("up status: %d, error: %s", res.StatusCode(), res.String())
|
|
}
|
|
return res.Header().Get("ETag"), nil
|
|
}
|
|
|
|
func (d *QuarkOrUC) upCommit(pre UpPreResp, md5s []string) error {
|
|
timeStr := time.Now().UTC().Format(http.TimeFormat)
|
|
log.Debugf("md5s: %+v", md5s)
|
|
bodyBuilder := strings.Builder{}
|
|
bodyBuilder.WriteString(`<?xml version="1.0" encoding="UTF-8"?>
|
|
<CompleteMultipartUpload>
|
|
`)
|
|
for i, m := range md5s {
|
|
bodyBuilder.WriteString(fmt.Sprintf(`<Part>
|
|
<PartNumber>%d</PartNumber>
|
|
<ETag>%s</ETag>
|
|
</Part>
|
|
`, i+1, m))
|
|
}
|
|
bodyBuilder.WriteString("</CompleteMultipartUpload>")
|
|
body := bodyBuilder.String()
|
|
m := md5.New()
|
|
m.Write([]byte(body))
|
|
contentMd5 := base64.StdEncoding.EncodeToString(m.Sum(nil))
|
|
callbackBytes, err := utils.Json.Marshal(pre.Data.Callback)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
callbackBase64 := base64.StdEncoding.EncodeToString(callbackBytes)
|
|
data := base.Json{
|
|
"auth_info": pre.Data.AuthInfo,
|
|
"auth_meta": fmt.Sprintf(`POST
|
|
%s
|
|
application/xml
|
|
%s
|
|
x-oss-callback:%s
|
|
x-oss-date:%s
|
|
x-oss-user-agent:aliyun-sdk-js/6.6.1 Chrome 98.0.4758.80 on Windows 10 64-bit
|
|
/%s/%s?uploadId=%s`,
|
|
contentMd5, timeStr, callbackBase64, timeStr,
|
|
pre.Data.Bucket, pre.Data.ObjKey, pre.Data.UploadId),
|
|
"task_id": pre.Data.TaskId,
|
|
}
|
|
log.Debugf("xml: %s", body)
|
|
log.Debugf("auth data: %+v", data)
|
|
var resp UpAuthResp
|
|
_, err = d.request("/file/upload/auth", http.MethodPost, func(req *resty.Request) {
|
|
req.SetBody(data)
|
|
}, &resp)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
u := fmt.Sprintf("https://%s.%s/%s", pre.Data.Bucket, pre.Data.UploadUrl[7:], pre.Data.ObjKey)
|
|
res, err := base.RestyClient.R().
|
|
SetHeaders(map[string]string{
|
|
"Authorization": resp.Data.AuthKey,
|
|
"Content-MD5": contentMd5,
|
|
"Content-Type": "application/xml",
|
|
"Referer": "https://pan.quark.cn/",
|
|
"x-oss-callback": callbackBase64,
|
|
"x-oss-date": timeStr,
|
|
"x-oss-user-agent": "aliyun-sdk-js/6.6.1 Chrome 98.0.4758.80 on Windows 10 64-bit",
|
|
}).
|
|
SetQueryParams(map[string]string{
|
|
"uploadId": pre.Data.UploadId,
|
|
}).SetBody(body).Post(u)
|
|
if res.StatusCode() != 200 {
|
|
return fmt.Errorf("up status: %d, error: %s", res.StatusCode(), res.String())
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (d *QuarkOrUC) upFinish(pre UpPreResp) error {
|
|
data := base.Json{
|
|
"obj_key": pre.Data.ObjKey,
|
|
"task_id": pre.Data.TaskId,
|
|
}
|
|
_, err := d.request("/file/upload/finish", http.MethodPost, func(req *resty.Request) {
|
|
req.SetBody(data)
|
|
}, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
time.Sleep(time.Second)
|
|
return nil
|
|
}
|