mirror of
https://github.com/AlistGo/alist.git
synced 2025-04-21 20:18:47 +08:00
parent
f9cf29e0b6
commit
3d336b328a
@ -21,6 +21,7 @@ import (
|
||||
_ "github.com/alist-org/alist/v3/drivers/mega"
|
||||
_ "github.com/alist-org/alist/v3/drivers/onedrive"
|
||||
_ "github.com/alist-org/alist/v3/drivers/pikpak"
|
||||
_ "github.com/alist-org/alist/v3/drivers/pikpak_share"
|
||||
_ "github.com/alist-org/alist/v3/drivers/quark"
|
||||
_ "github.com/alist-org/alist/v3/drivers/s3"
|
||||
_ "github.com/alist-org/alist/v3/drivers/sftp"
|
||||
|
112
drivers/pikpak_share/driver.go
Normal file
112
drivers/pikpak_share/driver.go
Normal file
@ -0,0 +1,112 @@
|
||||
package pikpak_share
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/alist-org/alist/v3/internal/driver"
|
||||
"github.com/alist-org/alist/v3/internal/model"
|
||||
"github.com/alist-org/alist/v3/internal/errs"
|
||||
"github.com/alist-org/alist/v3/pkg/utils"
|
||||
"github.com/go-resty/resty/v2"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type PikPakShare struct {
|
||||
model.Storage
|
||||
Addition
|
||||
RefreshToken string
|
||||
AccessToken string
|
||||
PassCodeToken string
|
||||
}
|
||||
|
||||
func (d *PikPakShare) Config() driver.Config {
|
||||
return config
|
||||
}
|
||||
|
||||
func (d *PikPakShare) GetAddition() driver.Additional {
|
||||
return &d.Addition
|
||||
}
|
||||
|
||||
func (d *PikPakShare) Init(ctx context.Context) error {
|
||||
err := d.login()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if d.SharePwd != "" {
|
||||
err = d.getSharePassToken()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *PikPakShare) Drop(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *PikPakShare) 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 *PikPakShare) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
|
||||
var resp ShareResp
|
||||
query := map[string]string{
|
||||
"share_id": d.ShareId,
|
||||
"file_id": file.GetID(),
|
||||
"pass_code_token": d.PassCodeToken,
|
||||
}
|
||||
_, err := d.request("https://api-drive.mypikpak.com/drive/v1/share/file_info", http.MethodGet, func(req *resty.Request) {
|
||||
req.SetQueryParams(query)
|
||||
}, &resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
link := model.Link{
|
||||
URL: resp.FileInfo.WebContentLink,
|
||||
}
|
||||
if len(resp.FileInfo.Medias) > 0 && resp.FileInfo.Medias[0].Link.Url != "" {
|
||||
log.Debugln("use media link")
|
||||
link.URL = resp.FileInfo.Medias[0].Link.Url
|
||||
}
|
||||
return &link, nil
|
||||
}
|
||||
|
||||
func (d *PikPakShare) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
|
||||
// TODO create folder
|
||||
return errs.NotSupport
|
||||
}
|
||||
|
||||
func (d *PikPakShare) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
|
||||
// TODO move obj
|
||||
return errs.NotSupport
|
||||
}
|
||||
|
||||
func (d *PikPakShare) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
|
||||
// TODO rename obj
|
||||
return errs.NotSupport
|
||||
}
|
||||
|
||||
func (d *PikPakShare) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
|
||||
// TODO copy obj
|
||||
return errs.NotSupport
|
||||
}
|
||||
|
||||
func (d *PikPakShare) Remove(ctx context.Context, obj model.Obj) error {
|
||||
// TODO remove obj
|
||||
return errs.NotSupport
|
||||
}
|
||||
|
||||
func (d *PikPakShare) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
|
||||
// TODO upload file
|
||||
return errs.NotSupport
|
||||
}
|
||||
|
||||
var _ driver.Driver = (*PikPakShare)(nil)
|
27
drivers/pikpak_share/meta.go
Normal file
27
drivers/pikpak_share/meta.go
Normal file
@ -0,0 +1,27 @@
|
||||
package pikpak_share
|
||||
|
||||
import (
|
||||
"github.com/alist-org/alist/v3/internal/driver"
|
||||
"github.com/alist-org/alist/v3/internal/op"
|
||||
)
|
||||
|
||||
type Addition struct {
|
||||
driver.RootID
|
||||
Username string `json:"username" required:"true"`
|
||||
Password string `json:"password" required:"true"`
|
||||
ShareId string `json:"share_id" required:"true"`
|
||||
SharePwd string `json:"share_pwd"`
|
||||
}
|
||||
|
||||
var config = driver.Config{
|
||||
Name: "PikPakShare",
|
||||
LocalSort: true,
|
||||
NoUpload: true,
|
||||
DefaultRoot: "",
|
||||
}
|
||||
|
||||
func init() {
|
||||
op.RegisterDriver(func() driver.Driver {
|
||||
return &PikPakShare{}
|
||||
})
|
||||
}
|
80
drivers/pikpak_share/types.go
Normal file
80
drivers/pikpak_share/types.go
Normal file
@ -0,0 +1,80 @@
|
||||
package pikpak_share
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/alist-org/alist/v3/internal/model"
|
||||
)
|
||||
|
||||
type RespErr struct {
|
||||
ErrorCode int `json:"error_code"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
type ShareResp struct {
|
||||
ShareStatus string `json:"share_status"`
|
||||
ShareStatusText string `json:"share_status_text"`
|
||||
FileInfo File `json:"file_info"`
|
||||
Files []File `json:"files"`
|
||||
NextPageToken string `json:"next_page_token"`
|
||||
PassCodeToken string `json:"pass_code_token"`
|
||||
}
|
||||
|
||||
type File struct {
|
||||
Id string `json:"id"`
|
||||
ShareId string `json:"share_id"`
|
||||
Kind string `json:"kind"`
|
||||
Name string `json:"name"`
|
||||
ModifiedTime time.Time `json:"modified_time"`
|
||||
Size string `json:"size"`
|
||||
ThumbnailLink string `json:"thumbnail_link"`
|
||||
WebContentLink string `json:"web_content_link"`
|
||||
Medias []Media `json:"medias"`
|
||||
}
|
||||
|
||||
func fileToObj(f File) *model.ObjThumb {
|
||||
size, _ := strconv.ParseInt(f.Size, 10, 64)
|
||||
return &model.ObjThumb{
|
||||
Object: model.Object{
|
||||
ID: f.Id,
|
||||
Name: f.Name,
|
||||
Size: size,
|
||||
Modified: f.ModifiedTime,
|
||||
IsFolder: f.Kind == "drive#folder",
|
||||
},
|
||||
Thumbnail: model.Thumbnail{
|
||||
Thumbnail: f.ThumbnailLink,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type Media struct {
|
||||
MediaId string `json:"media_id"`
|
||||
MediaName string `json:"media_name"`
|
||||
Video struct {
|
||||
Height int `json:"height"`
|
||||
Width int `json:"width"`
|
||||
Duration int `json:"duration"`
|
||||
BitRate int `json:"bit_rate"`
|
||||
FrameRate int `json:"frame_rate"`
|
||||
VideoCodec string `json:"video_codec"`
|
||||
AudioCodec string `json:"audio_codec"`
|
||||
VideoType string `json:"video_type"`
|
||||
} `json:"video"`
|
||||
Link struct {
|
||||
Url string `json:"url"`
|
||||
Token string `json:"token"`
|
||||
Expire time.Time `json:"expire"`
|
||||
} `json:"link"`
|
||||
NeedMoreQuota bool `json:"need_more_quota"`
|
||||
VipTypes []interface{} `json:"vip_types"`
|
||||
RedirectLink string `json:"redirect_link"`
|
||||
IconLink string `json:"icon_link"`
|
||||
IsDefault bool `json:"is_default"`
|
||||
Priority int `json:"priority"`
|
||||
IsOrigin bool `json:"is_origin"`
|
||||
ResolutionName string `json:"resolution_name"`
|
||||
IsVisible bool `json:"is_visible"`
|
||||
Category string `json:"category"`
|
||||
}
|
154
drivers/pikpak_share/util.go
Normal file
154
drivers/pikpak_share/util.go
Normal file
@ -0,0 +1,154 @@
|
||||
package pikpak_share
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/alist-org/alist/v3/drivers/base"
|
||||
"github.com/alist-org/alist/v3/internal/op"
|
||||
"github.com/go-resty/resty/v2"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
// do others that not defined in Driver interface
|
||||
|
||||
func (d *PikPakShare) login() error {
|
||||
url := "https://user.mypikpak.com/v1/auth/signin"
|
||||
var e RespErr
|
||||
res, err := base.RestyClient.R().SetError(&e).SetBody(base.Json{
|
||||
"captcha_token": "",
|
||||
"client_id": "YNxT9w7GMdWvEOKa",
|
||||
"client_secret": "dbw2OtmVEeuUvIptb1Coyg",
|
||||
"username": d.Username,
|
||||
"password": d.Password,
|
||||
}).Post(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if e.ErrorCode != 0 {
|
||||
return errors.New(e.Error)
|
||||
}
|
||||
data := res.Body()
|
||||
d.RefreshToken = jsoniter.Get(data, "refresh_token").ToString()
|
||||
d.AccessToken = jsoniter.Get(data, "access_token").ToString()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *PikPakShare) refreshToken() error {
|
||||
url := "https://user.mypikpak.com/v1/auth/token"
|
||||
var e RespErr
|
||||
res, err := base.RestyClient.R().SetError(&e).
|
||||
SetHeader("user-agent", "").SetBody(base.Json{
|
||||
"client_id": "YNxT9w7GMdWvEOKa",
|
||||
"client_secret": "dbw2OtmVEeuUvIptb1Coyg",
|
||||
"grant_type": "refresh_token",
|
||||
"refresh_token": d.RefreshToken,
|
||||
}).Post(url)
|
||||
if err != nil {
|
||||
d.Status = err.Error()
|
||||
op.MustSaveDriverStorage(d)
|
||||
return err
|
||||
}
|
||||
if e.ErrorCode != 0 {
|
||||
if e.ErrorCode == 4126 {
|
||||
// refresh_token invalid, re-login
|
||||
return d.login()
|
||||
}
|
||||
d.Status = e.Error
|
||||
op.MustSaveDriverStorage(d)
|
||||
return errors.New(e.Error)
|
||||
}
|
||||
data := res.Body()
|
||||
d.Status = "work"
|
||||
d.RefreshToken = jsoniter.Get(data, "refresh_token").ToString()
|
||||
d.AccessToken = jsoniter.Get(data, "access_token").ToString()
|
||||
op.MustSaveDriverStorage(d)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *PikPakShare) request(url string, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) {
|
||||
req := base.RestyClient.R()
|
||||
req.SetHeader("Authorization", "Bearer "+d.AccessToken)
|
||||
if callback != nil {
|
||||
callback(req)
|
||||
}
|
||||
if resp != nil {
|
||||
req.SetResult(resp)
|
||||
}
|
||||
var e RespErr
|
||||
req.SetError(&e)
|
||||
res, err := req.Execute(method, url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if e.ErrorCode != 0 {
|
||||
if e.ErrorCode == 16 {
|
||||
// login / refresh token
|
||||
err = d.refreshToken()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return d.request(url, method, callback, resp)
|
||||
}
|
||||
return nil, errors.New(e.Error)
|
||||
}
|
||||
return res.Body(), nil
|
||||
}
|
||||
|
||||
func (d *PikPakShare) getSharePassToken() error {
|
||||
query := map[string]string{
|
||||
"share_id": d.ShareId,
|
||||
"pass_code": d.SharePwd,
|
||||
"thumbnail_size": "SIZE_LARGE",
|
||||
"limit": "100",
|
||||
}
|
||||
var resp ShareResp
|
||||
_, err := d.request("https://api-drive.mypikpak.com/drive/v1/share", http.MethodGet, func(req *resty.Request) {
|
||||
req.SetQueryParams(query)
|
||||
}, &resp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.PassCodeToken = resp.PassCodeToken
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *PikPakShare) getFiles(id string) ([]File, error) {
|
||||
res := make([]File, 0)
|
||||
pageToken := "first"
|
||||
for pageToken != "" {
|
||||
if pageToken == "first" {
|
||||
pageToken = ""
|
||||
}
|
||||
query := map[string]string{
|
||||
"parent_id": id,
|
||||
"share_id": d.ShareId,
|
||||
"thumbnail_size": "SIZE_LARGE",
|
||||
"with_audit": "true",
|
||||
"limit": "100",
|
||||
"filters": `{"phase":{"eq":"PHASE_TYPE_COMPLETE"},"trashed":{"eq":false}}`,
|
||||
"page_token": pageToken,
|
||||
"pass_code_token": d.PassCodeToken,
|
||||
}
|
||||
var resp ShareResp
|
||||
_, err := d.request("https://api-drive.mypikpak.com/drive/v1/share/detail", http.MethodGet, func(req *resty.Request) {
|
||||
req.SetQueryParams(query)
|
||||
}, &resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp.ShareStatus != "OK" {
|
||||
if resp.ShareStatus == "PASS_CODE_EMPTY" || resp.ShareStatus == "PASS_CODE_ERROR" {
|
||||
err = d.getSharePassToken()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return d.getFiles(id)
|
||||
}
|
||||
return nil, errors.New(resp.ShareStatusText)
|
||||
}
|
||||
pageToken = resp.NextPageToken
|
||||
res = append(res, resp.Files...)
|
||||
}
|
||||
return res, nil
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user