mirror of https://github.com/Xhofe/alist
				
				
				
			🚧 MediaTrack support
							parent
							
								
									36b533cb16
								
							
						
					
					
						commit
						10f06fde5c
					
				| 
						 | 
				
			
			@ -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"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			@ -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{})
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue