Compare commits

...

13 Commits

Author SHA1 Message Date
foxxorcat b803b0070e fix(115): 20GB file upload restriction (#7452)
* fix(115): multipart upload error

* feat(115): Modify default page size

* fix(115): Replace temporary repair scheme
2024-11-02 16:41:33 +08:00
KirCute_ECT 64ceb5afb6 feat: support general users view and cancel own tasks (#7416 close #7398)
* feat: support general users view and cancel own tasks

Add a creator attribute to the upload, copy and offline download
tasks, so that a GENERAL task creator can view and cancel them.

BREAKING CHANGE:

1. A new internal package `task` including the struct `TaskWithCreator`
   which embeds `tache.Base` is created, and the past dependence on
   `tache.Task` will all be transferred to dependence on this package.
2. The API `/admin/task` can now also be accessed via `/task`, and the
   old endpoint is retained to ensure compatibility with legacy
   automation scripts.

Closes #7398

* fix(deps): update github.com/xhofe/tache to v0.1.3
2024-11-01 23:32:26 +08:00
Rirmach 10c7ebb1c0 fix(local): cross-device file move (#7430) 2024-11-01 23:31:33 +08:00
UUBulb d0cda62703 ci: add freebsd release build (#7344) 2024-11-01 21:37:53 +08:00
Maxwell Davis ce0b99a510 fix(cloudreve): path not exist when moving/copying files (#7432)
Co-authored-by: 马建军 <1432318228@qq.com>
2024-11-01 21:12:29 +08:00
Mmx 34a148c83d feat(local): thumbnail token bucket smooth migration (#7425)
* feat(local): allow to migrate static token buckets

* improve(local): token bucket migration boundary handling
2024-11-01 20:58:53 +08:00
Mmx 4955d8cec8 ci(docker): support riscv64 and ppc64le (#7426)
* ci(docker): bump cache key of musl library

* build(docker): add new arches to build script

* ci(docker): add new arches to buildx platforms
2024-11-01 20:53:53 +08:00
Shelton Zhu 216e3909f3 fix(115): enforce 20GB file size limit on uploadev (#7447 close #7413)
- Introduce a file size restriction to handle uploads more securely.
- Provide an informative error for uploads that exceed the new limit.
2024-11-01 20:52:19 +08:00
Andy Hsu a701432b8b ci: add freebsd to beta release 2024-10-21 00:05:56 +08:00
Jason-Fly a2dc45a80b fix(ilanzou): fix upload failure for small files (#7368 close #7250) 2024-10-20 23:53:56 +08:00
Jason-Fly 48ac23c8de fix(ilanzou): fix infinite loop when getting file list (#7366 close #7357) 2024-10-20 23:53:40 +08:00
hanbao233xD 2830575490 perf(123pan): change domain of login (#7325)
* Update driver.go

* 1

* Update util.go

* 123新登录接口

* Revert "Update util.go"

This reverts commit a13a58f8a86c7c36d4fd7d91137229a7667f1fb5.

* Update driver.go

* Update util.go

* Update util.go
2024-10-15 19:45:30 +08:00
YangXu e8538bd215 feat: add febbox driver (#7304 close #7293) 2024-10-14 22:44:20 +08:00
37 changed files with 1024 additions and 139 deletions
+7 -1
View File
@@ -8,6 +8,9 @@ concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
permissions:
contents: write
jobs:
changelog:
strategy:
@@ -54,7 +57,7 @@ jobs:
strategy:
matrix:
include:
- target: '!(*musl*|*windows-arm64*|*android*)' # xgo
- target: '!(*musl*|*windows-arm64*|*android*|*freebsd*)' # xgo
hash: "md5"
- target: 'linux-!(arm*)-musl*' #musl-not-arm
hash: "md5-linux-musl"
@@ -64,6 +67,9 @@ jobs:
hash: "md5-windows-arm64"
- target: 'android-*' #android
hash: "md5-android"
- target: 'freebsd-*' #freebsd
hash: "md5-freebsd"
name: Beta Release
runs-on: ubuntu-latest
steps:
+3 -3
View File
@@ -53,7 +53,7 @@ jobs:
uses: actions/cache@v4
with:
path: build/musl-libs
key: docker-musl-libs
key: docker-musl-libs-v2
- name: Download Musl Library
if: steps.cache-musl.outputs.cache-hit != 'true'
@@ -84,7 +84,7 @@ jobs:
push: ${{ github.event_name == 'push' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/386,linux/arm/v6,linux/s390x
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/386,linux/arm/v6,linux/s390x,linux/ppc64le,linux/riscv64
- name: Build and push with ffmpeg
id: docker_build_ffmpeg
@@ -96,7 +96,7 @@ jobs:
tags: ${{ steps.meta-ffmpeg.outputs.tags }}
labels: ${{ steps.meta-ffmpeg.outputs.labels }}
build-args: INSTALL_FFMPEG=true
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/386,linux/arm/v6,linux/s390x
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/386,linux/arm/v6,linux/s390x,linux/ppc64le,linux/riscv64
build_docker_with_aria2:
needs: build_docker
+3 -3
View File
@@ -22,7 +22,7 @@ jobs:
uses: actions/cache@v4
with:
path: build/musl-libs
key: docker-musl-libs
key: docker-musl-libs-v2
- name: Download Musl Library
if: steps.cache-musl.outputs.cache-hit != 'true'
@@ -58,7 +58,7 @@ jobs:
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/386,linux/arm/v6,linux/s390x
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/386,linux/arm/v6,linux/s390x,linux/ppc64le,linux/riscv64
- name: Docker meta with ffmpeg
id: meta-ffmpeg
@@ -79,7 +79,7 @@ jobs:
tags: ${{ steps.meta-ffmpeg.outputs.tags }}
labels: ${{ steps.meta-ffmpeg.outputs.labels }}
build-args: INSTALL_FFMPEG=true
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/386,linux/arm/v6,linux/s390x
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/386,linux/arm/v6,linux/s390x,linux/ppc64le,linux/riscv64
release_docker_with_aria2:
needs: release_docker
+35
View File
@@ -0,0 +1,35 @@
name: release_freebsd
on:
release:
types: [ published ]
jobs:
release_freebsd:
strategy:
matrix:
platform: [ ubuntu-latest ]
go-version: [ '1.21' ]
name: Release
runs-on: ${{ matrix.platform }}
steps:
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go-version }}
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Build
run: |
bash build.sh release freebsd
- name: Upload assets
uses: softprops/action-gh-release@v2
with:
tag_name: dev
files: build/compress/*
+34 -3
View File
@@ -93,7 +93,7 @@ BuildDocker() {
PrepareBuildDockerMusl() {
mkdir -p build/musl-libs
BASE="https://musl.cc/"
FILES=(x86_64-linux-musl-cross aarch64-linux-musl-cross i486-linux-musl-cross s390x-linux-musl-cross armv6-linux-musleabihf-cross armv7l-linux-musleabihf-cross)
FILES=(x86_64-linux-musl-cross aarch64-linux-musl-cross i486-linux-musl-cross s390x-linux-musl-cross armv6-linux-musleabihf-cross armv7l-linux-musleabihf-cross riscv64-linux-musl-cross powerpc64le-linux-musl-cross)
for i in "${FILES[@]}"; do
url="${BASE}${i}.tgz"
lib_tgz="build/${i}.tgz"
@@ -112,8 +112,8 @@ BuildDockerMultiplatform() {
docker_lflags="--extldflags '-static -fpic' $ldflags"
export CGO_ENABLED=1
OS_ARCHES=(linux-amd64 linux-arm64 linux-386 linux-s390x)
CGO_ARGS=(x86_64-linux-musl-gcc aarch64-linux-musl-gcc i486-linux-musl-gcc s390x-linux-musl-gcc)
OS_ARCHES=(linux-amd64 linux-arm64 linux-386 linux-s390x linux-riscv64 linux-ppc64le)
CGO_ARGS=(x86_64-linux-musl-gcc aarch64-linux-musl-gcc i486-linux-musl-gcc s390x-linux-musl-gcc riscv64-linux-musl-gcc powerpc64le-linux-musl-gcc)
for i in "${!OS_ARCHES[@]}"; do
os_arch=${OS_ARCHES[$i]}
cgo_cc=${CGO_ARGS[$i]}
@@ -233,6 +233,29 @@ BuildReleaseAndroid() {
done
}
BuildReleaseFreeBSD() {
rm -rf .git/
mkdir -p "build/freebsd"
OS_ARCHES=(amd64 arm64 i386)
GO_ARCHES=(amd64 arm64 386)
CGO_ARGS=(x86_64-unknown-freebsd14.1 aarch64-unknown-freebsd14.1 i386-unknown-freebsd14.1)
for i in "${!OS_ARCHES[@]}"; do
os_arch=${OS_ARCHES[$i]}
cgo_cc="clang --target=${CGO_ARGS[$i]} --sysroot=/opt/freebsd/${os_arch}"
echo building for freebsd-${os_arch}
sudo mkdir -p "/opt/freebsd/${os_arch}"
wget -q https://download.freebsd.org/releases/${os_arch}/14.1-RELEASE/base.txz
sudo tar -xf ./base.txz -C /opt/freebsd/${os_arch}
rm base.txz
export GOOS=freebsd
export GOARCH=${GO_ARCHES[$i]}
export CC=${cgo_cc}
export CGO_ENABLED=1
export CGO_LDFLAGS="-fuse-ld=lld"
go build -o ./build/$appName-freebsd-$os_arch -ldflags="$ldflags" -tags=jsoniter .
done
}
MakeRelease() {
cd build
mkdir compress
@@ -251,6 +274,11 @@ MakeRelease() {
tar -czvf compress/"$i".tar.gz alist
rm -f alist
done
for i in $(find . -type f -name "$appName-freebsd-*"); do
cp "$i" alist
tar -czvf compress/"$i".tar.gz alist
rm -f alist
done
for i in $(find . -type f -name "$appName-windows-*"); do
cp "$i" alist.exe
zip compress/$(echo $i | sed 's/\.[^.]*$//').zip alist.exe
@@ -288,6 +316,9 @@ elif [ "$1" = "release" ]; then
elif [ "$2" = "android" ]; then
BuildReleaseAndroid
MakeRelease "md5-android.txt"
elif [ "$2" = "freebsd" ]; then
BuildReleaseFreeBSD
MakeRelease "md5-freebsd.txt"
elif [ "$2" = "web" ]; then
echo "web only"
else
+9 -3
View File
@@ -2,6 +2,7 @@ package _115
import (
"context"
"fmt"
"strings"
"sync"
@@ -121,7 +122,10 @@ func (d *Pan115) Put(ctx context.Context, dstDir model.Obj, stream model.FileStr
if err := d.WaitLimit(ctx); err != nil {
return err
}
if stream.GetSize() > utils.GB*20 { // TODO 由于官方分片上传接口失效,所以使用普通上传小于20GB的文件
return fmt.Errorf("unsupported file size: 20GB limit exceeded")
}
// 分片上传
var (
fastInfo *driver115.UploadInitResp
dirID = dstDir.GetID()
@@ -177,11 +181,13 @@ func (d *Pan115) Put(ctx context.Context, dstDir model.Obj, stream model.FileStr
}
// 闪传失败,上传
if stream.GetSize() <= utils.KB { // 文件大小小于1KB,改用普通模式上传
// if stream.GetSize() <= utils.KB{ // 文件大小小于1KB,改用普通模式上传
if stream.GetSize() <= utils.GB*20 { // TODO 由于官方分片上传接口失效,所以使用普通上传小于20GB的文件
return d.client.UploadByOSS(&fastInfo.UploadOSSParams, stream, dirID)
}
return driver115.ErrUnexpected
// 分片上传
return d.UploadByMultipart(&fastInfo.UploadOSSParams, stream.GetSize(), stream, dirID)
// return d.UploadByMultipart(&fastInfo.UploadOSSParams, stream.GetSize(), stream, dirID)
}
func (d *Pan115) OfflineList(ctx context.Context) ([]*driver115.OfflineTask, error) {
+1 -1
View File
@@ -9,7 +9,7 @@ type Addition struct {
Cookie string `json:"cookie" type:"text" help:"one of QR code token and cookie required"`
QRCodeToken string `json:"qrcode_token" type:"text" help:"one of QR code token and cookie required"`
QRCodeSource string `json:"qrcode_source" type:"select" options:"web,android,ios,tv,alipaymini,wechatmini,qandroid" default:"linux" help:"select the QR code device, default linux"`
PageSize int64 `json:"page_size" type:"number" default:"56" help:"list api per page size of 115 driver"`
PageSize int64 `json:"page_size" type:"number" default:"1000" help:"list api per page size of 115 driver"`
LimitRate float64 `json:"limit_rate" type:"number" default:"2" help:"limit all api request rate (1r/[limit_rate]s)"`
driver.RootID
}
+17 -1
View File
@@ -1,10 +1,11 @@
package _115
import (
"time"
"github.com/SheltonZhu/115driver/pkg/driver"
"github.com/alist-org/alist/v3/internal/model"
"github.com/alist-org/alist/v3/pkg/utils"
"time"
)
var _ model.Obj = (*FileObj)(nil)
@@ -20,3 +21,18 @@ func (f *FileObj) CreateTime() time.Time {
func (f *FileObj) GetHash() utils.HashInfo {
return utils.NewHashInfo(utils.SHA1, f.Sha1)
}
type UploadResult struct {
driver.BasicResp
Data struct {
PickCode string `json:"pick_code"`
FileSize int `json:"file_size"`
FileID string `json:"file_id"`
ThumbURL string `json:"thumb_url"`
Sha1 string `json:"sha1"`
Aid int `json:"aid"`
FileName string `json:"file_name"`
Cid string `json:"cid"`
IsVideo int `json:"is_video"`
} `json:"data"`
}
+19 -32
View File
@@ -10,7 +10,6 @@ import (
"io"
"net/http"
"net/url"
"path/filepath"
"strconv"
"strings"
"sync"
@@ -254,6 +253,7 @@ func (d *Pan115) UploadByMultipart(params *driver115.UploadOSSParams, fileSize i
ossClient *oss.Client
bucket *oss.Bucket
ossToken *driver115.UploadOSSTokenResp
bodyBytes []byte
err error
)
@@ -268,12 +268,14 @@ func (d *Pan115) UploadByMultipart(params *driver115.UploadOSSParams, fileSize i
f(options)
}
}
// oss 启用Sequential必须按顺序上传
options.ThreadsNum = 1
if ossToken, err = d.client.GetOSSToken(); err != nil {
return err
}
if ossClient, err = oss.New(driver115.OSSEndpoint, ossToken.AccessKeyID, ossToken.AccessKeySecret); err != nil {
if ossClient, err = oss.New(driver115.OSSEndpoint, ossToken.AccessKeyID, ossToken.AccessKeySecret, oss.EnableMD5(true), oss.EnableCRC(true)); err != nil {
return err
}
@@ -294,6 +296,7 @@ func (d *Pan115) UploadByMultipart(params *driver115.UploadOSSParams, fileSize i
if imur, err = bucket.InitiateMultipartUpload(params.Object,
oss.SetHeader(driver115.OssSecurityTokenHeaderName, ossToken.SecurityToken),
oss.UserAgentHeader(driver115.OSSUserAgent),
oss.EnableSha1(), oss.Sequential(),
); err != nil {
return err
}
@@ -337,8 +340,7 @@ func (d *Pan115) UploadByMultipart(params *driver115.UploadOSSParams, fileSize i
continue
}
b := bytes.NewBuffer(buf)
if part, err = bucket.UploadPart(imur, b, chunk.Size, chunk.Number, driver115.OssOption(params, ossToken)...); err == nil {
if part, err = bucket.UploadPart(imur, bytes.NewBuffer(buf), chunk.Size, chunk.Number, driver115.OssOption(params, ossToken)...); err == nil {
break
}
}
@@ -373,14 +375,20 @@ LOOP:
}
}
// EOF错误是xml的Unmarshal导致的,响应其实是json格式,所以实际上上传是成功的
if _, err = bucket.CompleteMultipartUpload(imur, parts, driver115.OssOption(params, ossToken)...); err != nil && !errors.Is(err, io.EOF) {
// 当文件名含有 &< 这两个字符之一时响应的xml解析会出现错误,实际上上传是成功的
if filename := filepath.Base(stream.GetName()); !strings.ContainsAny(filename, "&<") {
return err
}
// 不知道啥原因,oss那边分片上传不计算sha1,导致115服务器校验错误
// params.Callback.Callback = strings.ReplaceAll(params.Callback.Callback, "${sha1}", params.SHA1)
if _, err := bucket.CompleteMultipartUpload(imur, parts, append(
driver115.OssOption(params, ossToken),
oss.CallbackResult(&bodyBytes),
)...); err != nil {
return err
}
return d.checkUploadStatus(dirID, params.SHA1)
var uploadResult UploadResult
if err = json.Unmarshal(bodyBytes, &uploadResult); err != nil {
return err
}
return uploadResult.Err(string(bodyBytes))
}
func chunksProducer(ch chan oss.FileChunk, chunks []oss.FileChunk) {
@@ -389,27 +397,6 @@ func chunksProducer(ch chan oss.FileChunk, chunks []oss.FileChunk) {
}
}
func (d *Pan115) checkUploadStatus(dirID, sha1 string) error {
// 验证上传是否成功
req := d.client.NewRequest().ForceContentType("application/json;charset=UTF-8")
opts := []driver115.GetFileOptions{
driver115.WithOrder(driver115.FileOrderByTime),
driver115.WithShowDirEnable(false),
driver115.WithAsc(false),
driver115.WithLimit(500),
}
fResp, err := driver115.GetFiles(req, dirID, opts...)
if err != nil {
return err
}
for _, fileInfo := range fResp.Files {
if fileInfo.Sha1 == sha1 {
return nil
}
}
return driver115.ErrUploadFailed
}
func SplitFile(fileSize int64) (chunks []oss.FileChunk, err error) {
for i := int64(1); i < 10; i++ {
if fileSize < i*utils.GB { // 文件大小小于iGB时分为i*1000片
+1 -1
View File
@@ -9,7 +9,7 @@ type Addition struct {
Cookie string `json:"cookie" type:"text" help:"one of QR code token and cookie required"`
QRCodeToken string `json:"qrcode_token" type:"text" help:"one of QR code token and cookie required"`
QRCodeSource string `json:"qrcode_source" type:"select" options:"web,android,ios,tv,alipaymini,wechatmini,qandroid" default:"linux" help:"select the QR code device, default linux"`
PageSize int64 `json:"page_size" type:"number" default:"20" help:"list api per page size of 115 driver"`
PageSize int64 `json:"page_size" type:"number" default:"1000" help:"list api per page size of 115 driver"`
LimitRate float64 `json:"limit_rate" type:"number" default:"2" help:"limit all api request rate (1r/[limit_rate]s)"`
ShareCode string `json:"share_code" type:"text" required:"true" help:"share code of 115 share link"`
ReceiveCode string `json:"receive_code" type:"text" required:"true" help:"receive code of 115 share link"`
+1
View File
@@ -82,6 +82,7 @@ func (d *Pan123) Link(ctx context.Context, file model.Obj, args model.LinkArgs)
"type": f.Type,
}
resp, err := d.request(DownloadInfo, http.MethodPost, func(req *resty.Request) {
req.SetBody(data).SetHeaders(headers)
}, nil)
if err != nil {
+2 -1
View File
@@ -26,8 +26,9 @@ const (
Api = "https://www.123pan.com/api"
AApi = "https://www.123pan.com/a/api"
BApi = "https://www.123pan.com/b/api"
LoginApi = "https://login.123pan.com/api"
MainApi = BApi
SignIn = MainApi + "/user/sign_in"
SignIn = LoginApi + "/user/sign_in"
Logout = MainApi + "/user/logout"
UserInfo = MainApi + "/user/info"
FileList = MainApi + "/file/list/new"
+1
View File
@@ -22,6 +22,7 @@ import (
_ "github.com/alist-org/alist/v3/drivers/cloudreve"
_ "github.com/alist-org/alist/v3/drivers/crypt"
_ "github.com/alist-org/alist/v3/drivers/dropbox"
_ "github.com/alist-org/alist/v3/drivers/febbox"
_ "github.com/alist-org/alist/v3/drivers/ftp"
_ "github.com/alist-org/alist/v3/drivers/google_drive"
_ "github.com/alist-org/alist/v3/drivers/google_photo"
+3 -2
View File
@@ -4,6 +4,7 @@ import (
"context"
"io"
"net/http"
"path"
"strconv"
"strings"
@@ -90,7 +91,7 @@ func (d *Cloudreve) MakeDir(ctx context.Context, parentDir model.Obj, dirName st
func (d *Cloudreve) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
body := base.Json{
"action": "move",
"src_dir": srcObj.GetPath(),
"src_dir": path.Dir(srcObj.GetPath()),
"dst": dstDir.GetPath(),
"src": convertSrc(srcObj),
}
@@ -112,7 +113,7 @@ func (d *Cloudreve) Rename(ctx context.Context, srcObj model.Obj, newName string
func (d *Cloudreve) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
body := base.Json{
"src_dir": srcObj.GetPath(),
"src_dir": path.Dir(srcObj.GetPath()),
"dst": dstDir.GetPath(),
"src": convertSrc(srcObj),
}
+132
View File
@@ -0,0 +1,132 @@
package febbox
import (
"context"
"github.com/alist-org/alist/v3/internal/op"
"github.com/alist-org/alist/v3/pkg/utils"
"golang.org/x/oauth2"
"golang.org/x/oauth2/clientcredentials"
"github.com/alist-org/alist/v3/internal/driver"
"github.com/alist-org/alist/v3/internal/errs"
"github.com/alist-org/alist/v3/internal/model"
)
type FebBox struct {
model.Storage
Addition
accessToken string
oauth2Token oauth2.TokenSource
}
func (d *FebBox) Config() driver.Config {
return config
}
func (d *FebBox) GetAddition() driver.Additional {
return &d.Addition
}
func (d *FebBox) Init(ctx context.Context) error {
// 初始化 oauth2Config
oauth2Config := &clientcredentials.Config{
ClientID: d.ClientID,
ClientSecret: d.ClientSecret,
AuthStyle: oauth2.AuthStyleInParams,
TokenURL: "https://api.febbox.com/oauth/token",
}
d.initializeOAuth2Token(ctx, oauth2Config, d.Addition.RefreshToken)
token, err := d.oauth2Token.Token()
if err != nil {
return err
}
d.accessToken = token.AccessToken
d.Addition.RefreshToken = token.RefreshToken
op.MustSaveDriverStorage(d)
return nil
}
func (d *FebBox) Drop(ctx context.Context) error {
return nil
}
func (d *FebBox) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
files, err := d.getFilesList(dir.GetID())
if err != nil {
return nil, err
}
return utils.SliceConvert(files, func(src File) (model.Obj, error) {
return fileToObj(src), nil
})
}
func (d *FebBox) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
var ip string
if d.Addition.UserIP != "" {
ip = d.Addition.UserIP
} else {
ip = args.IP
}
url, err := d.getDownloadLink(file.GetID(), ip)
if err != nil {
return nil, err
}
return &model.Link{
URL: url,
}, nil
}
func (d *FebBox) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) (model.Obj, error) {
err := d.makeDir(parentDir.GetID(), dirName)
if err != nil {
return nil, err
}
return nil, nil
}
func (d *FebBox) Move(ctx context.Context, srcObj, dstDir model.Obj) (model.Obj, error) {
err := d.move(srcObj.GetID(), dstDir.GetID())
if err != nil {
return nil, err
}
return nil, nil
}
func (d *FebBox) Rename(ctx context.Context, srcObj model.Obj, newName string) (model.Obj, error) {
err := d.rename(srcObj.GetID(), newName)
if err != nil {
return nil, err
}
return nil, nil
}
func (d *FebBox) Copy(ctx context.Context, srcObj, dstDir model.Obj) (model.Obj, error) {
err := d.copy(srcObj.GetID(), dstDir.GetID())
if err != nil {
return nil, err
}
return nil, nil
}
func (d *FebBox) Remove(ctx context.Context, obj model.Obj) error {
err := d.remove(obj.GetID())
if err != nil {
return err
}
return nil
}
func (d *FebBox) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) (model.Obj, error) {
return nil, errs.NotImplement
}
var _ driver.Driver = (*FebBox)(nil)
+36
View File
@@ -0,0 +1,36 @@
package febbox
import (
"github.com/alist-org/alist/v3/internal/driver"
"github.com/alist-org/alist/v3/internal/op"
)
type Addition struct {
driver.RootID
ClientID string `json:"client_id" required:"true" default:""`
ClientSecret string `json:"client_secret" required:"true" default:""`
RefreshToken string
SortRule string `json:"sort_rule" required:"true" type:"select" options:"size_asc,size_desc,name_asc,name_desc,update_asc,update_desc,ext_asc,ext_desc" default:"name_asc"`
PageSize int64 `json:"page_size" required:"true" type:"number" default:"100" help:"list api per page size of FebBox driver"`
UserIP string `json:"user_ip" default:"" help:"user ip address for download link which can speed up the download"`
}
var config = driver.Config{
Name: "FebBox",
LocalSort: false,
OnlyLocal: false,
OnlyProxy: false,
NoCache: false,
NoUpload: true,
NeedMs: false,
DefaultRoot: "0",
CheckStatus: false,
Alert: "",
NoOverwriteUpload: false,
}
func init() {
op.RegisterDriver(func() driver.Driver {
return &FebBox{}
})
}
+88
View File
@@ -0,0 +1,88 @@
package febbox
import (
"context"
"encoding/json"
"errors"
"net/http"
"net/url"
"strings"
"time"
"golang.org/x/oauth2"
"golang.org/x/oauth2/clientcredentials"
)
type customTokenSource struct {
config *clientcredentials.Config
ctx context.Context
refreshToken string
}
func (c *customTokenSource) Token() (*oauth2.Token, error) {
v := url.Values{}
if c.refreshToken != "" {
v.Set("grant_type", "refresh_token")
v.Set("refresh_token", c.refreshToken)
} else {
v.Set("grant_type", "client_credentials")
}
v.Set("client_id", c.config.ClientID)
v.Set("client_secret", c.config.ClientSecret)
req, err := http.NewRequest("POST", c.config.TokenURL, strings.NewReader(v.Encode()))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
resp, err := http.DefaultClient.Do(req.WithContext(c.ctx))
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, errors.New("oauth2: cannot fetch token")
}
var tokenResp struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data struct {
AccessToken string `json:"access_token"`
ExpiresIn int64 `json:"expires_in"`
TokenType string `json:"token_type"`
Scope string `json:"scope"`
RefreshToken string `json:"refresh_token"`
} `json:"data"`
}
if err := json.NewDecoder(resp.Body).Decode(&tokenResp); err != nil {
return nil, err
}
if tokenResp.Code != 1 {
return nil, errors.New("oauth2: server response error")
}
c.refreshToken = tokenResp.Data.RefreshToken
token := &oauth2.Token{
AccessToken: tokenResp.Data.AccessToken,
TokenType: tokenResp.Data.TokenType,
RefreshToken: tokenResp.Data.RefreshToken,
Expiry: time.Now().Add(time.Duration(tokenResp.Data.ExpiresIn) * time.Second),
}
return token, nil
}
func (d *FebBox) initializeOAuth2Token(ctx context.Context, oauth2Config *clientcredentials.Config, refreshToken string) {
d.oauth2Token = oauth2.ReuseTokenSource(nil, &customTokenSource{
config: oauth2Config,
ctx: ctx,
refreshToken: refreshToken,
})
}
+123
View File
@@ -0,0 +1,123 @@
package febbox
import (
"fmt"
"github.com/alist-org/alist/v3/internal/model"
"github.com/alist-org/alist/v3/pkg/utils"
hash_extend "github.com/alist-org/alist/v3/pkg/utils/hash"
"strconv"
"time"
)
type ErrResp struct {
ErrorCode int64 `json:"code"`
ErrorMsg string `json:"msg"`
ServerRunTime float64 `json:"server_runtime"`
ServerName string `json:"server_name"`
}
func (e *ErrResp) IsError() bool {
return e.ErrorCode != 0 || e.ErrorMsg != "" || e.ServerRunTime != 0 || e.ServerName != ""
}
func (e *ErrResp) Error() string {
return fmt.Sprintf("ErrorCode: %d ,Error: %s ,ServerRunTime: %f ,ServerName: %s", e.ErrorCode, e.ErrorMsg, e.ServerRunTime, e.ServerName)
}
type FileListResp struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data struct {
FileList []File `json:"file_list"`
ShowType string `json:"show_type"`
} `json:"data"`
}
type Rules struct {
AllowCopy int64 `json:"allow_copy"`
AllowDelete int64 `json:"allow_delete"`
AllowDownload int64 `json:"allow_download"`
AllowComment int64 `json:"allow_comment"`
HideLocation int64 `json:"hide_location"`
}
type File struct {
Fid int64 `json:"fid"`
UID int64 `json:"uid"`
FileSize int64 `json:"file_size"`
Path string `json:"path"`
FileName string `json:"file_name"`
Ext string `json:"ext"`
AddTime int64 `json:"add_time"`
FileCreateTime int64 `json:"file_create_time"`
FileUpdateTime int64 `json:"file_update_time"`
ParentID int64 `json:"parent_id"`
UpdateTime int64 `json:"update_time"`
LastOpenTime int64 `json:"last_open_time"`
IsDir int64 `json:"is_dir"`
Epub int64 `json:"epub"`
IsMusicList int64 `json:"is_music_list"`
OssFid int64 `json:"oss_fid"`
Faststart int64 `json:"faststart"`
HasVideoQuality int64 `json:"has_video_quality"`
TotalDownload int64 `json:"total_download"`
Status int64 `json:"status"`
Remark string `json:"remark"`
OldHash string `json:"old_hash"`
Hash string `json:"hash"`
HashType string `json:"hash_type"`
FromUID int64 `json:"from_uid"`
FidOrg int64 `json:"fid_org"`
ShareID int64 `json:"share_id"`
InvitePermission int64 `json:"invite_permission"`
ThumbSmall string `json:"thumb_small"`
ThumbSmallWidth int64 `json:"thumb_small_width"`
ThumbSmallHeight int64 `json:"thumb_small_height"`
Thumb string `json:"thumb"`
ThumbWidth int64 `json:"thumb_width"`
ThumbHeight int64 `json:"thumb_height"`
ThumbBig string `json:"thumb_big"`
ThumbBigWidth int64 `json:"thumb_big_width"`
ThumbBigHeight int64 `json:"thumb_big_height"`
IsCustomThumb int64 `json:"is_custom_thumb"`
Photos int64 `json:"photos"`
IsAlbum int64 `json:"is_album"`
ReadOnly int64 `json:"read_only"`
Rules Rules `json:"rules"`
IsShared int64 `json:"is_shared"`
}
func fileToObj(f File) *model.ObjThumb {
return &model.ObjThumb{
Object: model.Object{
ID: strconv.FormatInt(f.Fid, 10),
Name: f.FileName,
Size: f.FileSize,
Ctime: time.Unix(f.FileCreateTime, 0),
Modified: time.Unix(f.FileUpdateTime, 0),
IsFolder: f.IsDir == 1,
HashInfo: utils.NewHashInfo(hash_extend.GCID, f.Hash),
},
Thumbnail: model.Thumbnail{
Thumbnail: f.Thumb,
},
}
}
type FileDownloadResp struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data []struct {
Error int `json:"error"`
DownloadURL string `json:"download_url"`
Hash string `json:"hash"`
HashType string `json:"hash_type"`
Fid int `json:"fid"`
FileName string `json:"file_name"`
ParentID int `json:"parent_id"`
FileSize int `json:"file_size"`
Ext string `json:"ext"`
Thumb string `json:"thumb"`
VipLink int `json:"vip_link"`
} `json:"data"`
}
File diff suppressed because it is too large Load Diff
+6 -3
View File
@@ -66,12 +66,13 @@ func (d *ILanZou) Drop(ctx context.Context) error {
}
func (d *ILanZou) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
offset := 1
var res []ListItem
for {
var resp ListResp
_, err := d.proved("/record/file/list", http.MethodGet, func(req *resty.Request) {
params := []string{
"offset=1",
"offset=" + strconv.Itoa(offset),
"limit=60",
"folderId=" + dir.GetID(),
"type=0",
@@ -83,7 +84,9 @@ func (d *ILanZou) List(ctx context.Context, dir model.Obj, args model.ListArgs)
return nil, err
}
res = append(res, resp.List...)
if resp.TotalPage <= resp.Offset {
if resp.Offset < resp.TotalPage {
offset++
} else {
break
}
}
@@ -286,7 +289,7 @@ func (d *ILanZou) Put(ctx context.Context, dstDir model.Obj, stream model.FileSt
req.SetBody(base.Json{
"fileId": "",
"fileName": stream.GetName(),
"fileSize": stream.GetSize() / 1024,
"fileSize": stream.GetSize()/1024 + 1,
"folderId": dstDir.GetID(),
"md5": etag,
"type": 1,

Some files were not shown because too many files have changed in this diff Show More