diff --git a/drivers/aliyundrive_share/driver.go b/drivers/aliyundrive_share/driver.go new file mode 100644 index 00000000..65d5ac11 --- /dev/null +++ b/drivers/aliyundrive_share/driver.go @@ -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) diff --git a/drivers/aliyundrive_share/meta.go b/drivers/aliyundrive_share/meta.go new file mode 100644 index 00000000..755305b5 --- /dev/null +++ b/drivers/aliyundrive_share/meta.go @@ -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{} + }) +} diff --git a/drivers/aliyundrive_share/types.go b/drivers/aliyundrive_share/types.go new file mode 100644 index 00000000..9f16de63 --- /dev/null +++ b/drivers/aliyundrive_share/types.go @@ -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"` +//} diff --git a/drivers/aliyundrive_share/util.go b/drivers/aliyundrive_share/util.go new file mode 100644 index 00000000..a5e6fd42 --- /dev/null +++ b/drivers/aliyundrive_share/util.go @@ -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 +} diff --git a/drivers/all.go b/drivers/all.go index 59a65928..9dd30bb3 100644 --- a/drivers/all.go +++ b/drivers/all.go @@ -6,6 +6,7 @@ import ( _ "github.com/alist-org/alist/v3/drivers/189" _ "github.com/alist-org/alist/v3/drivers/189pc" _ "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_photo" _ "github.com/alist-org/alist/v3/drivers/ftp"