This update introduces the ability to mount 115 share links. Currently, only listing and downloading are supported. Note that login and share link are required for this feature to work. Close #5384
This commit is contained in:
112
drivers/115_share/driver.go
Normal file
112
drivers/115_share/driver.go
Normal file
@ -0,0 +1,112 @@
|
||||
package _115_share
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
driver115 "github.com/SheltonZhu/115driver/pkg/driver"
|
||||
"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/utils"
|
||||
"golang.org/x/time/rate"
|
||||
)
|
||||
|
||||
type Pan115Share struct {
|
||||
model.Storage
|
||||
Addition
|
||||
client *driver115.Pan115Client
|
||||
limiter *rate.Limiter
|
||||
}
|
||||
|
||||
func (d *Pan115Share) Config() driver.Config {
|
||||
return config
|
||||
}
|
||||
|
||||
func (d *Pan115Share) GetAddition() driver.Additional {
|
||||
return &d.Addition
|
||||
}
|
||||
|
||||
func (d *Pan115Share) Init(ctx context.Context) error {
|
||||
if d.LimitRate > 0 {
|
||||
d.limiter = rate.NewLimiter(rate.Limit(d.LimitRate), 1)
|
||||
}
|
||||
|
||||
return d.login()
|
||||
}
|
||||
|
||||
func (d *Pan115Share) WaitLimit(ctx context.Context) error {
|
||||
if d.limiter != nil {
|
||||
return d.limiter.Wait(ctx)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Pan115Share) Drop(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Pan115Share) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
|
||||
if err := d.WaitLimit(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
files := make([]driver115.ShareFile, 0)
|
||||
fileResp, err := d.client.GetShareSnap(d.ShareCode, d.ReceiveCode, dir.GetID(), driver115.QueryLimit(int(d.PageSize)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
files = append(files, fileResp.Data.List...)
|
||||
total := fileResp.Data.Count
|
||||
count := len(fileResp.Data.List)
|
||||
for total > count {
|
||||
fileResp, err := d.client.GetShareSnap(
|
||||
d.ShareCode, d.ReceiveCode, dir.GetID(),
|
||||
driver115.QueryLimit(int(d.PageSize)), driver115.QueryOffset(count),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
files = append(files, fileResp.Data.List...)
|
||||
count += len(fileResp.Data.List)
|
||||
}
|
||||
|
||||
return utils.SliceConvert(files, transFunc)
|
||||
}
|
||||
|
||||
func (d *Pan115Share) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
|
||||
if err := d.WaitLimit(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
downloadInfo, err := d.client.DownloadByShareCode(d.ShareCode, d.ReceiveCode, file.GetID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &model.Link{URL: downloadInfo.URL.URL}, nil
|
||||
}
|
||||
|
||||
func (d *Pan115Share) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
|
||||
return errs.NotSupport
|
||||
}
|
||||
|
||||
func (d *Pan115Share) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
|
||||
return errs.NotSupport
|
||||
}
|
||||
|
||||
func (d *Pan115Share) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
|
||||
return errs.NotSupport
|
||||
}
|
||||
|
||||
func (d *Pan115Share) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
|
||||
return errs.NotSupport
|
||||
}
|
||||
|
||||
func (d *Pan115Share) Remove(ctx context.Context, obj model.Obj) error {
|
||||
return errs.NotSupport
|
||||
}
|
||||
|
||||
func (d *Pan115Share) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
|
||||
return errs.NotSupport
|
||||
}
|
||||
|
||||
var _ driver.Driver = (*Pan115Share)(nil)
|
33
drivers/115_share/meta.go
Normal file
33
drivers/115_share/meta.go
Normal file
@ -0,0 +1,33 @@
|
||||
package _115_share
|
||||
|
||||
import (
|
||||
"github.com/alist-org/alist/v3/internal/driver"
|
||||
"github.com/alist-org/alist/v3/internal/op"
|
||||
)
|
||||
|
||||
type Addition struct {
|
||||
Cookie string `json:"cookie" type:"text" help:"one of QR code token and cookie required"`
|
||||
QRCodeToken string `json:"qrcode_token" type:"text" help:"one of QR code token and cookie required"`
|
||||
PageSize int64 `json:"page_size" type:"number" default:"20" help:"list api per page size of 115 driver"`
|
||||
LimitRate float64 `json:"limit_rate" type:"number" default:"2" help:"limit all api request rate (1r/[limit_rate]s)"`
|
||||
ShareCode string `json:"share_code" type:"text" required:"true" help:"share code of 115 share link"`
|
||||
ReceiveCode string `json:"receive_code" type:"text" required:"true" help:"receive code of 115 share link"`
|
||||
driver.RootID
|
||||
}
|
||||
|
||||
var config = driver.Config{
|
||||
Name: "115 Share",
|
||||
DefaultRoot: "",
|
||||
// OnlyProxy: true,
|
||||
// OnlyLocal: true,
|
||||
CheckStatus: false,
|
||||
Alert: "",
|
||||
NoOverwriteUpload: true,
|
||||
NoUpload: true,
|
||||
}
|
||||
|
||||
func init() {
|
||||
op.RegisterDriver(func() driver.Driver {
|
||||
return &Pan115Share{}
|
||||
})
|
||||
}
|
111
drivers/115_share/utils.go
Normal file
111
drivers/115_share/utils.go
Normal file
@ -0,0 +1,111 @@
|
||||
package _115_share
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
driver115 "github.com/SheltonZhu/115driver/pkg/driver"
|
||||
"github.com/alist-org/alist/v3/internal/model"
|
||||
"github.com/alist-org/alist/v3/pkg/utils"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var _ model.Obj = (*FileObj)(nil)
|
||||
|
||||
type FileObj struct {
|
||||
Size int64
|
||||
Sha1 string
|
||||
Utm time.Time
|
||||
FileName string
|
||||
isDir bool
|
||||
FileID string
|
||||
}
|
||||
|
||||
func (f *FileObj) CreateTime() time.Time {
|
||||
return f.Utm
|
||||
}
|
||||
|
||||
func (f *FileObj) GetHash() utils.HashInfo {
|
||||
return utils.NewHashInfo(utils.SHA1, f.Sha1)
|
||||
}
|
||||
|
||||
func (f *FileObj) GetSize() int64 {
|
||||
return f.Size
|
||||
}
|
||||
|
||||
func (f *FileObj) GetName() string {
|
||||
return f.FileName
|
||||
}
|
||||
|
||||
func (f *FileObj) ModTime() time.Time {
|
||||
return f.Utm
|
||||
}
|
||||
|
||||
func (f *FileObj) IsDir() bool {
|
||||
return f.isDir
|
||||
}
|
||||
|
||||
func (f *FileObj) GetID() string {
|
||||
return f.FileID
|
||||
}
|
||||
|
||||
func (f *FileObj) GetPath() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func transFunc(sf driver115.ShareFile) (model.Obj, error) {
|
||||
timeInt, err := strconv.ParseInt(sf.UpdateTime, 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var (
|
||||
utm = time.Unix(timeInt, 0)
|
||||
isDir = (sf.IsFile == 0)
|
||||
fileID = string(sf.FileID)
|
||||
)
|
||||
if isDir {
|
||||
fileID = string(sf.CategoryID)
|
||||
}
|
||||
return &FileObj{
|
||||
Size: int64(sf.Size),
|
||||
Sha1: sf.Sha1,
|
||||
Utm: utm,
|
||||
FileName: string(sf.FileName),
|
||||
isDir: isDir,
|
||||
FileID: fileID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
var UserAgent = driver115.UA115Browser
|
||||
|
||||
func (d *Pan115Share) login() error {
|
||||
var err error
|
||||
opts := []driver115.Option{
|
||||
driver115.UA(UserAgent),
|
||||
}
|
||||
d.client = driver115.New(opts...)
|
||||
if _, err := d.client.GetShareSnap(d.ShareCode, d.ReceiveCode, ""); err != nil {
|
||||
return errors.Wrap(err, "failed to get share snap")
|
||||
}
|
||||
cr := &driver115.Credential{}
|
||||
if d.QRCodeToken != "" {
|
||||
s := &driver115.QRCodeSession{
|
||||
UID: d.QRCodeToken,
|
||||
}
|
||||
if cr, err = d.client.QRCodeLogin(s); err != nil {
|
||||
return errors.Wrap(err, "failed to login by qrcode")
|
||||
}
|
||||
d.Cookie = fmt.Sprintf("UID=%s;CID=%s;SEID=%s", cr.UID, cr.CID, cr.SEID)
|
||||
d.QRCodeToken = ""
|
||||
} else if d.Cookie != "" {
|
||||
if err = cr.FromCookie(d.Cookie); err != nil {
|
||||
return errors.Wrap(err, "failed to login by cookies")
|
||||
}
|
||||
d.client.ImportCredential(cr)
|
||||
} else {
|
||||
return errors.New("missing cookie or qrcode account")
|
||||
}
|
||||
|
||||
return d.client.LoginCheck()
|
||||
}
|
Reference in New Issue
Block a user