From 571d20d1261e1f37bba9c37f1ccb63922dc4b734 Mon Sep 17 00:00:00 2001 From: IoT Maestro Date: Thu, 28 Mar 2024 11:19:59 +0000 Subject: [PATCH] ulozto: implement Mover and DirMover interfaces. --- backend/ulozto/api/types.go | 20 +++++++++ backend/ulozto/ulozto.go | 88 +++++++++++++++++++++++++++++++++++++ docs/content/overview.md | 2 +- docs/content/ulozto.md | 5 ++- 4 files changed, 112 insertions(+), 3 deletions(-) diff --git a/backend/ulozto/api/types.go b/backend/ulozto/api/types.go index 6f70e3b0b..8266fbc2d 100644 --- a/backend/ulozto/api/types.go +++ b/backend/ulozto/api/types.go @@ -200,6 +200,26 @@ type UpdateDescriptionRequest struct { Description string `json:"description"` } +// MoveFolderRequest represents the JSON API object that's +// sent to the folder moving API endpoint. +type MoveFolderRequest struct { + FolderSlugs []string `json:"slugs"` + NewParentFolderSlug string `json:"parent_folder_slug"` +} + +// RenameFolderRequest represents the JSON API object that's +// sent to the folder moving API endpoint. +type RenameFolderRequest struct { + NewName string `json:"name"` +} + +// MoveFileRequest represents the JSON API object that's +// sent to the file moving API endpoint. +type MoveFileRequest struct { + ParentFolderSlug string `json:"folder_slug,omitempty"` + NewFilename string `json:"name,omitempty"` +} + // GetDownloadLinkRequest represents the JSON API object that's // sent to the API endpoint that generates CDN download links for file payloads. type GetDownloadLinkRequest struct { diff --git a/backend/ulozto/ulozto.go b/backend/ulozto/ulozto.go index 44810f310..f1364c6f9 100644 --- a/backend/ulozto/ulozto.go +++ b/backend/ulozto/ulozto.go @@ -537,6 +537,92 @@ func (f *Fs) Rmdir(ctx context.Context, dir string) error { return nil } +// Move implements the optional method fs.Mover.Move. +func (f *Fs) Move(ctx context.Context, src fs.Object, remote string) (fs.Object, error) { + if remote == src.Remote() { + // Already there, do nothing + return src, nil + } + + srcObj, ok := src.(*Object) + if !ok { + fs.Debugf(src, "Can't move - not same remote type") + return nil, fs.ErrorCantMove + } + + filename, folderSlug, err := f.dirCache.FindPath(ctx, remote, true) + + if err != nil { + return nil, err + } + + newObj := &Object{} + newObj.copyFrom(srcObj) + newObj.remote = remote + + return newObj, newObj.updateFileProperties(ctx, api.MoveFileRequest{ + ParentFolderSlug: folderSlug, + NewFilename: filename, + }) +} + +// DirMove implements the optional method fs.DirMover.DirMove. +func (f *Fs) DirMove(ctx context.Context, src fs.Fs, srcRemote, dstRemote string) error { + srcFs, ok := src.(*Fs) + if !ok { + fs.Debugf(srcFs, "Can't move directory - not same remote type") + return fs.ErrorCantDirMove + } + + srcSlug, _, srcName, dstParentSlug, dstName, err := f.dirCache.DirMove(ctx, srcFs.dirCache, srcFs.root, srcRemote, f.root, dstRemote) + if err != nil { + return err + } + + opts := rest.Opts{ + Method: "PATCH", + Path: "/v6/user/" + f.opt.Username + "/folder-list/parent-folder", + } + + req := api.MoveFolderRequest{ + FolderSlugs: []string{srcSlug}, + NewParentFolderSlug: dstParentSlug, + } + + err = f.pacer.Call(func() (bool, error) { + httpResp, err := f.rest.CallJSON(ctx, &opts, &req, nil) + return f.shouldRetry(ctx, httpResp, err, true) + }) + + if err != nil { + return err + } + + // The old folder doesn't exist anymore so clear the cache now instead of after renaming + srcFs.dirCache.FlushDir(srcRemote) + + if srcName != dstName { + // There's no endpoint to rename the folder alongside moving it, so this has to happen separately. + opts = rest.Opts{ + Method: "PATCH", + Path: "/v7/user/" + f.opt.Username + "/folder/" + srcSlug, + } + + renameReq := api.RenameFolderRequest{ + NewName: dstName, + } + + err = f.pacer.Call(func() (bool, error) { + httpResp, err := f.rest.CallJSON(ctx, &opts, &renameReq, nil) + return f.shouldRetry(ctx, httpResp, err, true) + }) + + return err + } + + return nil +} + // Name of the remote (as passed into NewFs) func (f *Fs) Name() string { return f.name @@ -1179,6 +1265,8 @@ var ( _ dircache.DirCacher = (*Fs)(nil) _ fs.DirCacheFlusher = (*Fs)(nil) _ fs.PutUncheckeder = (*Fs)(nil) + _ fs.Mover = (*Fs)(nil) + _ fs.DirMover = (*Fs)(nil) _ fs.Object = (*Object)(nil) _ fs.ObjectInfo = (*RenamingObjectInfoProxy)(nil) ) diff --git a/docs/content/overview.md b/docs/content/overview.md index 1a4a5fe4c..36ff231f2 100644 --- a/docs/content/overview.md +++ b/docs/content/overview.md @@ -527,7 +527,7 @@ upon backend-specific capabilities. | SMB | No | No | Yes | Yes | No | No | Yes | Yes | No | No | Yes | | SugarSync | Yes | Yes | Yes | Yes | No | No | Yes | No | Yes | No | Yes | | Storj | Yes ² | Yes | Yes | No | No | Yes | Yes | No | Yes | No | No | -| Uloz.to | No | No | No | No | No | No | No | No | No | No | Yes | +| Uloz.to | No | No | Yes | Yes | No | No | No | No | No | No | Yes | | Uptobox | No | Yes | Yes | Yes | No | No | No | No | No | No | No | | WebDAV | Yes | Yes | Yes | Yes | No | No | Yes ³ | No | No | Yes | Yes | | Yandex Disk | Yes | Yes | Yes | Yes | Yes | No | Yes | No | Yes | Yes | Yes | diff --git a/docs/content/ulozto.md b/docs/content/ulozto.md index 1ad683698..cc7bc7d25 100644 --- a/docs/content/ulozto.md +++ b/docs/content/ulozto.md @@ -94,7 +94,8 @@ precision. A server calculated MD5 hash of the file is verified upon upload. Afterwards, the backend only serves the client-side calculated -hashes. +hashes. Hashes can also be retrieved upon creating a file download +link, but it's impractical for `list`-like use cases. ### Restricted filename characters @@ -111,7 +112,7 @@ as they can't be used in JSON strings. ### Transfers All files are currently uploaded using a single HTTP request, so -for uploading large files a stable connection is necesary. Rclone will +for uploading large files a stable connection is necessary. Rclone will upload up to `--transfers` chunks at the same time (shared among all uploads).