mirror of https://github.com/Xhofe/alist
227 lines
6.7 KiB
Go
227 lines
6.7 KiB
Go
package teldrive
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
"github.com/alist-org/alist/v3/drivers/base"
|
||
"github.com/alist-org/alist/v3/internal/driver"
|
||
"github.com/alist-org/alist/v3/internal/errs"
|
||
"github.com/alist-org/alist/v3/internal/model"
|
||
"github.com/alist-org/alist/v3/internal/op"
|
||
"github.com/alist-org/alist/v3/pkg/utils"
|
||
"github.com/go-resty/resty/v2"
|
||
"github.com/google/uuid"
|
||
"math"
|
||
"net/http"
|
||
"net/url"
|
||
"strings"
|
||
)
|
||
|
||
type Teldrive struct {
|
||
model.Storage
|
||
Addition
|
||
}
|
||
|
||
func (d *Teldrive) Config() driver.Config {
|
||
return config
|
||
}
|
||
|
||
func (d *Teldrive) GetAddition() driver.Additional {
|
||
return &d.Addition
|
||
}
|
||
|
||
func (d *Teldrive) Init(ctx context.Context) error {
|
||
// TODO login / refresh token
|
||
// op.MustSaveDriverStorage(d)
|
||
if d.Cookie == "" || !strings.HasPrefix(d.Cookie, "access_token=") {
|
||
return fmt.Errorf("cookie must start with 'access_token='")
|
||
}
|
||
if d.UploadConcurrency == 0 {
|
||
d.UploadConcurrency = 4
|
||
}
|
||
if d.ChunkSize == 0 {
|
||
d.ChunkSize = 10
|
||
}
|
||
if d.WebdavNative() {
|
||
d.WebProxy = true
|
||
} else {
|
||
d.WebProxy = false
|
||
}
|
||
|
||
op.MustSaveDriverStorage(d)
|
||
return nil
|
||
}
|
||
|
||
func (d *Teldrive) Drop(ctx context.Context) error {
|
||
return nil
|
||
}
|
||
|
||
func (d *Teldrive) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
|
||
// TODO return the files list, required
|
||
// endpoint /api/files, params ->page order sort path
|
||
var listResp ListResp
|
||
params := url.Values{}
|
||
params.Set("path", dir.GetPath())
|
||
//log.Info(dir.GetPath())
|
||
pathname, err := utils.InjectQuery("/api/files", params)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
err = d.request(http.MethodGet, pathname, nil, &listResp)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
return utils.SliceConvert(listResp.Items, func(src Object) (model.Obj, error) {
|
||
return &model.Object{
|
||
ID: src.ID,
|
||
Name: src.Name,
|
||
Size: func() int64 {
|
||
if src.Type == "folder" {
|
||
return 0
|
||
}
|
||
return src.Size
|
||
}(),
|
||
IsFolder: src.Type == "folder",
|
||
Modified: src.UpdatedAt,
|
||
}, nil
|
||
})
|
||
}
|
||
|
||
func (d *Teldrive) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
|
||
if !d.WebdavNative() {
|
||
if shareObj, err := d.getShareFileById(file.GetID()); err == nil && shareObj != nil {
|
||
return &model.Link{
|
||
URL: d.Address + fmt.Sprintf("/api/shares/%s/files/%s/%s", shareObj.Id, file.GetID(), file.GetName()),
|
||
}, nil
|
||
}
|
||
if err := d.createShareFile(file.GetID()); err != nil {
|
||
return nil, err
|
||
}
|
||
shareObj, err := d.getShareFileById(file.GetID())
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return &model.Link{
|
||
URL: d.Address + fmt.Sprintf("/api/shares/%s/files/%s/%s", shareObj.Id, file.GetID(), file.GetName()),
|
||
}, nil
|
||
}
|
||
return &model.Link{
|
||
URL: d.Address + "/api/files/" + file.GetID() + "/" + file.GetName(),
|
||
Header: http.Header{
|
||
"Cookie": {d.Cookie},
|
||
},
|
||
}, nil
|
||
}
|
||
|
||
func (d *Teldrive) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
|
||
return d.request(http.MethodPost, "/api/files/mkdir", func(req *resty.Request) {
|
||
req.SetBody(map[string]interface{}{
|
||
"path": parentDir.GetPath() + "/" + dirName,
|
||
})
|
||
}, nil)
|
||
}
|
||
|
||
func (d *Teldrive) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
|
||
body := base.Json{
|
||
"ids": []string{srcObj.GetID()},
|
||
"destinationParent": dstDir.GetID(),
|
||
}
|
||
return d.request(http.MethodPost, "/api/files/move", func(req *resty.Request) {
|
||
req.SetBody(body)
|
||
}, nil)
|
||
}
|
||
|
||
func (d *Teldrive) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
|
||
body := base.Json{
|
||
"name": newName,
|
||
}
|
||
return d.request(http.MethodPatch, "/api/files/"+srcObj.GetID(), func(req *resty.Request) {
|
||
req.SetBody(body)
|
||
}, nil)
|
||
}
|
||
|
||
func (d *Teldrive) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
|
||
copyConcurrentLimit := 4
|
||
copyManager := NewCopyManager(ctx, copyConcurrentLimit, d)
|
||
copyManager.startWorkers()
|
||
copyManager.G.Go(func() error {
|
||
defer close(copyManager.TaskChan)
|
||
return copyManager.generateTasks(ctx, srcObj, dstDir)
|
||
})
|
||
return copyManager.G.Wait()
|
||
}
|
||
|
||
func (d *Teldrive) Remove(ctx context.Context, obj model.Obj) error {
|
||
body := base.Json{
|
||
"ids": []string{obj.GetID()},
|
||
}
|
||
return d.request(http.MethodPost, "/api/files/delete", func(req *resty.Request) {
|
||
req.SetBody(body)
|
||
}, nil)
|
||
}
|
||
|
||
func (d *Teldrive) Put(ctx context.Context, dstDir model.Obj, file model.FileStreamer, up driver.UpdateProgress) error {
|
||
fileId := uuid.New().String()
|
||
chunkSizeInMB := d.ChunkSize
|
||
chunkSize := chunkSizeInMB * 1024 * 1024 // Convert MB to bytes
|
||
totalSize := file.GetSize()
|
||
totalParts := int(math.Ceil(float64(totalSize) / float64(chunkSize)))
|
||
retryCount := 0
|
||
maxRetried := 3
|
||
p := driver.NewProgress(totalSize, up)
|
||
|
||
// delete the upload task when finished or failed
|
||
defer func() {
|
||
_ = d.request(http.MethodDelete, "/api/uploads/"+fileId, nil, nil)
|
||
}()
|
||
|
||
if obj, err := d.getFile(dstDir.GetPath(), file.GetName(), file.IsDir()); err == nil {
|
||
if err = d.Remove(ctx, obj); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
// start the upload process
|
||
if err := d.request(http.MethodGet, "/api/uploads/"+fileId, nil, nil); err != nil {
|
||
return err
|
||
}
|
||
if totalSize == 0 {
|
||
return d.touch(file.GetName(), dstDir.GetPath())
|
||
}
|
||
|
||
if totalParts <= 1 {
|
||
return d.doSingleUpload(ctx, dstDir, file, p, retryCount, maxRetried, totalParts, fileId)
|
||
}
|
||
|
||
return d.doMultiUpload(ctx, dstDir, file, p, maxRetried, totalParts, chunkSize, fileId)
|
||
}
|
||
|
||
func (d *Teldrive) GetArchiveMeta(ctx context.Context, obj model.Obj, args model.ArchiveArgs) (model.ArchiveMeta, error) {
|
||
// TODO get archive file meta-info, return errs.NotImplement to use an internal archive tool, optional
|
||
return nil, errs.NotImplement
|
||
}
|
||
|
||
func (d *Teldrive) ListArchive(ctx context.Context, obj model.Obj, args model.ArchiveInnerArgs) ([]model.Obj, error) {
|
||
// TODO list args.InnerPath in the archive obj, return errs.NotImplement to use an internal archive tool, optional
|
||
return nil, errs.NotImplement
|
||
}
|
||
|
||
func (d *Teldrive) Extract(ctx context.Context, obj model.Obj, args model.ArchiveInnerArgs) (*model.Link, error) {
|
||
// TODO return link of file args.InnerPath in the archive obj, return errs.NotImplement to use an internal archive tool, optional
|
||
return nil, errs.NotImplement
|
||
}
|
||
|
||
func (d *Teldrive) ArchiveDecompress(ctx context.Context, srcObj, dstDir model.Obj, args model.ArchiveDecompressArgs) ([]model.Obj, error) {
|
||
// TODO extract args.InnerPath path in the archive srcObj to the dstDir location, optional
|
||
// a folder with the same name as the archive file needs to be created to store the extracted results if args.PutIntoNewDir
|
||
// return errs.NotImplement to use an internal archive tool
|
||
return nil, errs.NotImplement
|
||
}
|
||
|
||
//func (d *Teldrive) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) {
|
||
// return nil, errs.NotSupport
|
||
//}
|
||
|
||
var _ driver.Driver = (*Teldrive)(nil)
|