feat: add `baidu_netdisk` driver

pull/1604/head
Noah Hsu 2022-09-02 22:46:31 +08:00
parent decea4a739
commit 611457c0e7
21 changed files with 711 additions and 48 deletions

View File

@ -62,7 +62,7 @@ func (d *Pan123) List(ctx context.Context, dir model.Obj, args model.ListArgs) (
}
//func (d *Pan123) Get(ctx context.Context, path string) (model.Obj, error) {
// // TODO this is optional
// // this is optional
// return nil, errs.NotImplement
//}

View File

@ -30,6 +30,10 @@ type File struct {
DownloadUrl string `json:"DownloadUrl"`
}
func (f File) GetPath() string {
return ""
}
func (f File) GetSize() int64 {
return f.Size
}

View File

@ -32,7 +32,7 @@ func (d *Pan123) login() error {
return err
}
func (d *Pan123) request(url string, method string, callback func(*resty.Request), resp interface{}) ([]byte, error) {
func (d *Pan123) request(url string, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) {
req := base.RestyClient.R()
req.SetHeader("Authorization", "Bearer "+d.AccessToken)
if callback != nil {

View File

@ -34,7 +34,7 @@ func (d *AliDrive) refreshToken() error {
return nil
}
func (d *AliDrive) request(url, method string, callback func(*resty.Request), resp interface{}) ([]byte, error, RespErr) {
func (d *AliDrive) request(url, method string, callback base.ReqCallback, resp interface{}) ([]byte, error, RespErr) {
req := base.RestyClient.R()
req.SetHeader("Authorization", "Bearer\t"+d.AccessToken)
req.SetHeader("content-type", "application/json")

View File

@ -3,6 +3,7 @@ package drivers
import (
_ "github.com/alist-org/alist/v3/drivers/123"
_ "github.com/alist-org/alist/v3/drivers/aliyundrive"
_ "github.com/alist-org/alist/v3/drivers/baidu_netdisk"
_ "github.com/alist-org/alist/v3/drivers/local"
_ "github.com/alist-org/alist/v3/drivers/onedrive"
_ "github.com/alist-org/alist/v3/drivers/pikpak"

View File

@ -0,0 +1,252 @@
package baidu_netdisk
import (
"bytes"
"context"
"crypto/md5"
"encoding/hex"
"fmt"
"io"
"math"
"os"
stdpath "path"
"strconv"
"strings"
"github.com/alist-org/alist/v3/drivers/base"
"github.com/alist-org/alist/v3/internal/conf"
"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/pkg/utils"
log "github.com/sirupsen/logrus"
)
type BaiduNetdisk struct {
model.Storage
Addition
AccessToken string
}
func (d *BaiduNetdisk) Config() driver.Config {
return config
}
func (d *BaiduNetdisk) GetAddition() driver.Additional {
return d.Addition
}
func (d *BaiduNetdisk) Init(ctx context.Context, storage model.Storage) error {
d.Storage = storage
err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition)
if err != nil {
return err
}
return d.refreshToken()
}
func (d *BaiduNetdisk) Drop(ctx context.Context) error {
return nil
}
func (d *BaiduNetdisk) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
files, err := d.getFiles(dir.GetPath())
if err != nil {
return nil, err
}
objs := make([]model.Obj, len(files))
for i := 0; i < len(files); i++ {
objs[i] = fileToObj(files[i])
}
return objs, nil
}
//func (d *BaiduNetdisk) Get(ctx context.Context, path string) (model.Obj, error) {
// // this is optional
// return nil, errs.NotImplement
//}
func (d *BaiduNetdisk) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
if d.DownloadAPI == "crack" {
return d.linkCrack(file, args)
}
return d.linkOfficial(file, args)
}
func (d *BaiduNetdisk) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
_, err := d.create(stdpath.Join(parentDir.GetPath(), dirName), 0, 1, "", "")
return err
}
func (d *BaiduNetdisk) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
data := []base.Json{
{
"path": srcObj.GetPath(),
"dest": dstDir.GetPath(),
"newname": srcObj.GetName(),
},
}
_, err := d.manage("move", data)
return err
}
func (d *BaiduNetdisk) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
data := []base.Json{
{
"path": srcObj.GetPath(),
"newname": newName,
},
}
_, err := d.manage("rename", data)
return err
}
func (d *BaiduNetdisk) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
dest, newname := stdpath.Split(dstDir.GetPath())
data := []base.Json{
{
"path": srcObj.GetPath(),
"dest": dest,
"newname": newname,
},
}
_, err := d.manage("copy", data)
return err
}
func (d *BaiduNetdisk) Remove(ctx context.Context, obj model.Obj) error {
data := []string{obj.GetPath()}
_, err := d.manage("delete", data)
return err
}
func (d *BaiduNetdisk) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
var tempFile *os.File
var err error
if f, ok := stream.GetReadCloser().(*os.File); ok {
tempFile = f
} else {
tempFile, err = os.CreateTemp(conf.Conf.TempDir, "file-*")
if err != nil {
return err
}
defer func() {
_ = tempFile.Close()
_ = os.Remove(tempFile.Name())
}()
_, err = io.Copy(tempFile, stream)
if err != nil {
return err
}
_, err = tempFile.Seek(0, io.SeekStart)
if err != nil {
return err
}
}
var Default int64 = 4 * 1024 * 1024
defaultByteData := make([]byte, Default)
count := int(math.Ceil(float64(stream.GetSize()) / float64(Default)))
var SliceSize int64 = 256 * 1024
// cal md5
h1 := md5.New()
h2 := md5.New()
block_list := make([]string, 0)
content_md5 := ""
slice_md5 := ""
left := stream.GetSize()
for i := 0; i < count; i++ {
byteSize := Default
var byteData []byte
if left < Default {
byteSize = left
byteData = make([]byte, byteSize)
} else {
byteData = defaultByteData
}
left -= byteSize
_, err = io.ReadFull(tempFile, byteData)
if err != nil {
return err
}
h1.Write(byteData)
h2.Write(byteData)
block_list = append(block_list, fmt.Sprintf("\"%s\"", hex.EncodeToString(h2.Sum(nil))))
h2.Reset()
}
content_md5 = hex.EncodeToString(h1.Sum(nil))
_, err = tempFile.Seek(0, io.SeekStart)
if err != nil {
return err
}
if stream.GetSize() <= SliceSize {
slice_md5 = content_md5
} else {
sliceData := make([]byte, SliceSize)
_, err = io.ReadFull(tempFile, sliceData)
if err != nil {
return err
}
h2.Write(sliceData)
slice_md5 = hex.EncodeToString(h2.Sum(nil))
_, err = tempFile.Seek(0, io.SeekStart)
if err != nil {
return err
}
}
path := encodeURIComponent(stdpath.Join(dstDir.GetPath(), stream.GetName()))
block_list_str := fmt.Sprintf("[%s]", strings.Join(block_list, ","))
data := fmt.Sprintf("path=%s&size=%d&isdir=0&autoinit=1&block_list=%s&content-md5=%s&slice-md5=%s",
path, stream.GetSize(),
block_list_str,
content_md5, slice_md5)
params := map[string]string{
"method": "precreate",
}
var precreateResp PrecreateResp
_, err = d.post("/xpan/file", params, data, &precreateResp)
if err != nil {
return err
}
log.Debugf("%+v", precreateResp)
if precreateResp.ReturnType == 2 {
return nil
}
params = map[string]string{
"method": "upload",
"access_token": d.AccessToken,
"type": "tmpfile",
"path": path,
"uploadid": precreateResp.Uploadid,
}
left = stream.GetSize()
for _, partseq := range precreateResp.BlockList {
byteSize := Default
var byteData []byte
if left < Default {
byteSize = left
byteData = make([]byte, byteSize)
} else {
byteData = defaultByteData
}
left -= byteSize
_, err = io.ReadFull(tempFile, byteData)
if err != nil {
return err
}
u := "https://d.pcs.baidu.com/rest/2.0/pcs/superfile2"
params["partseq"] = strconv.Itoa(partseq)
res, err := base.RestyClient.R().SetQueryParams(params).SetFileReader("file", stream.GetName(), bytes.NewReader(byteData)).Post(u)
if err != nil {
return err
}
log.Debugln(res.String())
}
_, err = d.create(path, stream.GetSize(), 0, precreateResp.Uploadid, block_list_str)
return err
}
func (d *BaiduNetdisk) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) {
return nil, errs.NotSupport
}
var _ driver.Driver = (*BaiduNetdisk)(nil)

