mirror of https://github.com/Xhofe/alist
🔨 refactor driver: ali
parent
98691b2aa8
commit
f577d82242
|
@ -17,7 +17,7 @@ func InitAccounts() {
|
||||||
model.RegisterAccount(account)
|
model.RegisterAccount(account)
|
||||||
driver, ok := drivers.GetDriver(account.Type)
|
driver, ok := drivers.GetDriver(account.Type)
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Errorf("no [%s] driver", driver)
|
log.Errorf("no [%s] driver", account.Type)
|
||||||
} else {
|
} else {
|
||||||
err := driver.Save(&accounts[i], nil)
|
err := driver.Save(&accounts[i], nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
package bootstrap
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "github.com/Xhofe/alist/drivers/123pan"
|
||||||
|
_ "github.com/Xhofe/alist/drivers/189cloud"
|
||||||
|
_ "github.com/Xhofe/alist/drivers/alidrive"
|
||||||
|
)
|
|
@ -1,321 +0,0 @@
|
||||||
package drivers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/Xhofe/alist/conf"
|
|
||||||
"github.com/Xhofe/alist/model"
|
|
||||||
"github.com/Xhofe/alist/utils"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/go-resty/resty/v2"
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"path/filepath"
|
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Pan123 struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
var pan123Client = resty.New()
|
|
||||||
|
|
||||||
var pan123 = "123Pan"
|
|
||||||
|
|
||||||
func (p Pan123) Items() []Item {
|
|
||||||
return []Item{
|
|
||||||
{
|
|
||||||
Name: "proxy",
|
|
||||||
Label: "proxy",
|
|
||||||
Type: "bool",
|
|
||||||
Required: true,
|
|
||||||
Description: "allow proxy",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "username",
|
|
||||||
Label: "username",
|
|
||||||
Type: "string",
|
|
||||||
Required: true,
|
|
||||||
Description: "account username/phone number",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "password",
|
|
||||||
Label: "password",
|
|
||||||
Type: "string",
|
|
||||||
Required: true,
|
|
||||||
Description: "account password",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "root_folder",
|
|
||||||
Label: "root folder file_id",
|
|
||||||
Type: "string",
|
|
||||||
Required: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "order_by",
|
|
||||||
Label: "order_by",
|
|
||||||
Type: "select",
|
|
||||||
Values: "name,fileId,updateAt,createAt",
|
|
||||||
Required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "order_direction",
|
|
||||||
Label: "order_direction",
|
|
||||||
Type: "select",
|
|
||||||
Values: "asc,desc",
|
|
||||||
Required: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Pan123TokenResp struct {
|
|
||||||
Code int `json:"code"`
|
|
||||||
Data struct {
|
|
||||||
Token string `json:"token"`
|
|
||||||
} `json:"data"`
|
|
||||||
Message string `json:"message"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p Pan123) Login(account *model.Account) error {
|
|
||||||
var resp Pan123TokenResp
|
|
||||||
_, err := pan123Client.R().
|
|
||||||
SetResult(&resp).
|
|
||||||
SetBody(Json{
|
|
||||||
"passport": account.Username,
|
|
||||||
"password": account.Password,
|
|
||||||
}).Post("https://www.123pan.com/api/user/sign_in")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if resp.Code != 200 {
|
|
||||||
err = fmt.Errorf(resp.Message)
|
|
||||||
account.Status = resp.Message
|
|
||||||
} else {
|
|
||||||
account.Status = "work"
|
|
||||||
account.AccessToken = resp.Data.Token
|
|
||||||
}
|
|
||||||
_ = model.SaveAccount(account)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p Pan123) Save(account *model.Account, old *model.Account) error {
|
|
||||||
if account.RootFolder == "" {
|
|
||||||
account.RootFolder = "0"
|
|
||||||
}
|
|
||||||
err := p.Login(account)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
type Pan123File struct {
|
|
||||||
FileName string `json:"FileName"`
|
|
||||||
Size int64 `json:"Size"`
|
|
||||||
UpdateAt *time.Time `json:"UpdateAt"`
|
|
||||||
FileId int64 `json:"FileId"`
|
|
||||||
Type int `json:"Type"`
|
|
||||||
Etag string `json:"Etag"`
|
|
||||||
S3KeyFlag string `json:"S3KeyFlag"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p Pan123) FormatFile(file *Pan123File) *model.File {
|
|
||||||
f := &model.File{
|
|
||||||
Id: strconv.FormatInt(file.FileId, 10),
|
|
||||||
Name: file.FileName,
|
|
||||||
Size: file.Size,
|
|
||||||
Driver: "123Pan",
|
|
||||||
UpdatedAt: file.UpdateAt,
|
|
||||||
}
|
|
||||||
if file.Type == 1 {
|
|
||||||
f.Type = conf.FOLDER
|
|
||||||
} else {
|
|
||||||
f.Type = utils.GetFileType(filepath.Ext(file.FileName))
|
|
||||||
}
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
type Pan123Files struct {
|
|
||||||
Code int `json:"code"`
|
|
||||||
Message string `json:"message"`
|
|
||||||
Data struct {
|
|
||||||
InfoList []Pan123File `json:"InfoList"`
|
|
||||||
Next string `json:"Next"`
|
|
||||||
} `json:"data"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p Pan123) GetFiles(parentId string, account *model.Account) ([]Pan123File, error) {
|
|
||||||
next := "0"
|
|
||||||
res := make([]Pan123File, 0)
|
|
||||||
for next != "-1" {
|
|
||||||
var resp Pan123Files
|
|
||||||
_, err := pan123Client.R().SetResult(&resp).
|
|
||||||
SetHeader("authorization", "Bearer "+account.AccessToken).
|
|
||||||
SetQueryParams(map[string]string{
|
|
||||||
"driveId": "0",
|
|
||||||
"limit": "100",
|
|
||||||
"next": next,
|
|
||||||
"orderBy": account.OrderBy,
|
|
||||||
"orderDirection": account.OrderDirection,
|
|
||||||
"parentFileId": parentId,
|
|
||||||
"trashed": "false",
|
|
||||||
}).Get("https://www.123pan.com/api/file/list")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
log.Debugf("%+v", resp)
|
|
||||||
if resp.Code != 0 {
|
|
||||||
if resp.Code == 401 {
|
|
||||||
err := p.Login(account)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return p.GetFiles(parentId, account)
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf(resp.Message)
|
|
||||||
}
|
|
||||||
next = resp.Data.Next
|
|
||||||
res = append(res, resp.Data.InfoList...)
|
|
||||||
}
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p Pan123) 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: pan123,
|
|
||||||
UpdatedAt: account.UpdatedAt,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
dir, name := filepath.Split(path)
|
|
||||||
files, err := p.Files(dir, account)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
for _, file := range files {
|
|
||||||
if file.Name == name {
|
|
||||||
return &file, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, PathNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p Pan123) Files(path string, account *model.Account) ([]model.File, error) {
|
|
||||||
path = utils.ParsePath(path)
|
|
||||||
var rawFiles []Pan123File
|
|
||||||
cache, err := conf.Cache.Get(conf.Ctx, fmt.Sprintf("%s%s", account.Name, path))
|
|
||||||
if err == nil {
|
|
||||||
rawFiles, _ = cache.([]Pan123File)
|
|
||||||
} else {
|
|
||||||
file, err := p.File(path, account)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
rawFiles, err = p.GetFiles(file.Id, account)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(rawFiles) > 0 {
|
|
||||||
_ = conf.Cache.Set(conf.Ctx, fmt.Sprintf("%s%s", account.Name, path), rawFiles, nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
files := make([]model.File, 0)
|
|
||||||
for _, file := range rawFiles {
|
|
||||||
files = append(files, *p.FormatFile(&file))
|
|
||||||
}
|
|
||||||
return files, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p Pan123) Path(path string, account *model.Account) (*model.File, []model.File, error) {
|
|
||||||
path = utils.ParsePath(path)
|
|
||||||
log.Debugf("pan123 path: %s", path)
|
|
||||||
file, err := p.File(path, account)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
if file.Type != conf.FOLDER {
|
|
||||||
file.Url, _ = p.Link(path, account)
|
|
||||||
return file, nil, nil
|
|
||||||
}
|
|
||||||
files, err := p.Files(path, account)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
return nil, files, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p Pan123) GetFile(path string, account *model.Account) (*Pan123File, error) {
|
|
||||||
dir, name := filepath.Split(path)
|
|
||||||
dir = utils.ParsePath(dir)
|
|
||||||
_, err := p.Files(dir, account)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
parentFiles_, _ := conf.Cache.Get(conf.Ctx, fmt.Sprintf("%s%s", account.Name, dir))
|
|
||||||
parentFiles, _ := parentFiles_.([]Pan123File)
|
|
||||||
for _, file := range parentFiles {
|
|
||||||
if file.FileName == name {
|
|
||||||
if file.Type != 1 {
|
|
||||||
return &file, err
|
|
||||||
} else {
|
|
||||||
return nil, NotFile
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, PathNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
type Pan123DownResp struct {
|
|
||||||
Code int `json:"code"`
|
|
||||||
Message string `json:"message"`
|
|
||||||
Data struct {
|
|
||||||
DownloadUrl string `json:"DownloadUrl"`
|
|
||||||
} `json:"data"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p Pan123) Link(path string, account *model.Account) (string, error) {
|
|
||||||
file, err := p.GetFile(utils.ParsePath(path), account)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
var resp Pan123DownResp
|
|
||||||
_, err = pan123Client.R().SetResult(&resp).SetHeader("authorization", "Bearer "+account.AccessToken).
|
|
||||||
SetBody(Json{
|
|
||||||
"driveId": 0,
|
|
||||||
"etag": file.Etag,
|
|
||||||
"fileId": file.FileId,
|
|
||||||
"fileName": file.FileName,
|
|
||||||
"s3keyFlag": file.S3KeyFlag,
|
|
||||||
"size": file.Size,
|
|
||||||
"type": file.Type,
|
|
||||||
}).Post("https://www.123pan.com/api/file/download_info")
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if resp.Code != 0 {
|
|
||||||
if resp.Code == 401 {
|
|
||||||
err := p.Login(account)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return p.Link(path, account)
|
|
||||||
}
|
|
||||||
return "", fmt.Errorf(resp.Message)
|
|
||||||
}
|
|
||||||
return resp.Data.DownloadUrl, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p Pan123) Proxy(c *gin.Context, account *model.Account) {
|
|
||||||
c.Request.Header.Del("origin")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p Pan123) Preview(path string, account *model.Account) (interface{}, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ Driver = (*Pan123)(nil)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
RegisterDriver("123Pan", &Pan123{})
|
|
||||||
pan123Client.SetRetryCount(3)
|
|
||||||
}
|
|
|
@ -0,0 +1,150 @@
|
||||||
|
package _23pan
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/Xhofe/alist/conf"
|
||||||
|
"github.com/Xhofe/alist/drivers"
|
||||||
|
"github.com/Xhofe/alist/model"
|
||||||
|
"github.com/Xhofe/alist/utils"
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var pan123Client = resty.New()
|
||||||
|
|
||||||
|
type Pan123TokenResp struct {
|
||||||
|
Code int `json:"code"`
|
||||||
|
Data struct {
|
||||||
|
Token string `json:"token"`
|
||||||
|
} `json:"data"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Pan123File struct {
|
||||||
|
FileName string `json:"FileName"`
|
||||||
|
Size int64 `json:"Size"`
|
||||||
|
UpdateAt *time.Time `json:"UpdateAt"`
|
||||||
|
FileId int64 `json:"FileId"`
|
||||||
|
Type int `json:"Type"`
|
||||||
|
Etag string `json:"Etag"`
|
||||||
|
S3KeyFlag string `json:"S3KeyFlag"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Pan123Files struct {
|
||||||
|
Code int `json:"code"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
Data struct {
|
||||||
|
InfoList []Pan123File `json:"InfoList"`
|
||||||
|
Next string `json:"Next"`
|
||||||
|
} `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Pan123DownResp struct {
|
||||||
|
Code int `json:"code"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
Data struct {
|
||||||
|
DownloadUrl string `json:"DownloadUrl"`
|
||||||
|
} `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Pan123) Login(account *model.Account) error {
|
||||||
|
var resp Pan123TokenResp
|
||||||
|
_, err := pan123Client.R().
|
||||||
|
SetResult(&resp).
|
||||||
|
SetBody(drivers.Json{
|
||||||
|
"passport": account.Username,
|
||||||
|
"password": account.Password,
|
||||||
|
}).Post("https://www.123pan.com/api/user/sign_in")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if resp.Code != 200 {
|
||||||
|
err = fmt.Errorf(resp.Message)
|
||||||
|
account.Status = resp.Message
|
||||||
|
} else {
|
||||||
|
account.Status = "work"
|
||||||
|
account.AccessToken = resp.Data.Token
|
||||||
|
}
|
||||||
|
_ = model.SaveAccount(account)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Pan123) FormatFile(file *Pan123File) *model.File {
|
||||||
|
f := &model.File{
|
||||||
|
Id: strconv.FormatInt(file.FileId, 10),
|
||||||
|
Name: file.FileName,
|
||||||
|
Size: file.Size,
|
||||||
|
Driver: driverName,
|
||||||
|
UpdatedAt: file.UpdateAt,
|
||||||
|
}
|
||||||
|
if file.Type == 1 {
|
||||||
|
f.Type = conf.FOLDER
|
||||||
|
} else {
|
||||||
|
f.Type = utils.GetFileType(filepath.Ext(file.FileName))
|
||||||
|
}
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Pan123) GetFiles(parentId string, account *model.Account) ([]Pan123File, error) {
|
||||||
|
next := "0"
|
||||||
|
res := make([]Pan123File, 0)
|
||||||
|
for next != "-1" {
|
||||||
|
var resp Pan123Files
|
||||||
|
_, err := pan123Client.R().SetResult(&resp).
|
||||||
|
SetHeader("authorization", "Bearer "+account.AccessToken).
|
||||||
|
SetQueryParams(map[string]string{
|
||||||
|
"driveId": "0",
|
||||||
|
"limit": "100",
|
||||||
|
"next": next,
|
||||||
|
"orderBy": account.OrderBy,
|
||||||
|
"orderDirection": account.OrderDirection,
|
||||||
|
"parentFileId": parentId,
|
||||||
|
"trashed": "false",
|
||||||
|
}).Get("https://www.123pan.com/api/file/list")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
log.Debugf("%+v", resp)
|
||||||
|
if resp.Code != 0 {
|
||||||
|
if resp.Code == 401 {
|
||||||
|
err := driver.Login(account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return driver.GetFiles(parentId, account)
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf(resp.Message)
|
||||||
|
}
|
||||||
|
next = resp.Data.Next
|
||||||
|
res = append(res, resp.Data.InfoList...)
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Pan123) GetFile(path string, account *model.Account) (*Pan123File, error) {
|
||||||
|
dir, name := filepath.Split(path)
|
||||||
|
_, err := driver.Files(dir, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
parentFiles_, _ := conf.Cache.Get(conf.Ctx, fmt.Sprintf("%s%s", account.Name, dir))
|
||||||
|
parentFiles, _ := parentFiles_.([]Pan123File)
|
||||||
|
for _, file := range parentFiles {
|
||||||
|
if file.FileName == name {
|
||||||
|
if file.Type != 1 {
|
||||||
|
return &file, err
|
||||||
|
} else {
|
||||||
|
return nil, drivers.NotFile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, drivers.PathNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
drivers.RegisterDriver(driverName, &Pan123{})
|
||||||
|
pan123Client.SetRetryCount(3)
|
||||||
|
}
|
|
@ -0,0 +1,181 @@
|
||||||
|
package _23pan
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/Xhofe/alist/conf"
|
||||||
|
"github.com/Xhofe/alist/drivers"
|
||||||
|
"github.com/Xhofe/alist/model"
|
||||||
|
"github.com/Xhofe/alist/utils"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Pan123 struct {}
|
||||||
|
|
||||||
|
var driverName = "123Pan"
|
||||||
|
|
||||||
|
func (driver Pan123) Items() []drivers.Item {
|
||||||
|
return []drivers.Item{
|
||||||
|
{
|
||||||
|
Name: "proxy",
|
||||||
|
Label: "proxy",
|
||||||
|
Type: "bool",
|
||||||
|
Required: true,
|
||||||
|
Description: "allow proxy",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "username",
|
||||||
|
Label: "username",
|
||||||
|
Type: "string",
|
||||||
|
Required: true,
|
||||||
|
Description: "account username/phone number",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "password",
|
||||||
|
Label: "password",
|
||||||
|
Type: "string",
|
||||||
|
Required: true,
|
||||||
|
Description: "account password",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "root_folder",
|
||||||
|
Label: "root folder file_id",
|
||||||
|
Type: "string",
|
||||||
|
Required: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "order_by",
|
||||||
|
Label: "order_by",
|
||||||
|
Type: "select",
|
||||||
|
Values: "name,fileId,updateAt,createAt",
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "order_direction",
|
||||||
|
Label: "order_direction",
|
||||||
|
Type: "select",
|
||||||
|
Values: "asc,desc",
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Pan123) Save(account *model.Account, old *model.Account) error {
|
||||||
|
if account.RootFolder == "" {
|
||||||
|
account.RootFolder = "0"
|
||||||
|
}
|
||||||
|
err := driver.Login(account)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Pan123) 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: driverName,
|
||||||
|
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, drivers.PathNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Pan123) Files(path string, account *model.Account) ([]model.File, error) {
|
||||||
|
path = utils.ParsePath(path)
|
||||||
|
var rawFiles []Pan123File
|
||||||
|
cache, err := conf.Cache.Get(conf.Ctx, fmt.Sprintf("%s%s", account.Name, path))
|
||||||
|
if err == nil {
|
||||||
|
rawFiles, _ = cache.([]Pan123File)
|
||||||
|
} 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
|
||||||
|
}
|
||||||
|
if len(rawFiles) > 0 {
|
||||||
|
_ = conf.Cache.Set(conf.Ctx, fmt.Sprintf("%s%s", account.Name, path), rawFiles, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
files := make([]model.File, 0)
|
||||||
|
for _, file := range rawFiles {
|
||||||
|
files = append(files, *driver.FormatFile(&file))
|
||||||
|
}
|
||||||
|
return files, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Pan123) Link(path string, account *model.Account) (string, error) {
|
||||||
|
file, err := driver.GetFile(utils.ParsePath(path), account)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
var resp Pan123DownResp
|
||||||
|
_, err = pan123Client.R().SetResult(&resp).SetHeader("authorization", "Bearer "+account.AccessToken).
|
||||||
|
SetBody(drivers.Json{
|
||||||
|
"driveId": 0,
|
||||||
|
"etag": file.Etag,
|
||||||
|
"fileId": file.FileId,
|
||||||
|
"fileName": file.FileName,
|
||||||
|
"s3keyFlag": file.S3KeyFlag,
|
||||||
|
"size": file.Size,
|
||||||
|
"type": file.Type,
|
||||||
|
}).Post("https://www.123pan.com/api/file/download_info")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if resp.Code != 0 {
|
||||||
|
if resp.Code == 401 {
|
||||||
|
err := driver.Login(account)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return driver.Link(path, account)
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf(resp.Message)
|
||||||
|
}
|
||||||
|
return resp.Data.DownloadUrl, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Pan123) Path(path string, account *model.Account) (*model.File, []model.File, error) {
|
||||||
|
path = utils.ParsePath(path)
|
||||||
|
log.Debugf("pan123 path: %s", path)
|
||||||
|
file, err := driver.File(path, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if file.Type != conf.FOLDER {
|
||||||
|
file.Url, _ = driver.Link(path, account)
|
||||||
|
return file, nil, nil
|
||||||
|
}
|
||||||
|
files, err := driver.Files(path, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return nil, files, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Pan123) Proxy(c *gin.Context, account *model.Account) {
|
||||||
|
c.Request.Header.Del("origin")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Pan123) Preview(path string, account *model.Account) (interface{}, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ drivers.Driver = (*Pan123)(nil)
|
|
@ -1,4 +1,4 @@
|
||||||
package drivers
|
package _89cloud
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
|
@ -9,9 +9,9 @@ import (
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/Xhofe/alist/conf"
|
"github.com/Xhofe/alist/conf"
|
||||||
|
"github.com/Xhofe/alist/drivers"
|
||||||
"github.com/Xhofe/alist/model"
|
"github.com/Xhofe/alist/model"
|
||||||
"github.com/Xhofe/alist/utils"
|
"github.com/Xhofe/alist/utils"
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/go-resty/resty/v2"
|
"github.com/go-resty/resty/v2"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
mathRand "math/rand"
|
mathRand "math/rand"
|
||||||
|
@ -22,75 +22,10 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Cloud189 struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
var client189Map map[string]*resty.Client
|
var client189Map map[string]*resty.Client
|
||||||
|
|
||||||
func (c Cloud189) Items() []Item {
|
func (driver Cloud189) FormatFile(file *Cloud189File) *model.File {
|
||||||
return []Item{
|
|
||||||
{
|
|
||||||
Name: "proxy",
|
|
||||||
Label: "proxy",
|
|
||||||
Type: "bool",
|
|
||||||
Required: true,
|
|
||||||
Description: "allow proxy",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "username",
|
|
||||||
Label: "username",
|
|
||||||
Type: "string",
|
|
||||||
Required: true,
|
|
||||||
Description: "account username/phone number",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "password",
|
|
||||||
Label: "password",
|
|
||||||
Type: "string",
|
|
||||||
Required: true,
|
|
||||||
Description: "account password",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "root_folder",
|
|
||||||
Label: "root folder file_id",
|
|
||||||
Type: "string",
|
|
||||||
Required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "order_by",
|
|
||||||
Label: "order_by",
|
|
||||||
Type: "select",
|
|
||||||
Values: "name,size,lastOpTime,createdDate",
|
|
||||||
Required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "order_direction",
|
|
||||||
Label: "desc",
|
|
||||||
Type: "select",
|
|
||||||
Values: "true,false",
|
|
||||||
Required: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c Cloud189) Save(account *model.Account, old *model.Account) error {
|
|
||||||
if old != nil && old.Name != account.Name {
|
|
||||||
delete(client189Map, old.Name)
|
|
||||||
}
|
|
||||||
if err := c.Login(account); err != nil {
|
|
||||||
account.Status = err.Error()
|
|
||||||
_ = model.SaveAccount(account)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
account.Status = "work"
|
|
||||||
err := model.SaveAccount(account)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c Cloud189) FormatFile(file *Cloud189File) *model.File {
|
|
||||||
f := &model.File{
|
f := &model.File{
|
||||||
Id: strconv.FormatInt(file.Id, 10),
|
Id: strconv.FormatInt(file.Id, 10),
|
||||||
Name: file.Name,
|
Name: file.Name,
|
||||||
|
@ -114,75 +49,6 @@ func (c Cloud189) FormatFile(file *Cloud189File) *model.File {
|
||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Cloud189) 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: pan123,
|
|
||||||
UpdatedAt: account.UpdatedAt,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
dir, name := filepath.Split(path)
|
|
||||||
files, err := c.Files(dir, account)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
for _, file := range files {
|
|
||||||
if file.Name == name {
|
|
||||||
return &file, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, PathNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c Cloud189) Files(path string, account *model.Account) ([]model.File, error) {
|
|
||||||
path = utils.ParsePath(path)
|
|
||||||
var rawFiles []Cloud189File
|
|
||||||
cache, err := conf.Cache.Get(conf.Ctx, fmt.Sprintf("%s%s", account.Name, path))
|
|
||||||
if err == nil {
|
|
||||||
rawFiles, _ = cache.([]Cloud189File)
|
|
||||||
} else {
|
|
||||||
file, err := c.File(path, account)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
rawFiles, err = c.GetFiles(file.Id, account)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(rawFiles) > 0 {
|
|
||||||
_ = conf.Cache.Set(conf.Ctx, fmt.Sprintf("%s%s", account.Name, path), rawFiles, nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
files := make([]model.File, 0)
|
|
||||||
for _, file := range rawFiles {
|
|
||||||
files = append(files, *c.FormatFile(&file))
|
|
||||||
}
|
|
||||||
return files, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c Cloud189) Path(path string, account *model.Account) (*model.File, []model.File, error) {
|
|
||||||
path = utils.ParsePath(path)
|
|
||||||
log.Debugf("189 path: %s", path)
|
|
||||||
file, err := c.File(path, account)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
if file.Type != conf.FOLDER {
|
|
||||||
file.Url, _ = c.Link(path, account)
|
|
||||||
return file, nil, nil
|
|
||||||
}
|
|
||||||
files, err := c.Files(path, account)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
return nil, files, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
//func (c Cloud189) GetFile(path string, account *model.Account) (*Cloud189File, error) {
|
//func (c Cloud189) GetFile(path string, account *model.Account) (*Cloud189File, error) {
|
||||||
// dir, name := filepath.Split(path)
|
// dir, name := filepath.Split(path)
|
||||||
// dir = utils.ParsePath(dir)
|
// dir = utils.ParsePath(dir)
|
||||||
|
@ -210,63 +76,8 @@ type Cloud189Down struct {
|
||||||
FileDownloadUrl string `json:"fileDownloadUrl"`
|
FileDownloadUrl string `json:"fileDownloadUrl"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Cloud189) Link(path string, account *model.Account) (string, error) {
|
|
||||||
file, err := c.File(utils.ParsePath(path), account)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if file.Type == conf.FOLDER {
|
|
||||||
return "", NotFile
|
|
||||||
}
|
|
||||||
client, ok := client189Map[account.Name]
|
|
||||||
if !ok {
|
|
||||||
return "", fmt.Errorf("can't find [%s] client", account.Name)
|
|
||||||
}
|
|
||||||
var e Cloud189Error
|
|
||||||
var resp Cloud189Down
|
|
||||||
_, err = client.R().SetResult(&resp).SetError(&e).
|
|
||||||
SetHeader("Accept", "application/json;charset=UTF-8").
|
|
||||||
SetQueryParams(map[string]string{
|
|
||||||
"noCache": random(),
|
|
||||||
"fileId": file.Id,
|
|
||||||
}).Get("https://cloud.189.cn/api/open/file/getFileDownloadUrl.action")
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if e.ErrorCode != "" {
|
|
||||||
if e.ErrorCode == "InvalidSessionKey" {
|
|
||||||
err = c.Login(account)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return c.Link(path, account)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if resp.ResCode != 0 {
|
|
||||||
return "", fmt.Errorf(resp.ResMessage)
|
|
||||||
}
|
|
||||||
res, err := noRedirectClient.R().Get(resp.FileDownloadUrl)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if res.StatusCode() == 302 {
|
|
||||||
return res.Header().Get("location"), nil
|
|
||||||
}
|
|
||||||
return resp.FileDownloadUrl, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c Cloud189) Proxy(ctx *gin.Context, account *model.Account) {
|
|
||||||
ctx.Request.Header.Del("Origin")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c Cloud189) Preview(path string, account *model.Account) (interface{}, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ Driver = (*Cloud189)(nil)
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
RegisterDriver("189Cloud", &Cloud189{})
|
drivers.RegisterDriver("189Cloud", &Cloud189{})
|
||||||
client189Map = make(map[string]*resty.Client, 0)
|
client189Map = make(map[string]*resty.Client, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -277,7 +88,7 @@ type LoginResp struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Login refer to PanIndex
|
// Login refer to PanIndex
|
||||||
func (c Cloud189) Login(account *model.Account) error {
|
func (driver Cloud189) Login(account *model.Account) error {
|
||||||
client, ok := client189Map[account.Name]
|
client, ok := client189Map[account.Name]
|
||||||
if !ok {
|
if !ok {
|
||||||
//cookieJar, _ := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List})
|
//cookieJar, _ := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List})
|
||||||
|
@ -394,7 +205,7 @@ type Cloud189Files struct {
|
||||||
} `json:"fileListAO"`
|
} `json:"fileListAO"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Cloud189) GetFiles(fileId string, account *model.Account) ([]Cloud189File, error) {
|
func (driver Cloud189) GetFiles(fileId string, account *model.Account) ([]Cloud189File, error) {
|
||||||
client, ok := client189Map[account.Name]
|
client, ok := client189Map[account.Name]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("can't find [%s] client", account.Name)
|
return nil, fmt.Errorf("can't find [%s] client", account.Name)
|
||||||
|
@ -421,11 +232,11 @@ func (c Cloud189) GetFiles(fileId string, account *model.Account) ([]Cloud189Fil
|
||||||
}
|
}
|
||||||
if e.ErrorCode != "" {
|
if e.ErrorCode != "" {
|
||||||
if e.ErrorCode == "InvalidSessionKey" {
|
if e.ErrorCode == "InvalidSessionKey" {
|
||||||
err = c.Login(account)
|
err = driver.Login(account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return c.GetFiles(fileId, account)
|
return driver.GetFiles(fileId, account)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if resp.ResCode != 0 {
|
if resp.ResCode != 0 {
|
|
@ -0,0 +1,203 @@
|
||||||
|
package _89cloud
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/Xhofe/alist/conf"
|
||||||
|
"github.com/Xhofe/alist/drivers"
|
||||||
|
"github.com/Xhofe/alist/model"
|
||||||
|
"github.com/Xhofe/alist/utils"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Cloud189 struct {}
|
||||||
|
|
||||||
|
var driverName = "189Cloud"
|
||||||
|
|
||||||
|
func (driver Cloud189) Items() []drivers.Item {
|
||||||
|
return []drivers.Item{
|
||||||
|
{
|
||||||
|
Name: "proxy",
|
||||||
|
Label: "proxy",
|
||||||
|
Type: "bool",
|
||||||
|
Required: true,
|
||||||
|
Description: "allow proxy",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "username",
|
||||||
|
Label: "username",
|
||||||
|
Type: "string",
|
||||||
|
Required: true,
|
||||||
|
Description: "account username/phone number",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "password",
|
||||||
|
Label: "password",
|
||||||
|
Type: "string",
|
||||||
|
Required: true,
|
||||||
|
Description: "account password",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "root_folder",
|
||||||
|
Label: "root folder file_id",
|
||||||
|
Type: "string",
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "order_by",
|
||||||
|
Label: "order_by",
|
||||||
|
Type: "select",
|
||||||
|
Values: "name,size,lastOpTime,createdDate",
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "order_direction",
|
||||||
|
Label: "desc",
|
||||||
|
Type: "select",
|
||||||
|
Values: "true,false",
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Cloud189) Save(account *model.Account, old *model.Account) error {
|
||||||
|
if old != nil && old.Name != account.Name {
|
||||||
|
delete(client189Map, old.Name)
|
||||||
|
}
|
||||||
|
if err := driver.Login(account); err != nil {
|
||||||
|
account.Status = err.Error()
|
||||||
|
_ = model.SaveAccount(account)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
account.Status = "work"
|
||||||
|
err := model.SaveAccount(account)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Cloud189) 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: driverName,
|
||||||
|
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, drivers.PathNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Cloud189) Files(path string, account *model.Account) ([]model.File, error) {
|
||||||
|
path = utils.ParsePath(path)
|
||||||
|
var rawFiles []Cloud189File
|
||||||
|
cache, err := conf.Cache.Get(conf.Ctx, fmt.Sprintf("%s%s", account.Name, path))
|
||||||
|
if err == nil {
|
||||||
|
rawFiles, _ = cache.([]Cloud189File)
|
||||||
|
} 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
|
||||||
|
}
|
||||||
|
if len(rawFiles) > 0 {
|
||||||
|
_ = conf.Cache.Set(conf.Ctx, fmt.Sprintf("%s%s", account.Name, path), rawFiles, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
files := make([]model.File, 0)
|
||||||
|
for _, file := range rawFiles {
|
||||||
|
files = append(files, *driver.FormatFile(&file))
|
||||||
|
}
|
||||||
|
return files, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Cloud189) Link(path string, account *model.Account) (string, error) {
|
||||||
|
file, err := driver.File(utils.ParsePath(path), account)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if file.Type == conf.FOLDER {
|
||||||
|
return "", drivers.NotFile
|
||||||
|
}
|
||||||
|
client, ok := client189Map[account.Name]
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("can't find [%s] client", account.Name)
|
||||||
|
}
|
||||||
|
var e Cloud189Error
|
||||||
|
var resp Cloud189Down
|
||||||
|
_, err = client.R().SetResult(&resp).SetError(&e).
|
||||||
|
SetHeader("Accept", "application/json;charset=UTF-8").
|
||||||
|
SetQueryParams(map[string]string{
|
||||||
|
"noCache": random(),
|
||||||
|
"fileId": file.Id,
|
||||||
|
}).Get("https://cloud.189.cn/api/open/file/getFileDownloadUrl.action")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if e.ErrorCode != "" {
|
||||||
|
if e.ErrorCode == "InvalidSessionKey" {
|
||||||
|
err = driver.Login(account)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return driver.Link(path, account)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if resp.ResCode != 0 {
|
||||||
|
return "", fmt.Errorf(resp.ResMessage)
|
||||||
|
}
|
||||||
|
res, err := drivers.NoRedirectClient.R().Get(resp.FileDownloadUrl)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if res.StatusCode() == 302 {
|
||||||
|
return res.Header().Get("location"), nil
|
||||||
|
}
|
||||||
|
return resp.FileDownloadUrl, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Cloud189) Path(path string, account *model.Account) (*model.File, []model.File, error) {
|
||||||
|
path = utils.ParsePath(path)
|
||||||
|
log.Debugf("189 path: %s", path)
|
||||||
|
file, err := driver.File(path, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if file.Type != conf.FOLDER {
|
||||||
|
file.Url, _ = driver.Link(path, account)
|
||||||
|
return file, nil, nil
|
||||||
|
}
|
||||||
|
files, err := driver.Files(path, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return nil, files, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Cloud189) Proxy(ctx *gin.Context, account *model.Account) {
|
||||||
|
ctx.Request.Header.Del("Origin")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Cloud189) Preview(path string, account *model.Account) (interface{}, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ drivers.Driver = (*Cloud189)(nil)
|
|
@ -1,394 +0,0 @@
|
||||||
package drivers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/Xhofe/alist/conf"
|
|
||||||
"github.com/Xhofe/alist/model"
|
|
||||||
"github.com/Xhofe/alist/utils"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/go-resty/resty/v2"
|
|
||||||
"github.com/robfig/cron/v3"
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"path/filepath"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var aliClient = resty.New()
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
RegisterDriver("AliDrive", &AliDrive{})
|
|
||||||
aliClient.
|
|
||||||
SetRetryCount(3).
|
|
||||||
SetHeader("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36").
|
|
||||||
SetHeader("content-type", "application/json").
|
|
||||||
SetHeader("origin", "https://aliyundrive.com")
|
|
||||||
}
|
|
||||||
|
|
||||||
type AliDrive struct{}
|
|
||||||
|
|
||||||
func (a AliDrive) Preview(path string, account *model.Account) (interface{}, error) {
|
|
||||||
file, err := a.GetFile(path, account)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// office
|
|
||||||
var resp Json
|
|
||||||
var e AliRespError
|
|
||||||
var url string
|
|
||||||
req := Json{
|
|
||||||
"drive_id": account.DriveId,
|
|
||||||
"file_id": file.FileId,
|
|
||||||
}
|
|
||||||
switch file.Category {
|
|
||||||
case "doc":
|
|
||||||
{
|
|
||||||
url = "https://api.aliyundrive.com/v2/file/get_office_preview_url"
|
|
||||||
req["access_token"] = account.AccessToken
|
|
||||||
}
|
|
||||||
case "video":
|
|
||||||
{
|
|
||||||
url = "https://api.aliyundrive.com/v2/file/get_video_preview_play_info"
|
|
||||||
req["category"] = "live_transcoding"
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("don't support")
|
|
||||||
}
|
|
||||||
_, err = aliClient.R().SetResult(&resp).SetError(&e).
|
|
||||||
SetHeader("authorization", "Bearer\t"+account.AccessToken).
|
|
||||||
SetBody(req).Post(url)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if e.Code != "" {
|
|
||||||
return nil, fmt.Errorf("%s", e.Message)
|
|
||||||
}
|
|
||||||
return resp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a AliDrive) Items() []Item {
|
|
||||||
return []Item{
|
|
||||||
{
|
|
||||||
Name: "proxy",
|
|
||||||
Label: "proxy",
|
|
||||||
Type: "bool",
|
|
||||||
Required: true,
|
|
||||||
Description: "allow proxy",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "order_by",
|
|
||||||
Label: "order_by",
|
|
||||||
Type: "select",
|
|
||||||
Values: "name,size,updated_at,created_at",
|
|
||||||
Required: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "order_direction",
|
|
||||||
Label: "order_direction",
|
|
||||||
Type: "select",
|
|
||||||
Values: "ASC,DESC",
|
|
||||||
Required: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "refresh_token",
|
|
||||||
Label: "refresh token",
|
|
||||||
Type: "string",
|
|
||||||
Required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "root_folder",
|
|
||||||
Label: "root folder file_id",
|
|
||||||
Type: "string",
|
|
||||||
Required: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "limit",
|
|
||||||
Label: "limit",
|
|
||||||
Type: "number",
|
|
||||||
Required: false,
|
|
||||||
Description: ">0 and <=200",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a AliDrive) Proxy(c *gin.Context, account *model.Account) {
|
|
||||||
c.Request.Header.Del("Origin")
|
|
||||||
c.Request.Header.Set("Referer", "https://www.aliyundrive.com/")
|
|
||||||
}
|
|
||||||
|
|
||||||
type AliRespError struct {
|
|
||||||
Code string `json:"code"`
|
|
||||||
Message string `json:"message"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type AliFiles struct {
|
|
||||||
Items []AliFile `json:"items"`
|
|
||||||
NextMarker string `json:"next_marker"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type AliFile struct {
|
|
||||||
DriveId string `json:"drive_id"`
|
|
||||||
CreatedAt *time.Time `json:"created_at"`
|
|
||||||
FileExtension string `json:"file_extension"`
|
|
||||||
FileId string `json:"file_id"`
|
|
||||||
Type string `json:"type"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Category string `json:"category"`
|
|
||||||
ParentFileId string `json:"parent_file_id"`
|
|
||||||
UpdatedAt *time.Time `json:"updated_at"`
|
|
||||||
Size int64 `json:"size"`
|
|
||||||
Thumbnail string `json:"thumbnail"`
|
|
||||||
Url string `json:"url"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a AliDrive) FormatFile(file *AliFile) *model.File {
|
|
||||||
f := &model.File{
|
|
||||||
Name: file.Name,
|
|
||||||
Size: file.Size,
|
|
||||||
UpdatedAt: file.UpdatedAt,
|
|
||||||
Thumbnail: file.Thumbnail,
|
|
||||||
Driver: "AliDrive",
|
|
||||||
Url: file.Url,
|
|
||||||
}
|
|
||||||
if file.Type == "folder" {
|
|
||||||
f.Type = conf.FOLDER
|
|
||||||
} else {
|
|
||||||
f.Type = utils.GetFileType(file.FileExtension)
|
|
||||||
}
|
|
||||||
if file.Category == "video" {
|
|
||||||
f.Type = conf.VIDEO
|
|
||||||
}
|
|
||||||
if file.Category == "image" {
|
|
||||||
f.Type = conf.IMAGE
|
|
||||||
}
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a AliDrive) GetFiles(fileId string, account *model.Account) ([]AliFile, error) {
|
|
||||||
marker := "first"
|
|
||||||
res := make([]AliFile, 0)
|
|
||||||
for marker != "" {
|
|
||||||
if marker == "first" {
|
|
||||||
marker = ""
|
|
||||||
}
|
|
||||||
var resp AliFiles
|
|
||||||
var e AliRespError
|
|
||||||
_, err := aliClient.R().
|
|
||||||
SetResult(&resp).
|
|
||||||
SetError(&e).
|
|
||||||
SetHeader("authorization", "Bearer\t"+account.AccessToken).
|
|
||||||
SetBody(Json{
|
|
||||||
"drive_id": account.DriveId,
|
|
||||||
"fields": "*",
|
|
||||||
"image_thumbnail_process": "image/resize,w_400/format,jpeg",
|
|
||||||
"image_url_process": "image/resize,w_1920/format,jpeg",
|
|
||||||
"limit": account.Limit,
|
|
||||||
"marker": marker,
|
|
||||||
"order_by": account.OrderBy,
|
|
||||||
"order_direction": account.OrderDirection,
|
|
||||||
"parent_file_id": fileId,
|
|
||||||
"video_thumbnail_process": "video/snapshot,t_0,f_jpg,ar_auto,w_300",
|
|
||||||
"url_expire_sec": 14400,
|
|
||||||
}).Post("https://api.aliyundrive.com/v2/file/list")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if e.Code != "" {
|
|
||||||
if e.Code == "AccessTokenInvalid" {
|
|
||||||
err = a.RefreshToken(account)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
} else {
|
|
||||||
_ = model.SaveAccount(account)
|
|
||||||
return a.GetFiles(fileId, account)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("%s", e.Message)
|
|
||||||
}
|
|
||||||
marker = resp.NextMarker
|
|
||||||
res = append(res, resp.Items...)
|
|
||||||
}
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a AliDrive) GetFile(path string, account *model.Account) (*AliFile, error) {
|
|
||||||
dir, name := filepath.Split(path)
|
|
||||||
dir = utils.ParsePath(dir)
|
|
||||||
_, _, err := a.Path(dir, account)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
parentFiles_, _ := conf.Cache.Get(conf.Ctx, fmt.Sprintf("%s%s", account.Name, dir))
|
|
||||||
parentFiles, _ := parentFiles_.([]AliFile)
|
|
||||||
for _, file := range parentFiles {
|
|
||||||
if file.Name == name {
|
|
||||||
if file.Type == "file" {
|
|
||||||
return &file, err
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("not file")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("path not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
// path: /aaa/bbb
|
|
||||||
func (a AliDrive) Path(path string, account *model.Account) (*model.File, []model.File, error) {
|
|
||||||
path = utils.ParsePath(path)
|
|
||||||
log.Debugf("ali path: %s", path)
|
|
||||||
cache, err := conf.Cache.Get(conf.Ctx, fmt.Sprintf("%s%s", account.Name, path))
|
|
||||||
if err == nil {
|
|
||||||
files, _ := cache.([]AliFile)
|
|
||||||
if len(files) != 0 {
|
|
||||||
res := make([]model.File, 0)
|
|
||||||
for _, file := range files {
|
|
||||||
res = append(res, *a.FormatFile(&file))
|
|
||||||
}
|
|
||||||
return nil, res, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// no cache or len(files) == 0
|
|
||||||
fileId := account.RootFolder
|
|
||||||
if path != "/" {
|
|
||||||
dir, name := filepath.Split(path)
|
|
||||||
dir = utils.ParsePath(dir)
|
|
||||||
_, _, err = a.Path(dir, account)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
parentFiles_, _ := conf.Cache.Get(conf.Ctx, fmt.Sprintf("%s%s", account.Name, dir))
|
|
||||||
parentFiles, _ := parentFiles_.([]AliFile)
|
|
||||||
found := false
|
|
||||||
for _, file := range parentFiles {
|
|
||||||
if file.Name == name {
|
|
||||||
found = true
|
|
||||||
if file.Type == "file" {
|
|
||||||
url, err := a.Link(path, account)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
file.Url = url
|
|
||||||
return a.FormatFile(&file), nil, nil
|
|
||||||
} else {
|
|
||||||
fileId = file.FileId
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !found {
|
|
||||||
return nil, nil, fmt.Errorf("path not found")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
files, err := a.GetFiles(fileId, account)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
_ = conf.Cache.Set(conf.Ctx, fmt.Sprintf("%s%s", account.Name, path), files, nil)
|
|
||||||
res := make([]model.File, 0)
|
|
||||||
for _, file := range files {
|
|
||||||
res = append(res, *a.FormatFile(&file))
|
|
||||||
}
|
|
||||||
return nil, res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a AliDrive) Link(path string, account *model.Account) (string, error) {
|
|
||||||
file, err := a.GetFile(utils.ParsePath(path), account)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
var resp Json
|
|
||||||
var e AliRespError
|
|
||||||
_, err = aliClient.R().SetResult(&resp).
|
|
||||||
SetError(&e).
|
|
||||||
SetHeader("authorization", "Bearer\t"+account.AccessToken).
|
|
||||||
SetBody(Json{
|
|
||||||
"drive_id": account.DriveId,
|
|
||||||
"file_id": file.FileId,
|
|
||||||
"expire_sec": 14400,
|
|
||||||
}).Post("https://api.aliyundrive.com/v2/file/get_download_url")
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if e.Code != "" {
|
|
||||||
if e.Code == "AccessTokenInvalid" {
|
|
||||||
err = a.RefreshToken(account)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
} else {
|
|
||||||
_ = model.SaveAccount(account)
|
|
||||||
return a.Link(path, account)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "", fmt.Errorf("%s", e.Message)
|
|
||||||
}
|
|
||||||
return resp["url"].(string), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a AliDrive) RefreshToken(account *model.Account) error {
|
|
||||||
url := "https://auth.aliyundrive.com/v2/account/token"
|
|
||||||
var resp TokenResp
|
|
||||||
var e AliRespError
|
|
||||||
_, err := aliClient.R().
|
|
||||||
//ForceContentType("application/json").
|
|
||||||
SetBody(Json{"refresh_token": account.RefreshToken, "grant_type": "refresh_token"}).
|
|
||||||
SetResult(&resp).
|
|
||||||
SetError(&e).
|
|
||||||
Post(url)
|
|
||||||
if err != nil {
|
|
||||||
account.Status = err.Error()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
log.Debugf("%+v,%+v", resp, e)
|
|
||||||
if e.Code != "" {
|
|
||||||
account.Status = e.Message
|
|
||||||
return fmt.Errorf("failed to refresh token: %s", e.Message)
|
|
||||||
}else {
|
|
||||||
account.Status = "work"
|
|
||||||
}
|
|
||||||
account.RefreshToken, account.AccessToken = resp.RefreshToken, resp.AccessToken
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a AliDrive) Save(account *model.Account, old *model.Account) error {
|
|
||||||
if old != nil {
|
|
||||||
conf.Cron.Remove(cron.EntryID(old.CronId))
|
|
||||||
}
|
|
||||||
if account.RootFolder == "" {
|
|
||||||
account.RootFolder = "root"
|
|
||||||
}
|
|
||||||
if account.Limit == 0 {
|
|
||||||
account.Limit = 200
|
|
||||||
}
|
|
||||||
err := a.RefreshToken(account)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var resp Json
|
|
||||||
_, _ = aliClient.R().SetResult(&resp).
|
|
||||||
SetBody("{}").
|
|
||||||
SetHeader("authorization", "Bearer\t"+account.AccessToken).
|
|
||||||
Post("https://api.aliyundrive.com/v2/user/get")
|
|
||||||
log.Debugf("user info: %+v", resp)
|
|
||||||
account.DriveId = resp["default_drive_id"].(string)
|
|
||||||
cronId, err := conf.Cron.AddFunc("@every 2h", func() {
|
|
||||||
name := account.Name
|
|
||||||
log.Debugf("ali account name: %s", name)
|
|
||||||
newAccount, ok := model.GetAccount(name)
|
|
||||||
log.Debugf("ali account: %+v", newAccount)
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = a.RefreshToken(&newAccount)
|
|
||||||
_ = model.SaveAccount(&newAccount)
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
account.CronId = int(cronId)
|
|
||||||
err = model.SaveAccount(account)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ Driver = (*AliDrive)(nil)
|
|
|
@ -0,0 +1,165 @@
|
||||||
|
package alidrive
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/Xhofe/alist/conf"
|
||||||
|
"github.com/Xhofe/alist/drivers"
|
||||||
|
"github.com/Xhofe/alist/model"
|
||||||
|
"github.com/Xhofe/alist/utils"
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var aliClient = resty.New()
|
||||||
|
|
||||||
|
type AliRespError struct {
|
||||||
|
Code string `json:"code"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AliFiles struct {
|
||||||
|
Items []AliFile `json:"items"`
|
||||||
|
NextMarker string `json:"next_marker"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AliFile struct {
|
||||||
|
DriveId string `json:"drive_id"`
|
||||||
|
CreatedAt *time.Time `json:"created_at"`
|
||||||
|
FileExtension string `json:"file_extension"`
|
||||||
|
FileId string `json:"file_id"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Category string `json:"category"`
|
||||||
|
ParentFileId string `json:"parent_file_id"`
|
||||||
|
UpdatedAt *time.Time `json:"updated_at"`
|
||||||
|
Size int64 `json:"size"`
|
||||||
|
Thumbnail string `json:"thumbnail"`
|
||||||
|
Url string `json:"url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver AliDrive) FormatFile(file *AliFile) *model.File {
|
||||||
|
f := &model.File{
|
||||||
|
Id: file.FileId,
|
||||||
|
Name: file.Name,
|
||||||
|
Size: file.Size,
|
||||||
|
UpdatedAt: file.UpdatedAt,
|
||||||
|
Thumbnail: file.Thumbnail,
|
||||||
|
Driver: driverName,
|
||||||
|
Url: file.Url,
|
||||||
|
}
|
||||||
|
if file.Type == "folder" {
|
||||||
|
f.Type = conf.FOLDER
|
||||||
|
} else {
|
||||||
|
f.Type = utils.GetFileType(file.FileExtension)
|
||||||
|
}
|
||||||
|
if file.Category == "video" {
|
||||||
|
f.Type = conf.VIDEO
|
||||||
|
}
|
||||||
|
if file.Category == "image" {
|
||||||
|
f.Type = conf.IMAGE
|
||||||
|
}
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver AliDrive) GetFiles(fileId string, account *model.Account) ([]AliFile, error) {
|
||||||
|
marker := "first"
|
||||||
|
res := make([]AliFile, 0)
|
||||||
|
for marker != "" {
|
||||||
|
if marker == "first" {
|
||||||
|
marker = ""
|
||||||
|
}
|
||||||
|
var resp AliFiles
|
||||||
|
var e AliRespError
|
||||||
|
_, err := aliClient.R().
|
||||||
|
SetResult(&resp).
|
||||||
|
SetError(&e).
|
||||||
|
SetHeader("authorization", "Bearer\t"+account.AccessToken).
|
||||||
|
SetBody(drivers.Json{
|
||||||
|
"drive_id": account.DriveId,
|
||||||
|
"fields": "*",
|
||||||
|
"image_thumbnail_process": "image/resize,w_400/format,jpeg",
|
||||||
|
"image_url_process": "image/resize,w_1920/format,jpeg",
|
||||||
|
"limit": account.Limit,
|
||||||
|
"marker": marker,
|
||||||
|
"order_by": account.OrderBy,
|
||||||
|
"order_direction": account.OrderDirection,
|
||||||
|
"parent_file_id": fileId,
|
||||||
|
"video_thumbnail_process": "video/snapshot,t_0,f_jpg,ar_auto,w_300",
|
||||||
|
"url_expire_sec": 14400,
|
||||||
|
}).Post("https://api.aliyundrive.com/v2/file/list")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if e.Code != "" {
|
||||||
|
if e.Code == "AccessTokenInvalid" {
|
||||||
|
err = driver.RefreshToken(account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
_ = model.SaveAccount(account)
|
||||||
|
return driver.GetFiles(fileId, account)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("%s", e.Message)
|
||||||
|
}
|
||||||
|
marker = resp.NextMarker
|
||||||
|
res = append(res, resp.Items...)
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver AliDrive) GetFile(path string, account *model.Account) (*AliFile, error) {
|
||||||
|
dir, name := filepath.Split(path)
|
||||||
|
_, err := driver.Files(dir, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
parentFiles_, _ := conf.Cache.Get(conf.Ctx, fmt.Sprintf("%s%s", account.Name, dir))
|
||||||
|
parentFiles, _ := parentFiles_.([]AliFile)
|
||||||
|
for _, file := range parentFiles {
|
||||||
|
if file.Name == name {
|
||||||
|
if file.Type == "file" {
|
||||||
|
return &file, err
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("not file")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, drivers.PathNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver AliDrive) RefreshToken(account *model.Account) error {
|
||||||
|
url := "https://auth.aliyundrive.com/v2/account/token"
|
||||||
|
var resp drivers.TokenResp
|
||||||
|
var e AliRespError
|
||||||
|
_, err := aliClient.R().
|
||||||
|
//ForceContentType("application/json").
|
||||||
|
SetBody(drivers.Json{"refresh_token": account.RefreshToken, "grant_type": "refresh_token"}).
|
||||||
|
SetResult(&resp).
|
||||||
|
SetError(&e).
|
||||||
|
Post(url)
|
||||||
|
if err != nil {
|
||||||
|
account.Status = err.Error()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Debugf("%+v,%+v", resp, e)
|
||||||
|
if e.Code != "" {
|
||||||
|
account.Status = e.Message
|
||||||
|
return fmt.Errorf("failed to refresh token: %s", e.Message)
|
||||||
|
} else {
|
||||||
|
account.Status = "work"
|
||||||
|
}
|
||||||
|
account.RefreshToken, account.AccessToken = resp.RefreshToken, resp.AccessToken
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
drivers.RegisterDriver(driverName, &AliDrive{})
|
||||||
|
aliClient.
|
||||||
|
SetRetryCount(3).
|
||||||
|
SetHeader("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36").
|
||||||
|
SetHeader("content-type", "application/json").
|
||||||
|
SetHeader("origin", "https://aliyundrive.com")
|
||||||
|
}
|
|
@ -0,0 +1,253 @@
|
||||||
|
package alidrive
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/Xhofe/alist/conf"
|
||||||
|
"github.com/Xhofe/alist/drivers"
|
||||||
|
"github.com/Xhofe/alist/model"
|
||||||
|
"github.com/Xhofe/alist/utils"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/robfig/cron/v3"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AliDrive struct{}
|
||||||
|
|
||||||
|
var driverName = "AliDrive"
|
||||||
|
|
||||||
|
func (driver AliDrive) Items() []drivers.Item {
|
||||||
|
return []drivers.Item{
|
||||||
|
{
|
||||||
|
Name: "proxy",
|
||||||
|
Label: "proxy",
|
||||||
|
Type: "bool",
|
||||||
|
Required: true,
|
||||||
|
Description: "allow proxy",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "order_by",
|
||||||
|
Label: "order_by",
|
||||||
|
Type: "select",
|
||||||
|
Values: "name,size,updated_at,created_at",
|
||||||
|
Required: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "order_direction",
|
||||||
|
Label: "order_direction",
|
||||||
|
Type: "select",
|
||||||
|
Values: "ASC,DESC",
|
||||||
|
Required: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "refresh_token",
|
||||||
|
Label: "refresh token",
|
||||||
|
Type: "string",
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "root_folder",
|
||||||
|
Label: "root folder file_id",
|
||||||
|
Type: "string",
|
||||||
|
Required: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "limit",
|
||||||
|
Label: "limit",
|
||||||
|
Type: "number",
|
||||||
|
Required: false,
|
||||||
|
Description: ">0 and <=200",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver AliDrive) Save(account *model.Account, old *model.Account) error {
|
||||||
|
if old != nil {
|
||||||
|
conf.Cron.Remove(cron.EntryID(old.CronId))
|
||||||
|
}
|
||||||
|
if account.RootFolder == "" {
|
||||||
|
account.RootFolder = "root"
|
||||||
|
}
|
||||||
|
if account.Limit == 0 {
|
||||||
|
account.Limit = 200
|
||||||
|
}
|
||||||
|
err := driver.RefreshToken(account)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var resp drivers.Json
|
||||||
|
_, _ = aliClient.R().SetResult(&resp).
|
||||||
|
SetBody("{}").
|
||||||
|
SetHeader("authorization", "Bearer\t"+account.AccessToken).
|
||||||
|
Post("https://api.aliyundrive.com/v2/user/get")
|
||||||
|
log.Debugf("user info: %+v", resp)
|
||||||
|
account.DriveId = resp["default_drive_id"].(string)
|
||||||
|
cronId, err := conf.Cron.AddFunc("@every 2h", func() {
|
||||||
|
name := account.Name
|
||||||
|
log.Debugf("ali account name: %s", name)
|
||||||
|
newAccount, ok := model.GetAccount(name)
|
||||||
|
log.Debugf("ali account: %+v", newAccount)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = driver.RefreshToken(&newAccount)
|
||||||
|
_ = model.SaveAccount(&newAccount)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
account.CronId = int(cronId)
|
||||||
|
err = model.SaveAccount(account)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver AliDrive) 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: driverName,
|
||||||
|
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, drivers.PathNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver AliDrive) Files(path string, account *model.Account) ([]model.File, error) {
|
||||||
|
path = utils.ParsePath(path)
|
||||||
|
var rawFiles []AliFile
|
||||||
|
cache, err := conf.Cache.Get(conf.Ctx, fmt.Sprintf("%s%s", account.Name, path))
|
||||||
|
if err == nil {
|
||||||
|
rawFiles, _ = cache.([]AliFile)
|
||||||
|
} 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
|
||||||
|
}
|
||||||
|
if len(rawFiles) > 0 {
|
||||||
|
_ = conf.Cache.Set(conf.Ctx, fmt.Sprintf("%s%s", account.Name, path), rawFiles, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
files := make([]model.File, 0)
|
||||||
|
for _, file := range rawFiles {
|
||||||
|
files = append(files, *driver.FormatFile(&file))
|
||||||
|
}
|
||||||
|
return files, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver AliDrive) Link(path string, account *model.Account) (string, error) {
|
||||||
|
file, err := driver.File(path, account)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
var resp drivers.Json
|
||||||
|
var e AliRespError
|
||||||
|
_, err = aliClient.R().SetResult(&resp).
|
||||||
|
SetError(&e).
|
||||||
|
SetHeader("authorization", "Bearer\t"+account.AccessToken).
|
||||||
|
SetBody(drivers.Json{
|
||||||
|
"drive_id": account.DriveId,
|
||||||
|
"file_id": file.Id,
|
||||||
|
"expire_sec": 14400,
|
||||||
|
}).Post("https://api.aliyundrive.com/v2/file/get_download_url")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if e.Code != "" {
|
||||||
|
if e.Code == "AccessTokenInvalid" {
|
||||||
|
err = driver.RefreshToken(account)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
} else {
|
||||||
|
_ = model.SaveAccount(account)
|
||||||
|
return driver.Link(path, account)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("%s", e.Message)
|
||||||
|
}
|
||||||
|
return resp["url"].(string), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver AliDrive) Path(path string, account *model.Account) (*model.File, []model.File, error) {
|
||||||
|
path = utils.ParsePath(path)
|
||||||
|
log.Debugf("ali path: %s", path)
|
||||||
|
file, err := driver.File(path, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if file.Type != conf.FOLDER {
|
||||||
|
file.Url, _ = driver.Link(path, account)
|
||||||
|
return file, nil, nil
|
||||||
|
}
|
||||||
|
files, err := driver.Files(path, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return nil, files, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver AliDrive) Proxy(c *gin.Context, account *model.Account) {
|
||||||
|
c.Request.Header.Del("Origin")
|
||||||
|
c.Request.Header.Set("Referer", "https://www.aliyundrive.com/")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver AliDrive) Preview(path string, account *model.Account) (interface{}, error) {
|
||||||
|
file, err := driver.GetFile(path, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// office
|
||||||
|
var resp drivers.Json
|
||||||
|
var e AliRespError
|
||||||
|
var url string
|
||||||
|
req := drivers.Json{
|
||||||
|
"drive_id": account.DriveId,
|
||||||
|
"file_id": file.FileId,
|
||||||
|
}
|
||||||
|
switch file.Category {
|
||||||
|
case "doc":
|
||||||
|
{
|
||||||
|
url = "https://api.aliyundrive.com/v2/file/get_office_preview_url"
|
||||||
|
req["access_token"] = account.AccessToken
|
||||||
|
}
|
||||||
|
case "video":
|
||||||
|
{
|
||||||
|
url = "https://api.aliyundrive.com/v2/file/get_video_preview_play_info"
|
||||||
|
req["category"] = "live_transcoding"
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("don't support")
|
||||||
|
}
|
||||||
|
_, err = aliClient.R().SetResult(&resp).SetError(&e).
|
||||||
|
SetHeader("authorization", "Bearer\t"+account.AccessToken).
|
||||||
|
SetBody(req).Post(url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if e.Code != "" {
|
||||||
|
return nil, fmt.Errorf("%s", e.Message)
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ drivers.Driver = (*AliDrive)(nil)
|
|
@ -4,16 +4,17 @@ import (
|
||||||
"github.com/Xhofe/alist/model"
|
"github.com/Xhofe/alist/model"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/go-resty/resty/v2"
|
"github.com/go-resty/resty/v2"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Driver interface {
|
type Driver interface {
|
||||||
Items() []Item
|
Items() []Item
|
||||||
Save(account *model.Account, old *model.Account) error
|
Save(account *model.Account, old *model.Account) error
|
||||||
//File(path string, account *model.Account) (*model.File, error)
|
File(path string, account *model.Account) (*model.File, error)
|
||||||
//Files(path string, account *model.Account) ([]model.File, error)
|
Files(path string, account *model.Account) ([]model.File, error)
|
||||||
Path(path string, account *model.Account) (*model.File, []model.File, error)
|
|
||||||
Link(path string, account *model.Account) (string, error)
|
Link(path string, account *model.Account) (string, error)
|
||||||
|
Path(path string, account *model.Account) (*model.File, []model.File, error)
|
||||||
Proxy(c *gin.Context, account *model.Account)
|
Proxy(c *gin.Context, account *model.Account)
|
||||||
Preview(path string, account *model.Account) (interface{}, error)
|
Preview(path string, account *model.Account) (interface{}, error)
|
||||||
// TODO
|
// TODO
|
||||||
|
@ -41,6 +42,7 @@ type TokenResp struct {
|
||||||
var driversMap = map[string]Driver{}
|
var driversMap = map[string]Driver{}
|
||||||
|
|
||||||
func RegisterDriver(name string, driver Driver) {
|
func RegisterDriver(name string, driver Driver) {
|
||||||
|
log.Infof("register driver: [%s]", name)
|
||||||
driversMap[name] = driver
|
driversMap[name] = driver
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,10 +61,10 @@ func GetDrivers() map[string][]Item {
|
||||||
|
|
||||||
type Json map[string]interface{}
|
type Json map[string]interface{}
|
||||||
|
|
||||||
var noRedirectClient *resty.Client
|
var NoRedirectClient *resty.Client
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
noRedirectClient = resty.New().SetRedirectPolicy(
|
NoRedirectClient = resty.New().SetRedirectPolicy(
|
||||||
resty.RedirectPolicyFunc(func(req *http.Request, via []*http.Request) error {
|
resty.RedirectPolicyFunc(func(req *http.Request, via []*http.Request) error {
|
||||||
return http.ErrUseLastResponse
|
return http.ErrUseLastResponse
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -16,6 +16,14 @@ import (
|
||||||
type GoogleDrive struct {
|
type GoogleDrive struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g GoogleDrive) File(path string, account *model.Account) (*model.File, error) {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g GoogleDrive) Files(path string, account *model.Account) ([]model.File, error) {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
var googleClient = resty.New()
|
var googleClient = resty.New()
|
||||||
|
|
||||||
func (g GoogleDrive) Items() []Item {
|
func (g GoogleDrive) Items() []Item {
|
||||||
|
|
|
@ -16,6 +16,14 @@ import (
|
||||||
type Native struct {
|
type Native struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n Native) File(path string, account *model.Account) (*model.File, error) {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n Native) Files(path string, account *model.Account) ([]model.File, error) {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
func (n Native) Preview(path string, account *model.Account) (interface{}, error) {
|
func (n Native) Preview(path string, account *model.Account) (interface{}, error) {
|
||||||
return nil, fmt.Errorf("no need")
|
return nil, fmt.Errorf("no need")
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,14 @@ import (
|
||||||
|
|
||||||
type Onedrive struct{}
|
type Onedrive struct{}
|
||||||
|
|
||||||
|
func (o Onedrive) File(path string, account *model.Account) (*model.File, error) {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o Onedrive) Files(path string, account *model.Account) ([]model.File, error) {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
var oneClient = resty.New()
|
var oneClient = resty.New()
|
||||||
|
|
||||||
type OnedriveHost struct {
|
type OnedriveHost struct {
|
||||||
|
|
Loading…
Reference in New Issue