mirror of https://github.com/Xhofe/alist
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)pull/2956/head
parent
7902b646ff
commit
7c3ea193ff
|
@ -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) {
|
func (d *LanZou) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
|
||||||
if d.IsCookie() {
|
if d.IsCookie() {
|
||||||
return d.GetFiles(ctx, dir.GetID())
|
return d.GetAllFiles(dir.GetID())
|
||||||
} else {
|
} 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) {
|
func (d *LanZou) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
|
||||||
downID := file.GetID()
|
var (
|
||||||
pwd := d.SharePassword
|
err error
|
||||||
if d.IsCookie() {
|
dfile *FileOrFolderByShareUrl
|
||||||
share, err := d.getFileShareUrlByID(ctx, file.GetID())
|
)
|
||||||
|
switch file := file.(type) {
|
||||||
|
case *FileOrFolder:
|
||||||
|
// 先获取分享链接
|
||||||
|
sfile := file.GetShareInfo()
|
||||||
|
if sfile == nil {
|
||||||
|
sfile, err = d.getFileShareUrlByID(file.GetID())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
downID = share.FID
|
file.SetShareInfo(sfile)
|
||||||
pwd = share.Pwd
|
|
||||||
}
|
}
|
||||||
fileInfo, err := d.getFilesByShareUrl(ctx, downID, pwd, nil)
|
|
||||||
|
// 然后获取下载链接
|
||||||
|
dfile, err = d.GetFilesByShareUrl(sfile.FID, sfile.Pwd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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{
|
return &model.Link{
|
||||||
URL: fileInfo.Url,
|
URL: dfile.Url,
|
||||||
Header: http.Header{
|
Header: http.Header{
|
||||||
"User-Agent": []string{base.UserAgent},
|
"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
|
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 {
|
func (d *LanZou) Remove(ctx context.Context, obj model.Obj) error {
|
||||||
if d.IsCookie() {
|
if d.IsCookie() {
|
||||||
_, err := d.doupload(func(req *resty.Request) {
|
_, err := d.doupload(func(req *resty.Request) {
|
||||||
|
|
|
@ -17,6 +17,7 @@ const DAY time.Duration = 84600000000000
|
||||||
// 解析时间
|
// 解析时间
|
||||||
var timeSplitReg = regexp.MustCompile("([0-9.]*)\\s*([\u4e00-\u9fa5]+)")
|
var timeSplitReg = regexp.MustCompile("([0-9.]*)\\s*([\u4e00-\u9fa5]+)")
|
||||||
|
|
||||||
|
// 如果解析失败,则返回当前时间
|
||||||
func MustParseTime(str string) time.Time {
|
func MustParseTime(str string) time.Time {
|
||||||
lastOpTime, err := time.ParseInLocation("2006-01-02 -07", str+" +08", time.Local)
|
lastOpTime, err := time.ParseInLocation("2006-01-02 -07", str+" +08", time.Local)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -47,6 +48,7 @@ func MustParseTime(str string) time.Time {
|
||||||
// 解析大小
|
// 解析大小
|
||||||
var sizeSplitReg = regexp.MustCompile(`(?i)([0-9.]+)\s*([bkm]+)`)
|
var sizeSplitReg = regexp.MustCompile(`(?i)([0-9.]+)\s*([bkm]+)`)
|
||||||
|
|
||||||
|
// 解析失败返回0
|
||||||
func SizeStrToInt64(size string) int64 {
|
func SizeStrToInt64(size string) int64 {
|
||||||
strs := sizeSplitReg.FindStringSubmatch(size)
|
strs := sizeSplitReg.FindStringSubmatch(size)
|
||||||
if len(strs) < 3 {
|
if len(strs) < 3 {
|
||||||
|
@ -66,8 +68,13 @@ func SizeStrToInt64(size string) int64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 移除注释
|
// 移除注释
|
||||||
func RemoveNotes(html []byte) []byte {
|
func RemoveNotes(html string) string {
|
||||||
return regexp.MustCompile(`<!--.*?-->|//.*|/\*.*?\*/`).ReplaceAll(html, []byte{})
|
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]+)'`)
|
var findAcwScV2Reg = regexp.MustCompile(`arg1='([0-9A-Z]+)'`)
|
||||||
|
|
|
@ -7,11 +7,12 @@ import (
|
||||||
|
|
||||||
type Addition struct {
|
type Addition struct {
|
||||||
Type string `json:"type" type:"select" options:"cookie,url" default:"cookie"`
|
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
|
driver.RootID
|
||||||
SharePassword string `json:"share_password"`
|
SharePassword string `json:"share_password"`
|
||||||
BaseUrl string `json:"baseUrl" required:"true" default:"https://pc.woozooo.com"`
|
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"`
|
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 {
|
func (a *Addition) IsCookie() bool {
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
package lanzou
|
package lanzou
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"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 {
|
type FilesOrFoldersResp struct {
|
||||||
Text []FileOrFolder `json:"text"`
|
Text []FileOrFolder `json:"text"`
|
||||||
}
|
}
|
||||||
|
@ -34,27 +36,51 @@ type FileOrFolder struct {
|
||||||
FolID string `json:"fol_id"`
|
FolID string `json:"fol_id"`
|
||||||
//Folderlock string `json:"folderlock"`
|
//Folderlock string `json:"folderlock"`
|
||||||
//FolderDes string `json:"folder_des"`
|
//FolderDes string `json:"folder_des"`
|
||||||
|
|
||||||
|
// 缓存字段
|
||||||
|
size *int64 `json:"-"`
|
||||||
|
time *time.Time `json:"-"`
|
||||||
|
repairFlag bool `json:"-"`
|
||||||
|
shareInfo *FileShare `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *FileOrFolder) isFloder() bool {
|
func (f *FileOrFolder) GetID() string {
|
||||||
return f.FolID != ""
|
if f.IsDir() {
|
||||||
|
return f.FolID
|
||||||
}
|
}
|
||||||
func (f *FileOrFolder) ToObj() model.Obj {
|
return f.ID
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
return obj
|
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 {
|
type FileShareResp struct {
|
||||||
Info FileShare `json:"info"`
|
Info FileShare `json:"info"`
|
||||||
}
|
}
|
||||||
|
@ -73,31 +99,55 @@ type FileShare struct {
|
||||||
Des string `json:"des"`
|
Des string `json:"des"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 分享类型为文件夹 */
|
||||||
type FileOrFolderByShareUrlResp struct {
|
type FileOrFolderByShareUrlResp struct {
|
||||||
Text []FileOrFolderByShareUrl `json:"text"`
|
Text []FileOrFolderByShareUrl `json:"text"`
|
||||||
}
|
}
|
||||||
type FileOrFolderByShareUrl struct {
|
type FileOrFolderByShareUrl struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
NameAll string `json:"name_all"`
|
NameAll string `json:"name_all"`
|
||||||
|
|
||||||
|
// 文件特有
|
||||||
|
Duan string `json:"duan"`
|
||||||
Size string `json:"size"`
|
Size string `json:"size"`
|
||||||
Time string `json:"time"`
|
Time string `json:"time"`
|
||||||
Duan string `json:"duan"`
|
|
||||||
//Icon string `json:"icon"`
|
//Icon string `json:"icon"`
|
||||||
//PIco int `json:"p_ico"`
|
//PIco int `json:"p_ico"`
|
||||||
//T int `json:"t"`
|
//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 {
|
func (f *FileOrFolderByShareUrl) GetID() string { return f.ID }
|
||||||
return &model.Object{
|
func (f *FileOrFolderByShareUrl) GetName() string { return f.NameAll }
|
||||||
ID: f.ID,
|
func (f *FileOrFolderByShareUrl) GetPath() string { return "" }
|
||||||
Name: f.NameAll,
|
func (f *FileOrFolderByShareUrl) GetSize() int64 {
|
||||||
Size: SizeStrToInt64(f.Size),
|
if f.size == nil {
|
||||||
Modified: MustParseTime(f.Time),
|
size := SizeStrToInt64(f.Size)
|
||||||
IsFolder: f.IsFloder,
|
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 {
|
type FileShareInfoAndUrlResp[T string | int] struct {
|
||||||
Dom string `json:"dom"`
|
Dom string `json:"dom"`
|
||||||
URL string `json:"url"`
|
URL string `json:"url"`
|
||||||
|
@ -111,21 +161,3 @@ func (u *FileShareInfoAndUrlResp[T]) GetBaseUrl() string {
|
||||||
func (u *FileShareInfoAndUrlResp[T]) GetDownloadUrl() string {
|
func (u *FileShareInfoAndUrlResp[T]) GetDownloadUrl() string {
|
||||||
return fmt.Sprint(u.GetBaseUrl(), "/", u.URL)
|
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),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package lanzou
|
package lanzou
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"regexp"
|
"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) {
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
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) {
|
func (d *LanZou) GetAllFiles(folderID string) ([]model.Obj, error) {
|
||||||
folders, err := d.getFolders(ctx, folderID)
|
folders, err := d.GetFolders(folderID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
files, err := d.getFiles(ctx, folderID)
|
files, err := d.GetFiles(folderID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return append(
|
return append(
|
||||||
utils.MustSliceConvert(folders, func(folder FileOrFolder) model.Obj {
|
utils.MustSliceConvert(folders, func(folder FileOrFolder) model.Obj {
|
||||||
return folder.ToObj()
|
return &folder
|
||||||
}), utils.MustSliceConvert(files, func(file FileOrFolder) model.Obj {
|
}), utils.MustSliceConvert(files, func(file FileOrFolder) model.Obj {
|
||||||
return file.ToObj()
|
return &file
|
||||||
})...,
|
})...,
|
||||||
), nil
|
), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 通过ID获取文件夹
|
// 通过ID获取文件夹
|
||||||
func (d *LanZou) getFolders(ctx context.Context, folderID string) ([]FileOrFolder, error) {
|
func (d *LanZou) GetFolders(folderID string) ([]FileOrFolder, error) {
|
||||||
var resp FilesOrFoldersResp
|
var resp FilesOrFoldersResp
|
||||||
_, err := d.doupload(func(req *resty.Request) {
|
_, err := d.doupload(func(req *resty.Request) {
|
||||||
req.SetContext(ctx)
|
|
||||||
req.SetFormData(map[string]string{
|
req.SetFormData(map[string]string{
|
||||||
"task": "47",
|
"task": "47",
|
||||||
"folder_id": folderID,
|
"folder_id": folderID,
|
||||||
|
@ -120,12 +128,11 @@ func (d *LanZou) getFolders(ctx context.Context, folderID string) ([]FileOrFolde
|
||||||
}
|
}
|
||||||
|
|
||||||
// 通过ID获取文件
|
// 通过ID获取文件
|
||||||
func (d *LanZou) getFiles(ctx context.Context, folderID string) ([]FileOrFolder, error) {
|
func (d *LanZou) GetFiles(folderID string) ([]FileOrFolder, error) {
|
||||||
files := make([]FileOrFolder, 0)
|
files := make([]FileOrFolder, 0)
|
||||||
for pg := 1; ; pg++ {
|
for pg := 1; ; pg++ {
|
||||||
var resp FilesOrFoldersResp
|
var resp FilesOrFoldersResp
|
||||||
_, err := d.doupload(func(req *resty.Request) {
|
_, err := d.doupload(func(req *resty.Request) {
|
||||||
req.SetContext(ctx)
|
|
||||||
req.SetFormData(map[string]string{
|
req.SetFormData(map[string]string{
|
||||||
"task": "5",
|
"task": "5",
|
||||||
"folder_id": folderID,
|
"folder_id": folderID,
|
||||||
|
@ -144,37 +151,33 @@ func (d *LanZou) getFiles(ctx context.Context, folderID string) ([]FileOrFolder,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 通过ID获取文件夹分享地址
|
// 通过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
|
var resp FileShareResp
|
||||||
_, err = d.doupload(func(req *resty.Request) {
|
_, err := d.doupload(func(req *resty.Request) {
|
||||||
req.SetContext(ctx)
|
|
||||||
req.SetFormData(map[string]string{
|
req.SetFormData(map[string]string{
|
||||||
"task": "18",
|
"task": "18",
|
||||||
"file_id": fileID,
|
"file_id": fileID,
|
||||||
})
|
})
|
||||||
}, &resp)
|
}, &resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return nil, err
|
||||||
}
|
}
|
||||||
share = resp.Info
|
return &resp.Info, nil
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 通过ID获取文件分享地址
|
// 通过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
|
var resp FileShareResp
|
||||||
_, err = d.doupload(func(req *resty.Request) {
|
_, err := d.doupload(func(req *resty.Request) {
|
||||||
req.SetContext(ctx)
|
|
||||||
req.SetFormData(map[string]string{
|
req.SetFormData(map[string]string{
|
||||||
"task": "22",
|
"task": "22",
|
||||||
"file_id": fileID,
|
"file_id": fileID,
|
||||||
})
|
})
|
||||||
}, &resp)
|
}, &resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return nil, err
|
||||||
}
|
}
|
||||||
share = resp.Info
|
return &resp.Info, nil
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -186,234 +189,252 @@ var isFileReg = regexp.MustCompile(`class="fileinfo"|id="file"|文件描述`)
|
||||||
var isFolderReg = regexp.MustCompile(`id="infos"`)
|
var isFolderReg = regexp.MustCompile(`id="infos"`)
|
||||||
|
|
||||||
// 获取文件文件夹基础信息
|
// 获取文件文件夹基础信息
|
||||||
|
|
||||||
|
// 获取文件名称
|
||||||
var nameFindReg = regexp.MustCompile(`<title>(.+?) - 蓝奏云</title>|id="filenajax">(.+?)</div>|var filename = '(.+?)';|<div style="font-size.+?>([^<>].+?)</div>|<div class="filethetext".+?>([^<>]+?)</div>`)
|
var nameFindReg = regexp.MustCompile(`<title>(.+?) - 蓝奏云</title>|id="filenajax">(.+?)</div>|var filename = '(.+?)';|<div style="font-size.+?>([^<>].+?)</div>|<div class="filethetext".+?>([^<>]+?)</div>`)
|
||||||
|
|
||||||
|
// 获取文件大小
|
||||||
var sizeFindReg = regexp.MustCompile(`(?i)大小\W*([0-9.]+\s*[bkm]+)`)
|
var sizeFindReg = regexp.MustCompile(`(?i)大小\W*([0-9.]+\s*[bkm]+)`)
|
||||||
|
|
||||||
|
// 获取文件时间
|
||||||
var timeFindReg = regexp.MustCompile(`\d+\s*[秒天分小][钟时]?前|[昨前]天|\d{4}-\d{2}-\d{2}`)
|
var timeFindReg = regexp.MustCompile(`\d+\s*[秒天分小][钟时]?前|[昨前]天|\d{4}-\d{2}-\d{2}`)
|
||||||
|
|
||||||
var findSubFolaerReg = regexp.MustCompile(`(folderlink|mbxfolder).+href="/(.+?)"(.+filename")?>(.+?)<`) // 查找分享文件夹子文件夹ID和名称
|
// 查找分享文件夹子文件夹ID和名称
|
||||||
|
var findSubFolaerReg = regexp.MustCompile(`(?i)(?:folderlink|mbxfolder).+href="/(.+?)"(?:.+filename")?>(.+?)<`)
|
||||||
|
|
||||||
// 获取关键数据
|
// 获取下载页面链接
|
||||||
var findDownPageParamReg = regexp.MustCompile(`<iframe.*?src="(.+?)"`)
|
var findDownPageParamReg = regexp.MustCompile(`<iframe.*?src="(.+?)"`)
|
||||||
|
|
||||||
// 通过分享链接获取文件或文件夹,如果是文件则会返回下载链接
|
// 获取分享链接主界面
|
||||||
func (d *LanZou) GetFileOrFolderByShareUrl(ctx context.Context, downID, pwd string) ([]model.Obj, error) {
|
func (d *LanZou) getShareUrlHtml(shareID string) (string, error) {
|
||||||
pageData, err := d.get(fmt.Sprint(d.ShareUrl, "/", downID), func(req *resty.Request) { req.SetContext(ctx) }, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
pageData = RemoveNotes(pageData)
|
|
||||||
|
|
||||||
if !isFileReg.Match(pageData) {
|
|
||||||
files, err := d.getFolderByShareUrl(ctx, downID, pwd, pageData)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return utils.MustSliceConvert(files, func(file FileOrFolderByShareUrl) model.Obj {
|
|
||||||
return file.ToObj()
|
|
||||||
}), nil
|
|
||||||
} else {
|
|
||||||
file, err := d.getFilesByShareUrl(ctx, downID, pwd, pageData)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return []model.Obj{file.ToObj()}, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 通过分享链接获取文件(下载链接也使用此方法)
|
|
||||||
// 参考 https://github.com/zaxtyson/LanZouCloud-API/blob/ab2e9ec715d1919bf432210fc16b91c6775fbb99/lanzou/api/core.py#L440
|
|
||||||
func (d *LanZou) getFilesByShareUrl(ctx context.Context, downID, pwd string, firstPageData []byte) (file FileInfoAndUrlByShareUrl, err error) {
|
|
||||||
if firstPageData == nil {
|
|
||||||
firstPageData, err = d.get(fmt.Sprint(d.ShareUrl, "/", downID), func(req *resty.Request) { req.SetContext(ctx) }, nil)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
firstPageData = RemoveNotes(firstPageData)
|
|
||||||
}
|
|
||||||
firstPageDataStr := string(firstPageData)
|
|
||||||
|
|
||||||
if strings.Contains(firstPageDataStr, "acw_sc__v2") {
|
|
||||||
var vs string
|
var vs string
|
||||||
if vs, err = CalcAcwScV2(firstPageDataStr); err != nil {
|
for i := 0; i < 3; i++ {
|
||||||
return
|
firstPageData, err := d.get(fmt.Sprint(d.ShareUrl, "/", shareID),
|
||||||
}
|
func(req *resty.Request) {
|
||||||
firstPageData, err = d.get(fmt.Sprint(d.ShareUrl, "/", downID), func(req *resty.Request) {
|
if vs != "" {
|
||||||
req.SetCookie(&http.Cookie{
|
req.SetCookie(&http.Cookie{
|
||||||
Name: "acw_sc__v2",
|
Name: "acw_sc__v2",
|
||||||
Value: vs,
|
Value: vs,
|
||||||
})
|
})
|
||||||
req.SetContext(ctx)
|
}
|
||||||
}, nil)
|
}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return "", err
|
||||||
}
|
|
||||||
firstPageData = RemoveNotes(firstPageData)
|
|
||||||
firstPageDataStr = string(firstPageData)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
firstPageDataStr := RemoveNotes(string(firstPageData))
|
||||||
|
if strings.Contains(firstPageDataStr, "取消分享") {
|
||||||
|
return "", ErrFileShareCancel
|
||||||
|
}
|
||||||
|
if strings.Contains(firstPageDataStr, "文件不存在") {
|
||||||
|
return "", ErrFileNotExist
|
||||||
|
}
|
||||||
|
|
||||||
|
// acw_sc__v2
|
||||||
|
if strings.Contains(firstPageDataStr, "acw_sc__v2") {
|
||||||
|
if vs, err = CalcAcwScV2(firstPageDataStr); err != nil {
|
||||||
|
log.Errorf("lanzou: err => acw_sc__v2 validation error ,data => %s\n", firstPageDataStr)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return firstPageDataStr, nil
|
||||||
|
}
|
||||||
|
return "", errors.New("acw_sc__v2 validation error")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 通过分享链接获取文件或文件夹
|
||||||
|
func (d *LanZou) GetFileOrFolderByShareUrl(shareID, pwd string) ([]model.Obj, error) {
|
||||||
|
pageData, err := d.getShareUrlHtml(shareID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isFileReg.MatchString(pageData) {
|
||||||
|
files, err := d.getFolderByShareUrl(pwd, pageData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return utils.MustSliceConvert(files, func(file FileOrFolderByShareUrl) model.Obj {
|
||||||
|
return &file
|
||||||
|
}), nil
|
||||||
|
} else {
|
||||||
|
file, err := d.getFilesByShareUrl(shareID, pwd, pageData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return []model.Obj{file}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 通过分享链接获取文件(下载链接也使用此方法)
|
||||||
|
// FileOrFolderByShareUrl 包含 pwd 和 url 字段
|
||||||
|
// 参考 https://github.com/zaxtyson/LanZouCloud-API/blob/ab2e9ec715d1919bf432210fc16b91c6775fbb99/lanzou/api/core.py#L440
|
||||||
|
func (d *LanZou) GetFilesByShareUrl(shareID, pwd string) (file *FileOrFolderByShareUrl, err error) {
|
||||||
|
pageData, err := d.getShareUrlHtml(shareID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return d.getFilesByShareUrl(shareID, pwd, pageData)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *LanZou) getFilesByShareUrl(shareID, pwd string, sharePageData string) (*FileOrFolderByShareUrl, error) {
|
||||||
var (
|
var (
|
||||||
param map[string]string
|
param map[string]string
|
||||||
downloadUrl string
|
downloadUrl string
|
||||||
baseUrl string
|
baseUrl string
|
||||||
|
file FileOrFolderByShareUrl
|
||||||
)
|
)
|
||||||
|
|
||||||
// 需要密码
|
// 需要密码
|
||||||
if strings.Contains(firstPageDataStr, "pwdload") || strings.Contains(firstPageDataStr, "passwddiv") {
|
if strings.Contains(sharePageData, "pwdload") || strings.Contains(sharePageData, "passwddiv") {
|
||||||
param, err = htmlFormToMap(firstPageDataStr)
|
param, err := htmlFormToMap(sharePageData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return nil, err
|
||||||
}
|
}
|
||||||
param["p"] = pwd
|
param["p"] = pwd
|
||||||
var resp FileShareInfoAndUrlResp[string]
|
var resp FileShareInfoAndUrlResp[string]
|
||||||
_, err = d.post(d.ShareUrl+"/ajaxm.php", func(req *resty.Request) { req.SetFormData(param).SetContext(ctx) }, &resp)
|
_, err = d.post(d.ShareUrl+"/ajaxm.php", func(req *resty.Request) { req.SetFormData(param) }, &resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return nil, err
|
||||||
}
|
}
|
||||||
file.Name = resp.Inf
|
file.NameAll = resp.Inf
|
||||||
|
file.Pwd = pwd
|
||||||
baseUrl = resp.GetBaseUrl()
|
baseUrl = resp.GetBaseUrl()
|
||||||
downloadUrl = resp.GetDownloadUrl()
|
downloadUrl = resp.GetDownloadUrl()
|
||||||
} else {
|
} else {
|
||||||
urlpaths := findDownPageParamReg.FindStringSubmatch(firstPageDataStr)
|
urlpaths := findDownPageParamReg.FindStringSubmatch(sharePageData)
|
||||||
if len(urlpaths) != 2 {
|
if len(urlpaths) != 2 {
|
||||||
err = fmt.Errorf("not find file page param")
|
log.Errorf("lanzou: err => not find file page param ,data => %s\n", sharePageData)
|
||||||
return
|
return nil, fmt.Errorf("not find file page param")
|
||||||
}
|
}
|
||||||
var nextPageData []byte
|
data, err := d.get(fmt.Sprint(d.ShareUrl, urlpaths[1]), nil, nil)
|
||||||
nextPageData, err = d.get(fmt.Sprint(d.ShareUrl, urlpaths[1]), func(req *resty.Request) { req.SetContext(ctx) }, nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return nil, err
|
||||||
}
|
}
|
||||||
nextPageData = RemoveNotes(nextPageData)
|
nextPageData := RemoveNotes(string(data))
|
||||||
nextPageDataStr := string(nextPageData)
|
|
||||||
|
|
||||||
param, err = htmlJsonToMap(nextPageDataStr)
|
param, err = htmlJsonToMap(nextPageData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var resp FileShareInfoAndUrlResp[int]
|
var resp FileShareInfoAndUrlResp[int]
|
||||||
_, err = d.post(d.ShareUrl+"/ajaxm.php", func(req *resty.Request) { req.SetFormData(param).SetContext(ctx) }, &resp)
|
_, err = d.post(d.ShareUrl+"/ajaxm.php", func(req *resty.Request) { req.SetFormData(param) }, &resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return nil, err
|
||||||
}
|
}
|
||||||
baseUrl = resp.GetBaseUrl()
|
baseUrl = resp.GetBaseUrl()
|
||||||
downloadUrl = resp.GetDownloadUrl()
|
downloadUrl = resp.GetDownloadUrl()
|
||||||
|
|
||||||
names := nameFindReg.FindStringSubmatch(firstPageDataStr)
|
names := nameFindReg.FindStringSubmatch(sharePageData)
|
||||||
if len(names) > 1 {
|
if len(names) > 1 {
|
||||||
for _, name := range names[1:] {
|
for _, name := range names[1:] {
|
||||||
if name != "" {
|
if name != "" {
|
||||||
file.Name = name
|
file.NameAll = name
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sizes := sizeFindReg.FindStringSubmatch(firstPageDataStr)
|
sizes := sizeFindReg.FindStringSubmatch(sharePageData)
|
||||||
if len(sizes) == 2 {
|
if len(sizes) == 2 {
|
||||||
file.Size = sizes[1]
|
file.Size = sizes[1]
|
||||||
}
|
}
|
||||||
file.ID = downID
|
file.ID = shareID
|
||||||
file.Time = timeFindReg.FindString(firstPageDataStr)
|
file.Time = timeFindReg.FindString(sharePageData)
|
||||||
|
|
||||||
// 重定向获取真实链接
|
// 重定向获取真实链接
|
||||||
res, err := base.NoRedirectClient.R().SetHeaders(map[string]string{
|
res, err := base.NoRedirectClient.R().SetHeaders(map[string]string{
|
||||||
"accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
|
"accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
|
||||||
}).SetContext(ctx).Get(downloadUrl)
|
}).Get(downloadUrl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
file.Url = res.Header().Get("location")
|
file.Url = res.Header().Get("location")
|
||||||
|
|
||||||
// 触发验证
|
// 触发验证
|
||||||
rPageDataStr := res.String()
|
rPageData := res.String()
|
||||||
if res.StatusCode() != 302 && strings.Contains(rPageDataStr, "网络异常") {
|
if res.StatusCode() != 302 {
|
||||||
param, err = htmlJsonToMap(rPageDataStr)
|
param, err = htmlJsonToMap(rPageData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return nil, err
|
||||||
}
|
}
|
||||||
param["el"] = "2"
|
param["el"] = "2"
|
||||||
time.Sleep(time.Second * 2)
|
time.Sleep(time.Second * 2)
|
||||||
|
|
||||||
// 通过验证获取直连
|
// 通过验证获取直连
|
||||||
var rUrl struct {
|
data, err := d.post(fmt.Sprint(baseUrl, "/ajax.php"), func(req *resty.Request) { req.SetFormData(param) }, nil)
|
||||||
Url string `json:"url"`
|
|
||||||
}
|
|
||||||
_, err = d.post(fmt.Sprint(baseUrl, "/ajax.php"), func(req *resty.Request) { req.SetContext(ctx).SetFormData(param) }, &rUrl)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return nil, err
|
||||||
}
|
}
|
||||||
file.Url = rUrl.Url
|
file.Url = utils.Json.Get(data, "url").ToString()
|
||||||
}
|
}
|
||||||
return
|
return &file, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 通过分享链接获取文件夹
|
// 通过分享链接获取文件夹
|
||||||
|
// 似乎子目录和文件不会加密
|
||||||
// 参考 https://github.com/zaxtyson/LanZouCloud-API/blob/ab2e9ec715d1919bf432210fc16b91c6775fbb99/lanzou/api/core.py#L1089
|
// 参考 https://github.com/zaxtyson/LanZouCloud-API/blob/ab2e9ec715d1919bf432210fc16b91c6775fbb99/lanzou/api/core.py#L1089
|
||||||
func (d *LanZou) getFolderByShareUrl(ctx context.Context, downID, pwd string, firstPageData []byte) ([]FileOrFolderByShareUrl, error) {
|
func (d *LanZou) GetFolderByShareUrl(shareID, pwd string) ([]FileOrFolderByShareUrl, error) {
|
||||||
if firstPageData == nil {
|
pageData, err := d.getShareUrlHtml(shareID)
|
||||||
var err error
|
|
||||||
firstPageData, err = d.get(fmt.Sprint(d.ShareUrl, "/", downID), func(req *resty.Request) { req.SetContext(ctx) }, nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
firstPageData = RemoveNotes(firstPageData)
|
return d.getFolderByShareUrl(pwd, pageData)
|
||||||
}
|
|
||||||
firstPageDataStr := string(firstPageData)
|
|
||||||
|
|
||||||
//
|
|
||||||
if strings.Contains(firstPageDataStr, "acw_sc__v2") {
|
|
||||||
vs, err := CalcAcwScV2(firstPageDataStr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
firstPageData, err = d.get(fmt.Sprint(d.ShareUrl, "/", downID), func(req *resty.Request) {
|
|
||||||
req.SetCookie(&http.Cookie{
|
|
||||||
Name: "acw_sc__v2",
|
|
||||||
Value: vs,
|
|
||||||
})
|
|
||||||
req.SetContext(ctx)
|
|
||||||
}, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
firstPageData = RemoveNotes(firstPageData)
|
|
||||||
firstPageDataStr = string(firstPageData)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
from, err := htmlJsonToMap(firstPageDataStr)
|
func (d *LanZou) getFolderByShareUrl(pwd string, sharePageData string) ([]FileOrFolderByShareUrl, error) {
|
||||||
|
from, err := htmlJsonToMap(sharePageData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
from["pwd"] = pwd
|
|
||||||
|
|
||||||
files := make([]FileOrFolderByShareUrl, 0)
|
files := make([]FileOrFolderByShareUrl, 0)
|
||||||
// vip获取文件夹
|
// vip获取文件夹
|
||||||
floders := findSubFolaerReg.FindAllStringSubmatch(firstPageDataStr, -1)
|
floders := findSubFolaerReg.FindAllStringSubmatch(sharePageData, -1)
|
||||||
for _, floder := range floders {
|
for _, floder := range floders {
|
||||||
if len(floder) == 5 {
|
if len(floder) == 3 {
|
||||||
files = append(files, FileOrFolderByShareUrl{
|
files = append(files, FileOrFolderByShareUrl{
|
||||||
ID: floder[2],
|
// Pwd: pwd, // 子文件夹不加密
|
||||||
NameAll: floder[4],
|
ID: floder[1],
|
||||||
|
NameAll: floder[2],
|
||||||
IsFloder: true,
|
IsFloder: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取文件
|
||||||
|
from["pwd"] = pwd
|
||||||
for page := 1; ; page++ {
|
for page := 1; ; page++ {
|
||||||
from["pg"] = strconv.Itoa(page)
|
from["pg"] = strconv.Itoa(page)
|
||||||
var resp FileOrFolderByShareUrlResp
|
var resp FileOrFolderByShareUrlResp
|
||||||
_, err := d.post(d.ShareUrl+"/filemoreajax.php", func(req *resty.Request) { req.SetFormData(from).SetContext(ctx) }, &resp)
|
_, err := d.post(d.ShareUrl+"/filemoreajax.php", func(req *resty.Request) { req.SetFormData(from) }, &resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
files = append(files, resp.Text...)
|
/*// 文件夹中的文件也不加密
|
||||||
|
for i := 0; i < len(resp.Text); i++ {
|
||||||
|
resp.Text[i].Pwd = pwd
|
||||||
|
}*/
|
||||||
if len(resp.Text) == 0 {
|
if len(resp.Text) == 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
time.Sleep(time.Millisecond * 600)
|
files = append(files, resp.Text...)
|
||||||
|
time.Sleep(time.Second)
|
||||||
}
|
}
|
||||||
return files, nil
|
return files, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 通过下载头获取真实文件信息
|
||||||
|
func (d *LanZou) getFileRealInfo(downURL string) (*int64, *time.Time) {
|
||||||
|
res, _ := base.RestyClient.R().Head(downURL)
|
||||||
|
if res == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
time, _ := http.ParseTime(res.Header().Get("Last-Modified"))
|
||||||
|
size, _ := strconv.ParseInt(res.Header().Get("Content-Length"), 10, 64)
|
||||||
|
return &size, &time
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue