mirror of https://github.com/Xhofe/alist
				
				
				
			
		
			
				
	
	
		
			176 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			Go
		
	
	
			
		
		
	
	
			176 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			Go
		
	
	
| package aliyundrive_open
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	"net/http"
 | |
| 	"strings"
 | |
| 
 | |
| 	"github.com/alist-org/alist/v3/drivers/base"
 | |
| 	"github.com/alist-org/alist/v3/internal/op"
 | |
| 	"github.com/alist-org/alist/v3/pkg/utils"
 | |
| 	"github.com/go-resty/resty/v2"
 | |
| )
 | |
| 
 | |
| // do others that not defined in Driver interface
 | |
| 
 | |
| func (d *AliyundriveOpen) refreshToken() error {
 | |
| 	url := d.base + "/oauth/access_token"
 | |
| 	if d.OauthTokenURL != "" && d.ClientID == "" {
 | |
| 		url = d.OauthTokenURL
 | |
| 	}
 | |
| 	var resp base.TokenResp
 | |
| 	var e ErrResp
 | |
| 	_, err := base.RestyClient.R().
 | |
| 		ForceContentType("application/json").
 | |
| 		SetBody(base.Json{
 | |
| 			"client_id":     d.ClientID,
 | |
| 			"client_secret": d.ClientSecret,
 | |
| 			"grant_type":    "refresh_token",
 | |
| 			"refresh_token": d.RefreshToken,
 | |
| 		}).
 | |
| 		SetResult(&resp).
 | |
| 		SetError(&e).
 | |
| 		Post(url)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	if e.Code != "" {
 | |
| 		return fmt.Errorf("failed to refresh token: %s", e.Message)
 | |
| 	}
 | |
| 	if resp.RefreshToken == "" {
 | |
| 		return errors.New("failed to refresh token: refresh token is empty")
 | |
| 	}
 | |
| 	d.RefreshToken, d.AccessToken = resp.RefreshToken, resp.AccessToken
 | |
| 	op.MustSaveDriverStorage(d)
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (d *AliyundriveOpen) request(uri, method string, callback base.ReqCallback, retry ...bool) ([]byte, error) {
 | |
| 	req := base.RestyClient.R()
 | |
| 	// TODO check whether access_token is expired
 | |
| 	req.SetHeader("Authorization", "Bearer "+d.AccessToken)
 | |
| 	if method == http.MethodPost {
 | |
| 		req.SetHeader("Content-Type", "application/json")
 | |
| 	}
 | |
| 	if callback != nil {
 | |
| 		callback(req)
 | |
| 	}
 | |
| 	var e ErrResp
 | |
| 	req.SetError(&e)
 | |
| 	res, err := req.Execute(method, d.base+uri)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	isRetry := len(retry) > 0 && retry[0]
 | |
| 	if e.Code != "" {
 | |
| 		if !isRetry && (utils.SliceContains([]string{"AccessTokenInvalid", "AccessTokenExpired", "I400JD"}, e.Code) || d.AccessToken == "") {
 | |
| 			err = d.refreshToken()
 | |
| 			if err != nil {
 | |
| 				return nil, err
 | |
| 			}
 | |
| 			return d.request(uri, method, callback, true)
 | |
| 		}
 | |
| 		return nil, fmt.Errorf("%s:%s", e.Code, e.Message)
 | |
| 	}
 | |
| 	return res.Body(), nil
 | |
| }
 | |
| 
 | |
| func (d *AliyundriveOpen) list(ctx context.Context, data base.Json) (*Files, error) {
 | |
| 	var resp Files
 | |
| 	_, err := d.request("/adrive/v1.0/openFile/list", http.MethodPost, func(req *resty.Request) {
 | |
| 		req.SetBody(data).SetResult(&resp)
 | |
| 	})
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	return &resp, nil
 | |
| }
 | |
| 
 | |
| func (d *AliyundriveOpen) getFiles(ctx context.Context, fileId string) ([]File, error) {
 | |
| 	marker := "first"
 | |
| 	res := make([]File, 0)
 | |
| 	for marker != "" {
 | |
| 		if marker == "first" {
 | |
| 			marker = ""
 | |
| 		}
 | |
| 		data := base.Json{
 | |
| 			"drive_id":        d.DriveId,
 | |
| 			"limit":           200,
 | |
| 			"marker":          marker,
 | |
| 			"order_by":        d.OrderBy,
 | |
| 			"order_direction": d.OrderDirection,
 | |
| 			"parent_file_id":  fileId,
 | |
| 			//"category":              "",
 | |
| 			//"type":                  "",
 | |
| 			//"video_thumbnail_time":  120000,
 | |
| 			//"video_thumbnail_width": 480,
 | |
| 			//"image_thumbnail_width": 480,
 | |
| 		}
 | |
| 		resp, err := d.limitList(ctx, data)
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		marker = resp.NextMarker
 | |
| 		res = append(res, resp.Items...)
 | |
| 	}
 | |
| 	return res, nil
 | |
| }
 | |
| 
 | |
| func makePartInfos(size int) []base.Json {
 | |
| 	partInfoList := make([]base.Json, size)
 | |
| 	for i := 0; i < size; i++ {
 | |
| 		partInfoList[i] = base.Json{"part_number": 1 + i}
 | |
| 	}
 | |
| 	return partInfoList
 | |
| }
 | |
| 
 | |
| func (d *AliyundriveOpen) getUploadUrl(count int, fileId, uploadId string) ([]PartInfo, error) {
 | |
| 	partInfoList := makePartInfos(count)
 | |
| 	var resp CreateResp
 | |
| 	_, err := d.request("/adrive/v1.0/openFile/getUploadUrl", http.MethodPost, func(req *resty.Request) {
 | |
| 		req.SetBody(base.Json{
 | |
| 			"drive_id":       d.DriveId,
 | |
| 			"file_id":        fileId,
 | |
| 			"part_info_list": partInfoList,
 | |
| 			"upload_id":      uploadId,
 | |
| 		}).SetResult(&resp)
 | |
| 	})
 | |
| 	return resp.PartInfoList, err
 | |
| }
 | |
| 
 | |
| func (d *AliyundriveOpen) uploadPart(ctx context.Context, i, count int, reader *utils.MultiReadable, resp *CreateResp, retry bool) error {
 | |
| 	partInfo := resp.PartInfoList[i-1]
 | |
| 	uploadUrl := partInfo.UploadUrl
 | |
| 	if d.InternalUpload {
 | |
| 		uploadUrl = strings.ReplaceAll(uploadUrl, "https://cn-beijing-data.aliyundrive.net/", "http://ccp-bj29-bj-1592982087.oss-cn-beijing-internal.aliyuncs.com/")
 | |
| 	}
 | |
| 	req, err := http.NewRequest("PUT", uploadUrl, reader)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	req = req.WithContext(ctx)
 | |
| 	res, err := base.HttpClient.Do(req)
 | |
| 	if err != nil {
 | |
| 		if retry {
 | |
| 			reader.Reset()
 | |
| 			return d.uploadPart(ctx, i, count, reader, resp, false)
 | |
| 		}
 | |
| 		return err
 | |
| 	}
 | |
| 	res.Body.Close()
 | |
| 	if retry && res.StatusCode == http.StatusForbidden {
 | |
| 		resp.PartInfoList, err = d.getUploadUrl(count, resp.FileId, resp.UploadId)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		reader.Reset()
 | |
| 		return d.uploadPart(ctx, i, count, reader, resp, false)
 | |
| 	}
 | |
| 	if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusConflict {
 | |
| 		return fmt.Errorf("upload status: %d", res.StatusCode)
 | |
| 	}
 | |
| 	return nil
 | |
| }
 |