pcloud: fix recursive listing from the root - fixes #9315
pCloud now disallow recursive listing from the root so this change lists the root normally then uses recursive listings for subdirectories.
This commit is contained in:
@@ -238,6 +238,10 @@ func shouldRetry(ctx context.Context, resp *http.Response, err error) (bool, err
|
|||||||
|
|
||||||
// Check if it is an api.Error
|
// Check if it is an api.Error
|
||||||
if apiErr, ok := err.(*api.Error); ok {
|
if apiErr, ok := err.(*api.Error); ok {
|
||||||
|
// Handle error 1101 specifically - don't retry as it's a permanent API restriction
|
||||||
|
if apiErr.Result == 1101 {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
// See https://docs.pcloud.com/errors/ for error treatment
|
// See https://docs.pcloud.com/errors/ for error treatment
|
||||||
// Errors are classified as 1xxx, 2xxx, etc.
|
// Errors are classified as 1xxx, 2xxx, etc.
|
||||||
switch apiErr.Result / 1000 {
|
switch apiErr.Result / 1000 {
|
||||||
@@ -531,10 +535,63 @@ func fileIDtoNumber(fileID string) string {
|
|||||||
// Should return true to finish processing
|
// Should return true to finish processing
|
||||||
type listAllFn func(*api.Item) bool
|
type listAllFn func(*api.Item) bool
|
||||||
|
|
||||||
|
// listAllRootRecursive handles recursive listing for root directory (folderid=0)
|
||||||
|
// using a two-step approach to work around pCloud API limitation
|
||||||
|
func (f *Fs) listAllRootRecursive(ctx context.Context, dirID string, directoriesOnly bool, filesOnly bool, fn listAllFn) (found bool, err error) {
|
||||||
|
// List root directory non-recursively to get immediate contents
|
||||||
|
var rootSubdirs []*api.Item
|
||||||
|
found, err = f.listAll(ctx, dirID, false, false, false, func(item *api.Item) bool {
|
||||||
|
if item.IsFolder {
|
||||||
|
// Store subdirectories for recursive list later
|
||||||
|
rootSubdirs = append(rootSubdirs, item)
|
||||||
|
// Handle directories immediately if not filesOnly
|
||||||
|
if !filesOnly {
|
||||||
|
if fn(item) {
|
||||||
|
return true // stop iteration
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if !directoriesOnly {
|
||||||
|
// Handle files immediately if not directoriesOnly
|
||||||
|
if fn(item) {
|
||||||
|
return true // stop iteration
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false // continue iteration
|
||||||
|
})
|
||||||
|
if err != nil || found {
|
||||||
|
return found, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursively list each subdirectory
|
||||||
|
for _, subdir := range rootSubdirs {
|
||||||
|
subdirName := f.opt.Enc.ToStandardName(subdir.Name)
|
||||||
|
found, err = f.listAll(ctx, subdir.ID, directoriesOnly, filesOnly, true, func(item *api.Item) bool {
|
||||||
|
// Prepend the subdirectory name to create correct relative path
|
||||||
|
origItemName := item.Name
|
||||||
|
item.Name = subdirName + "/" + item.Name
|
||||||
|
result := fn(item)
|
||||||
|
item.Name = origItemName
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
if err != nil || found {
|
||||||
|
return found, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return found, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Lists the directory required calling the user function on each item found
|
// Lists the directory required calling the user function on each item found
|
||||||
//
|
//
|
||||||
// If the user fn ever returns true then it early exits with found = true
|
// If the user fn ever returns true then it early exits with found = true
|
||||||
func (f *Fs) listAll(ctx context.Context, dirID string, directoriesOnly bool, filesOnly bool, recursive bool, fn listAllFn) (found bool, err error) {
|
func (f *Fs) listAll(ctx context.Context, dirID string, directoriesOnly bool, filesOnly bool, recursive bool, fn listAllFn) (found bool, err error) {
|
||||||
|
// Special case: root directory with recursive listing
|
||||||
|
// pCloud API rejects folderid=0 with recursive=1 (error 1101)
|
||||||
|
// See: https://github.com/rclone/rclone/issues/9315
|
||||||
|
if recursive && dirIDtoNumber(dirID) == "0" {
|
||||||
|
return f.listAllRootRecursive(ctx, dirID, directoriesOnly, filesOnly, fn)
|
||||||
|
}
|
||||||
|
|
||||||
opts := rest.Opts{
|
opts := rest.Opts{
|
||||||
Method: "GET",
|
Method: "GET",
|
||||||
Path: "/listfolder",
|
Path: "/listfolder",
|
||||||
|
|||||||
Reference in New Issue
Block a user