package pcloud import ( "context" "fmt" "io" "net/http" "strconv" "time" "github.com/alist-org/alist/v3/drivers/base" "github.com/alist-org/alist/v3/pkg/utils" "github.com/go-resty/resty/v2" ) const ( defaultClientID = "DnONSzyJXpm" defaultClientSecret = "VKEnd3ze4jsKFGg8TJiznwFG8" ) // Get API base URL func (d *PCloud) getAPIURL() string { return "https://" + d.Hostname } // Get OAuth client credentials func (d *PCloud) getClientCredentials() (string, string) { clientID := d.ClientID clientSecret := d.ClientSecret if clientID == "" { clientID = defaultClientID } if clientSecret == "" { clientSecret = defaultClientSecret } return clientID, clientSecret } // Refresh OAuth access token func (d *PCloud) refreshToken() error { clientID, clientSecret := d.getClientCredentials() var resp TokenResponse _, err := base.RestyClient.R(). SetFormData(map[string]string{ "client_id": clientID, "client_secret": clientSecret, "grant_type": "refresh_token", "refresh_token": d.RefreshToken, }). SetResult(&resp). Post(d.getAPIURL() + "/oauth2_token") if err != nil { return err } d.AccessToken = resp.AccessToken return nil } // shouldRetry determines if an error should be retried based on pCloud-specific logic func (d *PCloud) shouldRetry(statusCode int, apiError *ErrorResult) bool { // HTTP-level retry conditions if statusCode == 429 || statusCode >= 500 { return true } // pCloud API-specific retry conditions (like rclone) if apiError != nil && apiError.Result != 0 { // 4xxx: rate limiting if apiError.Result/1000 == 4 { return true } // 5xxx: internal errors if apiError.Result/1000 == 5 { return true } } return false } // requestWithRetry makes authenticated API request with retry logic func (d *PCloud) requestWithRetry(endpoint string, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) { maxRetries := 3 baseDelay := 500 * time.Millisecond for attempt := 0; attempt <= maxRetries; attempt++ { body, err := d.request(endpoint, method, callback, resp) if err == nil { return body, nil } // If this is the last attempt, return the error if attempt == maxRetries { return nil, err } // Check if we should retry based on error type if !d.shouldRetryError(err) { return nil, err } // Exponential backoff delay := baseDelay * time.Duration(1<