feat: add 139yun
driver
This commit is contained in:
parent
3416861cab
commit
900e71f78f
326
drivers/139/driver.go
Normal file
326
drivers/139/driver.go
Normal file
@ -0,0 +1,326 @@
|
|||||||
|
package _139
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"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/utils"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Yun139 struct {
|
||||||
|
model.Storage
|
||||||
|
Addition
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Yun139) Config() driver.Config {
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Yun139) GetAddition() driver.Additional {
|
||||||
|
return d.Addition
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Yun139) 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.post("/orchestration/personalCloud/user/v1.0/qryUserExternInfo", base.Json{
|
||||||
|
"qryUserExternInfoReq": base.Json{
|
||||||
|
"commonAccountInfo": base.Json{
|
||||||
|
"account": d.Account,
|
||||||
|
"accountType": 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Yun139) Drop(ctx context.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Yun139) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
|
||||||
|
if d.isFamily() {
|
||||||
|
return d.familyGetFiles(dir.GetID())
|
||||||
|
} else {
|
||||||
|
return d.getFiles(dir.GetID())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//func (d *Yun139) Get(ctx context.Context, path string) (model.Obj, error) {
|
||||||
|
// // this is optional
|
||||||
|
// return nil, errs.NotImplement
|
||||||
|
//}
|
||||||
|
|
||||||
|
func (d *Yun139) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
|
||||||
|
u, err := d.getLink(file.GetID())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &model.Link{URL: u}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Yun139) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
|
||||||
|
data := base.Json{
|
||||||
|
"createCatalogExtReq": base.Json{
|
||||||
|
"parentCatalogID": parentDir.GetID(),
|
||||||
|
"newCatalogName": dirName,
|
||||||
|
"commonAccountInfo": base.Json{
|
||||||
|
"account": d.Account,
|
||||||
|
"accountType": 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
pathname := "/orchestration/personalCloud/catalog/v1.0/createCatalogExt"
|
||||||
|
if d.isFamily() {
|
||||||
|
data = base.Json{
|
||||||
|
"cloudID": d.CloudID,
|
||||||
|
"commonAccountInfo": base.Json{
|
||||||
|
"account": d.Account,
|
||||||
|
"accountType": 1,
|
||||||
|
},
|
||||||
|
"docLibName": dirName,
|
||||||
|
}
|
||||||
|
pathname = "/orchestration/familyCloud/cloudCatalog/v1.0/createCloudDoc"
|
||||||
|
}
|
||||||
|
_, err := d.post(pathname,
|
||||||
|
data, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Yun139) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
|
||||||
|
if d.isFamily() {
|
||||||
|
return errs.NotImplement
|
||||||
|
}
|
||||||
|
var contentInfoList []string
|
||||||
|
var catalogInfoList []string
|
||||||
|
if srcObj.IsDir() {
|
||||||
|
catalogInfoList = append(catalogInfoList, srcObj.GetID())
|
||||||
|
} else {
|
||||||
|
contentInfoList = append(contentInfoList, srcObj.GetID())
|
||||||
|
}
|
||||||
|
data := base.Json{
|
||||||
|
"createBatchOprTaskReq": base.Json{
|
||||||
|
"taskType": 3,
|
||||||
|
"actionType": "304",
|
||||||
|
"taskInfo": base.Json{
|
||||||
|
"contentInfoList": contentInfoList,
|
||||||
|
"catalogInfoList": catalogInfoList,
|
||||||
|
"newCatalogID": dstDir.GetID(),
|
||||||
|
},
|
||||||
|
"commonAccountInfo": base.Json{
|
||||||
|
"account": d.Account,
|
||||||
|
"accountType": 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
pathname := "/orchestration/personalCloud/batchOprTask/v1.0/createBatchOprTask"
|
||||||
|
_, err := d.post(pathname, data, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Yun139) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
|
||||||
|
if d.isFamily() {
|
||||||
|
return errs.NotImplement
|
||||||
|
}
|
||||||
|
var data base.Json
|
||||||
|
var pathname string
|
||||||
|
if srcObj.IsDir() {
|
||||||
|
data = base.Json{
|
||||||
|
"catalogID": srcObj.GetID(),
|
||||||
|
"catalogName": newName,
|
||||||
|
"commonAccountInfo": base.Json{
|
||||||
|
"account": d.Account,
|
||||||
|
"accountType": 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
pathname = "/orchestration/personalCloud/catalog/v1.0/updateCatalogInfo"
|
||||||
|
} else {
|
||||||
|
data = base.Json{
|
||||||
|
"contentID": srcObj.GetID(),
|
||||||
|
"contentName": newName,
|
||||||
|
"commonAccountInfo": base.Json{
|
||||||
|
"account": d.Account,
|
||||||
|
"accountType": 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
pathname = "/orchestration/personalCloud/content/v1.0/updateContentInfo"
|
||||||
|
}
|
||||||
|
_, err := d.post(pathname, data, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Yun139) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
|
||||||
|
if d.isFamily() {
|
||||||
|
return errs.NotImplement
|
||||||
|
}
|
||||||
|
var contentInfoList []string
|
||||||
|
var catalogInfoList []string
|
||||||
|
if srcObj.IsDir() {
|
||||||
|
catalogInfoList = append(catalogInfoList, srcObj.GetID())
|
||||||
|
} else {
|
||||||
|
contentInfoList = append(contentInfoList, srcObj.GetID())
|
||||||
|
}
|
||||||
|
data := base.Json{
|
||||||
|
"createBatchOprTaskReq": base.Json{
|
||||||
|
"taskType": 3,
|
||||||
|
"actionType": 309,
|
||||||
|
"taskInfo": base.Json{
|
||||||
|
"contentInfoList": contentInfoList,
|
||||||
|
"catalogInfoList": catalogInfoList,
|
||||||
|
"newCatalogID": dstDir.GetID(),
|
||||||
|
},
|
||||||
|
"commonAccountInfo": base.Json{
|
||||||
|
"account": d.Account,
|
||||||
|
"accountType": 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
pathname := "/orchestration/personalCloud/batchOprTask/v1.0/createBatchOprTask"
|
||||||
|
_, err := d.post(pathname, data, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Yun139) Remove(ctx context.Context, obj model.Obj) error {
|
||||||
|
var contentInfoList []string
|
||||||
|
var catalogInfoList []string
|
||||||
|
if obj.IsDir() {
|
||||||
|
catalogInfoList = append(catalogInfoList, obj.GetID())
|
||||||
|
} else {
|
||||||
|
contentInfoList = append(contentInfoList, obj.GetID())
|
||||||
|
}
|
||||||
|
data := base.Json{
|
||||||
|
"createBatchOprTaskReq": base.Json{
|
||||||
|
"taskType": 2,
|
||||||
|
"actionType": 201,
|
||||||
|
"taskInfo": base.Json{
|
||||||
|
"newCatalogID": "",
|
||||||
|
"contentInfoList": contentInfoList,
|
||||||
|
"catalogInfoList": catalogInfoList,
|
||||||
|
},
|
||||||
|
"commonAccountInfo": base.Json{
|
||||||
|
"account": d.Account,
|
||||||
|
"accountType": 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
pathname := "/orchestration/personalCloud/batchOprTask/v1.0/createBatchOprTask"
|
||||||
|
if d.isFamily() {
|
||||||
|
data = base.Json{
|
||||||
|
"catalogList": catalogInfoList,
|
||||||
|
"contentList": contentInfoList,
|
||||||
|
"commonAccountInfo": base.Json{
|
||||||
|
"account": d.Account,
|
||||||
|
"accountType": 1,
|
||||||
|
},
|
||||||
|
"sourceCatalogType": 1002,
|
||||||
|
"taskType": 2,
|
||||||
|
}
|
||||||
|
pathname = "/orchestration/familyCloud/batchOprTask/v1.0/createBatchOprTask"
|
||||||
|
}
|
||||||
|
_, err := d.post(pathname, data, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Yun139) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
|
||||||
|
data := base.Json{
|
||||||
|
"manualRename": 2,
|
||||||
|
"operation": 0,
|
||||||
|
"fileCount": 1,
|
||||||
|
"totalSize": stream.GetSize(),
|
||||||
|
"uploadContentList": []base.Json{{
|
||||||
|
"contentName": stream.GetName(),
|
||||||
|
"contentSize": stream.GetSize(),
|
||||||
|
// "digest": "5a3231986ce7a6b46e408612d385bafa"
|
||||||
|
}},
|
||||||
|
"parentCatalogID": dstDir.GetID(),
|
||||||
|
"newCatalogName": "",
|
||||||
|
"commonAccountInfo": base.Json{
|
||||||
|
"account": d.Account,
|
||||||
|
"accountType": 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
pathname := "/orchestration/personalCloud/uploadAndDownload/v1.0/pcUploadFileRequest"
|
||||||
|
if d.isFamily() {
|
||||||
|
data = d.newJson(base.Json{
|
||||||
|
"fileCount": 1,
|
||||||
|
"manualRename": 2,
|
||||||
|
"operation": 0,
|
||||||
|
"path": "",
|
||||||
|
"seqNo": "",
|
||||||
|
"totalSize": stream.GetSize(),
|
||||||
|
"uploadContentList": []base.Json{{
|
||||||
|
"contentName": stream.GetName(),
|
||||||
|
"contentSize": stream.GetSize(),
|
||||||
|
// "digest": "5a3231986ce7a6b46e408612d385bafa"
|
||||||
|
}},
|
||||||
|
})
|
||||||
|
pathname = "/orchestration/familyCloud/content/v1.0/getFileUploadURL"
|
||||||
|
return errs.NotImplement
|
||||||
|
}
|
||||||
|
var resp UploadResp
|
||||||
|
_, err := d.post(pathname, data, &resp)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var Default int64 = 10485760
|
||||||
|
part := int(math.Ceil(float64(stream.GetSize()) / float64(Default)))
|
||||||
|
var start int64 = 0
|
||||||
|
for i := 0; i < part; i++ {
|
||||||
|
byteSize := stream.GetSize() - start
|
||||||
|
if byteSize > Default {
|
||||||
|
byteSize = Default
|
||||||
|
}
|
||||||
|
byteData := make([]byte, byteSize)
|
||||||
|
_, err = io.ReadFull(stream, byteData)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
req, err := http.NewRequest("POST", resp.Data.UploadResult.RedirectionURL, bytes.NewBuffer(byteData))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
headers := map[string]string{
|
||||||
|
"Accept": "*/*",
|
||||||
|
"Content-Type": "text/plain;name=" + unicode(stream.GetName()),
|
||||||
|
"contentSize": strconv.FormatInt(stream.GetSize(), 10),
|
||||||
|
"range": fmt.Sprintf("bytes=%d-%d", start, start+byteSize-1),
|
||||||
|
"content-length": strconv.FormatInt(byteSize, 10),
|
||||||
|
"uploadtaskID": resp.Data.UploadResult.UploadTaskID,
|
||||||
|
"rangeType": "0",
|
||||||
|
"Referer": "https://yun.139.com/",
|
||||||
|
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36 Edg/95.0.1020.44",
|
||||||
|
"x-SvcType": "1",
|
||||||
|
}
|
||||||
|
for k, v := range headers {
|
||||||
|
req.Header.Set(k, v)
|
||||||
|
}
|
||||||
|
res, err := base.HttpClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Debugf("%+v", res)
|
||||||
|
res.Body.Close()
|
||||||
|
start += byteSize
|
||||||
|
up(i * 100 / part)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Yun139) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) {
|
||||||
|
return nil, errs.NotSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ driver.Driver = (*Yun139)(nil)
|
25
drivers/139/meta.go
Normal file
25
drivers/139/meta.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package _139
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/alist-org/alist/v3/internal/driver"
|
||||||
|
"github.com/alist-org/alist/v3/internal/op"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Addition struct {
|
||||||
|
Account string `json:"account" required:"true"`
|
||||||
|
Cookie string `json:"cookie" type:"text" required:"true"`
|
||||||
|
driver.RootID
|
||||||
|
Type string `json:"type" type:"select" options:"personal,family" default:"personal"`
|
||||||
|
CloudID string `json:"cloud_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var config = driver.Config{
|
||||||
|
Name: "139Yun",
|
||||||
|
LocalSort: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
op.RegisterDriver(config, func() driver.Driver {
|
||||||
|
return &Yun139{}
|
||||||
|
})
|
||||||
|
}
|
187
drivers/139/types.go
Normal file
187
drivers/139/types.go
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
package _139
|
||||||
|
|
||||||
|
type BaseResp struct {
|
||||||
|
Success bool `json:"success"`
|
||||||
|
Code string `json:"code"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Catalog struct {
|
||||||
|
CatalogID string `json:"catalogID"`
|
||||||
|
CatalogName string `json:"catalogName"`
|
||||||
|
//CatalogType int `json:"catalogType"`
|
||||||
|
//CreateTime string `json:"createTime"`
|
||||||
|
UpdateTime string `json:"updateTime"`
|
||||||
|
//IsShared bool `json:"isShared"`
|
||||||
|
//CatalogLevel int `json:"catalogLevel"`
|
||||||
|
//ShareDoneeCount int `json:"shareDoneeCount"`
|
||||||
|
//OpenType int `json:"openType"`
|
||||||
|
//ParentCatalogID string `json:"parentCatalogId"`
|
||||||
|
//DirEtag int `json:"dirEtag"`
|
||||||
|
//Tombstoned int `json:"tombstoned"`
|
||||||
|
//ProxyID interface{} `json:"proxyID"`
|
||||||
|
//Moved int `json:"moved"`
|
||||||
|
//IsFixedDir int `json:"isFixedDir"`
|
||||||
|
//IsSynced interface{} `json:"isSynced"`
|
||||||
|
//Owner string `json:"owner"`
|
||||||
|
//Modifier interface{} `json:"modifier"`
|
||||||
|
//Path string `json:"path"`
|
||||||
|
//ShareType int `json:"shareType"`
|
||||||
|
//SoftLink interface{} `json:"softLink"`
|
||||||
|
//ExtProp1 interface{} `json:"extProp1"`
|
||||||
|
//ExtProp2 interface{} `json:"extProp2"`
|
||||||
|
//ExtProp3 interface{} `json:"extProp3"`
|
||||||
|
//ExtProp4 interface{} `json:"extProp4"`
|
||||||
|
//ExtProp5 interface{} `json:"extProp5"`
|
||||||
|
//ETagOprType int `json:"ETagOprType"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Content struct {
|
||||||
|
ContentID string `json:"contentID"`
|
||||||
|
ContentName string `json:"contentName"`
|
||||||
|
//ContentSuffix string `json:"contentSuffix"`
|
||||||
|
ContentSize int64 `json:"contentSize"`
|
||||||
|
//ContentDesc string `json:"contentDesc"`
|
||||||
|
//ContentType int `json:"contentType"`
|
||||||
|
//ContentOrigin int `json:"contentOrigin"`
|
||||||
|
UpdateTime string `json:"updateTime"`
|
||||||
|
//CommentCount int `json:"commentCount"`
|
||||||
|
ThumbnailURL string `json:"thumbnailURL"`
|
||||||
|
//BigthumbnailURL string `json:"bigthumbnailURL"`
|
||||||
|
//PresentURL string `json:"presentURL"`
|
||||||
|
//PresentLURL string `json:"presentLURL"`
|
||||||
|
//PresentHURL string `json:"presentHURL"`
|
||||||
|
//ContentTAGList interface{} `json:"contentTAGList"`
|
||||||
|
//ShareDoneeCount int `json:"shareDoneeCount"`
|
||||||
|
//Safestate int `json:"safestate"`
|
||||||
|
//Transferstate int `json:"transferstate"`
|
||||||
|
//IsFocusContent int `json:"isFocusContent"`
|
||||||
|
//UpdateShareTime interface{} `json:"updateShareTime"`
|
||||||
|
//UploadTime string `json:"uploadTime"`
|
||||||
|
//OpenType int `json:"openType"`
|
||||||
|
//AuditResult int `json:"auditResult"`
|
||||||
|
//ParentCatalogID string `json:"parentCatalogId"`
|
||||||
|
//Channel string `json:"channel"`
|
||||||
|
//GeoLocFlag string `json:"geoLocFlag"`
|
||||||
|
//Digest string `json:"digest"`
|
||||||
|
//Version string `json:"version"`
|
||||||
|
//FileEtag string `json:"fileEtag"`
|
||||||
|
//FileVersion string `json:"fileVersion"`
|
||||||
|
//Tombstoned int `json:"tombstoned"`
|
||||||
|
//ProxyID string `json:"proxyID"`
|
||||||
|
//Moved int `json:"moved"`
|
||||||
|
//MidthumbnailURL string `json:"midthumbnailURL"`
|
||||||
|
//Owner string `json:"owner"`
|
||||||
|
//Modifier string `json:"modifier"`
|
||||||
|
//ShareType int `json:"shareType"`
|
||||||
|
//ExtInfo struct {
|
||||||
|
// Uploader string `json:"uploader"`
|
||||||
|
// Address string `json:"address"`
|
||||||
|
//} `json:"extInfo"`
|
||||||
|
//Exif struct {
|
||||||
|
// CreateTime string `json:"createTime"`
|
||||||
|
// Longitude interface{} `json:"longitude"`
|
||||||
|
// Latitude interface{} `json:"latitude"`
|
||||||
|
// LocalSaveTime interface{} `json:"localSaveTime"`
|
||||||
|
//} `json:"exif"`
|
||||||
|
//CollectionFlag interface{} `json:"collectionFlag"`
|
||||||
|
//TreeInfo interface{} `json:"treeInfo"`
|
||||||
|
//IsShared bool `json:"isShared"`
|
||||||
|
//ETagOprType int `json:"ETagOprType"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetDiskResp struct {
|
||||||
|
BaseResp
|
||||||
|
Data struct {
|
||||||
|
Result struct {
|
||||||
|
ResultCode string `json:"resultCode"`
|
||||||
|
ResultDesc interface{} `json:"resultDesc"`
|
||||||
|
} `json:"result"`
|
||||||
|
GetDiskResult struct {
|
||||||
|
ParentCatalogID string `json:"parentCatalogID"`
|
||||||
|
NodeCount int `json:"nodeCount"`
|
||||||
|
CatalogList []Catalog `json:"catalogList"`
|
||||||
|
ContentList []Content `json:"contentList"`
|
||||||
|
IsCompleted int `json:"isCompleted"`
|
||||||
|
} `json:"getDiskResult"`
|
||||||
|
} `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UploadResp struct {
|
||||||
|
BaseResp
|
||||||
|
Data struct {
|
||||||
|
Result struct {
|
||||||
|
ResultCode string `json:"resultCode"`
|
||||||
|
ResultDesc interface{} `json:"resultDesc"`
|
||||||
|
} `json:"result"`
|
||||||
|
UploadResult struct {
|
||||||
|
UploadTaskID string `json:"uploadTaskID"`
|
||||||
|
RedirectionURL string `json:"redirectionUrl"`
|
||||||
|
NewContentIDList []struct {
|
||||||
|
ContentID string `json:"contentID"`
|
||||||
|
ContentName string `json:"contentName"`
|
||||||
|
IsNeedUpload string `json:"isNeedUpload"`
|
||||||
|
FileEtag int64 `json:"fileEtag"`
|
||||||
|
FileVersion int64 `json:"fileVersion"`
|
||||||
|
OverridenFlag int `json:"overridenFlag"`
|
||||||
|
} `json:"newContentIDList"`
|
||||||
|
CatalogIDList interface{} `json:"catalogIDList"`
|
||||||
|
IsSlice interface{} `json:"isSlice"`
|
||||||
|
} `json:"uploadResult"`
|
||||||
|
} `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CloudContent struct {
|
||||||
|
ContentID string `json:"contentID"`
|
||||||
|
//Modifier string `json:"modifier"`
|
||||||
|
//Nickname string `json:"nickname"`
|
||||||
|
//CloudNickName string `json:"cloudNickName"`
|
||||||
|
ContentName string `json:"contentName"`
|
||||||
|
//ContentType int `json:"contentType"`
|
||||||
|
//ContentSuffix string `json:"contentSuffix"`
|
||||||
|
ContentSize int64 `json:"contentSize"`
|
||||||
|
//ContentDesc string `json:"contentDesc"`
|
||||||
|
//CreateTime string `json:"createTime"`
|
||||||
|
//Shottime interface{} `json:"shottime"`
|
||||||
|
LastUpdateTime string `json:"lastUpdateTime"`
|
||||||
|
ThumbnailURL string `json:"thumbnailURL"`
|
||||||
|
//MidthumbnailURL string `json:"midthumbnailURL"`
|
||||||
|
//BigthumbnailURL string `json:"bigthumbnailURL"`
|
||||||
|
//PresentURL string `json:"presentURL"`
|
||||||
|
//PresentLURL string `json:"presentLURL"`
|
||||||
|
//PresentHURL string `json:"presentHURL"`
|
||||||
|
//ParentCatalogID string `json:"parentCatalogID"`
|
||||||
|
//Uploader string `json:"uploader"`
|
||||||
|
//UploaderNickName string `json:"uploaderNickName"`
|
||||||
|
//TreeInfo interface{} `json:"treeInfo"`
|
||||||
|
//UpdateTime interface{} `json:"updateTime"`
|
||||||
|
//ExtInfo struct {
|
||||||
|
// Uploader string `json:"uploader"`
|
||||||
|
//} `json:"extInfo"`
|
||||||
|
//EtagOprType interface{} `json:"etagOprType"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CloudCatalog struct {
|
||||||
|
CatalogID string `json:"catalogID"`
|
||||||
|
CatalogName string `json:"catalogName"`
|
||||||
|
//CloudID string `json:"cloudID"`
|
||||||
|
//CreateTime string `json:"createTime"`
|
||||||
|
LastUpdateTime string `json:"lastUpdateTime"`
|
||||||
|
//Creator string `json:"creator"`
|
||||||
|
//CreatorNickname string `json:"creatorNickname"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type QueryContentListResp struct {
|
||||||
|
BaseResp
|
||||||
|
Data struct {
|
||||||
|
Result struct {
|
||||||
|
ResultCode string `json:"resultCode"`
|
||||||
|
ResultDesc string `json:"resultDesc"`
|
||||||
|
} `json:"result"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
CloudContentList []CloudContent `json:"cloudContentList"`
|
||||||
|
CloudCatalogList []CloudCatalog `json:"cloudCatalogList"`
|
||||||
|
TotalCount int `json:"totalCount"`
|
||||||
|
RecallContent interface{} `json:"recallContent"`
|
||||||
|
} `json:"data"`
|
||||||
|
}
|
247
drivers/139/util.go
Normal file
247
drivers/139/util.go
Normal file
@ -0,0 +1,247 @@
|
|||||||
|
package _139
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/alist-org/alist/v3/drivers/base"
|
||||||
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
|
"github.com/alist-org/alist/v3/pkg/utils"
|
||||||
|
"github.com/alist-org/alist/v3/pkg/utils/random"
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
jsoniter "github.com/json-iterator/go"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// do others that not defined in Driver interface
|
||||||
|
func (d *Yun139) isFamily() bool {
|
||||||
|
return d.Type == "family"
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeURIComponent(str string) string {
|
||||||
|
r := url.QueryEscape(str)
|
||||||
|
r = strings.Replace(r, "+", "%20", -1)
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func calSign(body, ts, randStr string) string {
|
||||||
|
body = strings.ReplaceAll(body, "\n", "")
|
||||||
|
body = strings.ReplaceAll(body, " ", "")
|
||||||
|
body = encodeURIComponent(body)
|
||||||
|
strs := strings.Split(body, "")
|
||||||
|
sort.Strings(strs)
|
||||||
|
body = strings.Join(strs, "")
|
||||||
|
body = base64.StdEncoding.EncodeToString([]byte(body))
|
||||||
|
res := utils.GetMD5Encode(body) + utils.GetMD5Encode(ts+":"+randStr)
|
||||||
|
res = strings.ToUpper(utils.GetMD5Encode(res))
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTime(t string) time.Time {
|
||||||
|
stamp, _ := time.ParseInLocation("20060102150405", t, time.Local)
|
||||||
|
return stamp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Yun139) request(pathname string, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) {
|
||||||
|
url := "https://yun.139.com" + pathname
|
||||||
|
req := base.RestyClient.R()
|
||||||
|
randStr := random.String(16)
|
||||||
|
ts := time.Now().Format("2006-01-02 15:04:05")
|
||||||
|
if callback != nil {
|
||||||
|
callback(req)
|
||||||
|
}
|
||||||
|
body, err := utils.Json.Marshal(req.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
sign := calSign(string(body), ts, randStr)
|
||||||
|
svcType := "1"
|
||||||
|
if d.isFamily() {
|
||||||
|
svcType = "2"
|
||||||
|
}
|
||||||
|
req.SetHeaders(map[string]string{
|
||||||
|
"Accept": "application/json, text/plain, */*",
|
||||||
|
"CMS-DEVICE": "default",
|
||||||
|
"Cookie": d.Cookie,
|
||||||
|
"mcloud-channel": "1000101",
|
||||||
|
"mcloud-client": "10701",
|
||||||
|
//"mcloud-route": "001",
|
||||||
|
"mcloud-sign": fmt.Sprintf("%s,%s,%s", ts, randStr, sign),
|
||||||
|
//"mcloud-skey":"",
|
||||||
|
"mcloud-version": "6.6.0",
|
||||||
|
"Origin": "https://yun.139.com",
|
||||||
|
"Referer": "https://yun.139.com/w/",
|
||||||
|
"x-DeviceInfo": "||9|6.6.0|chrome|95.0.4638.69|uwIy75obnsRPIwlJSd7D9GhUvFwG96ce||macos 10.15.2||zh-CN|||",
|
||||||
|
"x-huawei-channelSrc": "10000034",
|
||||||
|
"x-inner-ntwk": "2",
|
||||||
|
"x-m4c-caller": "PC",
|
||||||
|
"x-m4c-src": "10002",
|
||||||
|
"x-SvcType": svcType,
|
||||||
|
})
|
||||||
|
|
||||||
|
var e BaseResp
|
||||||
|
req.SetResult(&e)
|
||||||
|
res, err := req.Execute(method, url)
|
||||||
|
log.Debugln(res.String())
|
||||||
|
if !e.Success {
|
||||||
|
return nil, errors.New(e.Message)
|
||||||
|
}
|
||||||
|
if resp != nil {
|
||||||
|
err = utils.Json.Unmarshal(res.Body(), resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res.Body(), nil
|
||||||
|
}
|
||||||
|
func (d *Yun139) post(pathname string, data interface{}, resp interface{}) ([]byte, error) {
|
||||||
|
return d.request(pathname, http.MethodPost, func(req *resty.Request) {
|
||||||
|
req.SetBody(data)
|
||||||
|
}, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Yun139) getFiles(catalogID string) ([]model.Obj, error) {
|
||||||
|
start := 0
|
||||||
|
limit := 100
|
||||||
|
files := make([]model.Obj, 0)
|
||||||
|
for {
|
||||||
|
data := base.Json{
|
||||||
|
"catalogID": catalogID,
|
||||||
|
"sortDirection": 1,
|
||||||
|
"startNumber": start + 1,
|
||||||
|
"endNumber": start + limit,
|
||||||
|
"filterType": 0,
|
||||||
|
"catalogSortType": 0,
|
||||||
|
"contentSortType": 0,
|
||||||
|
"commonAccountInfo": base.Json{
|
||||||
|
"account": d.Account,
|
||||||
|
"accountType": 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
var resp GetDiskResp
|
||||||
|
_, err := d.post("/orchestration/personalCloud/catalog/v1.0/getDisk", data, &resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, catalog := range resp.Data.GetDiskResult.CatalogList {
|
||||||
|
f := model.Object{
|
||||||
|
ID: catalog.CatalogID,
|
||||||
|
Name: catalog.CatalogName,
|
||||||
|
Size: 0,
|
||||||
|
Modified: getTime(catalog.UpdateTime),
|
||||||
|
IsFolder: true,
|
||||||
|
}
|
||||||
|
files = append(files, &f)
|
||||||
|
}
|
||||||
|
for _, content := range resp.Data.GetDiskResult.ContentList {
|
||||||
|
f := model.ObjThumb{
|
||||||
|
Object: model.Object{
|
||||||
|
ID: content.ContentID,
|
||||||
|
Name: content.ContentName,
|
||||||
|
Size: content.ContentSize,
|
||||||
|
Modified: getTime(content.UpdateTime),
|
||||||
|
},
|
||||||
|
Thumbnail: model.Thumbnail{Thumbnail: content.ThumbnailURL},
|
||||||
|
//Thumbnail: content.BigthumbnailURL,
|
||||||
|
}
|
||||||
|
files = append(files, &f)
|
||||||
|
}
|
||||||
|
if start+limit >= resp.Data.GetDiskResult.NodeCount {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
start += limit
|
||||||
|
}
|
||||||
|
return files, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Yun139) newJson(data map[string]interface{}) base.Json {
|
||||||
|
common := map[string]interface{}{
|
||||||
|
"catalogType": 3,
|
||||||
|
"cloudID": d.CloudID,
|
||||||
|
"cloudType": 1,
|
||||||
|
"commonAccountInfo": base.Json{
|
||||||
|
"account": d.Account,
|
||||||
|
"accountType": 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return utils.MergeMap(data, common)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Yun139) familyGetFiles(catalogID string) ([]model.Obj, error) {
|
||||||
|
pageNum := 1
|
||||||
|
files := make([]model.Obj, 0)
|
||||||
|
for {
|
||||||
|
data := d.newJson(base.Json{
|
||||||
|
"catalogID": catalogID,
|
||||||
|
"contentSortType": 0,
|
||||||
|
"pageInfo": base.Json{
|
||||||
|
"pageNum": pageNum,
|
||||||
|
"pageSize": 100,
|
||||||
|
},
|
||||||
|
"sortDirection": 1,
|
||||||
|
})
|
||||||
|
var resp QueryContentListResp
|
||||||
|
_, err := d.post("/orchestration/familyCloud/content/v1.0/queryContentList", data, &resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, catalog := range resp.Data.CloudCatalogList {
|
||||||
|
f := model.Object{
|
||||||
|
ID: catalog.CatalogID,
|
||||||
|
Name: catalog.CatalogName,
|
||||||
|
Size: 0,
|
||||||
|
IsFolder: true,
|
||||||
|
Modified: getTime(catalog.LastUpdateTime),
|
||||||
|
}
|
||||||
|
files = append(files, &f)
|
||||||
|
}
|
||||||
|
for _, content := range resp.Data.CloudContentList {
|
||||||
|
f := model.ObjThumb{
|
||||||
|
Object: model.Object{
|
||||||
|
ID: content.ContentID,
|
||||||
|
Name: content.ContentName,
|
||||||
|
Size: content.ContentSize,
|
||||||
|
Modified: getTime(content.LastUpdateTime),
|
||||||
|
},
|
||||||
|
Thumbnail: model.Thumbnail{Thumbnail: content.ThumbnailURL},
|
||||||
|
//Thumbnail: content.BigthumbnailURL,
|
||||||
|
}
|
||||||
|
files = append(files, &f)
|
||||||
|
}
|
||||||
|
if 100*pageNum > resp.Data.TotalCount {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
pageNum++
|
||||||
|
}
|
||||||
|
return files, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Yun139) getLink(contentId string) (string, error) {
|
||||||
|
data := base.Json{
|
||||||
|
"appName": "",
|
||||||
|
"contentID": contentId,
|
||||||
|
"commonAccountInfo": base.Json{
|
||||||
|
"account": d.Account,
|
||||||
|
"accountType": 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
res, err := d.post("/orchestration/personalCloud/uploadAndDownload/v1.0/downloadRequest",
|
||||||
|
data, nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return jsoniter.Get(res, "data", "downloadURL").ToString(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func unicode(str string) string {
|
||||||
|
textQuoted := strconv.QuoteToASCII(str)
|
||||||
|
textUnquoted := textQuoted[1 : len(textQuoted)-1]
|
||||||
|
return textUnquoted
|
||||||
|
}
|
@ -2,6 +2,7 @@ package drivers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
_ "github.com/alist-org/alist/v3/drivers/123"
|
_ "github.com/alist-org/alist/v3/drivers/123"
|
||||||
|
_ "github.com/alist-org/alist/v3/drivers/139"
|
||||||
_ "github.com/alist-org/alist/v3/drivers/aliyundrive"
|
_ "github.com/alist-org/alist/v3/drivers/aliyundrive"
|
||||||
_ "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/ftp"
|
_ "github.com/alist-org/alist/v3/drivers/ftp"
|
||||||
|
@ -24,10 +24,8 @@ var config = driver.Config{
|
|||||||
DefaultRoot: "root, / or other",
|
DefaultRoot: "root, / or other",
|
||||||
}
|
}
|
||||||
|
|
||||||
func New() driver.Driver {
|
|
||||||
return &Template{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
op.RegisterDriver(config, New)
|
op.RegisterDriver(config, func() driver.Driver {
|
||||||
|
return &Template{}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
11
pkg/utils/map.go
Normal file
11
pkg/utils/map.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
func MergeMap(mObj ...map[string]interface{}) map[string]interface{} {
|
||||||
|
newObj := map[string]interface{}{}
|
||||||
|
for _, m := range mObj {
|
||||||
|
for k, v := range m {
|
||||||
|
newObj[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newObj
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user