feat(pikpak): support disk usage (#1426)

* feat(pikpak): support disk usage

* fix(alias): cannot list with details

* refactor: rename `NewDiskUsageFromUsedAndTotal`

* fix(disk-usage): get details of storages that is not initialized
This commit is contained in:
KirCute
2025-10-06 16:37:00 +08:00
committed by GitHub
parent 66645516e5
commit 4ba7696032
18 changed files with 98 additions and 57 deletions

View File

@@ -260,7 +260,7 @@ func (d *Pan123) GetDetails(ctx context.Context) (*model.StorageDetails, error)
}
total := userInfo.Data.SpacePermanent + userInfo.Data.SpaceTemp
return &model.StorageDetails{
DiskUsage: *model.NewDiskUsageFromUsedAndTotal(userInfo.Data.SpaceUsed, total),
DiskUsage: driver.DiskUsageFromUsedAndTotal(userInfo.Data.SpaceUsed, total),
}, nil
}

View File

@@ -146,22 +146,27 @@ func (d *Alias) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([
})
if err == nil {
tmp, err = utils.SliceConvert(tmp, func(obj model.Obj) (model.Obj, error) {
thumb, ok := model.GetThumb(obj)
objRes := model.Object{
Name: obj.GetName(),
Size: obj.GetSize(),
Modified: obj.ModTime(),
IsFolder: obj.IsDir(),
}
if !ok {
return &objRes, nil
if thumb, ok := model.GetThumb(obj); ok {
return &model.ObjThumb{
Object: objRes,
Thumbnail: model.Thumbnail{
Thumbnail: thumb,
},
}, nil
}
return &model.ObjThumb{
Object: objRes,
Thumbnail: model.Thumbnail{
Thumbnail: thumb,
},
}, nil
if details, ok := model.GetStorageDetails(obj); ok {
return &model.ObjStorageDetails{
Obj: &objRes,
StorageDetailsWithName: *details,
}, nil
}
return &objRes, nil
})
}
if err == nil {

View File

@@ -6,6 +6,7 @@ import (
stdpath "path"
"strings"
"sync"
"time"
"github.com/OpenListTeam/OpenList/v4/internal/driver"
"github.com/OpenListTeam/OpenList/v4/internal/errs"
@@ -49,9 +50,11 @@ func (d *Alias) listRoot(ctx context.Context, withDetails bool) []model.Obj {
wg.Add(1)
go func() {
defer wg.Done()
details, e := op.GetStorageDetails(ctx, remoteDriver)
c, cancel := context.WithTimeout(ctx, time.Second)
defer cancel()
details, e := op.GetStorageDetails(c, remoteDriver)
if e != nil {
if !errors.Is(e, errs.NotImplement) {
if !errors.Is(e, errs.NotImplement) && !errors.Is(e, errs.StorageNotInit) {
log.Errorf("failed get %s storage details: %+v", remoteDriver.GetStorage().MountPath, e)
}
return

View File

@@ -337,7 +337,7 @@ func (d *AliDrive) GetDetails(ctx context.Context) (*model.StorageDetails, error
used := utils.Json.Get(res, "drive_used_size").ToUint64()
total := utils.Json.Get(res, "drive_total_size").ToUint64()
return &model.StorageDetails{
DiskUsage: *model.NewDiskUsageFromUsedAndTotal(used, total),
DiskUsage: driver.DiskUsageFromUsedAndTotal(used, total),
}, nil
}

View File

@@ -369,7 +369,7 @@ func (d *BaiduNetdisk) GetDetails(ctx context.Context) (*model.StorageDetails, e
if err != nil {
return nil, err
}
return &model.StorageDetails{DiskUsage: *du}, nil
return &model.StorageDetails{DiskUsage: du}, nil
}
var _ driver.Driver = (*BaiduNetdisk)(nil)

View File

@@ -12,6 +12,7 @@ import (
"unicode"
"github.com/OpenListTeam/OpenList/v4/drivers/base"
"github.com/OpenListTeam/OpenList/v4/internal/driver"
"github.com/OpenListTeam/OpenList/v4/internal/errs"
"github.com/OpenListTeam/OpenList/v4/internal/model"
"github.com/OpenListTeam/OpenList/v4/internal/op"
@@ -382,15 +383,15 @@ func (d *BaiduNetdisk) getSliceSize(filesize int64) int64 {
return maxSliceSize
}
func (d *BaiduNetdisk) quota(ctx context.Context) (*model.DiskUsage, error) {
func (d *BaiduNetdisk) quota(ctx context.Context) (model.DiskUsage, error) {
var resp QuotaResp
_, err := d.request("https://pan.baidu.com/api/quota", http.MethodGet, func(req *resty.Request) {
req.SetContext(ctx)
}, &resp)
if err != nil {
return nil, err
return model.DiskUsage{}, err
}
return model.NewDiskUsageFromUsedAndTotal(resp.Used, resp.Total), nil
return driver.DiskUsageFromUsedAndTotal(resp.Used, resp.Total), nil
}
// func encodeURIComponent(str string) string {

View File

@@ -349,7 +349,7 @@ func (d *CloudreveV4) GetDetails(ctx context.Context) (*model.StorageDetails, er
return nil, err
}
return &model.StorageDetails{
DiskUsage: *model.NewDiskUsageFromUsedAndTotal(r.Used, r.Total),
DiskUsage: driver.DiskUsageFromUsedAndTotal(r.Used, r.Total),
}, nil
}

View File

@@ -189,7 +189,7 @@ func (d *GoogleDrive) GetDetails(ctx context.Context) (*model.StorageDetails, er
return nil, err
}
return &model.StorageDetails{
DiskUsage: *model.NewDiskUsageFromUsedAndTotal(used, total),
DiskUsage: driver.DiskUsageFromUsedAndTotal(used, total),
}, nil
}

View File

@@ -414,7 +414,7 @@ func (d *ILanZou) GetDetails(ctx context.Context) (*model.StorageDetails, error)
total := totalSize + rewardSize
used := utils.Json.Get(res, "map", "usedSize").ToUint64() * 1024
return &model.StorageDetails{
DiskUsage: *model.NewDiskUsageFromUsedAndTotal(used, total),
DiskUsage: driver.DiskUsageFromUsedAndTotal(used, total),
}, nil
}

View File

@@ -36,7 +36,6 @@ func (d *PikPak) GetAddition() driver.Additional {
}
func (d *PikPak) Init(ctx context.Context) (err error) {
if d.Common == nil {
d.Common = &Common{
client: base.NewRestyClient(),
@@ -247,7 +246,7 @@ func (d *PikPak) Put(ctx context.Context, dstDir model.Obj, stream model.FileStr
}
params := resp.Resumable.Params
//endpoint := strings.Join(strings.Split(params.Endpoint, ".")[1:], ".")
// endpoint := strings.Join(strings.Split(params.Endpoint, ".")[1:], ".")
// web 端上传 返回的endpoint 为 `mypikpak.net` | android 端上传 返回的endpoint 为 `vip-lixian-07.mypikpak.net`·
if d.Addition.Platform == "android" {
params.Endpoint = "mypikpak.net"
@@ -260,6 +259,27 @@ func (d *PikPak) Put(ctx context.Context, dstDir model.Obj, stream model.FileStr
return d.UploadByMultipart(ctx, &params, stream.GetSize(), stream, up)
}
func (d *PikPak) GetDetails(ctx context.Context) (*model.StorageDetails, error) {
var about AboutResponse
_, err := d.request("https://api-drive.mypikpak.com/drive/v1/about", http.MethodGet, func(req *resty.Request) {
req.SetContext(ctx)
}, &about)
if err != nil {
return nil, err
}
total, err := strconv.ParseUint(about.Quota.Limit, 10, 64)
if err != nil {
return nil, err
}
used, err := strconv.ParseUint(about.Quota.Usage, 10, 64)
if err != nil {
return nil, err
}
return &model.StorageDetails{
DiskUsage: driver.DiskUsageFromUsedAndTotal(used, total),
}, nil
}
// 离线下载文件
func (d *PikPak) OfflineDownload(ctx context.Context, fileUrl string, parentDir model.Obj, fileName string) (*OfflineTask, error) {
requestBody := base.Json{
@@ -278,7 +298,6 @@ func (d *PikPak) OfflineDownload(ctx context.Context, fileUrl string, parentDir
req.SetContext(ctx).
SetBody(requestBody)
}, &resp)
if err != nil {
return nil, err
}
@@ -325,7 +344,6 @@ func (d *PikPak) OfflineList(ctx context.Context, nextPageToken string, phase []
req.SetContext(ctx).
SetQueryParams(params)
}, &resp)
if err != nil {
return nil, fmt.Errorf("failed to get offline list: %w", err)
}

View File

@@ -78,7 +78,7 @@ type Media struct {
type UploadTaskData struct {
UploadType string `json:"upload_type"`
//UPLOAD_TYPE_RESUMABLE
// UPLOAD_TYPE_RESUMABLE
Resumable *struct {
Kind string `json:"kind"`
Params S3Params `json:"params"`
@@ -195,3 +195,15 @@ type CaptchaTokenResponse struct {
ExpiresIn int64 `json:"expires_in"`
Url string `json:"url"`
}
type AboutResponse struct {
Quota struct {
Limit string `json:"limit"`
Usage string `json:"usage"`
UsageInTrash string `json:"usage_in_trash"`
IsUnlimited bool `json:"is_unlimited"`
Complimentary string `json:"complimentary"`
} `json:"quota"`
ExpiresAt string `json:"expires_at"`
UserType int `json:"user_type"`
}

View File

@@ -20,7 +20,7 @@ func (p *Progress) Write(b []byte) (n int, err error) {
n = len(b)
p.Done += int64(n)
p.up(float64(p.Done) / float64(p.Total) * 100)
return
return n, err
}
func NewProgress(total int64, up UpdateProgress) *Progress {
@@ -61,3 +61,10 @@ type ReaderWithCtx = stream.ReaderWithCtx
type ReaderUpdatingProgress = stream.ReaderUpdatingProgress
type SimpleReaderWithSize = stream.SimpleReaderWithSize
func DiskUsageFromUsedAndTotal(used, total uint64) model.DiskUsage {
return model.DiskUsage{
TotalSpace: max(used, total),
FreeSpace: total - min(used, total),
}
}

View File

@@ -12,13 +12,12 @@ var (
NotSupport = errors.New("not support")
RelativePath = errors.New("using relative path is not allowed")
MoveBetweenTwoStorages = errors.New("can't move files between two storages, try to copy")
UploadNotSupported = errors.New("upload not supported")
MetaNotFound = errors.New("meta not found")
StorageNotFound = errors.New("storage not found")
StreamIncomplete = errors.New("upload/download stream incomplete, possible network issue")
StreamPeekFail = errors.New("StreamPeekFail")
UploadNotSupported = errors.New("upload not supported")
MetaNotFound = errors.New("meta not found")
StorageNotFound = errors.New("storage not found")
StorageNotInit = errors.New("storage not init")
StreamIncomplete = errors.New("upload/download stream incomplete, possible network issue")
StreamPeekFail = errors.New("StreamPeekFail")
UnknownArchiveFormat = errors.New("unknown archive format")
WrongArchivePassword = errors.New("wrong archive password")

View File

@@ -32,7 +32,7 @@ type Proxy struct {
WebdavPolicy string `json:"webdav_policy"`
ProxyRange bool `json:"proxy_range"`
DownProxyURL string `json:"down_proxy_url"`
//Disable sign for DownProxyURL
// Disable sign for DownProxyURL
DisableProxySign bool `json:"disable_proxy_sign"`
}
@@ -61,13 +61,6 @@ type DiskUsage struct {
FreeSpace uint64 `json:"free_space"`
}
func NewDiskUsageFromUsedAndTotal(used, total uint64) *DiskUsage {
return &DiskUsage{
TotalSpace: max(used, total),
FreeSpace: total - min(used, total),
}
}
type StorageDetails struct {
DiskUsage
}

View File

@@ -27,7 +27,7 @@ var archiveMetaG singleflight.Group[*model.ArchiveMetaProvider]
func GetArchiveMeta(ctx context.Context, storage driver.Driver, path string, args model.ArchiveMetaArgs) (*model.ArchiveMetaProvider, error) {
if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
return nil, errors.Errorf("storage not init: %s", storage.GetStorage().Status)
return nil, errors.WithMessagef(errs.StorageNotInit, "storage status: %s", storage.GetStorage().Status)
}
path = utils.FixAndCleanPath(path)
key := Key(storage, path)
@@ -163,7 +163,7 @@ var archiveListG singleflight.Group[[]model.Obj]
func ListArchive(ctx context.Context, storage driver.Driver, path string, args model.ArchiveListArgs) ([]model.Obj, error) {
if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
return nil, errors.Errorf("storage not init: %s", storage.GetStorage().Status)
return nil, errors.WithMessagef(errs.StorageNotInit, "storage status: %s", storage.GetStorage().Status)
}
path = utils.FixAndCleanPath(path)
metaKey := Key(storage, path)
@@ -309,7 +309,7 @@ func splitPath(path string) []string {
func ArchiveGet(ctx context.Context, storage driver.Driver, path string, args model.ArchiveListArgs) (model.Obj, model.Obj, error) {
if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
return nil, nil, errors.Errorf("storage not init: %s", storage.GetStorage().Status)
return nil, nil, errors.WithMessagef(errs.StorageNotInit, "storage status: %s", storage.GetStorage().Status)
}
path = utils.FixAndCleanPath(path)
af, err := GetUnwrap(ctx, storage, path)
@@ -364,7 +364,7 @@ var extractG = singleflight.Group[*extractLink]{Remember: true}
func DriverExtract(ctx context.Context, storage driver.Driver, path string, args model.ArchiveInnerArgs) (*model.Link, model.Obj, error) {
if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
return nil, nil, errors.Errorf("storage not init: %s", storage.GetStorage().Status)
return nil, nil, errors.WithMessagef(errs.StorageNotInit, "storage status: %s", storage.GetStorage().Status)
}
key := stdpath.Join(Key(storage, path), args.InnerPath)
if link, ok := extractCache.Get(key); ok {
@@ -480,7 +480,7 @@ func InternalExtract(ctx context.Context, storage driver.Driver, path string, ar
func ArchiveDecompress(ctx context.Context, storage driver.Driver, srcPath, dstDirPath string, args model.ArchiveDecompressArgs, lazyCache ...bool) error {
if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
return errors.Errorf("storage not init: %s", storage.GetStorage().Status)
return errors.WithMessagef(errs.StorageNotInit, "storage status: %s", storage.GetStorage().Status)
}
srcPath = utils.FixAndCleanPath(srcPath)
dstDirPath = utils.FixAndCleanPath(dstDirPath)

View File

@@ -116,7 +116,7 @@ func Key(storage driver.Driver, path string) string {
// List files in storage, not contains virtual file
func List(ctx context.Context, storage driver.Driver, path string, args model.ListArgs) ([]model.Obj, error) {
if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
return nil, errors.Errorf("storage not init: %s", storage.GetStorage().Status)
return nil, errors.WithMessagef(errs.StorageNotInit, "storage status: %s", storage.GetStorage().Status)
}
path = utils.FixAndCleanPath(path)
log.Debugf("op.List %s", path)
@@ -259,7 +259,7 @@ var errLinkMFileCache = stderrors.New("ErrLinkMFileCache")
// Link get link, if is an url. should have an expiry time
func Link(ctx context.Context, storage driver.Driver, path string, args model.LinkArgs) (*model.Link, model.Obj, error) {
if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
return nil, nil, errors.Errorf("storage not init: %s", storage.GetStorage().Status)
return nil, nil, errors.WithMessagef(errs.StorageNotInit, "storage status: %s", storage.GetStorage().Status)
}
var (
file model.Obj
@@ -369,7 +369,7 @@ var mkdirG singleflight.Group[interface{}]
func MakeDir(ctx context.Context, storage driver.Driver, path string, lazyCache ...bool) error {
if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
return errors.Errorf("storage not init: %s", storage.GetStorage().Status)
return errors.WithMessagef(errs.StorageNotInit, "storage status: %s", storage.GetStorage().Status)
}
path = utils.FixAndCleanPath(path)
key := Key(storage, path)
@@ -424,7 +424,7 @@ func MakeDir(ctx context.Context, storage driver.Driver, path string, lazyCache
func Move(ctx context.Context, storage driver.Driver, srcPath, dstDirPath string, lazyCache ...bool) error {
if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
return errors.Errorf("storage not init: %s", storage.GetStorage().Status)
return errors.WithMessagef(errs.StorageNotInit, "storage status: %s", storage.GetStorage().Status)
}
srcPath = utils.FixAndCleanPath(srcPath)
dstDirPath = utils.FixAndCleanPath(dstDirPath)
@@ -467,7 +467,7 @@ func Move(ctx context.Context, storage driver.Driver, srcPath, dstDirPath string
func Rename(ctx context.Context, storage driver.Driver, srcPath, dstName string, lazyCache ...bool) error {
if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
return errors.Errorf("storage not init: %s", storage.GetStorage().Status)
return errors.WithMessagef(errs.StorageNotInit, "storage status: %s", storage.GetStorage().Status)
}
srcPath = utils.FixAndCleanPath(srcPath)
srcRawObj, err := Get(ctx, storage, srcPath)
@@ -508,7 +508,7 @@ func Rename(ctx context.Context, storage driver.Driver, srcPath, dstName string,
// Copy Just copy file[s] in a storage
func Copy(ctx context.Context, storage driver.Driver, srcPath, dstDirPath string, lazyCache ...bool) error {
if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
return errors.Errorf("storage not init: %s", storage.GetStorage().Status)
return errors.WithMessagef(errs.StorageNotInit, "storage status: %s", storage.GetStorage().Status)
}
srcPath = utils.FixAndCleanPath(srcPath)
dstDirPath = utils.FixAndCleanPath(dstDirPath)
@@ -545,7 +545,7 @@ func Copy(ctx context.Context, storage driver.Driver, srcPath, dstDirPath string
func Remove(ctx context.Context, storage driver.Driver, path string) error {
if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
return errors.Errorf("storage not init: %s", storage.GetStorage().Status)
return errors.WithMessagef(errs.StorageNotInit, "storage status: %s", storage.GetStorage().Status)
}
if utils.PathEqual(path, "/") {
return errors.New("delete root folder is not allowed, please goto the manage page to delete the storage instead")
@@ -586,7 +586,7 @@ func Put(ctx context.Context, storage driver.Driver, dstDirPath string, file mod
}
}()
if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
return errors.Errorf("storage not init: %s", storage.GetStorage().Status)
return errors.WithMessagef(errs.StorageNotInit, "storage status: %s", storage.GetStorage().Status)
}
// UrlTree PUT
if storage.GetStorage().Driver == "UrlTree" {
@@ -678,7 +678,7 @@ func Put(ctx context.Context, storage driver.Driver, dstDirPath string, file mod
func PutURL(ctx context.Context, storage driver.Driver, dstDirPath, dstName, url string, lazyCache ...bool) error {
if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
return errors.Errorf("storage not init: %s", storage.GetStorage().Status)
return errors.WithMessagef(errs.StorageNotInit, "storage status: %s", storage.GetStorage().Status)
}
dstDirPath = utils.FixAndCleanPath(dstDirPath)
_, err := GetUnwrap(ctx, storage, stdpath.Join(dstDirPath, dstName))

View File

@@ -356,7 +356,7 @@ func GetStorageVirtualFilesWithDetailsByPath(ctx context.Context, prefix string,
defer cancel()
details, err := GetStorageDetails(timeoutCtx, d)
if err != nil {
if !errors.Is(err, errs.NotImplement) {
if !errors.Is(err, errs.NotImplement) && !errors.Is(err, errs.StorageNotInit) {
log.Errorf("failed get %s storage details: %+v", d.GetStorage().MountPath, err)
}
return ret
@@ -440,6 +440,9 @@ func GetBalancedStorage(path string) driver.Driver {
}
func GetStorageDetails(ctx context.Context, storage driver.Driver) (*model.StorageDetails, error) {
if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
return nil, errors.WithMessagef(errs.StorageNotInit, "storage status: %s", storage.GetStorage().Status)
}
wd, ok := storage.(driver.WithDetails)
if !ok {
return nil, errs.NotImplement

View File

@@ -50,7 +50,7 @@ func makeStorageResp(c *gin.Context, storages []model.Storage) []*StorageResp {
defer cancel()
details, err := op.GetStorageDetails(ctx, d)
if err != nil {
if !errors.Is(err, errs.NotImplement) {
if !errors.Is(err, errs.NotImplement) && !errors.Is(err, errs.StorageNotInit) {
log.Errorf("failed get %s details: %+v", s.MountPath, err)
}
return