1
mirror of https://github.com/rclone/rclone synced 2024-12-29 22:26:24 +01:00

pcloud: add support for recursive list

This commit is contained in:
Niels van de Weem 2021-11-26 20:55:50 +01:00 committed by Nick Craig-Wood
parent c41814fd2d
commit df09c3f555
2 changed files with 81 additions and 47 deletions

View File

@ -2,8 +2,6 @@
// object storage system. // object storage system.
package pcloud package pcloud
// FIXME implement ListR? /listfolder can do recursive lists
// FIXME cleanup returns login required? // FIXME cleanup returns login required?
// FIXME mime type? Fix overview if implement. // FIXME mime type? Fix overview if implement.
@ -27,6 +25,7 @@ import (
"github.com/rclone/rclone/fs/config/obscure" "github.com/rclone/rclone/fs/config/obscure"
"github.com/rclone/rclone/fs/fserrors" "github.com/rclone/rclone/fs/fserrors"
"github.com/rclone/rclone/fs/hash" "github.com/rclone/rclone/fs/hash"
"github.com/rclone/rclone/fs/walk"
"github.com/rclone/rclone/lib/dircache" "github.com/rclone/rclone/lib/dircache"
"github.com/rclone/rclone/lib/encoder" "github.com/rclone/rclone/lib/encoder"
"github.com/rclone/rclone/lib/oauthutil" "github.com/rclone/rclone/lib/oauthutil"
@ -246,7 +245,7 @@ func (f *Fs) readMetaDataForPath(ctx context.Context, path string) (info *api.It
return nil, err return nil, err
} }
found, err := f.listAll(ctx, directoryID, false, true, func(item *api.Item) bool { found, err := f.listAll(ctx, directoryID, false, true, false, func(item *api.Item) bool {
if item.Name == leaf { if item.Name == leaf {
info = item info = item
return true return true
@ -380,7 +379,7 @@ func (f *Fs) NewObject(ctx context.Context, remote string) (fs.Object, error) {
// FindLeaf finds a directory of name leaf in the folder with ID pathID // FindLeaf finds a directory of name leaf in the folder with ID pathID
func (f *Fs) FindLeaf(ctx context.Context, pathID, leaf string) (pathIDOut string, found bool, err error) { func (f *Fs) FindLeaf(ctx context.Context, pathID, leaf string) (pathIDOut string, found bool, err error) {
// Find the leaf in pathID // Find the leaf in pathID
found, err = f.listAll(ctx, pathID, true, false, func(item *api.Item) bool { found, err = f.listAll(ctx, pathID, true, false, false, func(item *api.Item) bool {
if item.Name == leaf { if item.Name == leaf {
pathIDOut = item.ID pathIDOut = item.ID
return true return true
@ -446,14 +445,16 @@ type listAllFn func(*api.Item) bool
// 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, 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) {
opts := rest.Opts{ opts := rest.Opts{
Method: "GET", Method: "GET",
Path: "/listfolder", Path: "/listfolder",
Parameters: url.Values{}, Parameters: url.Values{},
} }
if recursive {
opts.Parameters.Set("recursive", "1")
}
opts.Parameters.Set("folderid", dirIDtoNumber(dirID)) opts.Parameters.Set("folderid", dirIDtoNumber(dirID))
// FIXME can do recursive
var result api.ItemResult var result api.ItemResult
var resp *http.Response var resp *http.Response
@ -465,26 +466,71 @@ func (f *Fs) listAll(ctx context.Context, dirID string, directoriesOnly bool, fi
if err != nil { if err != nil {
return found, fmt.Errorf("couldn't list files: %w", err) return found, fmt.Errorf("couldn't list files: %w", err)
} }
for i := range result.Metadata.Contents { var recursiveContents func(is []api.Item, path string)
item := &result.Metadata.Contents[i] recursiveContents = func(is []api.Item, path string) {
if item.IsFolder { for i := range is {
if filesOnly { item := &is[i]
continue if item.IsFolder {
if filesOnly {
continue
}
} else {
if directoriesOnly {
continue
}
} }
} else { item.Name = path + f.opt.Enc.ToStandardName(item.Name)
if directoriesOnly { if fn(item) {
continue found = true
break
}
if recursive {
recursiveContents(item.Contents, item.Name+"/")
} }
}
item.Name = f.opt.Enc.ToStandardName(item.Name)
if fn(item) {
found = true
break
} }
} }
recursiveContents(result.Metadata.Contents, "")
return return
} }
// listHelper iterates over all items from the directory
// and calls the callback for each element.
func (f *Fs) listHelper(ctx context.Context, dir string, recursive bool, callback func(entries fs.DirEntry) error) (err error) {
directoryID, err := f.dirCache.FindDir(ctx, dir, false)
if err != nil {
return err
}
var iErr error
_, err = f.listAll(ctx, directoryID, false, false, recursive, func(info *api.Item) bool {
remote := path.Join(dir, info.Name)
if info.IsFolder {
// cache the directory ID for later lookups
f.dirCache.Put(remote, info.ID)
d := fs.NewDir(remote, info.ModTime()).SetID(info.ID)
// FIXME more info from dir?
iErr = callback(d)
} else {
o, err := f.newObjectWithInfo(ctx, remote, info)
if err != nil {
iErr = err
return true
}
iErr = callback(o)
}
if iErr != nil {
return true
}
return false
})
if err != nil {
return err
}
if iErr != nil {
return iErr
}
return nil
}
// List the objects and directories in dir into entries. The // List the objects and directories in dir into entries. The
// entries can be returned in any order but should be for a // entries can be returned in any order but should be for a
// complete directory. // complete directory.
@ -495,36 +541,24 @@ func (f *Fs) listAll(ctx context.Context, dirID string, directoriesOnly bool, fi
// This should return ErrDirNotFound if the directory isn't // This should return ErrDirNotFound if the directory isn't
// found. // found.
func (f *Fs) List(ctx context.Context, dir string) (entries fs.DirEntries, err error) { func (f *Fs) List(ctx context.Context, dir string) (entries fs.DirEntries, err error) {
directoryID, err := f.dirCache.FindDir(ctx, dir, false) err = f.listHelper(ctx, dir, false, func(o fs.DirEntry) error {
if err != nil { entries = append(entries, o)
return nil, err return nil
} })
var iErr error return entries, err
_, err = f.listAll(ctx, directoryID, false, false, func(info *api.Item) bool { }
remote := path.Join(dir, info.Name)
if info.IsFolder { // ListR lists the objects and directories of the Fs starting
// cache the directory ID for later lookups // from dir recursively into out.
f.dirCache.Put(remote, info.ID) func (f *Fs) ListR(ctx context.Context, dir string, callback fs.ListRCallback) (err error) {
d := fs.NewDir(remote, info.ModTime()).SetID(info.ID) list := walk.NewListRHelper(callback)
// FIXME more info from dir? err = f.listHelper(ctx, dir, true, func(o fs.DirEntry) error {
entries = append(entries, d) return list.Add(o)
} else {
o, err := f.newObjectWithInfo(ctx, remote, info)
if err != nil {
iErr = err
return true
}
entries = append(entries, o)
}
return false
}) })
if err != nil { if err != nil {
return nil, err return err
} }
if iErr != nil { return list.Flush()
return nil, iErr
}
return entries, nil
} }
// Creates from the parameters passed in a half finished Object which // Creates from the parameters passed in a half finished Object which

View File

@ -264,7 +264,7 @@ backends:
fastlist: true fastlist: true
- backend: "pcloud" - backend: "pcloud"
remote: "TestPcloud:" remote: "TestPcloud:"
fastlist: false fastlist: true
- backend: "webdav" - backend: "webdav"
remote: "TestWebdavNextcloud:" remote: "TestWebdavNextcloud:"
ignore: ignore: