mirror of https://github.com/Xhofe/alist
feat: add aliyundrive share driver (close #1215)
parent
9382f66f87
commit
3537153b91
|
@ -0,0 +1,144 @@
|
||||||
|
package aliyundrive_share
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/alist-org/alist/v3/drivers/base"
|
||||||
|
"github.com/alist-org/alist/v3/internal/driver"
|
||||||
|
"github.com/alist-org/alist/v3/internal/errs"
|
||||||
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
|
"github.com/alist-org/alist/v3/pkg/cron"
|
||||||
|
"github.com/alist-org/alist/v3/pkg/utils"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AliyundriveShare struct {
|
||||||
|
model.Storage
|
||||||
|
Addition
|
||||||
|
AccessToken string
|
||||||
|
ShareToken string
|
||||||
|
DriveId string
|
||||||
|
cron *cron.Cron
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliyundriveShare) Config() driver.Config {
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliyundriveShare) GetAddition() driver.Additional {
|
||||||
|
return d.Addition
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliyundriveShare) Init(ctx context.Context, storage model.Storage) error {
|
||||||
|
d.Storage = storage
|
||||||
|
err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = d.refreshToken()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = d.getShareToken()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
d.cron = cron.NewCron(time.Hour * 2)
|
||||||
|
d.cron.Do(func() {
|
||||||
|
err := d.refreshToken()
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("%+v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliyundriveShare) Drop(ctx context.Context) error {
|
||||||
|
if d.cron != nil {
|
||||||
|
d.cron.Stop()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliyundriveShare) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
|
||||||
|
files, err := d.getFiles(dir.GetID())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return utils.SliceConvert(files, func(src File) (model.Obj, error) {
|
||||||
|
return fileToObj(src), nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//func (d *AliyundriveShare) Get(ctx context.Context, path string) (model.Obj, error) {
|
||||||
|
// // this is optional
|
||||||
|
// return nil, errs.NotImplement
|
||||||
|
//}
|
||||||
|
|
||||||
|
func (d *AliyundriveShare) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
|
||||||
|
data := base.Json{
|
||||||
|
"drive_id": d.DriveId,
|
||||||
|
"file_id": file.GetID(),
|
||||||
|
"expire_sec": 14400,
|
||||||
|
}
|
||||||
|
var e ErrorResp
|
||||||
|
res, err := base.RestyClient.R().
|
||||||
|
SetError(&e).SetBody(data).
|
||||||
|
SetHeader("content-type", "application/json").
|
||||||
|
SetHeader("Authorization", "Bearer\t"+d.AccessToken).
|
||||||
|
Post("https://api.aliyundrive.com/v2/file/get_download_url")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if e.Code != "" {
|
||||||
|
if e.Code == "AccessTokenInvalid" {
|
||||||
|
err = d.refreshToken()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return d.Link(ctx, file, args)
|
||||||
|
}
|
||||||
|
return nil, errors.New(e.Message)
|
||||||
|
}
|
||||||
|
return &model.Link{
|
||||||
|
Header: http.Header{
|
||||||
|
"Referer": []string{"https://www.aliyundrive.com/"},
|
||||||
|
},
|
||||||
|
URL: utils.Json.Get(res.Body(), "url").ToString(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliyundriveShare) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
|
||||||
|
// TODO create folder
|
||||||
|
return errs.NotSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliyundriveShare) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
|
||||||
|
// TODO move obj
|
||||||
|
return errs.NotSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliyundriveShare) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
|
||||||
|
// TODO rename obj
|
||||||
|
return errs.NotSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliyundriveShare) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
|
||||||
|
// TODO copy obj
|
||||||
|
return errs.NotSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliyundriveShare) Remove(ctx context.Context, obj model.Obj) error {
|
||||||
|
// TODO remove obj
|
||||||
|
return errs.NotSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliyundriveShare) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
|
||||||
|
// TODO upload file
|
||||||
|
return errs.NotSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ driver.Driver = (*AliyundriveShare)(nil)
|
|
@ -0,0 +1,29 @@
|
||||||
|
package aliyundrive_share
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/alist-org/alist/v3/internal/driver"
|
||||||
|
"github.com/alist-org/alist/v3/internal/op"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Addition struct {
|
||||||
|
RefreshToken string `json:"refresh_token" required:"true"`
|
||||||
|
ShareId string `json:"share_id" required:"true"`
|
||||||
|
SharePwd string `json:"share_pwd"`
|
||||||
|
driver.RootID
|
||||||
|
OrderBy string `json:"order_by" type:"select" options:"name,size,updated_at,created_at"`
|
||||||
|
OrderDirection string `json:"order_direction" type:"select" options:"ASC,DESC"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var config = driver.Config{
|
||||||
|
Name: "AliyundriveShare",
|
||||||
|
LocalSort: false,
|
||||||
|
OnlyProxy: false,
|
||||||
|
NoUpload: true,
|
||||||
|
DefaultRoot: "root",
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
op.RegisterDriver(config, func() driver.Driver {
|
||||||
|
return &AliyundriveShare{}
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
package aliyundrive_share
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ErrorResp struct {
|
||||||
|
Code string `json:"code"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ShareTokenResp struct {
|
||||||
|
ShareToken string `json:"share_token"`
|
||||||
|
ExpireTime time.Time `json:"expire_time"`
|
||||||
|
ExpiresIn int `json:"expires_in"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListResp struct {
|
||||||
|
Items []File `json:"items"`
|
||||||
|
NextMarker string `json:"next_marker"`
|
||||||
|
PunishedFileCount int `json:"punished_file_count"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type File struct {
|
||||||
|
DriveId string `json:"drive_id"`
|
||||||
|
DomainId string `json:"domain_id"`
|
||||||
|
FileId string `json:"file_id"`
|
||||||
|
ShareId string `json:"share_id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
ParentFileId string `json:"parent_file_id"`
|
||||||
|
Size int64 `json:"size"`
|
||||||
|
Thumbnail string `json:"thumbnail"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func fileToObj(f File) *model.ObjThumb {
|
||||||
|
return &model.ObjThumb{
|
||||||
|
Object: model.Object{
|
||||||
|
ID: f.FileId,
|
||||||
|
Name: f.Name,
|
||||||
|
Size: f.Size,
|
||||||
|
Modified: f.UpdatedAt,
|
||||||
|
IsFolder: f.Type == "folder",
|
||||||
|
},
|
||||||
|
Thumbnail: model.Thumbnail{Thumbnail: f.Thumbnail},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//type ShareLinkResp struct {
|
||||||
|
// DownloadUrl string `json:"download_url"`
|
||||||
|
// Url string `json:"url"`
|
||||||
|
// Thumbnail string `json:"thumbnail"`
|
||||||
|
//}
|
|
@ -0,0 +1,99 @@
|
||||||
|
package aliyundrive_share
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/alist-org/alist/v3/drivers/base"
|
||||||
|
"github.com/alist-org/alist/v3/internal/op"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (d *AliyundriveShare) refreshToken() error {
|
||||||
|
url := "https://auth.aliyundrive.com/v2/account/token"
|
||||||
|
var resp base.TokenResp
|
||||||
|
var e ErrorResp
|
||||||
|
_, err := base.RestyClient.R().
|
||||||
|
SetBody(base.Json{"refresh_token": d.RefreshToken, "grant_type": "refresh_token"}).
|
||||||
|
SetResult(&resp).
|
||||||
|
SetError(&e).
|
||||||
|
Post(url)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if e.Code != "" {
|
||||||
|
return fmt.Errorf("failed to refresh token: %s", e.Message)
|
||||||
|
}
|
||||||
|
d.RefreshToken, d.AccessToken = resp.RefreshToken, resp.AccessToken
|
||||||
|
op.MustSaveDriverStorage(d)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// do others that not defined in Driver interface
|
||||||
|
func (d *AliyundriveShare) getShareToken() error {
|
||||||
|
data := base.Json{
|
||||||
|
"share_id": d.ShareId,
|
||||||
|
}
|
||||||
|
if d.SharePwd != "" {
|
||||||
|
data["share_pwd"] = d.SharePwd
|
||||||
|
}
|
||||||
|
var e ErrorResp
|
||||||
|
var resp ShareTokenResp
|
||||||
|
_, err := base.RestyClient.R().
|
||||||
|
SetResult(&resp).SetError(&e).SetBody(data).
|
||||||
|
Post("https://api.aliyundrive.com/v2/share_link/get_share_token")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if e.Code != "" {
|
||||||
|
return errors.New(e.Message)
|
||||||
|
}
|
||||||
|
d.ShareToken = resp.ShareToken
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliyundriveShare) getFiles(fileId string) ([]File, error) {
|
||||||
|
files := make([]File, 0)
|
||||||
|
data := base.Json{
|
||||||
|
"image_thumbnail_process": "image/resize,w_160/format,jpeg",
|
||||||
|
"image_url_process": "image/resize,w_1920/format,jpeg",
|
||||||
|
"limit": 100,
|
||||||
|
"order_by": d.OrderBy,
|
||||||
|
"order_direction": d.OrderDirection,
|
||||||
|
"parent_file_id": fileId,
|
||||||
|
"share_id": d.ShareId,
|
||||||
|
"video_thumbnail_process": "video/snapshot,t_1000,f_jpg,ar_auto,w_300",
|
||||||
|
"marker": "first",
|
||||||
|
}
|
||||||
|
for data["marker"] != "" {
|
||||||
|
if data["marker"] == "first" {
|
||||||
|
data["marker"] = ""
|
||||||
|
}
|
||||||
|
var e ErrorResp
|
||||||
|
var resp ListResp
|
||||||
|
res, err := base.RestyClient.R().
|
||||||
|
SetHeader("x-share-token", d.ShareToken).
|
||||||
|
SetResult(&resp).SetError(&e).SetBody(data).
|
||||||
|
Post("https://api.aliyundrive.com/adrive/v3/file/list")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
log.Debugf("aliyundrive share get files: %s", res.String())
|
||||||
|
if e.Code != "" {
|
||||||
|
if e.Code == "AccessTokenInvalid" {
|
||||||
|
err = d.getShareToken()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return d.getFiles(fileId)
|
||||||
|
}
|
||||||
|
return nil, errors.New(e.Message)
|
||||||
|
}
|
||||||
|
data["marker"] = resp.NextMarker
|
||||||
|
files = append(files, resp.Items...)
|
||||||
|
}
|
||||||
|
if len(files) > 0 && d.DriveId == "" {
|
||||||
|
d.DriveId = files[0].DriveId
|
||||||
|
}
|
||||||
|
return files, nil
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ import (
|
||||||
_ "github.com/alist-org/alist/v3/drivers/189"
|
_ "github.com/alist-org/alist/v3/drivers/189"
|
||||||
_ "github.com/alist-org/alist/v3/drivers/189pc"
|
_ "github.com/alist-org/alist/v3/drivers/189pc"
|
||||||
_ "github.com/alist-org/alist/v3/drivers/aliyundrive"
|
_ "github.com/alist-org/alist/v3/drivers/aliyundrive"
|
||||||
|
_ "github.com/alist-org/alist/v3/drivers/aliyundrive_share"
|
||||||
_ "github.com/alist-org/alist/v3/drivers/baidu_netdisk"
|
_ "github.com/alist-org/alist/v3/drivers/baidu_netdisk"
|
||||||
_ "github.com/alist-org/alist/v3/drivers/baidu_photo"
|
_ "github.com/alist-org/alist/v3/drivers/baidu_photo"
|
||||||
_ "github.com/alist-org/alist/v3/drivers/ftp"
|
_ "github.com/alist-org/alist/v3/drivers/ftp"
|
||||||
|
|
Loading…
Reference in New Issue