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) getFiles(fileId string) ([]File, error) { marker := "first" res := make([]File, 0) for marker != "" { if marker == "first" { marker = "" } var resp Files 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, } _, 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 } 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 }