Skip to content

Commit

Permalink
ulozto: implement Mover and DirMover interfaces.
Browse files Browse the repository at this point in the history
  • Loading branch information
iotmaestro authored and ncw committed Mar 29, 2024
1 parent c9ce384 commit 571d20d
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 3 deletions.
20 changes: 20 additions & 0 deletions backend/ulozto/api/types.go
Expand Up @@ -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 {
Expand Down
88 changes: 88 additions & 0 deletions backend/ulozto/ulozto.go
Expand Up @@ -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
Expand Down Expand Up @@ -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)
)
2 changes: 1 addition & 1 deletion docs/content/overview.md
Expand Up @@ -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 |
Expand Down
5 changes: 3 additions & 2 deletions docs/content/ulozto.md
Expand Up @@ -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

Expand All @@ -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).

Expand Down

0 comments on commit 571d20d

Please sign in to comment.