From 7c3ea193ff6f97f7b78a39c1c93b5300c7dfb1b6 Mon Sep 17 00:00:00 2001 From: foxxorcat <95907542+foxxorcat@users.noreply.github.com> Date: Sun, 8 Jan 2023 15:37:39 +0800 Subject: [PATCH] fix(lanzou):webdav unable to download and upload (close #2700) * fix(lanzou):Unable to get folder * fix(lanzou):webdav unable to download and upload. (close 2700) --- drivers/lanzou/driver.go | 65 ++++++--- drivers/lanzou/help.go | 11 +- drivers/lanzou/meta.go | 9 +- drivers/lanzou/types.go | 126 ++++++++++------- drivers/lanzou/util.go | 295 +++++++++++++++++++++------------------ 5 files changed, 299 insertions(+), 207 deletions(-) diff --git a/drivers/lanzou/driver.go b/drivers/lanzou/driver.go index 810b0193..4e96ad1d 100644 --- a/drivers/lanzou/driver.go +++ b/drivers/lanzou/driver.go @@ -52,30 +52,65 @@ func (d *LanZou) Drop(ctx context.Context) error { // 获取的大小和时间不准确 func (d *LanZou) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) { if d.IsCookie() { - return d.GetFiles(ctx, dir.GetID()) + return d.GetAllFiles(dir.GetID()) } else { - return d.GetFileOrFolderByShareUrl(ctx, dir.GetID(), d.SharePassword) + return d.GetFileOrFolderByShareUrl(dir.GetID(), d.SharePassword) } } func (d *LanZou) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { - downID := file.GetID() - pwd := d.SharePassword - if d.IsCookie() { - share, err := d.getFileShareUrlByID(ctx, file.GetID()) + var ( + err error + dfile *FileOrFolderByShareUrl + ) + switch file := file.(type) { + case *FileOrFolder: + // 先获取分享链接 + sfile := file.GetShareInfo() + if sfile == nil { + sfile, err = d.getFileShareUrlByID(file.GetID()) + if err != nil { + return nil, err + } + file.SetShareInfo(sfile) + } + + // 然后获取下载链接 + dfile, err = d.GetFilesByShareUrl(sfile.FID, sfile.Pwd) if err != nil { return nil, err } - downID = share.FID - pwd = share.Pwd - } - fileInfo, err := d.getFilesByShareUrl(ctx, downID, pwd, nil) - if err != nil { - return nil, err + // 修复文件大小 + if d.RepairFileInfo && !file.repairFlag { + size, time := d.getFileRealInfo(dfile.Url) + if size != nil { + file.size = size + file.repairFlag = true + } + if file.time != nil { + file.time = time + } + } + case *FileOrFolderByShareUrl: + dfile, err = d.GetFilesByShareUrl(file.GetID(), file.Pwd) + if err != nil { + return nil, err + } + // 修复文件大小 + if d.RepairFileInfo && !file.repairFlag { + size, time := d.getFileRealInfo(dfile.Url) + if size != nil { + file.size = size + file.repairFlag = true + } + if file.time != nil { + file.time = time + } + } } return &model.Link{ - URL: fileInfo.Url, + URL: dfile.Url, Header: http.Header{ "User-Agent": []string{base.UserAgent}, }, @@ -133,10 +168,6 @@ func (d *LanZou) Rename(ctx context.Context, srcObj model.Obj, newName string) e return errs.NotImplement } -func (d *LanZou) Copy(ctx context.Context, srcObj, dstDir model.Obj) error { - return errs.NotImplement -} - func (d *LanZou) Remove(ctx context.Context, obj model.Obj) error { if d.IsCookie() { _, err := d.doupload(func(req *resty.Request) { diff --git a/drivers/lanzou/help.go b/drivers/lanzou/help.go index f48a936f..4cb646c4 100644 --- a/drivers/lanzou/help.go +++ b/drivers/lanzou/help.go @@ -17,6 +17,7 @@ const DAY time.Duration = 84600000000000 // 解析时间 var timeSplitReg = regexp.MustCompile("([0-9.]*)\\s*([\u4e00-\u9fa5]+)") +// 如果解析失败,则返回当前时间 func MustParseTime(str string) time.Time { lastOpTime, err := time.ParseInLocation("2006-01-02 -07", str+" +08", time.Local) if err != nil { @@ -47,6 +48,7 @@ func MustParseTime(str string) time.Time { // 解析大小 var sizeSplitReg = regexp.MustCompile(`(?i)([0-9.]+)\s*([bkm]+)`) +// 解析失败返回0 func SizeStrToInt64(size string) int64 { strs := sizeSplitReg.FindStringSubmatch(size) if len(strs) < 3 { @@ -66,8 +68,13 @@ func SizeStrToInt64(size string) int64 { } // 移除注释 -func RemoveNotes(html []byte) []byte { - return regexp.MustCompile(`|//.*|/\*.*?\*/`).ReplaceAll(html, []byte{}) +func RemoveNotes(html string) string { + return regexp.MustCompile(`|[^:]//.*|/\*.*?\*/`).ReplaceAllStringFunc(html, func(b string) string { + if b[1:3] == "//" { + return b[:1] + } + return "\n" + }) } var findAcwScV2Reg = regexp.MustCompile(`arg1='([0-9A-Z]+)'`) diff --git a/drivers/lanzou/meta.go b/drivers/lanzou/meta.go index 8b2267e9..5fe21d26 100644 --- a/drivers/lanzou/meta.go +++ b/drivers/lanzou/meta.go @@ -7,11 +7,12 @@ import ( type Addition struct { Type string `json:"type" type:"select" options:"cookie,url" default:"cookie"` - Cookie string `json:"cookie" required:"true" help:"about 15 days valid"` + Cookie string `json:"cookie" required:"true" help:"about 15 days valid, ignore if shareUrl is used"` driver.RootID - SharePassword string `json:"share_password"` - BaseUrl string `json:"baseUrl" required:"true" default:"https://pc.woozooo.com"` - ShareUrl string `json:"shareUrl" required:"true" default:"https://pan.lanzouo.com"` + SharePassword string `json:"share_password"` + BaseUrl string `json:"baseUrl" required:"true" default:"https://pc.woozooo.com" help:"basic URL for file operation"` + ShareUrl string `json:"shareUrl" required:"true" default:"https://pan.lanzouo.com" help:"used to get the sharing page"` + RepairFileInfo bool `json:"repair_file_info" help:"To use webdav, you need to enable it"` } func (a *Addition) IsCookie() bool { diff --git a/drivers/lanzou/types.go b/drivers/lanzou/types.go index 50f79b09..9044b296 100644 --- a/drivers/lanzou/types.go +++ b/drivers/lanzou/types.go @@ -1,12 +1,14 @@ package lanzou import ( + "errors" "fmt" "time" - - "github.com/alist-org/alist/v3/internal/model" ) +var ErrFileShareCancel = errors.New("file sharing cancellation") +var ErrFileNotExist = errors.New("file does not exist") + type FilesOrFoldersResp struct { Text []FileOrFolder `json:"text"` } @@ -34,27 +36,51 @@ type FileOrFolder struct { FolID string `json:"fol_id"` //Folderlock string `json:"folderlock"` //FolderDes string `json:"folder_des"` + + // 缓存字段 + size *int64 `json:"-"` + time *time.Time `json:"-"` + repairFlag bool `json:"-"` + shareInfo *FileShare `json:"-"` } -func (f *FileOrFolder) isFloder() bool { - return f.FolID != "" -} -func (f *FileOrFolder) ToObj() model.Obj { - obj := &model.Object{} - if f.isFloder() { - obj.ID = f.FolID - obj.Name = f.Name - obj.Modified = time.Now() - obj.IsFolder = true - } else { - obj.ID = f.ID - obj.Name = f.NameAll - obj.Modified = MustParseTime(f.Time) - obj.Size = SizeStrToInt64(f.Size) +func (f *FileOrFolder) GetID() string { + if f.IsDir() { + return f.FolID } - return obj + return f.ID +} +func (f *FileOrFolder) GetName() string { + if f.IsDir() { + return f.Name + } + return f.NameAll +} +func (f *FileOrFolder) GetPath() string { return "" } +func (f *FileOrFolder) GetSize() int64 { + if f.size == nil { + size := SizeStrToInt64(f.Size) + f.size = &size + } + return *f.size +} +func (f *FileOrFolder) IsDir() bool { return f.FolID != "" } +func (f *FileOrFolder) ModTime() time.Time { + if f.time == nil { + time := MustParseTime(f.Time) + f.time = &time + } + return *f.time } +func (f *FileOrFolder) SetShareInfo(fs *FileShare) { + f.shareInfo = fs +} +func (f *FileOrFolder) GetShareInfo() *FileShare { + return f.shareInfo +} + +/* 通过ID获取文件/文件夹分享信息 */ type FileShareResp struct { Info FileShare `json:"info"` } @@ -73,31 +99,55 @@ type FileShare struct { Des string `json:"des"` } +/* 分享类型为文件夹 */ type FileOrFolderByShareUrlResp struct { Text []FileOrFolderByShareUrl `json:"text"` } type FileOrFolderByShareUrl struct { ID string `json:"id"` NameAll string `json:"name_all"` - Size string `json:"size"` - Time string `json:"time"` - Duan string `json:"duan"` + + // 文件特有 + Duan string `json:"duan"` + Size string `json:"size"` + Time string `json:"time"` //Icon string `json:"icon"` //PIco int `json:"p_ico"` //T int `json:"t"` - IsFloder bool + + // 文件夹特有 + IsFloder bool `json:"-"` + + // + Url string `json:"-"` + Pwd string `json:"-"` + + // 缓存字段 + size *int64 `json:"-"` + time *time.Time `json:"-"` + repairFlag bool `json:"-"` } -func (f *FileOrFolderByShareUrl) ToObj() model.Obj { - return &model.Object{ - ID: f.ID, - Name: f.NameAll, - Size: SizeStrToInt64(f.Size), - Modified: MustParseTime(f.Time), - IsFolder: f.IsFloder, +func (f *FileOrFolderByShareUrl) GetID() string { return f.ID } +func (f *FileOrFolderByShareUrl) GetName() string { return f.NameAll } +func (f *FileOrFolderByShareUrl) GetPath() string { return "" } +func (f *FileOrFolderByShareUrl) GetSize() int64 { + if f.size == nil { + size := SizeStrToInt64(f.Size) + f.size = &size } + return *f.size +} +func (f *FileOrFolderByShareUrl) IsDir() bool { return f.IsFloder } +func (f *FileOrFolderByShareUrl) ModTime() time.Time { + if f.time == nil { + time := MustParseTime(f.Time) + f.time = &time + } + return *f.time } +// 获取下载链接的响应 type FileShareInfoAndUrlResp[T string | int] struct { Dom string `json:"dom"` URL string `json:"url"` @@ -111,21 +161,3 @@ func (u *FileShareInfoAndUrlResp[T]) GetBaseUrl() string { func (u *FileShareInfoAndUrlResp[T]) GetDownloadUrl() string { return fmt.Sprint(u.GetBaseUrl(), "/", u.URL) } - -// 通过分享链接获取文件信息和下载链接 -type FileInfoAndUrlByShareUrl struct { - ID string - Name string - Size string - Time string - Url string -} - -func (f *FileInfoAndUrlByShareUrl) ToObj() model.Obj { - return &model.Object{ - ID: f.ID, - Name: f.Name, - Size: SizeStrToInt64(f.Size), - Modified: MustParseTime(f.Time), - } -} diff --git a/drivers/lanzou/util.go b/drivers/lanzou/util.go index 7b5d01fd..7bdc0799 100644 --- a/drivers/lanzou/util.go +++ b/drivers/lanzou/util.go @@ -1,7 +1,7 @@ package lanzou import ( - "context" + "errors" "fmt" "net/http" "regexp" @@ -32,7 +32,16 @@ func (d *LanZou) post(url string, callback base.ReqCallback, resp interface{}) ( } func (d *LanZou) _post(url string, callback base.ReqCallback, resp interface{}, up bool) ([]byte, error) { - data, err := d.request(url, http.MethodPost, callback, up) + data, err := d.request(url, http.MethodPost, func(req *resty.Request) { + req.AddRetryCondition(func(r *resty.Response, err error) bool { + if utils.Json.Get(r.Body(), "zt").ToInt() == 4 { + time.Sleep(time.Second) + return true + } + return false + }) + callback(req) + }, up) if err != nil { return nil, err } @@ -85,29 +94,28 @@ func (d *LanZou) request(url string, method string, callback base.ReqCallback, u */ // 获取文件和文件夹,获取到的文件大小、更改时间不可信 -func (d *LanZou) GetFiles(ctx context.Context, folderID string) ([]model.Obj, error) { - folders, err := d.getFolders(ctx, folderID) +func (d *LanZou) GetAllFiles(folderID string) ([]model.Obj, error) { + folders, err := d.GetFolders(folderID) if err != nil { return nil, err } - files, err := d.getFiles(ctx, folderID) + files, err := d.GetFiles(folderID) if err != nil { return nil, err } return append( utils.MustSliceConvert(folders, func(folder FileOrFolder) model.Obj { - return folder.ToObj() + return &folder }), utils.MustSliceConvert(files, func(file FileOrFolder) model.Obj { - return file.ToObj() + return &file })..., ), nil } // 通过ID获取文件夹 -func (d *LanZou) getFolders(ctx context.Context, folderID string) ([]FileOrFolder, error) { +func (d *LanZou) GetFolders(folderID string) ([]FileOrFolder, error) { var resp FilesOrFoldersResp _, err := d.doupload(func(req *resty.Request) { - req.SetContext(ctx) req.SetFormData(map[string]string{ "task": "47", "folder_id": folderID, @@ -120,12 +128,11 @@ func (d *LanZou) getFolders(ctx context.Context, folderID string) ([]FileOrFolde } // 通过ID获取文件 -func (d *LanZou) getFiles(ctx context.Context, folderID string) ([]FileOrFolder, error) { +func (d *LanZou) GetFiles(folderID string) ([]FileOrFolder, error) { files := make([]FileOrFolder, 0) for pg := 1; ; pg++ { var resp FilesOrFoldersResp _, err := d.doupload(func(req *resty.Request) { - req.SetContext(ctx) req.SetFormData(map[string]string{ "task": "5", "folder_id": folderID, @@ -144,37 +151,33 @@ func (d *LanZou) getFiles(ctx context.Context, folderID string) ([]FileOrFolder, } // 通过ID获取文件夹分享地址 -func (d *LanZou) getFolderShareUrlByID(ctx context.Context, fileID string) (share FileShare, err error) { +func (d *LanZou) getFolderShareUrlByID(fileID string) (*FileShare, error) { var resp FileShareResp - _, err = d.doupload(func(req *resty.Request) { - req.SetContext(ctx) + _, err := d.doupload(func(req *resty.Request) { req.SetFormData(map[string]string{ "task": "18", "file_id": fileID, }) }, &resp) if err != nil { - return + return nil, err } - share = resp.Info - return + return &resp.Info, nil } // 通过ID获取文件分享地址 -func (d *LanZou) getFileShareUrlByID(ctx context.Context, fileID string) (share FileShare, err error) { +func (d *LanZou) getFileShareUrlByID(fileID string) (*FileShare, error) { var resp FileShareResp - _, err = d.doupload(func(req *resty.Request) { - req.SetContext(ctx) + _, err := d.doupload(func(req *resty.Request) { req.SetFormData(map[string]string{ "task": "22", "file_id": fileID, }) }, &resp) if err != nil { - return + return nil, err } - share = resp.Info - return + return &resp.Info, nil } /* @@ -186,234 +189,252 @@ var isFileReg = regexp.MustCompile(`class="fileinfo"|id="file"|文件描述`) var isFolderReg = regexp.MustCompile(`id="infos"`) // 获取文件文件夹基础信息 + +// 获取文件名称 var nameFindReg = regexp.MustCompile(`