package mediatrack import ( "context" "crypto/md5" "encoding/hex" "fmt" "io" "net/http" "strconv" "time" "github.com/alist-org/alist/v3/drivers/base" "github.com/alist-org/alist/v3/internal/driver" "github.com/alist-org/alist/v3/internal/model" "github.com/alist-org/alist/v3/pkg/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/go-resty/resty/v2" "github.com/google/uuid" log "github.com/sirupsen/logrus" ) type MediaTrack struct { model.Storage Addition } func (d *MediaTrack) Config() driver.Config { return config } func (d *MediaTrack) GetAddition() driver.Additional { return &d.Addition } func (d *MediaTrack) Init(ctx context.Context) error { _, err := d.request("https://kayle.api.mediatrack.cn/users", http.MethodGet, nil, nil) return err } func (d *MediaTrack) Drop(ctx context.Context) error { return nil } func (d *MediaTrack) 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(f File) (model.Obj, error) { size, _ := strconv.ParseInt(f.Size, 10, 64) thumb := "" if f.File != nil && f.File.Cover != "" { thumb = "https://nano.mtres.cn/" + f.File.Cover } return &Object{ Object: model.Object{ ID: f.ID, Name: f.Title, Modified: f.UpdatedAt, IsFolder: f.File == nil, Size: size, }, Thumbnail: model.Thumbnail{Thumbnail: thumb}, ParentID: dir.GetID(), }, nil }) } func (d *MediaTrack) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { url := fmt.Sprintf("https://kayn.api.mediatrack.cn/v1/download_token/asset?asset_id=%s&source_type=project&password=&source_id=%s", file.GetID(), d.ProjectID) log.Debugf("media track url: %s", url) body, err := d.request(url, http.MethodGet, nil, nil) if err != nil { return nil, err } token := utils.Json.Get(body, "data", "token").ToString() url = "https://kayn.api.mediatrack.cn/v1/download/redirect?token=" + token res, err := base.NoRedirectClient.R().Get(url) if err != nil { return nil, err } log.Debug(res.String()) link := model.Link{ URL: url, } log.Debugln("res code: ", res.StatusCode()) if res.StatusCode() == 302 { link.URL = res.Header().Get("location") expired := time.Duration(60) * time.Second link.Expiration = &expired } return &link, nil } func (d *MediaTrack) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error { url := fmt.Sprintf("https://jayce.api.mediatrack.cn/v3/assets/%s/children", parentDir.GetID()) _, err := d.request(url, http.MethodPost, func(req *resty.Request) { req.SetBody(base.Json{ "type": 1, "title": dirName, }) }, nil) return err } func (d *MediaTrack) Move(ctx context.Context, srcObj, dstDir model.Obj) error { data := base.Json{ "parent_id": dstDir.GetID(), "ids": []string{srcObj.GetID()}, } url := "https://jayce.api.mediatrack.cn/v4/assets/batch/move" _, err := d.request(url, http.MethodPost, func(req *resty.Request) { req.SetBody(data) }, nil) return err } func (d *MediaTrack) Rename(ctx context.Context, srcObj model.Obj, newName string) error { url := "https://jayce.api.mediatrack.cn/v3/assets/" + srcObj.GetID() data := base.Json{ "title": newName, } _, err := d.request(url, http.MethodPut, func(req *resty.Request) { req.SetBody(data) }, nil) return err } func (d *MediaTrack) Copy(ctx context.Context, srcObj, dstDir model.Obj) error { data := base.Json{ "parent_id": dstDir.GetID(), "ids": []string{srcObj.GetID()}, } url := "https://jayce.api.mediatrack.cn/v4/assets/batch/clone" _, err := d.request(url, http.MethodPost, func(req *resty.Request) { req.SetBody(data) }, nil) return err } func (d *MediaTrack) Remove(ctx context.Context, obj model.Obj) error { var parentID string if o, ok := obj.(*Object); ok { parentID = o.ParentID } else { return fmt.Errorf("obj is not local Object") } data := base.Json{ "origin_id": parentID, "ids": []string{obj.GetID()}, } url := "https://jayce.api.mediatrack.cn/v4/assets/batch/delete" _, err := d.request(url, http.MethodDelete, func(req *resty.Request) { req.SetBody(data) }, nil) return err } func (d *MediaTrack) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error { src := "assets/" + uuid.New().String() var resp UploadResp _, err := d.request("https://jayce.api.mediatrack.cn/v3/storage/tokens/asset", http.MethodGet, func(req *resty.Request) { req.SetQueryParam("src", src) }, &resp) 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 } tempFile, err := stream.CacheFullInTempFile() if err != nil { return err } defer func() { _ = tempFile.Close() }() uploader := s3manager.NewUploader(s) if stream.GetSize() > s3manager.MaxUploadParts*s3manager.DefaultUploadPartSize { uploader.PartSize = stream.GetSize() / (s3manager.MaxUploadParts - 1) } input := &s3manager.UploadInput{ Bucket: &resp.Data.Bucket, Key: &resp.Data.Object, Body: tempFile, } _, err = uploader.UploadWithContext(ctx, input) if err != nil { return err } url := fmt.Sprintf("https://jayce.api.mediatrack.cn/v3/assets/%s/children", dstDir.GetID()) _, err = tempFile.Seek(0, io.SeekStart) if err != nil { return err } h := md5.New() _, err = utils.CopyWithBuffer(h, tempFile) if err != nil { return err } hash := hex.EncodeToString(h.Sum(nil)) data := base.Json{ "category": 0, "description": stream.GetName(), "hash": hash, "mime": stream.GetMimetype(), "size": stream.GetSize(), "src": src, "title": stream.GetName(), "type": 0, } _, err = d.request(url, http.MethodPost, func(req *resty.Request) { req.SetBody(data) }, nil) return err } var _ driver.Driver = (*MediaTrack)(nil)