alist/drivers/baidu_photo/driver.go

385 lines
9.5 KiB
Go
Raw Permalink Normal View History

2022-09-12 09:10:02 +00:00
package baiduphoto
import (
"context"
"crypto/md5"
"encoding/hex"
"errors"
2022-09-12 09:10:02 +00:00
"fmt"
"io"
"math"
"regexp"
"strconv"
"strings"
"time"
2022-09-12 09:10:02 +00:00
"github.com/alist-org/alist/v3/drivers/base"
2022-09-12 09:10:02 +00:00
"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/errgroup"
2022-09-12 09:10:02 +00:00
"github.com/alist-org/alist/v3/pkg/utils"
"github.com/avast/retry-go"
2022-09-12 09:10:02 +00:00
"github.com/go-resty/resty/v2"
)
type BaiduPhoto struct {
model.Storage
Addition
// AccessToken string
Uk int64
bdstoken string
root model.Obj
uploadThread int
2022-09-12 09:10:02 +00:00
}
func (d *BaiduPhoto) Config() driver.Config {
return config
}
func (d *BaiduPhoto) GetAddition() driver.Additional {
return &d.Addition
2022-09-12 09:10:02 +00:00
}
func (d *BaiduPhoto) Init(ctx context.Context) error {
d.uploadThread, _ = strconv.Atoi(d.UploadThread)
if d.uploadThread < 1 || d.uploadThread > 32 {
d.uploadThread, d.UploadThread = 3, "3"
}
// if err := d.refreshToken(); err != nil {
// return err
// }
// root
if d.AlbumID != "" {
albumID := strings.Split(d.AlbumID, "|")[0]
album, err := d.GetAlbumDetail(ctx, albumID)
if err != nil {
return err
}
d.root = album
} else {
d.root = &Root{
Name: "root",
Modified: d.Modified,
IsFolder: true,
}
}
// uk
info, err := d.uInfo()
if err != nil {
return err
}
d.bdstoken, err = d.getBDStoken()
if err != nil {
return err
}
d.Uk, err = strconv.ParseInt(info.YouaID, 10, 64)
return err
}
func (d *BaiduPhoto) GetRoot(ctx context.Context) (model.Obj, error) {
return d.root, nil
2022-09-12 09:10:02 +00:00
}
func (d *BaiduPhoto) Drop(ctx context.Context) error {
// d.AccessToken = ""
d.Uk = 0
d.root = nil
2022-09-12 09:10:02 +00:00
return nil
}
func (d *BaiduPhoto) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
var err error
/* album */
if album, ok := dir.(*Album); ok {
var files []AlbumFile
files, err = d.GetAllAlbumFile(ctx, album, "")
if err != nil {
return nil, err
2022-09-12 09:10:02 +00:00
}
return utils.MustSliceConvert(files, func(file AlbumFile) model.Obj {
return &file
}), nil
}
/* root */
var albums []Album
if d.ShowType != "root_only_file" {
albums, err = d.GetAllAlbum(ctx)
2022-09-12 09:10:02 +00:00
if err != nil {
return nil, err
}
}
var files []File
if d.ShowType != "root_only_album" {
files, err = d.GetAllFile(ctx)
if err != nil {
return nil, err
2022-09-12 09:10:02 +00:00
}
}
return append(
utils.MustSliceConvert(albums, func(album Album) model.Obj {
return &album
}),
utils.MustSliceConvert(files, func(album File) model.Obj {
return &album
})...,
), nil
2022-09-12 09:10:02 +00:00
}
func (d *BaiduPhoto) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
switch file := file.(type) {
case *File:
2022-09-12 09:10:02 +00:00
return d.linkFile(ctx, file, args)
case *AlbumFile:
// 处理共享相册
if d.Uk != file.Uk {
// 有概率无法获取到链接
// return d.linkAlbum(ctx, file, args)
f, err := d.CopyAlbumFile(ctx, file)
if err != nil {
return nil, err
}
return d.linkFile(ctx, f, args)
}
return d.linkFile(ctx, &file.File, args)
2022-09-12 09:10:02 +00:00
}
return nil, errs.NotFile
}
var joinReg = regexp.MustCompile(`(?i)join:([\S]*)`)
func (d *BaiduPhoto) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) (model.Obj, error) {
if _, ok := parentDir.(*Root); ok {
code := joinReg.FindStringSubmatch(dirName)
2022-09-12 09:10:02 +00:00
if len(code) > 1 {
return d.JoinAlbum(ctx, code[1])
}
return d.CreateAlbum(ctx, dirName)
}
return nil, errs.NotSupport
2022-09-12 09:10:02 +00:00
}
func (d *BaiduPhoto) Copy(ctx context.Context, srcObj, dstDir model.Obj) (model.Obj, error) {
switch file := srcObj.(type) {
case *File:
if album, ok := dstDir.(*Album); ok {
2022-09-12 09:10:02 +00:00
//rootfile -> album
return d.AddAlbumFile(ctx, album, file)
2022-09-12 09:10:02 +00:00
}
case *AlbumFile:
switch album := dstDir.(type) {
case *Root:
2022-09-12 09:10:02 +00:00
//albumfile -> root
return d.CopyAlbumFile(ctx, file)
case *Album:
2022-09-12 09:10:02 +00:00
// albumfile -> root -> album
rootfile, err := d.CopyAlbumFile(ctx, file)
2022-09-12 09:10:02 +00:00
if err != nil {
return nil, err
2022-09-12 09:10:02 +00:00
}
return d.AddAlbumFile(ctx, album, rootfile)
2022-09-12 09:10:02 +00:00
}
}
return nil, errs.NotSupport
2022-09-12 09:10:02 +00:00
}
func (d *BaiduPhoto) Move(ctx context.Context, srcObj, dstDir model.Obj) (model.Obj, error) {
if file, ok := srcObj.(*AlbumFile); ok {
switch dstDir.(type) {
case *Album, *Root: // albumfile -> root -> album or albumfile -> root
newObj, err := d.Copy(ctx, srcObj, dstDir)
if err != nil {
return nil, err
}
// 删除原相册文件
_ = d.DeleteAlbumFile(ctx, file)
return newObj, nil
2022-09-12 09:10:02 +00:00
}
}
return nil, errs.NotSupport
2022-09-12 09:10:02 +00:00
}
func (d *BaiduPhoto) Rename(ctx context.Context, srcObj model.Obj, newName string) (model.Obj, error) {
2022-09-12 09:10:02 +00:00
// 仅支持相册改名
if album, ok := srcObj.(*Album); ok {
return d.SetAlbumName(ctx, album, newName)
2022-09-12 09:10:02 +00:00
}
return nil, errs.NotSupport
2022-09-12 09:10:02 +00:00
}
func (d *BaiduPhoto) Remove(ctx context.Context, obj model.Obj) error {
switch obj := obj.(type) {
case *File:
return d.DeleteFile(ctx, obj)
case *AlbumFile:
return d.DeleteAlbumFile(ctx, obj)
case *Album:
return d.DeleteAlbum(ctx, obj)
2022-09-12 09:10:02 +00:00
}
return errs.NotSupport
}
func (d *BaiduPhoto) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) (model.Obj, error) {
// 不支持大小为0的文件
if stream.GetSize() == 0 {
return nil, fmt.Errorf("file size cannot be zero")
}
// TODO:
// 暂时没有找到妙传方式
2022-09-12 09:10:02 +00:00
// 需要获取完整文件md5,必须支持 io.Seek
tempFile, err := stream.CacheFullInTempFile()
2022-09-12 09:10:02 +00:00
if err != nil {
return nil, err
2022-09-12 09:10:02 +00:00
}
const DEFAULT int64 = 1 << 22
const SliceSize int64 = 1 << 18
2022-09-12 09:10:02 +00:00
// 计算需要的数据
streamSize := stream.GetSize()
count := int(math.Ceil(float64(streamSize) / float64(DEFAULT)))
lastBlockSize := streamSize % DEFAULT
if lastBlockSize == 0 {
lastBlockSize = DEFAULT
}
2022-09-12 09:10:02 +00:00
// step.1 计算MD5
2022-09-12 09:10:02 +00:00
sliceMD5List := make([]string, 0, count)
byteSize := int64(DEFAULT)
fileMd5H := md5.New()
sliceMd5H := md5.New()
sliceMd5H2 := md5.New()
slicemd5H2Write := utils.LimitWriter(sliceMd5H2, SliceSize)
2022-09-12 09:10:02 +00:00
for i := 1; i <= count; i++ {
if utils.IsCanceled(ctx) {
return nil, ctx.Err()
2022-09-12 09:10:02 +00:00
}
if i == count {
byteSize = lastBlockSize
}
_, err := utils.CopyWithBufferN(io.MultiWriter(fileMd5H, sliceMd5H, slicemd5H2Write), tempFile, byteSize)
if err != nil && err != io.EOF {
return nil, err
2022-09-12 09:10:02 +00:00
}
sliceMD5List = append(sliceMD5List, hex.EncodeToString(sliceMd5H.Sum(nil)))
sliceMd5H.Reset()
2022-09-12 09:10:02 +00:00
}
contentMd5 := hex.EncodeToString(fileMd5H.Sum(nil))
sliceMd5 := hex.EncodeToString(sliceMd5H2.Sum(nil))
blockListStr, _ := utils.Json.MarshalToString(sliceMD5List)
2022-09-12 09:10:02 +00:00
// step.2 预上传
2022-09-12 09:10:02 +00:00
params := map[string]string{
"autoinit": "1",
"isdir": "0",
"rtype": "1",
"ctype": "11",
"path": fmt.Sprintf("/%s", stream.GetName()),
2022-09-12 09:10:02 +00:00
"size": fmt.Sprint(stream.GetSize()),
"slice-md5": sliceMd5,
"content-md5": contentMd5,
"block_list": blockListStr,
2022-09-12 09:10:02 +00:00
}
// 尝试获取之前的进度
precreateResp, ok := base.GetUploadProgress[*PrecreateResp](d, strconv.FormatInt(d.Uk, 10), contentMd5)
if !ok {
_, err = d.Post(FILE_API_URL_V1+"/precreate", func(r *resty.Request) {
r.SetContext(ctx)
r.SetFormData(params)
r.SetQueryParam("bdstoken", d.bdstoken)
}, &precreateResp)
if err != nil {
return nil, err
}
2022-09-12 09:10:02 +00:00
}
switch precreateResp.ReturnType {
case 1: //step.3 上传文件切片
threadG, upCtx := errgroup.NewGroupWithContext(ctx, d.uploadThread,
retry.Attempts(3),
retry.Delay(time.Second),
retry.DelayType(retry.BackOffDelay))
for i, partseq := range precreateResp.BlockList {
if utils.IsCanceled(upCtx) {
break
}
i, partseq, offset, byteSize := i, partseq, int64(partseq)*DEFAULT, DEFAULT
if partseq+1 == count {
byteSize = lastBlockSize
2022-09-12 09:10:02 +00:00
}
threadG.Go(func(ctx context.Context) error {
uploadParams := map[string]string{
"method": "upload",
"path": params["path"],
"partseq": fmt.Sprint(partseq),
"uploadid": precreateResp.UploadID,
"app_id": "16051585",
}
_, err = d.Post("https://c3.pcs.baidu.com/rest/2.0/pcs/superfile2", func(r *resty.Request) {
r.SetContext(ctx)
r.SetQueryParams(uploadParams)
r.SetFileReader("file", stream.GetName(), io.NewSectionReader(tempFile, offset, byteSize))
}, nil)
if err != nil {
return err
}
feat: refactor offline download (#5408 close #4108) * wip: refactor offline download (#5331) * base tool * working: aria2 * refactor: change type of percentage to float64 * wip: adapt aria2 * wip: use items in offline_download * wip: use tool manager * wip: adapt qBittorrent * chore: fix typo * Squashed commit of the following: commit 4fc0a77565702f9bf498485d42336502f2ee9776 Author: Andy Hsu <i@nn.ci> Date: Fri Oct 20 21:06:25 2023 +0800 fix(baidu_netdisk): upload file > 4GB (close #5392) commit aaffaee2b54fc067d240ea0c20ea3c2f39615d6e Author: gmugu <94156510@qq.com> Date: Thu Oct 19 19:17:53 2023 +0800 perf(webdav): support request with cookies (#5391) commit 8ef8023c20bfeee97ec82155b52eae0d80b1410e Author: NewbieOrange <NewbieOrange@users.noreply.github.com> Date: Thu Oct 19 19:17:09 2023 +0800 fix(aliyundrive_open): upload progress for normal upload (#5398) commit cdfbe6dcf2b361e4c93c2703c2f8c9bddeac0ee6 Author: foxxorcat <95907542+foxxorcat@users.noreply.github.com> Date: Wed Oct 18 16:27:07 2023 +0800 fix: hash gcid empty file (#5394) commit 94d028743abf8e0d736f80c0ec4fb294a1cc064c Author: Andy Hsu <i@nn.ci> Date: Sat Oct 14 13:17:51 2023 +0800 ci: remove `pr-welcome` label when close issue [skip ci] commit 7f7335435c2f32a3eef76fac4c4f783d9d8624fd Author: itsHenry <2671230065@qq.com> Date: Sat Oct 14 13:12:46 2023 +0800 feat(cloudreve): support thumbnail (#5373 close #5348) * feat(cloudreve): support thumbnail * chore: remove unnecessary code commit b9e192b29cffddf14a0dfb2d3885def57a56ce16 Author: foxxorcat <95907542+foxxorcat@users.noreply.github.com> Date: Thu Oct 12 20:57:12 2023 +0800 fix(115): limit request rate (#5367 close #5275) * fix(115):limit request rate * chore(115): fix unit of `limit_rate` --------- Co-authored-by: Andy Hsu <i@nn.ci> commit 69a98eaef612b58596e5c26c341b6d7cedecdf19 Author: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Date: Wed Oct 11 22:01:55 2023 +0800 fix(deps): update module github.com/aliyun/aliyun-oss-go-sdk to v2.2.9+incompatible (#5141) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> commit 1ebc96a4e5220c979fd581bb3b5640e9436f6665 Author: Andy Hsu <i@nn.ci> Date: Tue Oct 10 18:32:00 2023 +0800 fix(wopan): fatal error concurrent map writes (close #5352) commit 66e2324cac75cb3ef05af45dbdd10b124d534aff Author: Andy Hsu <i@nn.ci> Date: Tue Oct 10 18:23:11 2023 +0800 chore(deps): upgrade dependencies commit 7600dc28df137c439e538b4257731c33a63db9b5 Author: Andy Hsu <i@nn.ci> Date: Tue Oct 10 18:13:58 2023 +0800 fix(aliyundrive_open): change default api to raw server (close #5358) commit 8ef89ad0a496d5acc398794c0afa4f77c67ad371 Author: foxxorcat <95907542+foxxorcat@users.noreply.github.com> Date: Tue Oct 10 18:08:27 2023 +0800 fix(baidu_netdisk): hash and `error 2` (#5356) * fix(baidu):hash and error:2 * fix:invalid memory address commit 35d672217dde69e65b41b1fcd9786c1cfebcdc45 Author: jeffmingup <1960588251@qq.com> Date: Sun Oct 8 19:29:45 2023 +0800 fix(onedrive_app): incorrect api on `_accessToken` (#5346) commit 1a283bb2720eff6d1b0c1dd6f1667a6449905a9b Author: foxxorcat <95907542+foxxorcat@users.noreply.github.com> Date: Fri Oct 6 16:04:39 2023 +0800 feat(google_drive): add `hash_info`, `ctime`, `thumbnail` (#5334) commit a008f54f4d5eda5738abfd54bf1abf1e18c08430 Author: nkh0472 <67589323+nkh0472@users.noreply.github.com> Date: Thu Oct 5 13:10:51 2023 +0800 docs: minor language improvements (#5329) [skip ci] * fix: adapt update progress type * Squashed commit of the following: commit 65c5ec0c34d5f027a65933fe89af53791747bdd4 Author: itsHenry <2671230065@qq.com> Date: Sat Nov 4 13:35:09 2023 +0800 feat(cloudreve): folder size count and switch (#5457 close #5395) commit a6325967d0de18e6b6c744f06cb1ebaa08ec687e Author: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Date: Mon Oct 30 15:11:20 2023 +0800 fix(deps): update module github.com/charmbracelet/lipgloss to v0.9.1 (#5234) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> commit 4dff49470adce36416d8c56594e84868c04d023b Author: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Date: Mon Oct 30 15:10:36 2023 +0800 fix(deps): update golang.org/x/exp digest to 7918f67 (#5366) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> commit cc86d6f3d1ff2120669c9dda719b7faabb922f52 Author: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Date: Sun Oct 29 14:45:55 2023 +0800 fix(deps): update module golang.org/x/net to v0.17.0 [security] (#5370) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> commit c0f9c8ebafdf8dd2afe5c0b9fba24456819c3155 Author: Andy Hsu <i@nn.ci> Date: Thu Oct 26 19:21:09 2023 +0800 feat: add ignore direct link params (close #5434)
2023-11-06 08:56:55 +00:00
up(float64(threadG.Success()) * 100 / float64(len(precreateResp.BlockList)))
precreateResp.BlockList[i] = -1
return nil
})
}
if err = threadG.Wait(); err != nil {
if errors.Is(err, context.Canceled) {
precreateResp.BlockList = utils.SliceFilter(precreateResp.BlockList, func(s int) bool { return s >= 0 })
base.SaveUploadProgress(d, strconv.FormatInt(d.Uk, 10), contentMd5)
}
return nil, err
2022-09-12 09:10:02 +00:00
}
fallthrough
case 2: //step.4 创建文件
2022-09-12 09:10:02 +00:00
params["uploadid"] = precreateResp.UploadID
_, err = d.Post(FILE_API_URL_V1+"/create", func(r *resty.Request) {
r.SetContext(ctx)
r.SetFormData(params)
r.SetQueryParam("bdstoken", d.bdstoken)
2022-09-12 09:10:02 +00:00
}, &precreateResp)
if err != nil {
return nil, err
2022-09-12 09:10:02 +00:00
}
fallthrough
case 3: //step.5 增加到相册
rootfile := precreateResp.Data.toFile()
if album, ok := dstDir.(*Album); ok {
return d.AddAlbumFile(ctx, album, rootfile)
2022-09-12 09:10:02 +00:00
}
return rootfile, nil
2022-09-12 09:10:02 +00:00
}
return nil, errs.NotSupport
2022-09-12 09:10:02 +00:00
}
var _ driver.Driver = (*BaiduPhoto)(nil)
2023-01-16 11:55:43 +00:00
var _ driver.GetRooter = (*BaiduPhoto)(nil)
var _ driver.MkdirResult = (*BaiduPhoto)(nil)
var _ driver.CopyResult = (*BaiduPhoto)(nil)
var _ driver.MoveResult = (*BaiduPhoto)(nil)
var _ driver.Remove = (*BaiduPhoto)(nil)
var _ driver.PutResult = (*BaiduPhoto)(nil)
var _ driver.RenameResult = (*BaiduPhoto)(nil)