From 10f06fde5ce3340cc9d2cb85e05eaf5c01aaafd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=AE=E5=87=89?= <927625802@qq.com> Date: Thu, 6 Jan 2022 01:14:32 +0800 Subject: [PATCH] :construction: MediaTrack support --- drivers/all.go | 1 + drivers/mediatrack/driver.go | 276 +++++++++++++++++++++++++++++++ drivers/mediatrack/mediatrack.go | 181 ++++++++++++++++++++ 3 files changed, 458 insertions(+) create mode 100644 drivers/mediatrack/driver.go create mode 100644 drivers/mediatrack/mediatrack.go diff --git a/drivers/all.go b/drivers/all.go index 7b58e6fe..ab351ef1 100644 --- a/drivers/all.go +++ b/drivers/all.go @@ -9,6 +9,7 @@ import ( _ "github.com/Xhofe/alist/drivers/ftp" _ "github.com/Xhofe/alist/drivers/google" _ "github.com/Xhofe/alist/drivers/lanzou" + _ "github.com/Xhofe/alist/drivers/mediatrack" _ "github.com/Xhofe/alist/drivers/native" _ "github.com/Xhofe/alist/drivers/onedrive" _ "github.com/Xhofe/alist/drivers/pikpak" diff --git a/drivers/mediatrack/driver.go b/drivers/mediatrack/driver.go new file mode 100644 index 00000000..e5ce1c31 --- /dev/null +++ b/drivers/mediatrack/driver.go @@ -0,0 +1,276 @@ +package mediatrack + +import ( + "fmt" + "github.com/Xhofe/alist/conf" + "github.com/Xhofe/alist/drivers/base" + "github.com/Xhofe/alist/model" + "github.com/Xhofe/alist/utils" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/s3/s3manager" + "github.com/gin-gonic/gin" + jsoniter "github.com/json-iterator/go" + log "github.com/sirupsen/logrus" + "path/filepath" +) + +type MediaTrack struct{} + +func (driver MediaTrack) Config() base.DriverConfig { + return base.DriverConfig{ + Name: "MediaTrack", + } +} + +func (driver MediaTrack) Items() []base.Item { + return []base.Item{ + { + Name: "access_token", + Label: "Token", + Type: base.TypeString, + Description: "Unknown expiration time", + }, + { + Name: "root_folder", + Label: "root folder file_id", + Type: base.TypeString, + }, + { + Name: "order_by", + Label: "order_by", + Type: base.TypeSelect, + Values: "updated_at,title,size", + Required: true, + }, + { + Name: "order_direction", + Label: "desc", + Type: base.TypeSelect, + Values: "true,false", + Required: true, + }, + } +} + +func (driver MediaTrack) Save(account *model.Account, old *model.Account) error { + return nil +} + +func (driver MediaTrack) File(path string, account *model.Account) (*model.File, error) { + path = utils.ParsePath(path) + if path == "/" { + return &model.File{ + Id: account.RootFolder, + Name: account.Name, + Size: 0, + Type: conf.FOLDER, + Driver: driver.Config().Name, + UpdatedAt: account.UpdatedAt, + }, nil + } + dir, name := filepath.Split(path) + files, err := driver.Files(dir, account) + if err != nil { + return nil, err + } + for _, file := range files { + if file.Name == name { + return &file, nil + } + } + return nil, base.ErrPathNotFound +} + +func (driver MediaTrack) Files(path string, account *model.Account) ([]model.File, error) { + path = utils.ParsePath(path) + var files []model.File + cache, err := base.GetCache(path, account) + if err == nil { + files, _ = cache.([]model.File) + } else { + file, err := driver.File(path, account) + if err != nil { + return nil, err + } + files, err = driver.GetFiles(file.Id, account) + if err != nil { + return nil, err + } + if len(files) > 0 { + _ = base.SetCache(path, files, account) + } + } + return files, nil +} + +func (driver MediaTrack) Link(args base.Args, account *model.Account) (*base.Link, error) { + file, err := driver.File(args.Path, account) + if err != nil { + return nil, err + } + pre := "https://jayce.api.mediatrack.cn/v3/assets/" + file.Id + body, err := driver.Request(pre+"/token", base.Get, nil, nil, nil, nil, nil, account) + if err != nil { + return nil, err + } + url := pre + "/raw" + res, err := base.NoRedirectClient.R().SetQueryParam("token", jsoniter.Get(body, "data").ToString()).Get(url) + return &base.Link{Url: res.Header().Get("location")}, nil +} + +func (driver MediaTrack) Path(path string, account *model.Account) (*model.File, []model.File, error) { + path = utils.ParsePath(path) + log.Debugf("MediaTrack path: %s", path) + file, err := driver.File(path, account) + if err != nil { + return nil, nil, err + } + if !file.IsDir() { + return file, nil, nil + } + files, err := driver.Files(path, account) + if err != nil { + return nil, nil, err + } + return nil, files, nil +} + +func (driver MediaTrack) Proxy(c *gin.Context, account *model.Account) { + +} + +func (driver MediaTrack) Preview(path string, account *model.Account) (interface{}, error) { + return nil, base.ErrNotImplement +} + +func (driver MediaTrack) MakeDir(path string, account *model.Account) error { + parentFile, err := driver.File(utils.Dir(path), account) + if err != nil { + return err + } + url := fmt.Sprintf("https://jayce.api.mediatrack.cn/v4/assets/%s/children", parentFile.Id) + _, err = driver.Request(url, base.Post, nil, nil, nil, base.Json{ + "type": 1, + "title": utils.Base(path), + }, nil, account) + return err +} + +func (driver MediaTrack) Move(src string, dst string, account *model.Account) error { + srcFile, err := driver.File(src, account) + if err != nil { + return err + } + dstParentFile, err := driver.File(utils.Dir(dst), account) + if err != nil { + return err + } + data := base.Json{ + "parent_id": dstParentFile.Id, + "ids": []string{srcFile.Id}, + } + url := "https://jayce.api.mediatrack.cn/v4/assets/batch/move" + _, err = driver.Request(url, base.Post, nil, nil, nil, data, nil, account) + return err +} + +func (driver MediaTrack) Rename(src string, dst string, account *model.Account) error { + srcFile, err := driver.File(src, account) + if err != nil { + return err + } + url := "https://jayce.api.mediatrack.cn/v3/assets/" + srcFile.Id + data := base.Json{ + "title": utils.Base(dst), + } + _, err = driver.Request(url, base.Put, nil, nil, nil, data, nil, account) + return err +} + +func (driver MediaTrack) Copy(src string, dst string, account *model.Account) error { + srcFile, err := driver.File(src, account) + if err != nil { + return err + } + dstParentFile, err := driver.File(utils.Dir(dst), account) + if err != nil { + return err + } + data := base.Json{ + "parent_id": dstParentFile.Id, + "ids": []string{srcFile.Id}, + } + url := "https://jayce.api.mediatrack.cn/v4/assets/batch/clone" + _, err = driver.Request(url, base.Post, nil, nil, nil, data, nil, account) + return err +} + +func (driver MediaTrack) Delete(path string, account *model.Account) error { + parentFile, err := driver.File(utils.Dir(path), account) + if err != nil { + return err + } + file, err := driver.File(path, account) + if err != nil { + return err + } + data := base.Json{ + "origin_id": parentFile.Id, + "ids": []string{file.Id}, + } + url := "https://jayce.api.mediatrack.cn/v4/assets/batch/delete" + _, err = driver.Request(url, base.Delete, nil, nil, nil, data, nil, account) + return err +} + +func (driver MediaTrack) Upload(file *model.FileStream, account *model.Account) error { + parentFile, err := driver.File(file.ParentPath, account) + if err != nil { + return err + } + src := "assets/56461b45-6d08-40a0-bfbf-0a47689bffaa" // random? + var resp UploadResp + _, err = driver.Request("https://jayce.api.mediatrack.cn/v3/storage/tokens/asset", base.Get, nil, map[string]string{ + "src": src, + }, nil, nil, &resp, account) + if err != nil { + return err + } + credential := resp.Data.Credentials + cfg := &aws.Config{ + Credentials: credentials.NewStaticCredentials(credential.TmpSecretID, credential.TmpSecretKey, credential.Token), + Region: &resp.Data.Region, + Endpoint: aws.String("cos.accelerate.myqcloud.com"), + } + s, err := session.NewSession(cfg) + if err != nil { + return err + } + uploader := s3manager.NewUploader(s) + input := &s3manager.UploadInput{ + Bucket: &resp.Data.Bucket, + Key: &resp.Data.Object, + Body: file, + } + _, err = uploader.Upload(input) + if err != nil { + return err + } + url := fmt.Sprintf("https://jayce.api.mediatrack.cn/v3/assets/%s/children", parentFile.Id) + data := base.Json{ + "category": 10, + "description": file.GetFileName(), + "hash": "c459fda4d79554d692555932a5d5e6c1", + "mime": file.MIMEType, + "size": file.GetSize(), + "src": src, + "title": file.GetFileName(), + "type": 0, + } + _, err = driver.Request(url, base.Post, nil, nil, nil, data, nil, account) + return err +} + +var _ base.Driver = (*MediaTrack)(nil) diff --git a/drivers/mediatrack/mediatrack.go b/drivers/mediatrack/mediatrack.go new file mode 100644 index 00000000..9fa4391e --- /dev/null +++ b/drivers/mediatrack/mediatrack.go @@ -0,0 +1,181 @@ +package mediatrack + +import ( + "errors" + "fmt" + "github.com/Xhofe/alist/conf" + "github.com/Xhofe/alist/drivers/base" + "github.com/Xhofe/alist/model" + "github.com/Xhofe/alist/utils" + "github.com/go-resty/resty/v2" + "path" + "strconv" + "time" +) + +type BaseResp struct { + Status string `json:"status"` + Message string `json:"message"` +} + +type T struct { + BaseResp +} + +func (driver MediaTrack) Request(url string, method int, headers, query, form map[string]string, data interface{}, resp interface{}, account *model.Account) ([]byte, error) { + req := base.RestyClient.R() + req.SetHeader("Authorization", "Bearer "+account.AccessToken) + if headers != nil { + req.SetHeaders(headers) + } + if query != nil { + req.SetQueryParams(query) + } + if form != nil { + req.SetFormData(form) + } + if data != nil { + req.SetBody(data) + } + var e BaseResp + req.SetResult(&e) + var err error + var res *resty.Response + switch method { + case base.Get: + res, err = req.Get(url) + case base.Post: + res, err = req.Post(url) + case base.Delete: + res, err = req.Delete(url) + case base.Patch: + res, err = req.Patch(url) + case base.Put: + res, err = req.Put(url) + default: + return nil, base.ErrNotSupport + } + if err != nil { + return nil, err + } + if e.Status != "SUCCESS" { + return nil, errors.New(e.Message) + } + if resp != nil { + err = utils.Json.Unmarshal(res.Body(), resp) + } + return res.Body(), err +} + +type File struct { + Category int `json:"category"` + ChildAssets []interface{} `json:"childAssets"` + CommentCount int `json:"comment_count"` + CoverAsset interface{} `json:"cover_asset"` + CoverAssetID string `json:"cover_asset_id"` + CreatedAt time.Time `json:"created_at"` + DeletedAt string `json:"deleted_at"` + Description string `json:"description"` + File struct { + Cover string `json:"cover"` + Src string `json:"src"` + } `json:"file"` + //FileID string `json:"file_id"` + ID string `json:"id"` + + Size string `json:"size"` + Thumbnails []interface{} `json:"thumbnails"` + Title string `json:"title"` + UpdatedAt time.Time `json:"updated_at"` +} + +func (driver MediaTrack) formatFile(file *File) *model.File { + f := model.File{ + Id: file.ID, + Name: file.Title, + Size: 0, + Driver: driver.Config().Name, + UpdatedAt: &file.UpdatedAt, + } + if file.Category == 0 { + // folder + f.Type = conf.FOLDER + } else { + size, _ := strconv.ParseInt(file.Size, 10, 64) + f.Size = size + f.Type = utils.GetFileType(path.Ext(file.Title)) + if file.File.Cover != "" { + f.Thumbnail = "https://nano.mtres.cn/" + file.File.Cover + } + } + return &f +} + +type ChildrenResp struct { + Status string `json:"status"` + Data struct { + Total int `json:"total"` + Assets []File `json:"assets"` + } `json:"data"` + Path string `json:"path"` + TraceID string `json:"trace_id"` + RequestID string `json:"requestId"` +} + +func (driver MediaTrack) GetFiles(parentId string, account *model.Account) ([]model.File, error) { + files := make([]model.File, 0) + url := fmt.Sprintf("https://jayce.api.mediatrack.cn/v4/assets/%s/children", parentId) + sort := "" + if account.OrderBy != "" { + if account.OrderDirection == "true" { + sort = "-" + } + sort += account.OrderBy + } + page := 1 + for { + var resp ChildrenResp + _, err := driver.Request(url, base.Get, nil, map[string]string{ + "page": strconv.Itoa(page), + "size": "50", + "sort": sort, + }, nil, nil, &resp, account) + if err != nil { + return nil, err + } + if len(resp.Data.Assets) == 0 { + break + } + page++ + for _, file := range resp.Data.Assets { + files = append(files, *driver.formatFile(&file)) + } + } + return files, nil +} + +type UploadResp struct { + Status string `json:"status"` + Data struct { + Credentials struct { + TmpSecretID string `json:"TmpSecretId"` + TmpSecretKey string `json:"TmpSecretKey"` + Token string `json:"Token"` + ExpiredTime int `json:"ExpiredTime"` + Expiration time.Time `json:"Expiration"` + StartTime int `json:"StartTime"` + } `json:"credentials"` + Object string `json:"object"` + Bucket string `json:"bucket"` + Region string `json:"region"` + URL string `json:"url"` + Size string `json:"size"` + } `json:"data"` + Path string `json:"path"` + TraceID string `json:"trace_id"` + RequestID string `json:"requestId"` +} + +func init() { + base.RegisterDriver(&MediaTrack{}) +}