View File

@ -0,0 +1,35 @@
package baidu_netdisk
import (
"github.com/alist-org/alist/v3/internal/driver"
"github.com/alist-org/alist/v3/internal/op"
)
type Addition struct {
RefreshToken string `json:"refresh_token" required:"true"`
driver.RootFolderPath
OrderBy string `json:"order_by" type:"select" options:"name,time,size" default:"name"`
OrderDirection string `json:"order_direction" type:"select" options:"asc,desc" default:"asc"`
DownloadAPI string `json:"download_api" type:"select" options:"official,crack" default:"official"`
ClientID string `json:"client_id" required:"true" default:"iYCeC9g08h5vuP9UqvPHKKSVrKFXGa1v"`
ClientSecret string `json:"client_secret" required:"true" default:"jXiFMOPVPCWlO2M5CwWQzffpNPaGTRBG"`
}
var config = driver.Config{
Name: "BaiduNetdisk",
LocalSort: false,
OnlyLocal: false,
OnlyProxy: false,
NoCache: false,
NoUpload: false,
NeedMs: false,
DefaultRoot: "root, / or other",
}
func New() driver.Driver {
return &BaiduNetdisk{}
}
func init() {
op.RegisterDriver(config, New)
}

View File

@ -0,0 +1,163 @@
package baidu_netdisk
import (
"strconv"
"time"
"github.com/alist-org/alist/v3/internal/model"
)
type TokenErrResp struct {
ErrorDescription string `json:"error_description"`
Error string `json:"error"`
}
type File struct {
//TkbindId int `json:"tkbind_id"`
//OwnerType int `json:"owner_type"`
//Category int `json:"category"`
//RealCategory string `json:"real_category"`
FsId int64 `json:"fs_id"`
ServerMtime int64 `json:"server_mtime"`
//OperId int `json:"oper_id"`
//ServerCtime int `json:"server_ctime"`
Thumbs struct {
//Icon string `json:"icon"`
Url3 string `json:"url3"`
//Url2 string `json:"url2"`
//Url1 string `json:"url1"`
} `json:"thumbs"`
//Wpfile int `json:"wpfile"`
//LocalMtime int `json:"local_mtime"`
Size int64 `json:"size"`
//ExtentTinyint7 int `json:"extent_tinyint7"`
Path string `json:"path"`
//Share int `json:"share"`
//ServerAtime int `json:"server_atime"`
//Pl int `json:"pl"`
//LocalCtime int `json:"local_ctime"`
ServerFilename string `json:"server_filename"`
//Md5 string `json:"md5"`
//OwnerId int `json:"owner_id"`
//Unlist int `json:"unlist"`
Isdir int `json:"isdir"`
}
func fileToObj(f File) *model.ObjThumb {
return &model.ObjThumb{
Object: model.Object{
ID: strconv.FormatInt(f.FsId, 10),
Name: f.ServerFilename,
Size: f.Size,
Modified: time.Unix(f.ServerMtime, 0),
IsFolder: f.Isdir == 1,
},
Thumbnail: model.Thumbnail{Thumbnail: f.Thumbs.Url3},
}
}
type ListResp struct {
Errno int `json:"errno"`
GuidInfo string `json:"guid_info"`
List []File `json:"list"`
RequestId int64 `json:"request_id"`
Guid int `json:"guid"`
}
type DownloadResp struct {
Errmsg string `json:"errmsg"`
Errno int `json:"errno"`
List []struct {
//Category int `json:"category"`
//DateTaken int `json:"date_taken,omitempty"`
Dlink string `json:"dlink"`
//Filename string `json:"filename"`
//FsId int64 `json:"fs_id"`
//Height int `json:"height,omitempty"`
//Isdir int `json:"isdir"`
//Md5 string `json:"md5"`
//OperId int `json:"oper_id"`
//Path string `json:"path"`
//ServerCtime int `json:"server_ctime"`
//ServerMtime int `json:"server_mtime"`
//Size int `json:"size"`
//Thumbs struct {
// Icon string `json:"icon,omitempty"`
// Url1 string `json:"url1,omitempty"`
// Url2 string `json:"url2,omitempty"`
// Url3 string `json:"url3,omitempty"`
//} `json:"thumbs"`
//Width int `json:"width,omitempty"`
} `json:"list"`
//Names struct {
//} `json:"names"`
RequestId string `json:"request_id"`
}
type DownloadResp2 struct {
Errno int `json:"errno"`
Info []struct {
//ExtentTinyint4 int `json:"extent_tinyint4"`
//ExtentTinyint1 int `json:"extent_tinyint1"`
//Bitmap string `json:"bitmap"`
//Category int `json:"category"`
//Isdir int `json:"isdir"`
//Videotag int `json:"videotag"`
Dlink string `json:"dlink"`
//OperID int64 `json:"oper_id"`
//PathMd5 int `json:"path_md5"`
//Wpfile int `json:"wpfile"`
//LocalMtime int `json:"local_mtime"`
/*Thumbs struct {
Icon string `json:"icon"`
URL3 string `json:"url3"`
URL2 string `json:"url2"`
URL1 string `json:"url1"`
} `json:"thumbs"`*/
//PlaySource int `json:"play_source"`
//Share int `json:"share"`
//FileKey string `json:"file_key"`
//Errno int `json:"errno"`
//LocalCtime int `json:"local_ctime"`
//Rotate int `json:"rotate"`
//Metadata time.Time `json:"metadata"`
//Height int `json:"height"`
//SampleRate int `json:"sample_rate"`
//Width int `json:"width"`
//OwnerType int `json:"owner_type"`
//Privacy int `json:"privacy"`
//ExtentInt3 int64 `json:"extent_int3"`
//RealCategory string `json:"real_category"`
//SrcLocation string `json:"src_location"`
//MetaInfo string `json:"meta_info"`
//ID string `json:"id"`
//Duration int `json:"duration"`
//FileSize string `json:"file_size"`
//Channels int `json:"channels"`
//UseSegment int `json:"use_segment"`
//ServerCtime int `json:"server_ctime"`
//Resolution string `json:"resolution"`
//OwnerID int `json:"owner_id"`
//ExtraInfo string `json:"extra_info"`
//Size int `json:"size"`
//FsID int64 `json:"fs_id"`
//ExtentTinyint3 int `json:"extent_tinyint3"`
//Md5 string `json:"md5"`
//Path string `json:"path"`
//FrameRate int `json:"frame_rate"`
//ExtentTinyint2 int `json:"extent_tinyint2"`
//ServerFilename string `json:"server_filename"`
//ServerMtime int `json:"server_mtime"`
//TkbindID int `json:"tkbind_id"`
} `json:"info"`
RequestID int64 `json:"request_id"`
}
type PrecreateResp struct {
Path string `json:"path"`
Uploadid string `json:"uploadid"`
ReturnType int `json:"return_type"`
BlockList []int `json:"block_list"`
Errno int `json:"errno"`
RequestId int64 `json:"request_id"`
}

