mirror of https://github.com/Xhofe/alist
				
				
				
			✨ support shandianpan #234
							parent
							
								
									ba66e33913
								
							
						
					
					
						commit
						0f079827e5
					
				|  | @ -11,4 +11,5 @@ import ( | |||
| 	_ "github.com/Xhofe/alist/drivers/native" | ||||
| 	_ "github.com/Xhofe/alist/drivers/onedrive" | ||||
| 	_ "github.com/Xhofe/alist/drivers/pikpak" | ||||
| 	_ "github.com/Xhofe/alist/drivers/shandian" | ||||
| ) | ||||
|  |  | |||
|  | @ -0,0 +1,277 @@ | |||
| package shandian | ||||
| 
 | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"github.com/Xhofe/alist/conf" | ||||
| 	"github.com/Xhofe/alist/drivers/base" | ||||
| 	"github.com/Xhofe/alist/model" | ||||
| 	"github.com/Xhofe/alist/utils" | ||||
| 	"github.com/gin-gonic/gin" | ||||
| 	log "github.com/sirupsen/logrus" | ||||
| 	"path/filepath" | ||||
| 	"strconv" | ||||
| ) | ||||
| 
 | ||||
| type Shandian struct{} | ||||
| 
 | ||||
| func (driver Shandian) Config() base.DriverConfig { | ||||
| 	return base.DriverConfig{ | ||||
| 		Name:          "ShandianPan", | ||||
| 		NoNeedSetLink: true, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (driver Shandian) Items() []base.Item { | ||||
| 	return []base.Item{ | ||||
| 		{ | ||||
| 			Name:        "username", | ||||
| 			Label:       "username", | ||||
| 			Type:        base.TypeString, | ||||
| 			Required:    true, | ||||
| 			Description: "account username/phone number", | ||||
| 		}, | ||||
| 		{ | ||||
| 			Name:        "password", | ||||
| 			Label:       "password", | ||||
| 			Type:        base.TypeString, | ||||
| 			Required:    true, | ||||
| 			Description: "account password", | ||||
| 		}, | ||||
| 		{ | ||||
| 			Name:     "root_folder", | ||||
| 			Label:    "root folder file_id", | ||||
| 			Type:     base.TypeString, | ||||
| 			Required: false, | ||||
| 		}, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (driver Shandian) Save(account *model.Account, old *model.Account) error { | ||||
| 	if account.RootFolder == "" { | ||||
| 		account.RootFolder = "0" | ||||
| 	} | ||||
| 	return driver.Login(account) | ||||
| } | ||||
| 
 | ||||
| func (driver Shandian) File(path string, account *model.Account) (*model.File, error) { | ||||
| 	path = utils.ParsePath(path) | ||||
| 	if path == "/" { | ||||
| 		return &model.File{ | ||||
| 			Id:        account.RootFolder, | ||||
| 			Name:      account.Name, | ||||
| 			Size:      0, | ||||
| 			Type:      conf.FOLDER, | ||||
| 			Driver:    driver.Config().Name, | ||||
| 			UpdatedAt: account.UpdatedAt, | ||||
| 		}, nil | ||||
| 	} | ||||
| 	dir, name := filepath.Split(path) | ||||
| 	files, err := driver.Files(dir, account) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	for _, file := range files { | ||||
| 		if file.Name == name { | ||||
| 			return &file, nil | ||||
| 		} | ||||
| 	} | ||||
| 	return nil, base.ErrPathNotFound | ||||
| } | ||||
| 
 | ||||
| func (driver Shandian) Files(path string, account *model.Account) ([]model.File, error) { | ||||
| 	path = utils.ParsePath(path) | ||||
| 	var files []model.File | ||||
| 	cache, err := base.GetCache(path, account) | ||||
| 	if err == nil { | ||||
| 		files, _ = cache.([]model.File) | ||||
| 	} else { | ||||
| 		file, err := driver.File(path, account) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		rawFiles, err := driver.GetFiles(file.Id, account) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		files = make([]model.File, 0) | ||||
| 		for _, file := range rawFiles { | ||||
| 			files = append(files, *driver.FormatFile(&file)) | ||||
| 		} | ||||
| 		if len(files) > 0 { | ||||
| 			_ = base.SetCache(path, files, account) | ||||
| 		} | ||||
| 	} | ||||
| 	return files, nil | ||||
| } | ||||
| 
 | ||||
| func (driver Shandian) Link(args base.Args, account *model.Account) (*base.Link, error) { | ||||
| 	log.Debugf("shandian link") | ||||
| 	file, err := driver.File(args.Path, account) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	var e Resp | ||||
| 	res, err := base.NoRedirectClient.R().SetError(&e).SetHeader("Accept", "application/json").SetQueryParams(map[string]string{ | ||||
| 		"id":    file.Id, | ||||
| 		"token": account.AccessToken, | ||||
| 	}).Get("https://shandianpan.com/api/pan/file-download") | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if e.Code != 0 { | ||||
| 		if e.Code == 10 { | ||||
| 			err = driver.Login(account) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			return driver.Link(args, account) | ||||
| 		} | ||||
| 		return nil, errors.New(e.Msg) | ||||
| 	} | ||||
| 	return &base.Link{ | ||||
| 		Url: res.Header().Get("location"), | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| func (driver Shandian) Path(path string, account *model.Account) (*model.File, []model.File, error) { | ||||
| 	path = utils.ParsePath(path) | ||||
| 	log.Debugf("shandian path: %s", path) | ||||
| 	file, err := driver.File(path, account) | ||||
| 	if err != nil { | ||||
| 		return nil, nil, err | ||||
| 	} | ||||
| 	if !file.IsDir() { | ||||
| 		return file, nil, nil | ||||
| 	} | ||||
| 	files, err := driver.Files(path, account) | ||||
| 	if err != nil { | ||||
| 		return nil, nil, err | ||||
| 	} | ||||
| 	return nil, files, nil | ||||
| } | ||||
| 
 | ||||
| func (driver Shandian) Proxy(c *gin.Context, account *model.Account) { | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| func (driver Shandian) Preview(path string, account *model.Account) (interface{}, error) { | ||||
| 	return nil, base.ErrNotSupport | ||||
| } | ||||
| 
 | ||||
| func (driver Shandian) MakeDir(path string, account *model.Account) error { | ||||
| 	parentFile, err := driver.File(utils.Dir(path), account) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	data := map[string]interface{}{ | ||||
| 		"id":   parentFile.Id, | ||||
| 		"name": utils.Base(path), | ||||
| 	} | ||||
| 	_, err = driver.Post("https://shandianpan.com/api/pan/mkdir", data, nil, account) | ||||
| 	if err == nil { | ||||
| 		_ = base.DeleteCache(utils.Dir(path), account) | ||||
| 	} | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| func (driver Shandian) Move(src string, dst string, account *model.Account) error { | ||||
| 	srcFile, err := driver.File(src, account) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if utils.Dir(src) == utils.Dir(dst) { | ||||
| 		// rename
 | ||||
| 		data := map[string]interface{}{ | ||||
| 			"id":   srcFile.Id, | ||||
| 			"name": utils.Base(dst), | ||||
| 		} | ||||
| 		_, err = driver.Post("https://shandianpan.com/api/pan/change", data, nil, account) | ||||
| 	} else { | ||||
| 		dstParentFile, err := driver.File(utils.Dir(dst), account) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		data := map[string]interface{}{ | ||||
| 			"id":    srcFile.Id, | ||||
| 			"to_id": dstParentFile.Id, | ||||
| 		} | ||||
| 		_, err = driver.Post("https://shandianpan.com/api/pan/move", data, nil, account) | ||||
| 	} | ||||
| 	if err == nil { | ||||
| 		_ = base.DeleteCache(utils.Dir(src), account) | ||||
| 		if utils.Dir(src) != utils.Dir(dst) { | ||||
| 			_ = base.DeleteCache(utils.Dir(dst), account) | ||||
| 		} | ||||
| 	} | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| func (driver Shandian) Copy(src string, dst string, account *model.Account) error { | ||||
| 	return base.ErrNotSupport | ||||
| } | ||||
| 
 | ||||
| func (driver Shandian) Delete(path string, account *model.Account) error { | ||||
| 	file, err := driver.File(path, account) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	data := map[string]interface{}{ | ||||
| 		"id": file.Id, | ||||
| 	} | ||||
| 	_, err = driver.Post("https://shandianpan.com/api/pan/recycle-in", data, nil, account) | ||||
| 	if err == nil { | ||||
| 		_ = base.DeleteCache(utils.Dir(path), account) | ||||
| 	} | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| func (driver Shandian) Upload(file *model.FileStream, account *model.Account) error { | ||||
| 	parentFile, err := driver.File(file.ParentPath, account) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	var resp UploadResp | ||||
| 	parentId, err := strconv.Atoi(parentFile.Id) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	data := map[string]interface{}{ | ||||
| 		"id":   parentId, | ||||
| 		"name": file.GetFileName(), | ||||
| 	} | ||||
| 	_, err = driver.Post("https://shandianpan.com/api/pan/upload", data, &resp, account) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if resp.Code != 0 { | ||||
| 		if resp.Code == 10 { | ||||
| 			err = driver.Login(account) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			return driver.Upload(file, account) | ||||
| 		} | ||||
| 		return errors.New(resp.Msg) | ||||
| 	} | ||||
| 	var r Resp | ||||
| 	_, err = base.RestyClient.R().SetMultipartFormData(map[string]string{ | ||||
| 		"token":          account.AccessToken, | ||||
| 		"id":             "0", | ||||
| 		"key":            resp.Data.Key, | ||||
| 		"ossAccessKeyId": resp.Data.Accessid, | ||||
| 		"policy":         resp.Data.Policy, | ||||
| 		"signature":      resp.Data.Signature, | ||||
| 		"callback":       resp.Data.Callback, | ||||
| 	}).SetMultipartField("file", file.GetFileName(), file.GetMIMEType(), file). | ||||
| 		SetResult(&r).SetError(&r).Post("https:" + resp.Data.Host + "/") | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if r.Code == 0 { | ||||
| 		_ = base.DeleteCache(file.ParentPath, account) | ||||
| 		return nil | ||||
| 	} | ||||
| 	return errors.New(r.Msg) | ||||
| } | ||||
| 
 | ||||
| var _ base.Driver = (*Shandian)(nil) | ||||
|  | @ -0,0 +1,147 @@ | |||
| package shandian | ||||
| 
 | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"github.com/Xhofe/alist/conf" | ||||
| 	"github.com/Xhofe/alist/drivers/base" | ||||
| 	"github.com/Xhofe/alist/model" | ||||
| 	"github.com/Xhofe/alist/utils" | ||||
| 	log "github.com/sirupsen/logrus" | ||||
| 	"strconv" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| type Resp struct { | ||||
| 	Code int    `json:"code"` | ||||
| 	Msg  string `json:"msg"` | ||||
| } | ||||
| 
 | ||||
| type LoginResp struct { | ||||
| 	Resp | ||||
| 	Data struct { | ||||
| 		Token string `json:"token"` | ||||
| 	} `json:"data"` | ||||
| } | ||||
| 
 | ||||
| func (driver Shandian) Login(account *model.Account) error { | ||||
| 	var resp LoginResp | ||||
| 	_, err := base.RestyClient.R().SetResult(&resp).SetHeader("Accept", "application/json").SetBody(base.Json{ | ||||
| 		"mobile":   account.Username, | ||||
| 		"password": account.Password, | ||||
| 		"smscode":  "", | ||||
| 	}).Post("https://shandianpan.com/api/login") | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if resp.Code != 0 { | ||||
| 		return errors.New(resp.Msg) | ||||
| 	} | ||||
| 	account.AccessToken = resp.Data.Token | ||||
| 	_ = model.SaveAccount(account) | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| type File struct { | ||||
| 	Id         int64  `json:"id"` | ||||
| 	Type       int    `json:"type"` | ||||
| 	Name       string `json:"name"` | ||||
| 	UpdateTime int64  `json:"update_time"` | ||||
| 	Size       int64  `json:"size"` | ||||
| 	Ext        string `json:"ext"` | ||||
| } | ||||
| 
 | ||||
| func (driver Shandian) FormatFile(file *File) *model.File { | ||||
| 	t := time.Unix(file.UpdateTime, 0) | ||||
| 	f := &model.File{ | ||||
| 		Id:        strconv.FormatInt(file.Id, 10), | ||||
| 		Name:      file.Name, | ||||
| 		Size:      file.Size, | ||||
| 		Driver:    driver.Config().Name, | ||||
| 		UpdatedAt: &t, | ||||
| 	} | ||||
| 	if file.Type == 1 { | ||||
| 		f.Type = conf.FOLDER | ||||
| 	} else { | ||||
| 		f.Type = utils.GetFileType(file.Ext) | ||||
| 		if file.Ext != "" { | ||||
| 			f.Name += "." + file.Ext | ||||
| 		} | ||||
| 	} | ||||
| 	return f | ||||
| } | ||||
| 
 | ||||
| func (driver Shandian) Post(url string, data map[string]interface{}, resp interface{}, account *model.Account) ([]byte, error) { | ||||
| 	req := base.RestyClient.R() | ||||
| 	req.SetHeader("Accept", "application/json") | ||||
| 	data["token"] = account.AccessToken | ||||
| 	req.SetBody(data) | ||||
| 	var e Resp | ||||
| 	if resp != nil { | ||||
| 		req.SetResult(resp) | ||||
| 	} else { | ||||
| 		req.SetResult(&e) | ||||
| 	} | ||||
| 	req.SetError(&e) | ||||
| 	res, err := req.Post(url) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	log.Debug(res.String()) | ||||
| 	if e.Code != 0 { | ||||
| 		if e.Code == 10 { | ||||
| 			err = driver.Login(account) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			return driver.Post(url, data, resp, account) | ||||
| 		} | ||||
| 		return nil, errors.New(e.Msg) | ||||
| 	} | ||||
| 	return res.Body(), nil | ||||
| } | ||||
| 
 | ||||
| type FilesResp struct { | ||||
| 	Resp | ||||
| 	Data []File `json:"data"` | ||||
| } | ||||
| 
 | ||||
| func (driver Shandian) GetFiles(id string, account *model.Account) ([]File, error) { | ||||
| 	// TODO page not wok
 | ||||
| 	//res := make([]File, 0)
 | ||||
| 	page := 1 | ||||
| 	//for {
 | ||||
| 	data := map[string]interface{}{ | ||||
| 		"id":        id, | ||||
| 		"page":      page, | ||||
| 		"page_size": 100, | ||||
| 	} | ||||
| 	var resp FilesResp | ||||
| 	_, err := driver.Post("https://shandianpan.com/api/pan", data, &resp, account) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	//res = append(res, resp.Data...)
 | ||||
| 	//	if len(resp.Data) == 0 {
 | ||||
| 	//		break
 | ||||
| 	//	}
 | ||||
| 	//}
 | ||||
| 	//return res, nil
 | ||||
| 	return resp.Data, nil | ||||
| } | ||||
| 
 | ||||
| type UploadResp struct { | ||||
| 	Resp | ||||
| 	Data struct { | ||||
| 		Accessid  string `json:"accessid"` | ||||
| 		Policy    string `json:"policy"` | ||||
| 		Expire    int    `json:"expire"` | ||||
| 		Callback  string `json:"callback"` | ||||
| 		Key       string `json:"key"` | ||||
| 		Host      string `json:"host"` | ||||
| 		Signature string `json:"signature"` | ||||
| 	} `json:"data"` | ||||
| } | ||||
| 
 | ||||
| func init() { | ||||
| 	base.RegisterDriver(&Shandian{}) | ||||
| } | ||||
		Loading…
	
		Reference in New Issue
	
	 微凉
						微凉