View File

@ -0,0 +1,199 @@
package baidu_netdisk
import (
"fmt"
"net/http"
"net/url"
"strconv"
"strings"
"github.com/alist-org/alist/v3/drivers/base"
"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"
)
// do others that not defined in Driver interface
func (d *BaiduNetdisk) refreshToken() error {
err := d._refreshToken()
if err != nil && err == errs.EmptyToken {
err = d._refreshToken()
}
return err
}
func (d *BaiduNetdisk) _refreshToken() error {
u := "https://openapi.baidu.com/oauth/2.0/token"
var resp base.TokenResp
var e TokenErrResp
_, err := base.RestyClient.R().SetResult(&resp).SetError(&e).SetQueryParams(map[string]string{
"grant_type": "refresh_token",
"refresh_token": d.RefreshToken,
"client_id": d.ClientID,
"client_secret": d.ClientSecret,
}).Get(u)
if err != nil {
return err
}
if e.Error != "" {
return fmt.Errorf("%s : %s", e.Error, e.ErrorDescription)
}
if resp.RefreshToken == "" {
return errs.EmptyToken
}
d.AccessToken, d.RefreshToken = resp.AccessToken, resp.RefreshToken
op.MustSaveDriverStorage(d)
return nil
}
func (d *BaiduNetdisk) request(furl string, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) {
req := base.RestyClient.R()
req.SetQueryParam("access_token", d.AccessToken)
if callback != nil {
callback(req)
}
if resp != nil {
req.SetResult(resp)
}
res, err := req.Execute(method, furl)
if err != nil {
return nil, err
}
errno := utils.Json.Get(res.Body(), "errno").ToInt()
if errno != 0 {
if errno == -6 {
err = d.refreshToken()
if err != nil {
return nil, err
}
return d.request(furl, method, callback, resp)
}
return nil, fmt.Errorf("errno: %d, refer to https://pan.baidu.com/union/doc/", errno)
}
return res.Body(), nil
}
func (d *BaiduNetdisk) get(pathname string, params map[string]string, resp interface{}) ([]byte, error) {
return d.request("https://pan.baidu.com/rest/2.0"+pathname, http.MethodGet, func(req *resty.Request) {
req.SetQueryParams(params)
}, resp)
}
func (d *BaiduNetdisk) post(pathname string, params map[string]string, data interface{}, resp interface{}) ([]byte, error) {
return d.request("https://pan.baidu.com/rest/2.0"+pathname, http.MethodPost, func(req *resty.Request) {
req.SetQueryParams(params)
req.SetBody(data)
}, resp)
}
func (d *BaiduNetdisk) getFiles(dir string) ([]File, error) {
start := 0
limit := 200
params := map[string]string{
"method": "list",
"dir": dir,
"web": "web",
}
if d.OrderBy != "" {
params["order"] = d.OrderBy
if d.OrderDirection == "desc" {
params["desc"] = "1"
}
}
res := make([]File, 0)
for {
params["start"] = strconv.Itoa(start)
params["limit"] = strconv.Itoa(limit)
start += limit
var resp ListResp
_, err := d.get("/xpan/file", params, &resp)
if err != nil {
return nil, err
}
if len(resp.List) == 0 {
break
}
res = append(res, resp.List...)
}
return res, nil
}
func (d *BaiduNetdisk) linkOfficial(file model.Obj, args model.LinkArgs) (*model.Link, error) {
var resp DownloadResp
params := map[string]string{
"method": "filemetas",
"fsids": fmt.Sprintf("[%s]", file.GetID()),
"dlink": "1",
}
_, err := d.get("/xpan/multimedia", params, &resp)
if err != nil {
return nil, err
}
u := fmt.Sprintf("%s&access_token=%s", resp.List[0].Dlink, d.AccessToken)
res, err := base.NoRedirectClient.R().SetHeader("User-Agent", "pan.baidu.com").Head(u)
if err != nil {
return nil, err
}
//if res.StatusCode() == 302 {
u = res.Header().Get("location")
//}
return &model.Link{
URL: u,
Header: http.Header{
"User-Agent": []string{"pan.baidu.com"},
},
}, nil
}
func (d *BaiduNetdisk) linkCrack(file model.Obj, args model.LinkArgs) (*model.Link, error) {
var resp DownloadResp2
param := map[string]string{
"target": fmt.Sprintf("[\"%s\"]", file.GetPath()),
"dlink": "1",
"web": "5",
"origin": "dlna",
}
_, err := d.get("https://pan.baidu.com/api/filemetas", param, &resp)
if err != nil {
return nil, err
}
return &model.Link{
URL: resp.Info[0].Dlink,
Header: http.Header{
"User-Agent": []string{"pan.baidu.com"},
},
}, nil
}
func (d *BaiduNetdisk) manage(opera string, filelist interface{}) ([]byte, error) {
params := map[string]string{
"method": "filemanager",
"opera": opera,
}
marshal, err := utils.Json.Marshal(filelist)
if err != nil {
return nil, err
}
data := fmt.Sprintf("async=0&filelist=%s&ondup=newcopy", string(marshal))
return d.post("/xpan/file", params, data, nil)
}
func (d *BaiduNetdisk) create(path string, size int64, isdir int, uploadid, block_list string) ([]byte, error) {
params := map[string]string{
"method": "create",
}
data := fmt.Sprintf("path=%s&size=%d&isdir=%d", path, size, isdir)
if uploadid != "" {
data += fmt.Sprintf("&uploadid=%s&block_list=%s", uploadid, block_list)
}
return d.post("/xpan/file", params, data, nil)
}
func encodeURIComponent(str string) string {
r := url.QueryEscape(str)
r = strings.ReplaceAll(r, "+", "%20")
return r
}

View File

@ -1,8 +1,12 @@
package base
import "github.com/go-resty/resty/v2"
type Json map[string]interface{}
type TokenResp struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
}
type ReqCallback func(req *resty.Request)

View File

@ -62,7 +62,7 @@ func (d *Local) GetAddition() driver.Additional {
}
func (d *Local) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
fullPath := dir.GetID()
fullPath := dir.GetPath()
rawFiles, err := ioutil.ReadDir(fullPath)
if err != nil {
return nil, err
@ -103,7 +103,7 @@ func (d *Local) Get(ctx context.Context, path string) (model.Obj, error) {
return nil, err
}
file := model.Object{
ID: path,
Path: path,
Name: f.Name(),
Modified: f.ModTime(),
Size: f.Size(),
@ -113,7 +113,7 @@ func (d *Local) Get(ctx context.Context, path string) (model.Obj, error) {
}
func (d *Local) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
fullPath := file.GetID()
fullPath := file.GetPath()
var link model.Link
if args.Type == "thumb" && utils.Ext(file.GetName()) != "svg" {
imgData, err := ioutil.ReadFile(fullPath)
@ -143,7 +143,7 @@ func (d *Local) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (
}
func (d *Local) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
fullPath := filepath.Join(parentDir.GetID(), dirName)
fullPath := filepath.Join(parentDir.GetPath(), dirName)
err := os.MkdirAll(fullPath, 0700)
if err != nil {
return err
@ -152,8 +152,8 @@ func (d *Local) MakeDir(ctx context.Context, parentDir model.Obj, dirName string
}
func (d *Local) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
srcPath := srcObj.GetID()
dstPath := filepath.Join(dstDir.GetID(), srcObj.GetName())
srcPath := srcObj.GetPath()
dstPath := filepath.Join(dstDir.GetPath(), srcObj.GetName())
err := os.Rename(srcPath, dstPath)
if err != nil {
return err
@ -162,7 +162,7 @@ func (d *Local) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
}
func (d *Local) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
srcPath := srcObj.GetID()
srcPath := srcObj.GetPath()
dstPath := filepath.Join(filepath.Dir(srcPath), newName)
err := os.Rename(srcPath, dstPath)
if err != nil {
@ -172,8 +172,8 @@ func (d *Local) Rename(ctx context.Context, srcObj model.Obj, newName string) er
}
func (d *Local) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
srcPath := srcObj.GetID()
dstPath := filepath.Join(dstDir.GetID(), srcObj.GetName())
srcPath := srcObj.GetPath()
dstPath := filepath.Join(dstDir.GetPath(), srcObj.GetName())
var err error
if srcObj.IsDir() {
err = copyDir(srcPath, dstPath)
@ -189,9 +189,9 @@ func (d *Local) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
func (d *Local) Remove(ctx context.Context, obj model.Obj) error {
var err error
if obj.IsDir() {
err = os.RemoveAll(obj.GetID())
err = os.RemoveAll(obj.GetPath())
} else {
err = os.Remove(obj.GetID())
err = os.Remove(obj.GetPath())
}
if err != nil {
return err
@ -200,7 +200,7 @@ func (d *Local) Remove(ctx context.Context, obj model.Obj) error {
}
func (d *Local) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
fullPath := filepath.Join(dstDir.GetID(), stream.GetName())
fullPath := filepath.Join(dstDir.GetPath(), stream.GetName())
out, err := os.Create(fullPath)
if err != nil {
return err

View File

@ -9,6 +9,7 @@ import (
"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"
)
@ -41,7 +42,7 @@ func (d *Onedrive) Drop(ctx context.Context) error {
}
func (d *Onedrive) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
files, err := d.GetFiles(dir.GetID())
files, err := d.GetFiles(dir.GetPath())
if err != nil {
return nil, err
}
@ -53,7 +54,7 @@ func (d *Onedrive) List(ctx context.Context, dir model.Obj, args model.ListArgs)
}
func (d *Onedrive) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
f, err := d.GetFile(file.GetID())
f, err := d.GetFile(file.GetPath())
if err != nil {
return nil, err
}
@ -66,7 +67,7 @@ func (d *Onedrive) Link(ctx context.Context, file model.Obj, args model.LinkArgs
}
func (d *Onedrive) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
url := d.GetMetaUrl(false, parentDir.GetID()) + "/children"
url := d.GetMetaUrl(false, parentDir.GetPath()) + "/children"
data := base.Json{
"name": dirName,
"folder": base.Json{},
@ -79,35 +80,31 @@ func (d *Onedrive) MakeDir(ctx context.Context, parentDir model.Obj, dirName str
}
func (d *Onedrive) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
dst, err := d.GetFile(dstDir.GetID())
if err != nil {
return err
}
data := base.Json{
"parentReference": base.Json{
"id": dst.Id,
"id": dstDir.GetID(),
},
"name": srcObj.GetName(),
}
url := d.GetMetaUrl(false, srcObj.GetID())
_, err = d.Request(url, http.MethodPatch, func(req *resty.Request) {
url := d.GetMetaUrl(false, srcObj.GetPath())
_, err := d.Request(url, http.MethodPatch, func(req *resty.Request) {
req.SetBody(data)
}, nil)
return err
}
func (d *Onedrive) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
dstDir, err := d.GetFile(stdpath.Dir(srcObj.GetID()))
dstDir, err := op.Get(ctx, d, stdpath.Dir(srcObj.GetPath()))
if err != nil {
return err
}
data := base.Json{
"parentReference": base.Json{
"id": dstDir.Id,
"id": dstDir.GetID(),
},
"name": newName,
}
url := d.GetMetaUrl(false, srcObj.GetID())
url := d.GetMetaUrl(false, srcObj.GetPath())
_, err = d.Request(url, http.MethodPatch, func(req *resty.Request) {
req.SetBody(data)
}, nil)
@ -115,7 +112,7 @@ func (d *Onedrive) Rename(ctx context.Context, srcObj model.Obj, newName string)
}
func (d *Onedrive) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
dst, err := d.GetFile(dstDir.GetID())
dst, err := d.GetFile(dstDir.GetPath())
if err != nil {
return err
}
@ -126,7 +123,7 @@ func (d *Onedrive) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
},
"name": srcObj.GetName(),
}
url := d.GetMetaUrl(false, srcObj.GetID()) + "/copy"
url := d.GetMetaUrl(false, srcObj.GetPath()) + "/copy"
_, err = d.Request(url, http.MethodPost, func(req *resty.Request) {
req.SetBody(data)
}, nil)
@ -134,7 +131,7 @@ func (d *Onedrive) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
}
func (d *Onedrive) Remove(ctx context.Context, obj model.Obj) error {
url := d.GetMetaUrl(false, obj.GetID())
url := d.GetMetaUrl(false, obj.GetPath())
_, err := d.Request(url, http.MethodDelete, nil, nil)
return err
}

View File

@ -9,7 +9,7 @@ type Addition struct {
driver.RootFolderPath
Region string `json:"region" type:"select" required:"true" options:"global,cn,us,de"`
IsSharepoint bool `json:"is_sharepoint"`
ClientId string `json:"client_id" required:"true"`
ClientID string `json:"client_id" required:"true"`
ClientSecret string `json:"client_secret" required:"true"`
RedirectUri string `json:"redirect_uri" required:"true" default:"https://tool.nn.ci/onedrive/callback"`
RefreshToken string `json:"refresh_token" required:"true"`

View File

@ -49,7 +49,7 @@ func fileToObj(f File) *model.ObjThumbURL {
}
return &model.ObjThumbURL{
Object: model.Object{
//ID: f.Id,
ID: f.Id,
Name: f.Name,
Size: f.Size,
Modified: f.LastModifiedDateTime,

View File

@ -77,7 +77,7 @@ func (d *Onedrive) _refreshToken() error {
var e TokenErr
_, err := base.RestyClient.R().SetResult(&resp).SetError(&e).SetFormData(map[string]string{
"grant_type": "refresh_token",
"client_id": d.ClientId,
"client_id": d.ClientID,
"client_secret": d.ClientSecret,
"redirect_uri": d.RedirectUri,
"refresh_token": d.RefreshToken,
@ -96,7 +96,7 @@ func (d *Onedrive) _refreshToken() error {
return nil
}
func (d *Onedrive) Request(url string, method string, callback func(*resty.Request), resp interface{}) ([]byte, error) {
func (d *Onedrive) Request(url string, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) {
req := base.RestyClient.R()
req.SetHeader("Authorization", "Bearer "+d.AccessToken)
if callback != nil {
@ -147,7 +147,7 @@ func (d *Onedrive) GetFile(path string) (*File, error) {
}
func (d *Onedrive) upSmall(dstDir model.Obj, stream model.FileStreamer) error {
url := d.GetMetaUrl(false, stdpath.Join(dstDir.GetID(), stream.GetName())) + "/content"
url := d.GetMetaUrl(false, stdpath.Join(dstDir.GetPath(), stream.GetName())) + "/content"
data, err := io.ReadAll(stream)
if err != nil {
return err
@ -159,7 +159,7 @@ func (d *Onedrive) upSmall(dstDir model.Obj, stream model.FileStreamer) error {
}
func (d *Onedrive) upBig(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
url := d.GetMetaUrl(false, stdpath.Join(dstDir.GetID(), stream.GetName())) + "/createUploadSession"
url := d.GetMetaUrl(false, stdpath.Join(dstDir.GetPath(), stream.GetName())) + "/createUploadSession"
res, err := d.Request(url, http.MethodPost, nil, nil)
if err != nil {
return err

View File

@ -66,7 +66,7 @@ func (d *PikPak) refreshToken() error {
return nil
}
func (d *PikPak) request(url string, method string, callback func(*resty.Request), resp interface{}) ([]byte, error) {
func (d *PikPak) request(url string, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) {
req := base.RestyClient.R()
req.SetHeader("Authorization", "Bearer "+d.AccessToken)
if callback != nil {

View File

@ -21,7 +21,7 @@ import (
// do others that not defined in Driver interface
func (d *Quark) request(pathname string, method string, callback func(req *resty.Request), resp interface{}) ([]byte, error) {
func (d *Quark) request(pathname string, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) {
u := "https://drive.quark.cn/1/clouddrive" + pathname
req := base.RestyClient.R()
req.SetHeaders(map[string]string{

View File

@ -22,7 +22,7 @@ func (d *Teambition) isInternational() bool {
return d.Region == "international"
}
func (d *Teambition) request(pathname string, method string, callback func(req *resty.Request), resp interface{}) ([]byte, error) {
func (d *Teambition) request(pathname string, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) {
url := "https://www.teambition.com" + pathname
if d.isInternational() {
url = "https://us.teambition.com" + pathname

View File

@ -13,6 +13,7 @@ type Obj interface {
ModTime() time.Time
IsDir() bool
GetID() string
GetPath() string
}
type FileStreamer interface {
@ -32,8 +33,8 @@ type Thumb interface {
Thumb() string
}
type SetID interface {
SetID(id string)
type SetPath interface {
SetPath(path string)
}
func SortFiles(objs []Obj, orderBy, orderDirection string) {

View File

@ -4,6 +4,7 @@ import "time"
type Object struct {
ID string
Path string
Name string
Size int64
Modified time.Time
@ -30,8 +31,12 @@ func (o *Object) GetID() string {
return o.ID
}
func (o *Object) SetID(id string) {
o.ID = id
func (o *Object) GetPath() string {
return o.Path
}
func (o *Object) SetPath(id string) {
o.Path = id
}
type Thumbnail struct {

View File

@ -35,6 +35,7 @@ func List(ctx context.Context, storage driver.Driver, path string, args model.Li
if err != nil {
return nil, errors.WithMessage(err, "failed get dir")
}
log.Debugf("list dir: %+v", dir)
if !dir.IsDir() {
return nil, errors.WithStack(errs.NotFolder)
}
@ -94,7 +95,7 @@ func Get(ctx context.Context, storage driver.Driver, path string) (model.Obj, er
}
if r, ok := storage.GetAddition().(driver.IRootFolderPath); ok && isRoot(path, r.GetRootFolderPath()) {
return &model.Object{
ID: r.GetRootFolderPath(),
Path: r.GetRootFolderPath(),
Name: "root",
Size: 0,
Modified: storage.GetStorage().Modified,
@ -108,12 +109,13 @@ func Get(ctx context.Context, storage driver.Driver, path string) (model.Obj, er
return nil, errors.WithMessage(err, "failed get parent list")
}
for _, f := range files {
// TODO maybe copy obj here
if f.GetName() == name {
// use path as id, why don't set id in List function?
// because files maybe cache, set id here can reduce memory usage
if f.GetID() == "" {
if s, ok := f.(model.SetID); ok {
s.SetID(path)
if f.GetPath() == "" {
if s, ok := f.(model.SetPath); ok {
s.SetPath(path)
}
}
return f, nil