Compare commits

...

107 Commits

Author SHA1 Message Date
4a0e47dbac fmt: go mod tidy 2023-01-05 19:34:18 +08:00
510d266da8 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.173 [skip ci] (#2832)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-05 19:32:58 +08:00
35dfb36884 chore(deps): update module gorm.io/driver/mysql to v1.4.5 [skip ci] (#2881)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-05 19:31:47 +08:00
b88f4d2ba6 chore(deps): update module gorm.io/driver/sqlite to v1.4.4 [skip ci] (#2869)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-05 19:31:28 +08:00
50318da879 chore(deps): update module gorm.io/driver/postgres to v1.4.6 [skip ci] (#2867)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-05 19:18:42 +08:00
575487a0e2 chore(deps): update module gorm.io/gorm to v1.24.3 [skip ci] (#2870)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-05 19:18:15 +08:00
69d3ccaed2 chore(deps): update module golang.org/x/net to v0.5.0 [skip ci] (#2908)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-05 19:17:41 +08:00
170859a112 chore(deps): update module golang.org/x/crypto to v0.5.0 [skip ci] (#2905)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-05 19:16:56 +08:00
7fdcb106a5 chore(deps): update module golang.org/x/image to v0.3.0 [skip ci] (#2906)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-05 17:49:45 +08:00
14d4ddb752 fix(mysql): change mysql against mode (close #2903 close #2844 pr #2904) 2023-01-05 17:11:58 +08:00
428e59a844 fix(uss): close of closed channel (close #2847 #2896)
* fix(uss): close of closed channel

* fix(uss): close of closed channel

Co-authored-by: zxdstyle <xiangdong.zhu@maitang001.com>
2023-01-04 21:43:47 +08:00
1c8d895fc0 feat(terabox): add terabox driver (close #2825 close #2678 #2849) 2022-12-31 16:44:20 +08:00
fbf3fb825b fix(baidu_netdisk): file copy and file upload [skip ci] (#2848) 2022-12-31 16:43:22 +08:00
16e07ae016 fix(s3): set default root path (close #2834) 2022-12-30 14:53:01 +08:00
d1b9db38c7 feat(docker): add docker-compose file (close #2067) 2022-12-30 14:25:22 +08:00
395f0fc5f3 fix(docker): use root user as default 2022-12-30 14:21:39 +08:00
143e4cd077 fix: mysql FULLTEXT search (#2840) 2022-12-30 14:20:04 +08:00
f777a2fab4 fix: version doesn't update 2022-12-30 01:24:37 +08:00
dad3012ec3 fix(deps): update module github.com/aws/aws-sdk-go to v1.44.169 (#2816)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-29 21:22:50 +08:00
d45209edb2 fix: /entrypoint.sh permission denied 2022-12-29 17:16:30 +08:00
e89489453d fix: cache nil value for meta 2022-12-28 17:44:34 +08:00
ed6c8194a7 feat: add PUID, PGID, Umask settings to docker image (close #2525 pr #2818)
Co-authored-by: DDSRem <1448139087@qq.com>
2022-12-28 17:18:27 +08:00
83fe17c6ec feat: support github login (#2639)
* Support Github Login

* improve according to codefactor

* fix due to last updates

* optimization

Co-authored-by: Noah Hsu <i@nn.ci>
2022-12-27 22:11:22 +08:00
c00dcc8f39 fix(deps): update module github.com/gin-gonic/gin to v1.8.2 (#2785)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-25 18:20:24 +08:00
e118f4a3b9 feat: update index by req.Paths 2022-12-24 20:23:04 +08:00
5e28d0f96a fix(deps): update module github.com/aws/aws-sdk-go to v1.44.167 (#2781)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-24 16:14:20 +08:00
3af23f6792 feat: batch reload all storages (close #2762 pr #2775) 2022-12-21 19:21:18 +08:00
3a41b929c9 fix: pgsql search [skip ci] (close #2761 pr #2774) 2022-12-21 19:19:37 +08:00
105f22969c feat: support cancel for some drivers (close #2717) 2022-12-21 15:03:09 +08:00
e4a88a7c13 fix(deps): update module github.com/aws/aws-sdk-go to v1.44.164 (#2773)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-21 12:04:32 +08:00
b0255040c6 chore: fix typo 2022-12-20 20:07:19 +08:00
f1e842e12a feat: customize settings layout (close #2765) 2022-12-20 20:04:37 +08:00
d756cf3e9f fix(local): disable copying or moving to subfolders (close #2760) 2022-12-20 16:27:04 +08:00
146619134d feat: customize proxy ignore headers (close #2763 pr #2766)
* clean referer when use proxy

* feat: customize proxy ignore headers

Co-authored-by: Noah Hsu <i@nn.ci>
2022-12-20 16:08:32 +08:00
372030071e fix(deps): update module github.com/aws/aws-sdk-go to v1.44.163 [skip ci] (#2738)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-20 15:13:14 +08:00
62a06fa0f9 feat: optimize file operation interface (#2757)
* feat: optimize file operation interface

* chore: fix typo

Co-authored-by: Noah Hsu <i@nn.ci>
2022-12-20 15:02:40 +08:00
e2bcca2fbd feat: static files for embed viewers (#2739) 2022-12-19 13:34:06 +08:00
4568af9542 feat: better static file Cache-Control (#2751) 2022-12-19 13:32:00 +08:00
b50d486a63 fix: sub path check if subPath = / 2022-12-18 21:28:38 +08:00
0ae3fc608b feat: export all cmd (#2746) 2022-12-18 19:53:39 +08:00
6024e8d832 refactor: split the db package hook and cache to the op package (#2747)
* refactor:separate the setting method from the db package to the op package and add the cache

* refactor:separate the meta method from the db package to the op package

* fix:setting not load database data

* refactor:separate the user method from the db package to the op package

* refactor:remove user JoinPath error

* fix:op package user cache

* refactor:fs package list method

* fix:tile virtual paths (close #2743)

* Revert "refactor:remove user JoinPath error"

This reverts commit 4e20daaf9e700da047000d4fd4900abbe05c3848.

* clean path directly may lead to unknown behavior

* fix: The path of the meta passed in must be prefix of reqPath

* chore: rename all virtualPath to mountPath

* fix: `getStoragesByPath` and `GetStorageVirtualFilesByPath`

is_sub_path:

/a/b isn't subpath of /a/bc

* fix: don't save setting if hook error

Co-authored-by: Noah Hsu <i@nn.ci>
2022-12-18 19:51:20 +08:00
f38f4f401b fix(139): modify chunk size to avoid large file upload failure (close #2744 close #2682 pr #2745) 2022-12-18 17:48:09 +08:00
3b2ae85009 chore: only ignore root dirs (#2741) 2022-12-18 16:48:32 +08:00
faf4150d1e docs: fix badges on README.md and README_cn.md [skip ci] (#2749) 2022-12-18 16:48:03 +08:00
fb64f00640 refactor: obj name mapping and internal path processing (#2733)
* refactor:Prepare to remove the get interface

* feat:add obj Unwarp interface

* refactor:obj name mapping and program internal path processing

* chore: fix typo

* feat: unwrap get

* fix: no use op.Get to get parent id

* fix: set the path uniformly

Co-authored-by: Noah Hsu <i@nn.ci>
2022-12-17 19:49:05 +08:00
3d336b328a feat: add pikpak share driver (close #2728 pr #2731) 2022-12-16 19:10:19 +08:00
f9cf29e0b6 fix(deps): update module golang.org/x/crypto to v0.4.0 (#2638) [skip ci]
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-16 19:08:52 +08:00
cbd038f30f fix(deps): update module golang.org/x/net to v0.4.0 (#2608) [skip ci]
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-16 19:05:20 +08:00
2aeb75a779 fix(deps): update module github.com/blevesearch/bleve/v2 to v2.3.6 (#2727) [skip ci]
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-16 19:05:06 +08:00
2f8eaf6bea fix(deps): update module github.com/pquerna/otp to v1.4.0 (#2708) [skip ci]
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-16 18:15:59 +08:00
fb7a5dec1b fix(deps): update module golang.org/x/image to v0.2.0 (#2601) [skip ci]
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-16 18:15:19 +08:00
e61bac039a fix(deps): update module github.com/aws/aws-sdk-go to v1.44.161 (#2595) [skip ci]
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-16 18:14:56 +08:00
b3be9ef428 feat(search): use FULLTEXT index (close #2716 pr #2726) 2022-12-16 16:51:36 +08:00
5a6b600ace feat: show gorm log on debug/dev mode (#2720) 2022-12-15 17:48:52 +08:00
e58ca686e3 feat: cache static files (#2715) 2022-12-15 17:48:29 +08:00
6f4b1ba4b3 feat: log to stdout & file (#2709) 2022-12-14 13:19:08 +08:00
cdc45630ae fix: whereInParent when parent = "/" (#2706) 2022-12-14 10:37:09 +08:00
7947ff1ae4 feat: limit max connection count (#2701) 2022-12-14 10:33:58 +08:00
33bae52fa1 refactor: optimize driver initialization need to manually deserialize and assign values, and remove redundant driver registration parameters (#2691)
* refactor: optimize driver initialization need to manually deserialize and assign values, and remove redundant driver registration parameters

* fix typo

Co-authored-by: Noah Hsu <i@nn.ci>
2022-12-13 18:03:30 +08:00
3ee45c69a7 fix(baidu_netdisk): encode path for create (close #2690) 2022-12-13 17:57:41 +08:00
179d285564 feat: optimize database search (#2687)
* feat: remove index on `SearchNode.Name`

As we do not use s% on name column, index there does not work

* fix: init index after init data

Or on the first run, it will log 'init index error: readObjectStart: expect { or n, but found , error found in #0 byte of ...||..., bigger context ...||...'

* fix: match parent more precisely

It will match `/a/bc` if we search in `/a/b` originally.
But it is not backward compatible by adding a suffix `/`
to all the data in parent field
2022-12-12 20:20:01 +08:00
a2e8e96c71 feat: respond static file on loading storages (#2686) 2022-12-12 20:17:58 +08:00
5043815d48 fix(search): don't delete virtual folder while update indexes (close #2677) 2022-12-11 14:59:58 +08:00
1640f06e13 feat(search): multiple keywords split by space (#2669) 2022-12-10 19:28:34 +08:00
62ea93837c feat: alist v3 index permission (#2653)
* feat: alist v3 index permission

* fix allowIndexed check

Co-authored-by: Noah Hsu <i@nn.ci>
2022-12-10 19:03:09 +08:00
446f82888c fix(local): add sign to thumbnail (close #2536 close #2650) 2022-12-09 10:08:31 +08:00
6f1aeb47fd feat: index enhancement (close #2632 pr #2636)
* feat: index paths as setting

* feat: clear index (#2632)

* feat: check indexMQ more frequently
2022-12-09 10:02:13 +08:00
1f7c1b4f43 fix(cors): allow all methods (close #2640) 2022-12-08 11:35:21 +08:00
3fa0217c4b feat(alist-v3): support write (close #2626 pr #2635) 2022-12-07 19:02:28 +08:00
2dd30f2b77 feat(search): support with password 2022-12-07 10:45:02 +08:00
6e23c8b4c0 feat: partial update index (close #2593 close #2621 pr #2624) 2022-12-07 10:41:52 +08:00
72aa63adce fix: skip virtual driver on building index (close #2604 pr #2617) 2022-12-06 20:43:32 +08:00
e65e8be59e fix(search): missed base_path of user for parent (close #2611) 2022-12-06 17:28:39 +08:00
7aa4dfb240 feat: use natural sort in SortFiles (#2612) 2022-12-06 17:28:18 +08:00
bd324233a0 fix: can't paste image while report bug (#2597) [skip ci] 2022-12-06 09:19:49 +08:00
f1a9b68022 fix(index): update indexes in database 2022-12-05 20:23:37 +08:00
dda1da4576 fix(index): nil pointer call 2022-12-05 20:22:35 +08:00
5b7aa9c1cf feat: allow all cors headers (close #2571) 2022-12-05 20:05:20 +08:00
a28aaceaad chore(ci): only build on main branch 2022-12-05 19:52:02 +08:00
2bb200af87 fix(deps): update modules by renovate[bot]
fix(deps): update module github.com/sheltonzhu/115driver to v1.0.13 (#2413) [skip ci]

fix(deps): update module github.com/golang-jwt/jwt/v4 to v4.4.3 (#2526) [skip ci]

fix(deps): update module golang.org/x/image to v0.1.0 (#2587) [skip ci]

chore: go mod tidy
Co-Authored-By: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-05 19:50:49 +08:00
97f1efbb72 feat!: disable --force-bin-dir if --data is abs
related issues: #2580 #2542

after this commit, the `--force-bin-dir` would take no effect if `--data` is absolute path
2022-12-05 18:32:48 +08:00
bf8b6f4c2c feat: customize ignore paths of indexes 2022-12-05 16:45:11 +08:00
bd33c200dc feat: optimize index build 2022-12-05 16:07:36 +08:00
bc6baf1be0 fix(ci): sort lang json file 2022-12-05 14:40:46 +08:00
dc8d5106f9 feat: auto fix address in alist & smb storages (#2582) 2022-12-05 13:31:34 +08:00
8c0dfe2f3d feat: Search enhancement (#2562)
* feat: ignore AList storage on indexing

* fix: remove unused err in `walkFn`

* chore(ci): fix auto_lang trigger and run it

* feat: batch index

* feat: quit index & init index

* feat: set DocType for bleve data

* fix: build index cleanup check origin err
2022-12-05 13:28:39 +08:00
4e1be9bee6 fix: async init aria2 to optimize start duration 2022-12-04 00:00:40 +08:00
4c5285e094 chore(ci): format lang file (#2558) 2022-12-03 12:19:10 +08:00
0838feeb82 fix:introduce buffered response writer for webdav, fix status/error return failed. (#2544)
* fix: introduce buffered response writer for webdav, fix webdav status/error return failed.

* fix: bypass buffered writer for GET/HEAD/POST requests
2022-12-02 17:59:59 +08:00
ae791c8634 fix: hide check in canAccess (#2556)
修复 meta.Password 和 meta.Hide 都为空的情况下,会导致无权限访问
2022-12-02 17:44:29 +08:00
09f480318c fix: unify settings string (#2555) 2022-12-02 17:42:42 +08:00
4c5be5f07f feat: only show CanAccess search results (#2548)
* feat: only show `CanAccess` search results

* have done in frontend

Co-authored-by: Noah Hsu <i@nn.ci>
2022-12-02 10:09:39 +08:00
9c1ffdbb82 fix(aliyundrive): return error if got wrong http code (#2543) 2022-12-01 21:48:19 +08:00
18a63e34dd fix(task): memory alignment for curID (close #2541) 2022-12-01 13:16:31 +08:00
ff0bcfef8a feat: optional sign all files 2022-11-30 22:10:07 +08:00
4980b71ba3 fix: add hide check to canAccess (close #2532) 2022-11-30 22:01:33 +08:00
b5bf5f4325 fix: check if the req path is relative path (close #2531) 2022-11-30 21:38:00 +08:00
f9788ea7cf feat(webdav): delete privacy header and optimize 302 (#2534)
* fix: delete set-cookie from sharepoint webdav response header

* fix: avoid two redirects when using webdav

* fix: return the correct Content-Type instead of just `application/octet-stream`

* feat: webdav backend localOnly -> proxyOnly
2022-11-30 20:52:33 +08:00
83644dab85 fix: mapping filename in GetName
some missed filename mapping
2022-11-30 20:46:54 +08:00
d94cf72da2 fix(local): webp image decode while generate thumbnail (close #2484 pr #2520)
* Fix that webp thumb  in local storage won't load

* Simplify code

Co-authored-by: Noah Hsu <i@nn.ci>
2022-11-29 09:47:40 +08:00
e98561ceb1 fix: filename char mapping while build index 2022-11-28 21:08:11 +08:00
76f37373e0 fix: settings map read and write concurrently 2022-11-28 16:54:03 +08:00
61a06992c3 fix(aria2): directory missing (close #1856 pr #2504) 2022-11-28 14:05:28 +08:00
ddcba93eea feat: multiple search indexes (#2514)
* refactor: abstract search interface

* wip: ~

* fix cycle import

* objs update hook

* wip: ~

* Delete search/none

* auto update index while cache changed

* db searcher

TODO: bleve init issue

cannot open index, metadata missing

* fix size type

why float64??

* fix typo

* fix nil pointer using

* api adapt ui

* bleve: fix clear & change struct
2022-11-28 13:45:25 +08:00
bb969d8dc6 fix(aliyundrive_share): get share link download url directly (close #2472) 2022-11-24 18:50:04 +08:00
2383e851e2 fix: reset index before build new one (#2471) 2022-11-24 14:47:49 +08:00
330a767fd7 feat: build index & search with bleve (close #1740 pr #2386)
* feat: build index & search with bleve (#1740)

* delete unused struct

Co-authored-by: Noah Hsu <i@nn.ci>
2022-11-24 11:46:47 +08:00
205 changed files with 4842 additions and 1964 deletions

View File

@ -54,4 +54,3 @@ body:
description: |
Please copy and paste any relevant log output.
请复制粘贴错误日志,或者截图
render: shell

View File

@ -7,6 +7,8 @@ on:
paths:
- 'drivers/**'
- 'internal/bootstrap/data/setting.go'
- 'internal/conf/const.go'
- 'cmd/lang.go'
workflow_dispatch:
jobs:
@ -42,6 +44,7 @@ jobs:
cd alist
go run ./main.go lang
cd ..
- name: Copy lang file
run: |
cp -f ./alist/lang/*.json ./alist-web/src/lang/en/ 2>/dev/null || :

View File

@ -2,9 +2,9 @@ name: build
on:
push:
branches: [ '**' ]
branches: [ 'main' ]
pull_request:
branches: [ '**' ]
branches: [ 'main' ]
jobs:
build:

15
.gitignore vendored
View File

@ -20,11 +20,12 @@ output/
# Dependency directories (remove the comment below to include it)
# vendor/
bin/*
/bin/*
*.json
data/
log/
lang/
daemon/
public/dist/*
!public/dist/README.md
/build
/data/
/log/
/lang/
/daemon/
/public/dist/*
/!public/dist/README.md

View File

@ -10,6 +10,9 @@ LABEL MAINTAINER="i@nn.ci"
VOLUME /opt/alist/data/
WORKDIR /opt/alist/
COPY --from=builder /app/bin/alist ./
RUN apk add ca-certificates
COPY entrypoint.sh /entrypoint.sh
RUN apk add ca-certificates bash su-exec; \
chmod +x /entrypoint.sh
ENV PUID=0 PGID=0 UMASK=022
EXPOSE 5244
CMD [ "./alist", "server", "--no-prefix" ]
ENTRYPOINT [ "/entrypoint.sh" ]

View File

@ -9,7 +9,7 @@
<img src="https://img.shields.io/github/license/Xhofe/alist" alt="License" />
</a>
<a href="https://github.com/Xhofe/alist/actions?query=workflow%3ABuild">
<img src="https://img.shields.io/github/workflow/status/Xhofe/alist/build" alt="Build status" />
<img src="https://img.shields.io/github/actions/workflow/status/Xhofe/alist/build.yml?branch=main" alt="Build status" />
</a>
<a href="https://github.com/Xhofe/alist/releases">
<img src="https://img.shields.io/github/release/Xhofe/alist" alt="latest version" />

View File

@ -9,7 +9,7 @@
<img src="https://img.shields.io/github/license/Xhofe/alist" alt="License" />
</a>
<a href="https://github.com/Xhofe/alist/actions?query=workflow%3ABuild">
<img src="https://img.shields.io/github/workflow/status/Xhofe/alist/build" alt="Build status" />
<img src="https://img.shields.io/github/actions/workflow/status/Xhofe/alist/build.yml?branch=main" alt="Build status" />
</a>
<a href="https://github.com/Xhofe/alist/releases">
<img src="https://img.shields.io/github/release/Xhofe/alist" alt="latest version" />

View File

@ -4,19 +4,19 @@ Copyright © 2022 NAME HERE <EMAIL ADDRESS>
package cmd
import (
"github.com/alist-org/alist/v3/internal/db"
"github.com/alist-org/alist/v3/internal/op"
"github.com/alist-org/alist/v3/pkg/utils"
"github.com/spf13/cobra"
)
// passwordCmd represents the password command
var passwordCmd = &cobra.Command{
// PasswordCmd represents the password command
var PasswordCmd = &cobra.Command{
Use: "admin",
Aliases: []string{"password"},
Short: "Show admin user's info",
Run: func(cmd *cobra.Command, args []string) {
Init()
admin, err := db.GetAdmin()
admin, err := op.GetAdmin()
if err != nil {
utils.Log.Errorf("failed get admin user: %+v", err)
} else {
@ -26,7 +26,7 @@ var passwordCmd = &cobra.Command{
}
func init() {
rootCmd.AddCommand(passwordCmd)
RootCmd.AddCommand(PasswordCmd)
// Here you will define your flags and configuration settings.

View File

@ -4,22 +4,22 @@ Copyright © 2022 NAME HERE <EMAIL ADDRESS>
package cmd
import (
"github.com/alist-org/alist/v3/internal/db"
"github.com/alist-org/alist/v3/internal/op"
"github.com/alist-org/alist/v3/pkg/utils"
"github.com/spf13/cobra"
)
// cancel2FACmd represents the delete2fa command
var cancel2FACmd = &cobra.Command{
// Cancel2FACmd represents the delete2fa command
var Cancel2FACmd = &cobra.Command{
Use: "cancel2fa",
Short: "Delete 2FA of admin user",
Run: func(cmd *cobra.Command, args []string) {
Init()
admin, err := db.GetAdmin()
admin, err := op.GetAdmin()
if err != nil {
utils.Log.Errorf("failed to get admin user: %+v", err)
} else {
err := db.Cancel2FAByUser(admin)
err := op.Cancel2FAByUser(admin)
if err != nil {
utils.Log.Errorf("failed to cancel 2FA: %+v", err)
}
@ -28,7 +28,7 @@ var cancel2FACmd = &cobra.Command{
}
func init() {
rootCmd.AddCommand(cancel2FACmd)
RootCmd.AddCommand(Cancel2FACmd)
// Here you will define your flags and configuration settings.

View File

@ -16,6 +16,7 @@ func Init() {
bootstrap.Log()
bootstrap.InitDB()
data.InitData()
bootstrap.InitIndex()
}
var pid = -1

View File

@ -71,7 +71,7 @@ func writeFile(name string, data interface{}) {
} else {
log.Infof("%s.json changed, update file", name)
//log.Infof("old: %+v\nnew:%+v", oldData, data)
utils.WriteJsonToFile(fmt.Sprintf("lang/%s.json", name), data)
utils.WriteJsonToFile(fmt.Sprintf("lang/%s.json", name), newData, true)
}
}
@ -123,8 +123,8 @@ func generateSettingsJson() {
//utils.WriteJsonToFile("lang/settings.json", settingsLang)
}
// langCmd represents the lang command
var langCmd = &cobra.Command{
// LangCmd represents the lang command
var LangCmd = &cobra.Command{
Use: "lang",
Short: "Generate language json file",
Run: func(cmd *cobra.Command, args []string) {
@ -138,7 +138,7 @@ var langCmd = &cobra.Command{
}
func init() {
rootCmd.AddCommand(langCmd)
RootCmd.AddCommand(LangCmd)
// Here you will define your flags and configuration settings.

View File

@ -7,8 +7,8 @@ import (
"github.com/spf13/cobra"
)
// restartCmd represents the restart command
var restartCmd = &cobra.Command{
// RestartCmd represents the restart command
var RestartCmd = &cobra.Command{
Use: "restart",
Short: "Restart alist server by daemon/pid file",
Run: func(cmd *cobra.Command, args []string) {
@ -18,7 +18,7 @@ var restartCmd = &cobra.Command{
}
func init() {
rootCmd.AddCommand(restartCmd)
RootCmd.AddCommand(RestartCmd)
// Here you will define your flags and configuration settings.

View File

@ -8,7 +8,7 @@ import (
"github.com/spf13/cobra"
)
var rootCmd = &cobra.Command{
var RootCmd = &cobra.Command{
Use: "alist",
Short: "A file list program that supports multiple storage.",
Long: `A file list program that supports multiple storage,
@ -17,16 +17,16 @@ Complete documentation is available at https://alist.nn.ci/`,
}
func Execute() {
if err := rootCmd.Execute(); err != nil {
if err := RootCmd.Execute(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
func init() {
rootCmd.PersistentFlags().StringVar(&flags.DataDir, "data", "data", "config file")
rootCmd.PersistentFlags().BoolVar(&flags.Debug, "debug", false, "start with debug mode")
rootCmd.PersistentFlags().BoolVar(&flags.NoPrefix, "no-prefix", false, "disable env prefix")
rootCmd.PersistentFlags().BoolVar(&flags.Dev, "dev", false, "start with dev mode")
rootCmd.PersistentFlags().BoolVar(&flags.ForceBinDir, "force-bin-dir", false, "Force to use the directory where the binary file is located as data directory")
RootCmd.PersistentFlags().StringVar(&flags.DataDir, "data", "data", "config file")
RootCmd.PersistentFlags().BoolVar(&flags.Debug, "debug", false, "start with debug mode")
RootCmd.PersistentFlags().BoolVar(&flags.NoPrefix, "no-prefix", false, "disable env prefix")
RootCmd.PersistentFlags().BoolVar(&flags.Dev, "dev", false, "start with dev mode")
RootCmd.PersistentFlags().BoolVar(&flags.ForceBinDir, "force-bin-dir", false, "Force to use the directory where the binary file is located as data directory")
}

View File

@ -20,8 +20,8 @@ import (
"github.com/spf13/cobra"
)
// serverCmd represents the server command
var serverCmd = &cobra.Command{
// ServerCmd represents the server command
var ServerCmd = &cobra.Command{
Use: "server",
Short: "Start the server at the specified address",
Long: `Start the server at the specified address
@ -76,7 +76,7 @@ the address is defined in config file`,
}
func init() {
rootCmd.AddCommand(serverCmd)
RootCmd.AddCommand(ServerCmd)
// Here you will define your flags and configuration settings.
@ -95,5 +95,5 @@ func OutAlistInit() {
cmd *cobra.Command
args []string
)
serverCmd.Run(cmd, args)
ServerCmd.Run(cmd, args)
}

View File

@ -13,8 +13,8 @@ import (
"github.com/spf13/cobra"
)
// startCmd represents the start command
var startCmd = &cobra.Command{
// StartCmd represents the start command
var StartCmd = &cobra.Command{
Use: "start",
Short: "Silent start alist server with `--force-bin-dir`",
Run: func(cmd *cobra.Command, args []string) {
@ -57,7 +57,7 @@ func start() {
}
func init() {
rootCmd.AddCommand(startCmd)
RootCmd.AddCommand(StartCmd)
// Here you will define your flags and configuration settings.

View File

@ -10,8 +10,8 @@ import (
"github.com/spf13/cobra"
)
// stopCmd represents the stop command
var stopCmd = &cobra.Command{
// StopCmd represents the stop command
var StopCmd = &cobra.Command{
Use: "stop",
Short: "Stop alist server by daemon/pid file",
Run: func(cmd *cobra.Command, args []string) {
@ -44,7 +44,7 @@ func stop() {
}
func init() {
rootCmd.AddCommand(stopCmd)
RootCmd.AddCommand(StopCmd)
// Here you will define your flags and configuration settings.

View File

@ -1,6 +1,5 @@
/*
Copyright © 2022 NAME HERE <EMAIL ADDRESS>
*/
package cmd
@ -12,8 +11,8 @@ import (
"github.com/spf13/cobra"
)
// versionCmd represents the version command
var versionCmd = &cobra.Command{
// VersionCmd represents the version command
var VersionCmd = &cobra.Command{
Use: "version",
Short: "Show current version of AList",
Run: func(cmd *cobra.Command, args []string) {
@ -30,7 +29,7 @@ WebVersion: %s
}
func init() {
rootCmd.AddCommand(versionCmd)
RootCmd.AddCommand(VersionCmd)
// Here you will define your flags and configuration settings.

14
docker-compose.yml Normal file
View File

@ -0,0 +1,14 @@
version: '3.3'
services:
alist:
restart: always
volumes:
- '/etc/alist:/opt/alist/data'
ports:
- '5244:5244'
environment:
- PUID=0
- PGID=0
- UMASK=022
container_name: alist
image: 'xhofe/alist:latest'

View File

@ -22,15 +22,10 @@ func (d *Pan115) Config() driver.Config {
}
func (d *Pan115) GetAddition() driver.Additional {
return d.Addition
return &d.Addition
}
func (d *Pan115) Init(ctx context.Context, storage model.Storage) error {
d.Storage = storage
err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition)
if err != nil {
return err
}
func (d *Pan115) Init(ctx context.Context) error {
return d.login()
}

View File

@ -18,10 +18,8 @@ var config = driver.Config{
OnlyLocal: true,
}
func New() driver.Driver {
return &Pan115{}
}
func init() {
op.RegisterDriver(config, New)
op.RegisterDriver(func() driver.Driver {
return &Pan115{}
})
}

View File

@ -37,15 +37,10 @@ func (d *Pan123) Config() driver.Config {
}
func (d *Pan123) GetAddition() driver.Additional {
return d.Addition
return &d.Addition
}
func (d *Pan123) Init(ctx context.Context, storage model.Storage) error {
d.Storage = storage
err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition)
if err != nil {
return err
}
func (d *Pan123) Init(ctx context.Context) error {
return d.login()
}
@ -63,11 +58,6 @@ func (d *Pan123) List(ctx context.Context, dir model.Obj, args model.ListArgs) (
})
}
//func (d *Pan123) Get(ctx context.Context, path string) (model.Obj, error) {
// // this is optional
// return nil, errs.NotImplement
//}
func (d *Pan123) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
if f, ok := file.(File); ok {
//var resp DownResp
@ -231,7 +221,7 @@ func (d *Pan123) Put(ctx context.Context, dstDir model.Obj, stream model.FileStr
}
var resp UploadResp
_, err := d.request("https://www.123pan.com/a/api/file/upload_request", http.MethodPost, func(req *resty.Request) {
req.SetBody(data)
req.SetBody(data).SetContext(ctx)
}, &resp)
if err != nil {
return err
@ -255,14 +245,14 @@ func (d *Pan123) Put(ctx context.Context, dstDir model.Obj, stream model.FileStr
Key: &resp.Data.Key,
Body: uploadFile,
}
_, err = uploader.Upload(input)
_, err = uploader.UploadWithContext(ctx, input)
if err != nil {
return err
}
_, err = d.request("https://www.123pan.com/api/file/upload_complete", http.MethodPost, func(req *resty.Request) {
req.SetBody(base.Json{
"fileId": resp.Data.FileId,
})
}).SetContext(ctx)
}, nil)
return err
}

View File

@ -21,10 +21,8 @@ var config = driver.Config{
DefaultRoot: "0",
}
func New() driver.Driver {
return &Pan123{}
}
func init() {
op.RegisterDriver(config, New)
op.RegisterDriver(func() driver.Driver {
return &Pan123{}
})
}

View File

@ -27,16 +27,11 @@ func (d *Yun139) Config() driver.Config {
}
func (d *Yun139) GetAddition() driver.Additional {
return d.Addition
return &d.Addition
}
func (d *Yun139) Init(ctx context.Context, storage model.Storage) error {
d.Storage = storage
err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition)
if err != nil {
return err
}
_, err = d.post("/orchestration/personalCloud/user/v1.0/qryUserExternInfo", base.Json{
func (d *Yun139) Init(ctx context.Context) error {
_, err := d.post("/orchestration/personalCloud/user/v1.0/qryUserExternInfo", base.Json{
"qryUserExternInfoReq": base.Json{
"commonAccountInfo": base.Json{
"account": d.Account,
@ -59,11 +54,6 @@ func (d *Yun139) List(ctx context.Context, dir model.Obj, args model.ListArgs) (
}
}
//func (d *Yun139) Get(ctx context.Context, path string) (model.Obj, error) {
// // this is optional
// return nil, errs.NotImplement
//}
func (d *Yun139) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
u, err := d.getLink(file.GetID())
if err != nil {
@ -275,10 +265,13 @@ func (d *Yun139) Put(ctx context.Context, dstDir model.Obj, stream model.FileStr
if err != nil {
return err
}
var Default int64 = 10485760
var Default int64 = 104857600
part := int(math.Ceil(float64(stream.GetSize()) / float64(Default)))
var start int64 = 0
for i := 0; i < part; i++ {
if utils.IsCanceled(ctx) {
return ctx.Err()
}
byteSize := stream.GetSize() - start
if byteSize > Default {
byteSize = Default
@ -292,6 +285,7 @@ func (d *Yun139) Put(ctx context.Context, dstDir model.Obj, stream model.FileStr
if err != nil {
return err
}
req = req.WithContext(ctx)
headers := map[string]string{
"Accept": "*/*",
"Content-Type": "text/plain;name=" + unicode(stream.GetName()),

View File

@ -19,7 +19,7 @@ var config = driver.Config{
}
func init() {
op.RegisterDriver(config, func() driver.Driver {
op.RegisterDriver(func() driver.Driver {
return &Yun139{}
})
}

View File

@ -26,15 +26,10 @@ func (d *Cloud189) Config() driver.Config {
}
func (d *Cloud189) GetAddition() driver.Additional {
return d.Addition
return &d.Addition
}
func (d *Cloud189) Init(ctx context.Context, storage model.Storage) error {
d.Storage = storage
err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition)
if err != nil {
return err
}
func (d *Cloud189) Init(ctx context.Context) error {
d.client = resty.New().
SetTimeout(base.DefaultTimeout).
SetRetryCount(3).
@ -51,11 +46,6 @@ func (d *Cloud189) List(ctx context.Context, dir model.Obj, args model.ListArgs)
return d.getFiles(dir.GetID())
}
//func (d *Cloud189) Get(ctx context.Context, path string) (model.Obj, error) {
// // this is optional
// return nil, errs.NotImplement
//}
func (d *Cloud189) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
var resp DownResp
u := "https://cloud.189.cn/api/portal/getFileInfo.action"
@ -204,7 +194,7 @@ func (d *Cloud189) Remove(ctx context.Context, obj model.Obj) error {
}
func (d *Cloud189) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
return d.newUpload(dstDir, stream, up)
return d.newUpload(ctx, dstDir, stream, up)
}
var _ driver.Driver = (*Cloud189)(nil)

View File

@ -18,7 +18,7 @@ var config = driver.Config{
}
func init() {
op.RegisterDriver(config, func() driver.Driver {
op.RegisterDriver(func() driver.Driver {
return &Cloud189{}
})
}

View File

@ -2,6 +2,7 @@ package _189
import (
"bytes"
"context"
"crypto/md5"
"encoding/base64"
"encoding/hex"
@ -306,7 +307,7 @@ func (d *Cloud189) uploadRequest(uri string, form map[string]string, resp interf
return data, nil
}
func (d *Cloud189) newUpload(dstDir model.Obj, file model.FileStreamer, up driver.UpdateProgress) error {
func (d *Cloud189) newUpload(ctx context.Context, dstDir model.Obj, file model.FileStreamer, up driver.UpdateProgress) error {
sessionKey, err := d.getSessionKey()
if err != nil {
return err
@ -335,6 +336,9 @@ func (d *Cloud189) newUpload(dstDir model.Obj, file model.FileStreamer, up drive
md5s := make([]string, 0)
md5Sum := md5.New()
for i = 1; i <= count; i++ {
if utils.IsCanceled(ctx) {
return ctx.Err()
}
byteSize = file.GetSize() - finish
if DEFAULT < byteSize {
byteSize = DEFAULT
@ -364,12 +368,15 @@ func (d *Cloud189) newUpload(dstDir model.Obj, file model.FileStreamer, up drive
log.Debugf("uploadData: %+v", uploadData)
requestURL := uploadData.RequestURL
uploadHeaders := strings.Split(decodeURIComponent(uploadData.RequestHeader), "&")
req, _ := http.NewRequest(http.MethodPut, requestURL, bytes.NewReader(byteData))
req, err := http.NewRequest(http.MethodPut, requestURL, bytes.NewReader(byteData))
if err != nil {
return err
}
req = req.WithContext(ctx)
for _, v := range uploadHeaders {
i := strings.Index(v, "=")
req.Header.Set(v[0:i], v[i+1:])
}
r, err := base.HttpClient.Do(req)
log.Debugf("%+v %+v", r, r.Request.Header)
r.Body.Close()

View File

@ -13,7 +13,7 @@ import (
"github.com/go-resty/resty/v2"
)
type Yun189PC struct {
type Cloud189PC struct {
model.Storage
Addition
@ -26,20 +26,15 @@ type Yun189PC struct {
tokenInfo *AppSessionResp
}
func (y *Yun189PC) Config() driver.Config {
func (y *Cloud189PC) Config() driver.Config {
return config
}
func (y *Yun189PC) GetAddition() driver.Additional {
return y.Addition
func (y *Cloud189PC) GetAddition() driver.Additional {
return &y.Addition
}
func (y *Yun189PC) Init(ctx context.Context, storage model.Storage) (err error) {
y.Storage = storage
if err = utils.Json.UnmarshalFromString(y.Storage.Addition, &y.Addition); err != nil {
return err
}
func (y *Cloud189PC) Init(ctx context.Context) (err error) {
// 处理个人云和家庭云参数
if y.isFamily() && y.RootFolderID == "-11" {
y.RootFolderID = ""
@ -78,15 +73,15 @@ func (y *Yun189PC) Init(ctx context.Context, storage model.Storage) (err error)
return
}
func (y *Yun189PC) Drop(ctx context.Context) error {
func (y *Cloud189PC) Drop(ctx context.Context) error {
return nil
}
func (y *Yun189PC) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
func (y *Cloud189PC) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
return y.getFiles(ctx, dir.GetID())
}
func (y *Yun189PC) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
func (y *Cloud189PC) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
var downloadUrl struct {
URL string `json:"fileDownloadUrl"`
}
@ -145,7 +140,7 @@ func (y *Yun189PC) Link(ctx context.Context, file model.Obj, args model.LinkArgs
return like, nil
}
func (y *Yun189PC) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
func (y *Cloud189PC) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
fullUrl := API_URL
if y.isFamily() {
fullUrl += "/family/file"
@ -172,7 +167,7 @@ func (y *Yun189PC) MakeDir(ctx context.Context, parentDir model.Obj, dirName str
return err
}
func (y *Yun189PC) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
func (y *Cloud189PC) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
_, err := y.post(API_URL+"/batch/createBatchTask.action", func(req *resty.Request) {
req.SetContext(ctx)
req.SetFormData(map[string]string{
@ -196,7 +191,7 @@ func (y *Yun189PC) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
return err
}
func (y *Yun189PC) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
func (y *Cloud189PC) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
queryParam := make(map[string]string)
fullUrl := API_URL
method := http.MethodPost
@ -221,7 +216,7 @@ func (y *Yun189PC) Rename(ctx context.Context, srcObj model.Obj, newName string)
return err
}
func (y *Yun189PC) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
func (y *Cloud189PC) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
_, err := y.post(API_URL+"/batch/createBatchTask.action", func(req *resty.Request) {
req.SetContext(ctx)
req.SetFormData(map[string]string{
@ -246,7 +241,7 @@ func (y *Yun189PC) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
return err
}
func (y *Yun189PC) Remove(ctx context.Context, obj model.Obj) error {
func (y *Cloud189PC) Remove(ctx context.Context, obj model.Obj) error {
_, err := y.post(API_URL+"/batch/createBatchTask.action", func(req *resty.Request) {
req.SetContext(ctx)
req.SetFormData(map[string]string{
@ -270,7 +265,7 @@ func (y *Yun189PC) Remove(ctx context.Context, obj model.Obj) error {
return err
}
func (y *Yun189PC) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
func (y *Cloud189PC) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
if y.RapidUpload {
return y.FastUpload(ctx, dstDir, stream, up)
}

View File

@ -24,7 +24,7 @@ var config = driver.Config{
}
func init() {
op.RegisterDriver(config, func() driver.Driver {
return &Yun189PC{}
op.RegisterDriver(func() driver.Driver {
return &Cloud189PC{}
})
}

View File

@ -47,7 +47,7 @@ const (
CHANNEL_ID = "web_cloud.189.cn"
)
func (y *Yun189PC) request(url, method string, callback base.ReqCallback, params Params, resp interface{}) ([]byte, error) {
func (y *Cloud189PC) request(url, method string, callback base.ReqCallback, params Params, resp interface{}) ([]byte, error) {
dateOfGmt := getHttpDateStr()
sessionKey := y.tokenInfo.SessionKey
sessionSecret := y.tokenInfo.SessionSecret
@ -124,15 +124,15 @@ func (y *Yun189PC) request(url, method string, callback base.ReqCallback, params
}
}
func (y *Yun189PC) get(url string, callback base.ReqCallback, resp interface{}) ([]byte, error) {
func (y *Cloud189PC) get(url string, callback base.ReqCallback, resp interface{}) ([]byte, error) {
return y.request(url, http.MethodGet, callback, nil, resp)
}
func (y *Yun189PC) post(url string, callback base.ReqCallback, resp interface{}) ([]byte, error) {
func (y *Cloud189PC) post(url string, callback base.ReqCallback, resp interface{}) ([]byte, error) {
return y.request(url, http.MethodPost, callback, nil, resp)
}
func (y *Yun189PC) getFiles(ctx context.Context, fileId string) ([]model.Obj, error) {
func (y *Cloud189PC) getFiles(ctx context.Context, fileId string) ([]model.Obj, error) {
fullUrl := API_URL
if y.isFamily() {
fullUrl += "/family/file"
@ -184,7 +184,7 @@ func (y *Yun189PC) getFiles(ctx context.Context, fileId string) ([]model.Obj, er
return res, nil
}
func (y *Yun189PC) login() (err error) {
func (y *Cloud189PC) login() (err error) {
// 初始化登陆所需参数
if y.loginParam == nil || !y.NoUseOcr {
if err = y.initLoginParam(); err != nil {
@ -264,7 +264,7 @@ func (y *Yun189PC) login() (err error) {
/* 初始化登陆需要的参数
* 如果遇到验证码返回错误
*/
func (y *Yun189PC) initLoginParam() error {
func (y *Cloud189PC) initLoginParam() error {
// 清除cookie
jar, _ := cookiejar.New(nil)
y.client.SetCookieJar(jar)
@ -335,7 +335,7 @@ func (y *Yun189PC) initLoginParam() error {
}
// 刷新会话
func (y *Yun189PC) refreshSession() (err error) {
func (y *Cloud189PC) refreshSession() (err error) {
var erron RespErr
var userSessionResp UserSessionResp
_, err = y.client.R().
@ -381,7 +381,7 @@ func (y *Yun189PC) refreshSession() (err error) {
}
// 普通上传
func (y *Yun189PC) CommonUpload(ctx context.Context, dstDir model.Obj, file model.FileStreamer, up driver.UpdateProgress) (err error) {
func (y *Cloud189PC) CommonUpload(ctx context.Context, dstDir model.Obj, file model.FileStreamer, up driver.UpdateProgress) (err error) {
const DEFAULT int64 = 10485760
var count = int64(math.Ceil(float64(file.GetSize()) / float64(DEFAULT)))
@ -418,10 +418,8 @@ func (y *Yun189PC) CommonUpload(ctx context.Context, dstDir model.Obj, file mode
silceMd5Hexs := make([]string, 0, count)
byteData := bytes.NewBuffer(make([]byte, DEFAULT))
for i := int64(1); i <= count; i++ {
select {
case <-ctx.Done():
if utils.IsCanceled(ctx) {
return ctx.Err()
default:
}
// 读取块
@ -491,7 +489,7 @@ func (y *Yun189PC) CommonUpload(ctx context.Context, dstDir model.Obj, file mode
}
// 快传
func (y *Yun189PC) FastUpload(ctx context.Context, dstDir model.Obj, file model.FileStreamer, up driver.UpdateProgress) (err error) {
func (y *Cloud189PC) FastUpload(ctx context.Context, dstDir model.Obj, file model.FileStreamer, up driver.UpdateProgress) (err error) {
// 需要获取完整文件md5,必须支持 io.Seek
tempFile, err := utils.CreateTempFile(file.GetReadCloser())
if err != nil {
@ -511,10 +509,8 @@ func (y *Yun189PC) FastUpload(ctx context.Context, dstDir model.Obj, file model.
silceMd5Hexs := make([]string, 0, count)
silceMd5Base64s := make([]string, 0, count)
for i := 1; i <= count; i++ {
select {
case <-ctx.Done():
if utils.IsCanceled(ctx) {
return ctx.Err()
default:
}
silceMd5.Reset()
@ -616,11 +612,11 @@ func (y *Yun189PC) FastUpload(ctx context.Context, dstDir model.Obj, file model.
return err
}
func (y *Yun189PC) isFamily() bool {
func (y *Cloud189PC) isFamily() bool {
return y.Type == "family"
}
func (y *Yun189PC) isLogin() bool {
func (y *Cloud189PC) isLogin() bool {
if y.tokenInfo == nil {
return false
}
@ -629,7 +625,7 @@ func (y *Yun189PC) isLogin() bool {
}
// 获取家庭云所有用户信息
func (y *Yun189PC) getFamilyInfoList() ([]FamilyInfoResp, error) {
func (y *Cloud189PC) getFamilyInfoList() ([]FamilyInfoResp, error) {
var resp FamilyInfoListResp
_, err := y.get(API_URL+"/family/manage/getFamilyList.action", nil, &resp)
if err != nil {
@ -639,7 +635,7 @@ func (y *Yun189PC) getFamilyInfoList() ([]FamilyInfoResp, error) {
}
// 抽取家庭云ID
func (y *Yun189PC) getFamilyID() (string, error) {
func (y *Cloud189PC) getFamilyID() (string, error) {
infos, err := y.getFamilyInfoList()
if err != nil {
return "", err

View File

@ -7,7 +7,6 @@ import (
"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/utils"
"github.com/alist-org/alist/v3/server/common"
)
@ -21,18 +20,16 @@ func (d *AListV2) Config() driver.Config {
}
func (d *AListV2) GetAddition() driver.Additional {
return d.Addition
return &d.Addition
}
func (d *AListV2) Init(ctx context.Context, storage model.Storage) error {
d.Storage = storage
err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition)
if err != nil {
return err
func (d *AListV2) Init(ctx context.Context) error {
if len(d.Addition.Address) > 0 && string(d.Addition.Address[len(d.Addition.Address)-1]) == "/" {
d.Addition.Address = d.Addition.Address[0 : len(d.Addition.Address)-1]
}
// TODO login / refresh token
//op.MustSaveDriverStorage(d)
return err
return nil
}
func (d *AListV2) Drop(ctx context.Context) error {
@ -70,11 +67,6 @@ func (d *AListV2) List(ctx context.Context, dir model.Obj, args model.ListArgs)
return files, nil
}
//func (d *AList) Get(ctx context.Context, path string) (model.Obj, error) {
// // this is optional
// return nil, errs.NotImplement
//}
func (d *AListV2) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
url := d.Address + "/api/public/path"
var resp common.Resp[PathResp]

View File

@ -20,7 +20,7 @@ var config = driver.Config{
}
func init() {
op.RegisterDriver(config, func() driver.Driver {
op.RegisterDriver(func() driver.Driver {
return &AListV2{}
})
}

View File

@ -2,12 +2,14 @@ package alist_v3
import (
"context"
"io"
"path"
"strconv"
"strings"
"github.com/alist-org/alist/v3/drivers/base"
"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/utils"
"github.com/alist-org/alist/v3/server/common"
)
@ -21,18 +23,14 @@ func (d *AListV3) Config() driver.Config {
}
func (d *AListV3) GetAddition() driver.Additional {
return d.Addition
return &d.Addition
}
func (d *AListV3) Init(ctx context.Context, storage model.Storage) error {
d.Storage = storage
err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition)
if err != nil {
return err
}
func (d *AListV3) Init(ctx context.Context) error {
d.Addition.Address = strings.TrimSuffix(d.Addition.Address, "/")
// TODO login / refresh token
//op.MustSaveDriverStorage(d)
return err
return nil
}
func (d *AListV3) Drop(ctx context.Context) error {
@ -46,7 +44,7 @@ func (d *AListV3) List(ctx context.Context, dir model.Obj, args model.ListArgs)
SetResult(&resp).
SetHeader("Authorization", d.AccessToken).
SetBody(ListReq{
PageReq: common.PageReq{
PageReq: model.PageReq{
Page: 1,
PerPage: 0,
},
@ -73,11 +71,6 @@ func (d *AListV3) List(ctx context.Context, dir model.Obj, args model.ListArgs)
return files, nil
}
//func (d *AList) Get(ctx context.Context, path string) (model.Obj, error) {
// // this is optional
// return nil, errs.NotImplement
//}
func (d *AListV3) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
url := d.Address + "/api/fs/get"
var resp common.Resp[FsGetResp]
@ -97,27 +90,86 @@ func (d *AListV3) Link(ctx context.Context, file model.Obj, args model.LinkArgs)
}
func (d *AListV3) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
return errs.NotImplement
url := d.Address + "/api/fs/mkdir"
var resp common.Resp[interface{}]
_, err := base.RestyClient.R().
SetResult(&resp).
SetHeader("Authorization", d.AccessToken).
SetBody(MkdirOrLinkReq{
Path: path.Join(parentDir.GetPath(), dirName),
}).Post(url)
return checkResp(resp, err)
}
func (d *AListV3) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
return errs.NotImplement
url := d.Address + "/api/fs/move"
var resp common.Resp[interface{}]
_, err := base.RestyClient.R().
SetResult(&resp).
SetHeader("Authorization", d.AccessToken).
SetBody(MoveCopyReq{
SrcDir: srcObj.GetPath(),
DstDir: dstDir.GetPath(),
Names: []string{srcObj.GetName()},
}).Post(url)
return checkResp(resp, err)
}
func (d *AListV3) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
return errs.NotImplement
url := d.Address + "/api/fs/rename"
var resp common.Resp[interface{}]
_, err := base.RestyClient.R().
SetResult(&resp).
SetHeader("Authorization", d.AccessToken).
SetBody(RenameReq{
Path: srcObj.GetPath(),
Name: newName,
}).Post(url)
return checkResp(resp, err)
}
func (d *AListV3) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
return errs.NotImplement
url := d.Address + "/api/fs/copy"
var resp common.Resp[interface{}]
_, err := base.RestyClient.R().
SetResult(&resp).
SetHeader("Authorization", d.AccessToken).
SetBody(MoveCopyReq{
SrcDir: srcObj.GetPath(),
DstDir: dstDir.GetPath(),
Names: []string{srcObj.GetName()},
}).Post(url)
return checkResp(resp, err)
}
func (d *AListV3) Remove(ctx context.Context, obj model.Obj) error {
return errs.NotImplement
url := d.Address + "/api/fs/remove"
var resp common.Resp[interface{}]
_, err := base.RestyClient.R().
SetResult(&resp).
SetHeader("Authorization", d.AccessToken).
SetBody(RemoveReq{
Dir: obj.GetPath(),
Names: []string{obj.GetName()},
}).Post(url)
return checkResp(resp, err)
}
func (d *AListV3) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
return errs.NotImplement
url := d.Address + "/api/fs/put"
var resp common.Resp[interface{}]
fileBytes, err := io.ReadAll(stream.GetReadCloser())
if err != nil {
return nil
}
_, err = base.RestyClient.R().SetContext(ctx).
SetResult(&resp).
SetHeader("Authorization", d.AccessToken).
SetHeader("File-Path", path.Join(dstDir.GetPath(), stream.GetName())).
SetHeader("Password", d.Password).
SetHeader("Content-Length", strconv.FormatInt(stream.GetSize(), 10)).
SetBody(fileBytes).Put(url)
return checkResp(resp, err)
}
//func (d *AList) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) {

View File

@ -15,12 +15,11 @@ type Addition struct {
var config = driver.Config{
Name: "AList V3",
LocalSort: true,
NoUpload: true,
DefaultRoot: "/",
}
func init() {
op.RegisterDriver(config, func() driver.Driver {
op.RegisterDriver(func() driver.Driver {
return &AListV3{}
})
}

View File

@ -3,11 +3,11 @@ package alist_v3
import (
"time"
"github.com/alist-org/alist/v3/server/common"
"github.com/alist-org/alist/v3/internal/model"
)
type ListReq struct {
common.PageReq
model.PageReq
Path string `json:"path" form:"path"`
Password string `json:"password" form:"password"`
Refresh bool `json:"refresh"`
@ -43,3 +43,23 @@ type FsGetResp struct {
Provider string `json:"provider"`
Related []ObjResp `json:"related"`
}
type MkdirOrLinkReq struct {
Path string `json:"path" form:"path"`
}
type MoveCopyReq struct {
SrcDir string `json:"src_dir"`
DstDir string `json:"dst_dir"`
Names []string `json:"names"`
}
type RenameReq struct {
Path string `json:"path"`
Name string `json:"name"`
}
type RemoveReq struct {
Dir string `json:"dir"`
Names []string `json:"names"`
}

View File

@ -1 +1,17 @@
package alist_v3
import (
"errors"
"github.com/alist-org/alist/v3/server/common"
)
func checkResp(resp common.Resp[interface{}], err error) error {
if err != nil {
return err
}
if resp.Message == "success" {
return nil
}
return errors.New(resp.Message)
}

View File

@ -38,18 +38,13 @@ func (d *AliDrive) Config() driver.Config {
}
func (d *AliDrive) GetAddition() driver.Additional {
return d.Addition
return &d.Addition
}
func (d *AliDrive) Init(ctx context.Context, storage model.Storage) error {
d.Storage = storage
err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition)
if err != nil {
return err
}
func (d *AliDrive) Init(ctx context.Context) error {
// TODO login / refresh token
//op.MustSaveDriverStorage(d)
err = d.refreshToken()
err := d.refreshToken()
if err != nil {
return err
}
@ -86,11 +81,6 @@ func (d *AliDrive) List(ctx context.Context, dir model.Obj, args model.ListArgs)
})
}
//func (d *AliDrive) Get(ctx context.Context, path string) (model.Obj, error) {
// // TODO this is optional
// return nil, errs.NotImplement
//}
func (d *AliDrive) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
data := base.Json{
"drive_id": d.DriveId,
@ -258,10 +248,14 @@ func (d *AliDrive) Put(ctx context.Context, dstDir model.Obj, stream model.FileS
}
for i, partInfo := range resp.PartInfoList {
if utils.IsCanceled(ctx) {
return ctx.Err()
}
req, err := http.NewRequest("PUT", partInfo.UploadUrl, io.LimitReader(file, DEFAULT))
if err != nil {
return err
}
req = req.WithContext(ctx)
res, err := base.HttpClient.Do(req)
if err != nil {
return err

View File

@ -18,10 +18,8 @@ var config = driver.Config{
DefaultRoot: "root",
}
func New() driver.Driver {
return &AliDrive{}
}
func init() {
op.RegisterDriver(config, New)
op.RegisterDriver(func() driver.Driver {
return &AliDrive{}
})
}

View File

@ -62,6 +62,8 @@ func (d *AliDrive) request(url, method string, callback base.ReqCallback, resp i
return d.request(url, method, callback, resp)
}
return nil, errors.New(e.Message), e
} else if res.IsError() {
return nil, errors.New("bad status code " + res.Status()), e
}
return res.Body(), nil, e
}

View File

@ -8,7 +8,6 @@ import (
"github.com/alist-org/alist/v3/drivers/base"
"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/cron"
"github.com/alist-org/alist/v3/pkg/utils"
@ -29,16 +28,11 @@ func (d *AliyundriveShare) Config() driver.Config {
}
func (d *AliyundriveShare) GetAddition() driver.Additional {
return d.Addition
return &d.Addition
}
func (d *AliyundriveShare) Init(ctx context.Context, storage model.Storage) error {
d.Storage = storage
err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition)
if err != nil {
return err
}
err = d.refreshToken()
func (d *AliyundriveShare) Init(ctx context.Context) error {
err := d.refreshToken()
if err != nil {
return err
}
@ -73,44 +67,18 @@ func (d *AliyundriveShare) List(ctx context.Context, dir model.Obj, args model.L
})
}
//func (d *AliyundriveShare) Get(ctx context.Context, path string) (model.Obj, error) {
// // this is optional
// return nil, errs.NotImplement
//}
func (d *AliyundriveShare) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
data := base.Json{
"drive_id": d.DriveId,
"file_id": file.GetID(),
"expire_sec": 14400,
}
var e ErrorResp
res, err := base.RestyClient.R().
SetError(&e).SetBody(data).
SetHeader("content-type", "application/json").
SetHeader("Authorization", "Bearer\t"+d.AccessToken).
Post("https://api.aliyundrive.com/v2/file/get_download_url")
if err != nil {
return nil, err
}
var u string
if e.Code != "" {
if e.Code == "AccessTokenInvalid" {
err = d.refreshToken()
if err != nil {
return nil, err
}
return d.Link(ctx, file, args)
} else if e.Code == "ForbiddenNoPermission.File" {
data = utils.MergeMap(data, base.Json{
// Only ten minutes valid
// // Only ten minutes lifetime
"expire_sec": 600,
"share_id": d.ShareId,
})
}
var resp ShareLinkResp
var e2 ErrorResp
_, err = base.RestyClient.R().
SetError(&e2).SetBody(data).SetResult(&resp).
var e ErrorResp
_, err := base.RestyClient.R().
SetError(&e).SetBody(data).SetResult(&resp).
SetHeader("content-type", "application/json").
SetHeader("Authorization", "Bearer\t"+d.AccessToken).
SetHeader("x-share-token", d.ShareToken).
@ -118,25 +86,24 @@ func (d *AliyundriveShare) Link(ctx context.Context, file model.Obj, args model.
if err != nil {
return nil, err
}
if e2.Code != "" {
if e2.Code == "AccessTokenInvalid" || e2.Code == "ShareLinkTokenInvalid" {
var u string
if e.Code != "" {
if e.Code == "AccessTokenInvalid" || e.Code == "ShareLinkTokenInvalid" {
if e.Code == "AccessTokenInvalid" {
err = d.refreshToken()
} else {
err = d.getShareToken()
}
if err != nil {
return nil, err
}
return d.Link(ctx, file, args)
} else {
return nil, errors.New(e2.Code + ":" + e2.Message)
return nil, errors.New(e.Code + ": " + e.Message)
}
} else {
u = resp.DownloadUrl
}
} else {
return nil, errors.New(e.Code + ":" + e.Message)
}
} else {
u = utils.Json.Get(res.Body(), "url").ToString()
}
return &model.Link{
Header: http.Header{
"Referer": []string{"https://www.aliyundrive.com/"},
@ -145,34 +112,4 @@ func (d *AliyundriveShare) Link(ctx context.Context, file model.Obj, args model.
}, nil
}
func (d *AliyundriveShare) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
// TODO create folder
return errs.NotSupport
}
func (d *AliyundriveShare) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
// TODO move obj
return errs.NotSupport
}
func (d *AliyundriveShare) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
// TODO rename obj
return errs.NotSupport
}
func (d *AliyundriveShare) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
// TODO copy obj
return errs.NotSupport
}
func (d *AliyundriveShare) Remove(ctx context.Context, obj model.Obj) error {
// TODO remove obj
return errs.NotSupport
}
func (d *AliyundriveShare) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
// TODO upload file
return errs.NotSupport
}
var _ driver.Driver = (*AliyundriveShare)(nil)

View File

@ -23,7 +23,7 @@ var config = driver.Config{
}
func init() {
op.RegisterDriver(config, func() driver.Driver {
op.RegisterDriver(func() driver.Driver {
return &AliyundriveShare{}
})
}

View File

@ -21,11 +21,13 @@ import (
_ "github.com/alist-org/alist/v3/drivers/mega"
_ "github.com/alist-org/alist/v3/drivers/onedrive"
_ "github.com/alist-org/alist/v3/drivers/pikpak"
_ "github.com/alist-org/alist/v3/drivers/pikpak_share"
_ "github.com/alist-org/alist/v3/drivers/quark"
_ "github.com/alist-org/alist/v3/drivers/s3"
_ "github.com/alist-org/alist/v3/drivers/sftp"
_ "github.com/alist-org/alist/v3/drivers/smb"
_ "github.com/alist-org/alist/v3/drivers/teambition"
_ "github.com/alist-org/alist/v3/drivers/terabox"
_ "github.com/alist-org/alist/v3/drivers/thunder"
_ "github.com/alist-org/alist/v3/drivers/uss"
_ "github.com/alist-org/alist/v3/drivers/virtual"

View File

@ -31,15 +31,10 @@ func (d *BaiduNetdisk) Config() driver.Config {
}
func (d *BaiduNetdisk) GetAddition() driver.Additional {
return d.Addition
return &d.Addition
}
func (d *BaiduNetdisk) Init(ctx context.Context, storage model.Storage) error {
d.Storage = storage
err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition)
if err != nil {
return err
}
func (d *BaiduNetdisk) Init(ctx context.Context) error {
return d.refreshToken()
}
@ -57,11 +52,6 @@ func (d *BaiduNetdisk) List(ctx context.Context, dir model.Obj, args model.ListA
})
}
//func (d *BaiduNetdisk) Get(ctx context.Context, path string) (model.Obj, error) {
// // this is optional
// return nil, errs.NotImplement
//}
func (d *BaiduNetdisk) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
if d.DownloadAPI == "crack" {
return d.linkCrack(file, args)
@ -98,12 +88,11 @@ func (d *BaiduNetdisk) Rename(ctx context.Context, srcObj model.Obj, newName str
}
func (d *BaiduNetdisk) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
dest, newname := stdpath.Split(dstDir.GetPath())
data := []base.Json{
{
"path": srcObj.GetPath(),
"dest": dest,
"newname": newname,
"dest": dstDir.GetPath(),
"newname": srcObj.GetName(),
},
}
_, err := d.manage("copy", data)
@ -175,7 +164,8 @@ func (d *BaiduNetdisk) Put(ctx context.Context, dstDir model.Obj, stream model.F
return err
}
}
path := encodeURIComponent(stdpath.Join(dstDir.GetPath(), stream.GetName()))
rawPath := stdpath.Join(dstDir.GetPath(), stream.GetName())
path := encodeURIComponent(rawPath)
block_list_str := fmt.Sprintf("[%s]", strings.Join(block_list, ","))
data := fmt.Sprintf("path=%s&size=%d&isdir=0&autoinit=1&block_list=%s&content-md5=%s&slice-md5=%s",
path, stream.GetSize(),
@ -202,6 +192,9 @@ func (d *BaiduNetdisk) Put(ctx context.Context, dstDir model.Obj, stream model.F
}
left = stream.GetSize()
for i, partseq := range precreateResp.BlockList {
if utils.IsCanceled(ctx) {
return ctx.Err()
}
byteSize := Default
var byteData []byte
if left < Default {
@ -217,7 +210,11 @@ func (d *BaiduNetdisk) Put(ctx context.Context, dstDir model.Obj, stream model.F
}
u := "https://d.pcs.baidu.com/rest/2.0/pcs/superfile2"
params["partseq"] = strconv.Itoa(partseq)
res, err := base.RestyClient.R().SetQueryParams(params).SetFileReader("file", stream.GetName(), bytes.NewReader(byteData)).Post(u)
res, err := base.RestyClient.R().
SetContext(ctx).
SetQueryParams(params).
SetFileReader("file", stream.GetName(), bytes.NewReader(byteData)).
Post(u)
if err != nil {
return err
}
@ -226,7 +223,7 @@ func (d *BaiduNetdisk) Put(ctx context.Context, dstDir model.Obj, stream model.F
up(i * 100 / len(precreateResp.BlockList))
}
}
_, err = d.create(path, stream.GetSize(), 0, precreateResp.Uploadid, block_list_str)
_, err = d.create(rawPath, stream.GetSize(), 0, precreateResp.Uploadid, block_list_str)
return err
}

View File

@ -20,10 +20,8 @@ var config = driver.Config{
DefaultRoot: "/",
}
func New() driver.Driver {
return &BaiduNetdisk{}
}
func init() {
op.RegisterDriver(config, New)
op.RegisterDriver(func() driver.Driver {
return &BaiduNetdisk{}
})
}

View File

@ -187,7 +187,7 @@ func (d *BaiduNetdisk) create(path string, size int64, isdir int, uploadid, bloc
params := map[string]string{
"method": "create",
}
data := fmt.Sprintf("path=%s&size=%d&isdir=%d", path, size, isdir)
data := fmt.Sprintf("path=%s&size=%d&isdir=%d", encodeURIComponent(path), size, isdir)
if uploadid != "" {
data += fmt.Sprintf("&uploadid=%s&block_list=%s", uploadid, block_list)
}

View File

@ -29,15 +29,10 @@ func (d *BaiduPhoto) Config() driver.Config {
}
func (d *BaiduPhoto) GetAddition() driver.Additional {
return d.Addition
return &d.Addition
}
func (d *BaiduPhoto) Init(ctx context.Context, storage model.Storage) error {
d.Storage = storage
err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition)
if err != nil {
return err
}
func (d *BaiduPhoto) Init(ctx context.Context) error {
return d.refreshToken()
}
@ -245,6 +240,9 @@ func (d *BaiduPhoto) Put(ctx context.Context, dstDir model.Obj, stream model.Fil
}
for i := 0; i < count; i++ {
if utils.IsCanceled(ctx) {
return ctx.Err()
}
uploadParams["partseq"] = fmt.Sprint(i)
_, err = d.Post("https://c3.pcs.baidu.com/rest/2.0/pcs/superfile2", func(r *resty.Request) {
r.SetContext(ctx)

View File

@ -24,7 +24,7 @@ var config = driver.Config{
}
func init() {
op.RegisterDriver(config, func() driver.Driver {
op.RegisterDriver(func() driver.Driver {
return &BaiduPhoto{}
})
}

View File

@ -100,7 +100,7 @@ type (
)
func (a *Album) GetSize() int64 { return 0 }
func (a *Album) GetName() string { return fmt.Sprint(a.Title) }
func (a *Album) GetName() string { return a.Title }
func (a *Album) ModTime() time.Time {
if a.parseTime == nil {
a.parseTime = toTime(a.Mtime)

View File

@ -7,7 +7,6 @@ import (
"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/utils"
"github.com/jlaffaye/ftp"
)
@ -22,15 +21,10 @@ func (d *FTP) Config() driver.Config {
}
func (d *FTP) GetAddition() driver.Additional {
return d.Addition
return &d.Addition
}
func (d *FTP) Init(ctx context.Context, storage model.Storage) error {
d.Storage = storage
err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition)
if err != nil {
return err
}
func (d *FTP) Init(ctx context.Context) error {
return d.login()
}
@ -66,11 +60,6 @@ func (d *FTP) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]m
return res, nil
}
//func (d *FTP) Get(ctx context.Context, path string) (model.Obj, error) {
// // this is optional
// return nil, errs.NotImplement
//}
func (d *FTP) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
if err := d.login(); err != nil {
return nil, err
@ -124,6 +113,7 @@ func (d *FTP) Put(ctx context.Context, dstDir model.Obj, stream model.FileStream
if err := d.login(); err != nil {
return err
}
// TODO: support cancel
return d.conn.Stor(stdpath.Join(dstDir.GetPath(), stream.GetName()), stream)
}

View File

@ -19,10 +19,8 @@ var config = driver.Config{
DefaultRoot: "/",
}
func New() driver.Driver {
return &FTP{}
}
func init() {
op.RegisterDriver(config, New)
op.RegisterDriver(func() driver.Driver {
return &FTP{}
})
}

View File

@ -4,14 +4,12 @@ import (
"context"
"fmt"
"net/http"
stdpath "path"
"strconv"
"github.com/alist-org/alist/v3/drivers/base"
"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/internal/op"
"github.com/alist-org/alist/v3/pkg/utils"
"github.com/go-resty/resty/v2"
)
@ -27,15 +25,10 @@ func (d *GoogleDrive) Config() driver.Config {
}
func (d *GoogleDrive) GetAddition() driver.Additional {
return d.Addition
return &d.Addition
}
func (d *GoogleDrive) Init(ctx context.Context, storage model.Storage) error {
d.Storage = storage
err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition)
if err != nil {
return err
}
func (d *GoogleDrive) Init(ctx context.Context) error {
if d.ChunkSize == 0 {
d.ChunkSize = 5
}
@ -56,11 +49,6 @@ func (d *GoogleDrive) List(ctx context.Context, dir model.Obj, args model.ListAr
})
}
//func (d *GoogleDrive) Get(ctx context.Context, path string) (model.Obj, error) {
// // this is optional
// return nil, errs.NotImplement
//}
func (d *GoogleDrive) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
url := fmt.Sprintf("https://www.googleapis.com/drive/v3/files/%s?includeItemsFromAllDrives=true&supportsAllDrives=true", file.GetID())
_, err := d.request(url, http.MethodGet, nil, nil)
@ -122,8 +110,7 @@ func (d *GoogleDrive) Remove(ctx context.Context, obj model.Obj) error {
}
func (d *GoogleDrive) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
obj, _ := op.Get(ctx, d, stdpath.Join(dstDir.GetPath(), stream.GetName()))
obj := stream.GetOld()
var (
e Error
url string
@ -147,7 +134,7 @@ func (d *GoogleDrive) Put(ctx context.Context, dstDir model.Obj, stream model.Fi
"X-Upload-Content-Type": stream.GetMimetype(),
"X-Upload-Content-Length": strconv.FormatInt(stream.GetSize(), 10),
}).
SetError(&e).SetBody(data)
SetError(&e).SetBody(data).SetContext(ctx)
if obj != nil {
res, err = req.Patch(url)
} else {

View File

@ -21,10 +21,8 @@ var config = driver.Config{
DefaultRoot: "root",
}
func New() driver.Driver {
return &GoogleDrive{}
}
func init() {
op.RegisterDriver(config, New)
op.RegisterDriver(func() driver.Driver {
return &GoogleDrive{}
})
}

View File

@ -9,6 +9,7 @@ import (
"github.com/alist-org/alist/v3/drivers/base"
"github.com/alist-org/alist/v3/internal/model"
"github.com/alist-org/alist/v3/pkg/utils"
"github.com/go-resty/resty/v2"
log "github.com/sirupsen/logrus"
)
@ -104,6 +105,9 @@ func (d *GoogleDrive) chunkUpload(ctx context.Context, stream model.FileStreamer
var defaultChunkSize = d.ChunkSize * 1024 * 1024
var finish int64 = 0
for finish < stream.GetSize() {
if utils.IsCanceled(ctx) {
return ctx.Err()
}
chunkSize := stream.GetSize() - finish
if chunkSize > defaultChunkSize {
chunkSize = defaultChunkSize
@ -112,7 +116,7 @@ func (d *GoogleDrive) chunkUpload(ctx context.Context, stream model.FileStreamer
req.SetHeaders(map[string]string{
"Content-Length": strconv.FormatInt(chunkSize, 10),
"Content-Range": fmt.Sprintf("bytes %d-%d/%d", finish, finish+chunkSize-1, stream.GetSize()),
}).SetBody(io.LimitReader(stream.GetReadCloser(), chunkSize))
}).SetBody(io.LimitReader(stream.GetReadCloser(), chunkSize)).SetContext(ctx)
}, nil)
if err != nil {
return err

View File

@ -26,15 +26,10 @@ func (d *GooglePhoto) Config() driver.Config {
}
func (d *GooglePhoto) GetAddition() driver.Additional {
return d.Addition
return &d.Addition
}
func (d *GooglePhoto) Init(ctx context.Context, storage model.Storage) error {
d.Storage = storage
err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition)
if err != nil {
return err
}
func (d *GooglePhoto) Init(ctx context.Context) error {
return d.refreshToken()
}
@ -52,11 +47,6 @@ func (d *GooglePhoto) List(ctx context.Context, dir model.Obj, args model.ListAr
})
}
//func (d *GooglePhoto) Get(ctx context.Context, path string) (model.Obj, error) {
// // this is optional
// return nil, errs.NotImplement
//}
func (d *GooglePhoto) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
f, err := d.getMedia(file.GetID())
if err != nil {
@ -134,7 +124,7 @@ func (d *GooglePhoto) Put(ctx context.Context, dstDir model.Obj, stream model.Fi
}
resp, err := d.request(postUrl, http.MethodPost, func(req *resty.Request) {
req.SetBody(stream.GetReadCloser())
req.SetBody(stream.GetReadCloser()).SetContext(ctx)
}, nil, postHeaders)
if err != nil {

View File

@ -21,10 +21,8 @@ var config = driver.Config{
LocalSort: true,
}
func New() driver.Driver {
return &GooglePhoto{}
}
func init() {
op.RegisterDriver(config, New)
op.RegisterDriver(func() driver.Driver {
return &GooglePhoto{}
})
}

View File

@ -9,7 +9,6 @@ import (
"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/utils"
"github.com/go-resty/resty/v2"
)
@ -25,15 +24,10 @@ func (d *LanZou) Config() driver.Config {
}
func (d *LanZou) GetAddition() driver.Additional {
return d.Addition
return &d.Addition
}
func (d *LanZou) Init(ctx context.Context, storage model.Storage) error {
d.Storage = storage
err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition)
if err != nil {
return err
}
func (d *LanZou) Init(ctx context.Context) error {
if d.IsCookie() {
if d.RootFolderID == "" {
d.RootFolderID = "-1"
@ -163,7 +157,7 @@ func (d *LanZou) Put(ctx context.Context, dstDir model.Obj, stream model.FileStr
"id": "WU_FILE_0",
"name": stream.GetName(),
"folder_id": dstDir.GetID(),
}).SetFileReader("upload_file", stream.GetName(), stream)
}).SetFileReader("upload_file", stream.GetName(), stream).SetContext(ctx)
}, nil, true)
return err
}

View File

@ -25,7 +25,7 @@ var config = driver.Config{
}
func init() {
op.RegisterDriver(config, func() driver.Driver {
op.RegisterDriver(func() driver.Driver {
return &LanZou{}
})
}

View File

@ -16,11 +16,12 @@ import (
"github.com/alist-org/alist/v3/internal/conf"
"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/internal/sign"
"github.com/alist-org/alist/v3/pkg/utils"
"github.com/alist-org/alist/v3/server/common"
"github.com/disintegration/imaging"
_ "golang.org/x/image/webp"
)
type Local struct {
@ -32,24 +33,18 @@ func (d *Local) Config() driver.Config {
return config
}
func (d *Local) Init(ctx context.Context, storage model.Storage) error {
d.Storage = storage
err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition)
if err != nil {
return err
}
func (d *Local) Init(ctx context.Context) error {
if !utils.Exists(d.GetRootPath()) {
err = fmt.Errorf("root folder %s not exists", d.GetRootPath())
} else {
return fmt.Errorf("root folder %s not exists", d.GetRootPath())
}
if !filepath.IsAbs(d.GetRootPath()) {
abs, err := filepath.Abs(d.GetRootPath())
if err != nil {
return err
}
d.SetRootPath(abs)
d.Addition.RootFolderPath = abs
}
}
return err
return nil
}
func (d *Local) Drop(ctx context.Context) error {
@ -57,7 +52,7 @@ func (d *Local) Drop(ctx context.Context) error {
}
func (d *Local) GetAddition() driver.Additional {
return d.Addition
return &d.Addition
}
func (d *Local) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
@ -75,15 +70,16 @@ func (d *Local) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([
if d.Thumbnail && utils.GetFileType(f.Name()) == conf.IMAGE {
thumb = common.GetApiUrl(nil) + stdpath.Join("/d", args.ReqPath, f.Name())
thumb = utils.EncodePath(thumb, true)
thumb += "?type=thumb"
thumb += "?type=thumb&sign=" + sign.Sign(stdpath.Join(args.ReqPath, f.Name()))
}
isFolder := f.IsDir() || isSymlinkDir(f, fullPath)
size := f.Size()
if isFolder {
size = 0
var size int64
if !isFolder {
size = f.Size()
}
file := model.ObjThumb{
Object: model.Object{
Path: filepath.Join(dir.GetPath(), f.Name()),
Name: f.Name(),
Modified: f.ModTime(),
Size: size,
@ -98,29 +94,6 @@ func (d *Local) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([
return files, nil
}
func (d *Local) Get(ctx context.Context, path string) (model.Obj, error) {
f, err := os.Stat(path)
if err != nil {
if strings.Contains(err.Error(), "cannot find the file") {
return nil, errs.ObjectNotFound
}
return nil, err
}
isFolder := f.IsDir() || isSymlinkDir(f, path)
size := f.Size()
if isFolder {
size = 0
}
file := model.Object{
Path: path,
Name: f.Name(),
Modified: f.ModTime(),
Size: size,
IsFolder: isFolder,
}
return &file, nil
}
func (d *Local) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
fullPath := file.GetPath()
var link model.Link
@ -163,6 +136,9 @@ func (d *Local) MakeDir(ctx context.Context, parentDir model.Obj, dirName string
func (d *Local) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
srcPath := srcObj.GetPath()
dstPath := filepath.Join(dstDir.GetPath(), srcObj.GetName())
if utils.IsSubPath(srcPath, dstPath) {
return fmt.Errorf("the destination folder is a subfolder of the source folder")
}
err := os.Rename(srcPath, dstPath)
if err != nil {
return err
@ -183,6 +159,9 @@ func (d *Local) Rename(ctx context.Context, srcObj model.Obj, newName string) er
func (d *Local) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
srcPath := srcObj.GetPath()
dstPath := filepath.Join(dstDir.GetPath(), srcObj.GetName())
if utils.IsSubPath(srcPath, dstPath) {
return fmt.Errorf("the destination folder is a subfolder of the source folder")
}
var err error
if srcObj.IsDir() {
err = utils.CopyDir(srcPath, dstPath)

View File

@ -19,10 +19,8 @@ var config = driver.Config{
DefaultRoot: "/",
}
func New() driver.Driver {
return &Local{}
}
func init() {
op.RegisterDriver(config, New)
op.RegisterDriver(func() driver.Driver {
return &Local{}
})
}

View File

@ -8,14 +8,12 @@ import (
"io"
"net/http"
"os"
"path"
"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/internal/op"
"github.com/alist-org/alist/v3/pkg/utils"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
@ -36,16 +34,11 @@ func (d *MediaTrack) Config() driver.Config {
}
func (d *MediaTrack) GetAddition() driver.Additional {
return d.Addition
return &d.Addition
}
func (d *MediaTrack) Init(ctx context.Context, storage model.Storage) error {
d.Storage = storage
err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition)
if err != nil {
return err
}
_, err = d.request("https://kayle.api.mediatrack.cn/users", http.MethodGet, nil, nil)
func (d *MediaTrack) Init(ctx context.Context) error {
_, err := d.request("https://kayle.api.mediatrack.cn/users", http.MethodGet, nil, nil)
return err
}
@ -64,7 +57,7 @@ func (d *MediaTrack) List(ctx context.Context, dir model.Obj, args model.ListArg
if f.File != nil && f.File.Cover != "" {
thumb = "https://nano.mtres.cn/" + f.File.Cover
}
return &model.ObjThumb{
return &Object{
Object: model.Object{
ID: f.ID,
Name: f.Title,
@ -73,15 +66,11 @@ func (d *MediaTrack) List(ctx context.Context, dir model.Obj, args model.ListArg
Size: size,
},
Thumbnail: model.Thumbnail{Thumbnail: thumb},
ParentID: dir.GetID(),
}, nil
})
}
//func (d *MediaTrack) Get(ctx context.Context, path string) (model.Obj, error) {
// // this is optional
// return nil, errs.NotImplement
//}
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)
@ -156,16 +145,18 @@ func (d *MediaTrack) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
}
func (d *MediaTrack) Remove(ctx context.Context, obj model.Obj) error {
dir, err := op.Get(ctx, d, path.Dir(obj.GetPath()))
if err != nil {
return err
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": dir.GetID(),
"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) {
_, err := d.request(url, http.MethodDelete, func(req *resty.Request) {
req.SetBody(data)
}, nil)
return err
@ -204,7 +195,7 @@ func (d *MediaTrack) Put(ctx context.Context, dstDir model.Obj, stream model.Fil
Key: &resp.Data.Object,
Body: tempFile,
}
_, err = uploader.Upload(input)
_, err = uploader.UploadWithContext(ctx, input)
if err != nil {
return err
}

View File

@ -18,7 +18,7 @@ var config = driver.Config{
}
func init() {
op.RegisterDriver(config, func() driver.Driver {
op.RegisterDriver(func() driver.Driver {
return &MediaTrack{}
})
}

View File

@ -1,6 +1,10 @@
package mediatrack
import "time"
import (
"time"
"github.com/alist-org/alist/v3/internal/model"
)
type BaseResp struct {
Status string `json:"status"`
@ -60,3 +64,9 @@ type UploadResp struct {
TraceID string `json:"trace_id"`
RequestID string `json:"requestId"`
}
type Object struct {
model.Object
model.Thumbnail
ParentID string
}

View File

@ -9,7 +9,6 @@ import (
"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/chanio"
"github.com/alist-org/alist/v3/pkg/utils"
log "github.com/sirupsen/logrus"
"github.com/t3rm1n4l/go-mega"
@ -26,15 +25,10 @@ func (d *Mega) Config() driver.Config {
}
func (d *Mega) GetAddition() driver.Additional {
return d.Addition
return &d.Addition
}
func (d *Mega) Init(ctx context.Context, storage model.Storage) error {
d.Storage = storage
err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition)
if err != nil {
return err
}
func (d *Mega) Init(ctx context.Context) error {
d.c = mega.New()
return d.c.Login(d.Email, d.Password)
}
@ -62,13 +56,10 @@ func (d *Mega) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]
return nil, fmt.Errorf("unable to convert dir to mega node")
}
func (d *Mega) Get(ctx context.Context, path string) (model.Obj, error) {
if path == "/" {
func (d *Mega) GetRoot(ctx context.Context) (model.Obj, error) {
n := d.c.FS.GetRoot()
log.Debugf("mega root: %+v", *n)
return &MegaNode{n}, nil
}
return nil, errs.NotSupport
}
func (d *Mega) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
@ -85,17 +76,21 @@ func (d *Mega) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*
//u := down.GetResourceUrl()
//u = strings.Replace(u, "http", "https", 1)
//return &model.Link{URL: u}, nil
c := chanio.New()
r, w := io.Pipe()
go func() {
defer func() {
_ = recover()
}()
log.Debugf("chunk size: %d", down.Chunks())
var (
chunk []byte
err error
)
for id := 0; id < down.Chunks(); id++ {
chunk, err := down.DownloadChunk(id)
chunk, err = down.DownloadChunk(id)
if err != nil {
log.Errorf("mega down: %+v", err)
return
break
}
log.Debugf("id: %d,len: %d", id, len(chunk))
//_, _, err = down.ChunkLocation(id)
@ -104,14 +99,16 @@ func (d *Mega) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*
// return
//}
//_, err = c.Write(chunk)
_, err = c.Write(chunk)
if _, err = w.Write(chunk); err != nil {
break
}
err := c.Close()
}
err = w.CloseWithError(err)
if err != nil {
log.Errorf("mega down: %+v", err)
}
}()
return &model.Link{Data: c}, nil
return &model.Link{Data: r}, nil
}
return nil, fmt.Errorf("unable to convert dir to mega node")
}
@ -159,6 +156,9 @@ func (d *Mega) Put(ctx context.Context, dstDir model.Obj, stream model.FileStrea
}
for id := 0; id < u.Chunks(); id++ {
if utils.IsCanceled(ctx) {
return ctx.Err()
}
_, chkSize, err := u.ChunkLocation(id)
if err != nil {
return err

View File

@ -20,7 +20,7 @@ var config = driver.Config{
}
func init() {
op.RegisterDriver(config, func() driver.Driver {
op.RegisterDriver(func() driver.Driver {
return &Mega{}
})
}

View File

@ -2,14 +2,13 @@ package onedrive
import (
"context"
"fmt"
"net/http"
stdpath "path"
"github.com/alist-org/alist/v3/drivers/base"
"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/internal/op"
"github.com/alist-org/alist/v3/pkg/utils"
"github.com/go-resty/resty/v2"
)
@ -25,15 +24,10 @@ func (d *Onedrive) Config() driver.Config {
}
func (d *Onedrive) GetAddition() driver.Additional {
return d.Addition
return &d.Addition
}
func (d *Onedrive) Init(ctx context.Context, storage model.Storage) error {
d.Storage = storage
err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition)
if err != nil {
return err
}
func (d *Onedrive) Init(ctx context.Context) error {
if d.ChunkSize < 1 {
d.ChunkSize = 5
}
@ -50,7 +44,7 @@ func (d *Onedrive) List(ctx context.Context, dir model.Obj, args model.ListArgs)
return nil, err
}
return utils.SliceConvert(files, func(src File) (model.Obj, error) {
return fileToObj(src), nil
return fileToObj(src, dir.GetID()), nil
})
}
@ -95,18 +89,21 @@ func (d *Onedrive) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
}
func (d *Onedrive) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
dstDir, err := op.Get(ctx, d, stdpath.Dir(srcObj.GetPath()))
if err != nil {
return err
//dstDir, err := op.GetUnwrap(ctx, d, stdpath.Dir(srcObj.GetPath()))
var parentID string
if o, ok := srcObj.(*Object); ok {
parentID = o.ParentID
} else {
return fmt.Errorf("srcObj is not Object")
}
data := base.Json{
"parentReference": base.Json{
"id": dstDir.GetID(),
"id": parentID,
},
"name": newName,
}
url := d.GetMetaUrl(false, srcObj.GetPath())
_, err = d.Request(url, http.MethodPatch, func(req *resty.Request) {
_, err := d.Request(url, http.MethodPatch, func(req *resty.Request) {
req.SetBody(data)
}, nil)
return err
@ -140,7 +137,7 @@ func (d *Onedrive) Remove(ctx context.Context, obj model.Obj) error {
func (d *Onedrive) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
var err error
if stream.GetSize() <= 4*1024*1024 {
err = d.upSmall(dstDir, stream)
err = d.upSmall(ctx, dstDir, stream)
} else {
err = d.upBig(ctx, dstDir, stream, up)
}

View File

@ -23,10 +23,8 @@ var config = driver.Config{
DefaultRoot: "/",
}
func New() driver.Driver {
return &Onedrive{}
}
func init() {
op.RegisterDriver(config, New)
op.RegisterDriver(func() driver.Driver {
return &Onedrive{}
})
}

View File

@ -42,12 +42,18 @@ type File struct {
} `json:"parentReference"`
}
func fileToObj(f File) *model.ObjThumbURL {
type Object struct {
model.ObjThumbURL
ParentID string
}
func fileToObj(f File, parentID string) *Object {
thumb := ""
if len(f.Thumbnails) > 0 {
thumb = f.Thumbnails[0].Medium.Url
}
return &model.ObjThumbURL{
return &Object{
ObjThumbURL: model.ObjThumbURL{
Object: model.Object{
ID: f.Id,
Name: f.Name,
@ -57,6 +63,8 @@ func fileToObj(f File) *model.ObjThumbURL {
},
Thumbnail: model.Thumbnail{Thumbnail: thumb},
Url: model.Url{Url: f.Url},
},
ParentID: parentID,
}
}

View File

@ -147,14 +147,14 @@ func (d *Onedrive) GetFile(path string) (*File, error) {
return &file, err
}
func (d *Onedrive) upSmall(dstDir model.Obj, stream model.FileStreamer) error {
func (d *Onedrive) upSmall(ctx context.Context, dstDir model.Obj, stream model.FileStreamer) error {
url := d.GetMetaUrl(false, stdpath.Join(dstDir.GetPath(), stream.GetName())) + "/content"
data, err := io.ReadAll(stream)
if err != nil {
return err
}
_, err = d.Request(url, http.MethodPut, func(req *resty.Request) {
req.SetBody(data)
req.SetBody(data).SetContext(ctx)
}, nil)
return err
}
@ -185,6 +185,10 @@ func (d *Onedrive) upBig(ctx context.Context, dstDir model.Obj, stream model.Fil
return err
}
req, err := http.NewRequest("PUT", uploadUrl, bytes.NewBuffer(byteData))
if err != nil {
return err
}
req = req.WithContext(ctx)
req.Header.Set("Content-Length", strconv.Itoa(int(byteSize)))
req.Header.Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", finish, finish+byteSize-1, stream.GetSize()))
finish += byteSize

View File

@ -35,15 +35,10 @@ func (d *PikPak) Config() driver.Config {
}
func (d *PikPak) GetAddition() driver.Additional {
return d.Addition
return &d.Addition
}
func (d *PikPak) Init(ctx context.Context, storage model.Storage) error {
d.Storage = storage
err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition)
if err != nil {
return err
}
func (d *PikPak) Init(ctx context.Context) error {
return d.login()
}
@ -194,7 +189,7 @@ func (d *PikPak) Put(ctx context.Context, dstDir model.Obj, stream model.FileStr
Key: &key,
Body: tempFile,
}
_, err = uploader.Upload(input)
_, err = uploader.UploadWithContext(ctx, input)
return err
}

View File

@ -17,10 +17,8 @@ var config = driver.Config{
DefaultRoot: "",
}
func New() driver.Driver {
return &PikPak{}
}
func init() {
op.RegisterDriver(config, New)
op.RegisterDriver(func() driver.Driver {
return &PikPak{}
})
}

View File

@ -0,0 +1,81 @@
package pikpak_share
import (
"context"
"net/http"
"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/go-resty/resty/v2"
log "github.com/sirupsen/logrus"
)
type PikPakShare struct {
model.Storage
Addition
RefreshToken string
AccessToken string
PassCodeToken string
}
func (d *PikPakShare) Config() driver.Config {
return config
}
func (d *PikPakShare) GetAddition() driver.Additional {
return &d.Addition
}
func (d *PikPakShare) Init(ctx context.Context) error {
err := d.login()
if err != nil {
return err
}
if d.SharePwd != "" {
err = d.getSharePassToken()
if err != nil {
return err
}
}
return nil
}
func (d *PikPakShare) Drop(ctx context.Context) error {
return nil
}
func (d *PikPakShare) 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(src File) (model.Obj, error) {
return fileToObj(src), nil
})
}
func (d *PikPakShare) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
var resp ShareResp
query := map[string]string{
"share_id": d.ShareId,
"file_id": file.GetID(),
"pass_code_token": d.PassCodeToken,
}
_, err := d.request("https://api-drive.mypikpak.com/drive/v1/share/file_info", http.MethodGet, func(req *resty.Request) {
req.SetQueryParams(query)
}, &resp)
if err != nil {
return nil, err
}
link := model.Link{
URL: resp.FileInfo.WebContentLink,
}
if len(resp.FileInfo.Medias) > 0 && resp.FileInfo.Medias[0].Link.Url != "" {
log.Debugln("use media link")
link.URL = resp.FileInfo.Medias[0].Link.Url
}
return &link, nil
}
var _ driver.Driver = (*PikPakShare)(nil)

View File

@ -0,0 +1,27 @@
package pikpak_share
import (
"github.com/alist-org/alist/v3/internal/driver"
"github.com/alist-org/alist/v3/internal/op"
)
type Addition struct {
driver.RootID
Username string `json:"username" required:"true"`
Password string `json:"password" required:"true"`
ShareId string `json:"share_id" required:"true"`
SharePwd string `json:"share_pwd"`
}
var config = driver.Config{
Name: "PikPakShare",
LocalSort: true,
NoUpload: true,
DefaultRoot: "",
}
func init() {
op.RegisterDriver(func() driver.Driver {
return &PikPakShare{}
})
}

View File

@ -0,0 +1,80 @@
package pikpak_share
import (
"strconv"
"time"
"github.com/alist-org/alist/v3/internal/model"
)
type RespErr struct {
ErrorCode int `json:"error_code"`
Error string `json:"error"`
}
type ShareResp struct {
ShareStatus string `json:"share_status"`
ShareStatusText string `json:"share_status_text"`
FileInfo File `json:"file_info"`
Files []File `json:"files"`
NextPageToken string `json:"next_page_token"`
PassCodeToken string `json:"pass_code_token"`
}
type File struct {
Id string `json:"id"`
ShareId string `json:"share_id"`
Kind string `json:"kind"`
Name string `json:"name"`
ModifiedTime time.Time `json:"modified_time"`
Size string `json:"size"`
ThumbnailLink string `json:"thumbnail_link"`
WebContentLink string `json:"web_content_link"`
Medias []Media `json:"medias"`
}
func fileToObj(f File) *model.ObjThumb {
size, _ := strconv.ParseInt(f.Size, 10, 64)
return &model.ObjThumb{
Object: model.Object{
ID: f.Id,
Name: f.Name,
Size: size,
Modified: f.ModifiedTime,
IsFolder: f.Kind == "drive#folder",
},
Thumbnail: model.Thumbnail{
Thumbnail: f.ThumbnailLink,
},
}
}
type Media struct {
MediaId string `json:"media_id"`
MediaName string `json:"media_name"`
Video struct {
Height int `json:"height"`
Width int `json:"width"`
Duration int `json:"duration"`
BitRate int `json:"bit_rate"`
FrameRate int `json:"frame_rate"`
VideoCodec string `json:"video_codec"`
AudioCodec string `json:"audio_codec"`
VideoType string `json:"video_type"`
} `json:"video"`
Link struct {
Url string `json:"url"`
Token string `json:"token"`
Expire time.Time `json:"expire"`
} `json:"link"`
NeedMoreQuota bool `json:"need_more_quota"`
VipTypes []interface{} `json:"vip_types"`
RedirectLink string `json:"redirect_link"`
IconLink string `json:"icon_link"`
IsDefault bool `json:"is_default"`
Priority int `json:"priority"`
IsOrigin bool `json:"is_origin"`
ResolutionName string `json:"resolution_name"`
IsVisible bool `json:"is_visible"`
Category string `json:"category"`
}

View File

@ -0,0 +1,154 @@
package pikpak_share
import (
"errors"
"net/http"
"github.com/alist-org/alist/v3/drivers/base"
"github.com/alist-org/alist/v3/internal/op"
"github.com/go-resty/resty/v2"
jsoniter "github.com/json-iterator/go"
)
// do others that not defined in Driver interface
func (d *PikPakShare) login() error {
url := "https://user.mypikpak.com/v1/auth/signin"
var e RespErr
res, err := base.RestyClient.R().SetError(&e).SetBody(base.Json{
"captcha_token": "",
"client_id": "YNxT9w7GMdWvEOKa",
"client_secret": "dbw2OtmVEeuUvIptb1Coyg",
"username": d.Username,
"password": d.Password,
}).Post(url)
if err != nil {
return err
}
if e.ErrorCode != 0 {
return errors.New(e.Error)
}
data := res.Body()
d.RefreshToken = jsoniter.Get(data, "refresh_token").ToString()
d.AccessToken = jsoniter.Get(data, "access_token").ToString()
return nil
}
func (d *PikPakShare) refreshToken() error {
url := "https://user.mypikpak.com/v1/auth/token"
var e RespErr
res, err := base.RestyClient.R().SetError(&e).
SetHeader("user-agent", "").SetBody(base.Json{
"client_id": "YNxT9w7GMdWvEOKa",
"client_secret": "dbw2OtmVEeuUvIptb1Coyg",
"grant_type": "refresh_token",
"refresh_token": d.RefreshToken,
}).Post(url)
if err != nil {
d.Status = err.Error()
op.MustSaveDriverStorage(d)
return err
}
if e.ErrorCode != 0 {
if e.ErrorCode == 4126 {
// refresh_token invalid, re-login
return d.login()
}
d.Status = e.Error
op.MustSaveDriverStorage(d)
return errors.New(e.Error)
}
data := res.Body()
d.Status = "work"
d.RefreshToken = jsoniter.Get(data, "refresh_token").ToString()
d.AccessToken = jsoniter.Get(data, "access_token").ToString()
op.MustSaveDriverStorage(d)
return nil
}
func (d *PikPakShare) request(url string, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) {
req := base.RestyClient.R()
req.SetHeader("Authorization", "Bearer "+d.AccessToken)
if callback != nil {
callback(req)
}
if resp != nil {
req.SetResult(resp)
}
var e RespErr
req.SetError(&e)
res, err := req.Execute(method, url)
if err != nil {
return nil, err
}
if e.ErrorCode != 0 {
if e.ErrorCode == 16 {
// login / refresh token
err = d.refreshToken()
if err != nil {
return nil, err
}
return d.request(url, method, callback, resp)
}
return nil, errors.New(e.Error)
}
return res.Body(), nil
}
func (d *PikPakShare) getSharePassToken() error {
query := map[string]string{
"share_id": d.ShareId,
"pass_code": d.SharePwd,
"thumbnail_size": "SIZE_LARGE",
"limit": "100",
}
var resp ShareResp
_, err := d.request("https://api-drive.mypikpak.com/drive/v1/share", http.MethodGet, func(req *resty.Request) {
req.SetQueryParams(query)
}, &resp)
if err != nil {
return err
}
d.PassCodeToken = resp.PassCodeToken
return nil
}
func (d *PikPakShare) getFiles(id string) ([]File, error) {
res := make([]File, 0)
pageToken := "first"
for pageToken != "" {
if pageToken == "first" {
pageToken = ""
}
query := map[string]string{
"parent_id": id,
"share_id": d.ShareId,
"thumbnail_size": "SIZE_LARGE",
"with_audit": "true",
"limit": "100",
"filters": `{"phase":{"eq":"PHASE_TYPE_COMPLETE"},"trashed":{"eq":false}}`,
"page_token": pageToken,
"pass_code_token": d.PassCodeToken,
}
var resp ShareResp
_, err := d.request("https://api-drive.mypikpak.com/drive/v1/share/detail", http.MethodGet, func(req *resty.Request) {
req.SetQueryParams(query)
}, &resp)
if err != nil {
return nil, err
}
if resp.ShareStatus != "OK" {
if resp.ShareStatus == "PASS_CODE_EMPTY" || resp.ShareStatus == "PASS_CODE_ERROR" {
err = d.getSharePassToken()
if err != nil {
return nil, err
}
return d.getFiles(id)
}
return nil, errors.New(resp.ShareStatusText)
}
pageToken = resp.NextPageToken
res = append(res, resp.Files...)
}
return res, nil
}

View File

@ -29,16 +29,11 @@ func (d *Quark) Config() driver.Config {
}
func (d *Quark) GetAddition() driver.Additional {
return d.Addition
return &d.Addition
}
func (d *Quark) Init(ctx context.Context, storage model.Storage) error {
d.Storage = storage
err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition)
if err != nil {
return err
}
_, err = d.request("/config", http.MethodGet, nil, nil)
func (d *Quark) Init(ctx context.Context) error {
_, err := d.request("/config", http.MethodGet, nil, nil)
return err
}
@ -56,11 +51,6 @@ func (d *Quark) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([
})
}
//func (d *Quark) Get(ctx context.Context, path string) (model.Obj, error) {
// // TODO this is optional
// return nil, errs.NotImplement
//}
func (d *Quark) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
data := base.Json{
"fids": []string{file.GetID()},
@ -189,6 +179,9 @@ func (d *Quark) Put(ctx context.Context, dstDir model.Obj, stream model.FileStre
partNumber := 1
sizeDivide100 := stream.GetSize() / 100
for left > 0 {
if utils.IsCanceled(ctx) {
return ctx.Err()
}
if left > int64(partSize) {
bytes = defaultBytes
} else {
@ -200,7 +193,7 @@ func (d *Quark) Put(ctx context.Context, dstDir model.Obj, stream model.FileStre
}
left -= int64(partSize)
log.Debugf("left: %d", left)
m, err := d.upPart(pre, stream.GetMimetype(), partNumber, bytes)
m, err := d.upPart(ctx, pre, stream.GetMimetype(), partNumber, bytes)
//m, err := driver.UpPart(pre, file.GetMIMEType(), partNumber, bytes, account, md5Str, sha1Str)
if err != nil {
return err

View File

@ -18,10 +18,8 @@ var config = driver.Config{
DefaultRoot: "0",
}
func New() driver.Driver {
return &Quark{}
}
func init() {
op.RegisterDriver(config, New)
op.RegisterDriver(func() driver.Driver {
return &Quark{}
})
}

View File

@ -1,6 +1,7 @@
package quark
import (
"context"
"crypto/md5"
"encoding/base64"
"errors"
@ -118,7 +119,7 @@ func (d *Quark) upHash(md5, sha1, taskId string) (bool, error) {
return resp.Data.Finish, err
}
func (d *Quark) upPart(pre UpPreResp, mineType string, partNumber int, bytes []byte) (string, error) {
func (d *Quark) upPart(ctx context.Context, pre UpPreResp, mineType string, partNumber int, bytes []byte) (string, error) {
//func (driver Quark) UpPart(pre UpPreResp, mineType string, partNumber int, bytes []byte, account *model.Account, md5Str, sha1Str string) (string, error) {
timeStr := time.Now().UTC().Format(http.TimeFormat)
data := base.Json{
@ -135,7 +136,7 @@ x-oss-user-agent:aliyun-sdk-js/6.6.1 Chrome 98.0.4758.80 on Windows 10 64-bit
}
var resp UpAuthResp
_, err := d.request("/file/upload/auth", http.MethodPost, func(req *resty.Request) {
req.SetBody(data)
req.SetBody(data).SetContext(ctx)
}, &resp)
if err != nil {
return "", err
@ -150,7 +151,7 @@ x-oss-user-agent:aliyun-sdk-js/6.6.1 Chrome 98.0.4758.80 on Windows 10 64-bit
// }
//}
u := fmt.Sprintf("https://%s.%s/%s", pre.Data.Bucket, pre.Data.UploadUrl[7:], pre.Data.ObjKey)
res, err := base.RestyClient.R().
res, err := base.RestyClient.R().SetContext(ctx).
SetHeaders(map[string]string{
"Authorization": resp.Data.AuthKey,
"Content-Type": mineType,

View File

@ -11,7 +11,6 @@ import (
"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/session"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/aws/aws-sdk-go/service/s3/s3manager"
@ -31,19 +30,14 @@ func (d *S3) Config() driver.Config {
}
func (d *S3) GetAddition() driver.Additional {
return d.Addition
return &d.Addition
}
func (d *S3) Init(ctx context.Context, storage model.Storage) error {
d.Storage = storage
err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition)
if err != nil {
return err
}
func (d *S3) Init(ctx context.Context) error {
if d.Region == "" {
d.Region = "alist"
}
err = d.initSession()
err := d.initSession()
if err != nil {
return err
}
@ -63,11 +57,6 @@ func (d *S3) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]mo
return d.listV1(dir.GetPath())
}
//func (d *S3) Get(ctx context.Context, path string) (model.Obj, error) {
// // this is optional
// return nil, errs.NotImplement
//}
func (d *S3) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
path := getKey(file.GetPath(), false)
disposition := fmt.Sprintf(`attachment;filename="%s"`, url.QueryEscape(stdpath.Base(path)))
@ -145,7 +134,7 @@ func (d *S3) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreame
Key: &key,
Body: stream,
}
_, err := uploader.Upload(input)
_, err := uploader.UploadWithContext(ctx, input)
return err
}

View File

@ -21,14 +21,13 @@ type Addition struct {
var config = driver.Config{
Name: "S3",
DefaultRoot: "/",
LocalSort: true,
CheckStatus: true,
}
func New() driver.Driver {
return &S3{}
}
func init() {
op.RegisterDriver(config, New)
op.RegisterDriver(func() driver.Driver {
return &S3{}
})
}

View File

@ -23,15 +23,10 @@ func (d *SFTP) Config() driver.Config {
}
func (d *SFTP) GetAddition() driver.Additional {
return d.Addition
return &d.Addition
}
func (d *SFTP) Init(ctx context.Context, storage model.Storage) error {
d.Storage = storage
err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition)
if err != nil {
return err
}
func (d *SFTP) Init(ctx context.Context) error {
return d.initClient()
}
@ -52,11 +47,6 @@ func (d *SFTP) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]
})
}
//func (d *SFTP) Get(ctx context.Context, path string) (model.Obj, error) {
// // this is optional
// return nil, errs.NotImplement
//}
func (d *SFTP) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
remoteFile, err := d.client.Open(file.GetPath())
if err != nil {

View File

@ -21,10 +21,8 @@ var config = driver.Config{
CheckStatus: true,
}
func New() driver.Driver {
return &SFTP{}
}
func init() {
op.RegisterDriver(config, New)
op.RegisterDriver(func() driver.Driver {
return &SFTP{}
})
}

View File

@ -4,6 +4,7 @@ import (
"context"
"errors"
"path/filepath"
"strings"
"time"
"github.com/alist-org/alist/v3/internal/driver"
@ -25,14 +26,12 @@ func (d *SMB) Config() driver.Config {
}
func (d *SMB) GetAddition() driver.Additional {
return d.Addition
return &d.Addition
}
func (d *SMB) Init(ctx context.Context, storage model.Storage) error {
d.Storage = storage
err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition)
if err != nil {
return err
func (d *SMB) Init(ctx context.Context) error {
if strings.Index(d.Addition.Address, ":") < 0 {
d.Addition.Address = d.Addition.Address + ":445"
}
return d.initFS()
}
@ -70,11 +69,6 @@ func (d *SMB) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]m
return files, nil
}
//func (d *SMB) Get(ctx context.Context, path string) (model.Obj, error) {
// // this is optional
// return nil, errs.NotImplement
//}
func (d *SMB) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
if err := d.checkConn(); err != nil {
return nil, err

View File

@ -22,7 +22,7 @@ var config = driver.Config{
}
func init() {
op.RegisterDriver(config, func() driver.Driver {
op.RegisterDriver(func() driver.Driver {
return &SMB{}
})
}

View File

@ -8,7 +8,6 @@ import (
"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/go-resty/resty/v2"
)
@ -22,16 +21,11 @@ func (d *Teambition) Config() driver.Config {
}
func (d *Teambition) GetAddition() driver.Additional {
return d.Addition
return &d.Addition
}
func (d *Teambition) Init(ctx context.Context, storage model.Storage) error {
d.Storage = storage
err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition)
if err != nil {
return err
}
_, err = d.request("/api/v2/roles", http.MethodGet, nil, nil)
func (d *Teambition) Init(ctx context.Context) error {
_, err := d.request("/api/v2/roles", http.MethodGet, nil, nil)
return err
}
@ -43,11 +37,6 @@ func (d *Teambition) List(ctx context.Context, dir model.Obj, args model.ListArg
return d.getFiles(dir.GetID())
}
//func (d *Teambition) Get(ctx context.Context, path string) (model.Obj, error) {
// // this is optional
// return nil, errs.NotImplement
//}
func (d *Teambition) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
if u, ok := file.(model.URL); ok {
url := u.URL()
@ -143,11 +132,11 @@ func (d *Teambition) Put(ctx context.Context, dstDir model.Obj, stream model.Fil
var newFile *FileUpload
if stream.GetSize() <= 20971520 {
// post upload
newFile, err = d.upload(stream, token)
newFile, err = d.upload(ctx, stream, token)
} else {
// chunk upload
//err = base.ErrNotImplement
newFile, err = d.chunkUpload(stream, token, up)
newFile, err = d.chunkUpload(ctx, stream, token, up)
}
if err != nil {
return err

View File

@ -18,10 +18,8 @@ var config = driver.Config{
Name: "Teambition",
}
func New() driver.Driver {
return &Teambition{}
}
func init() {
op.RegisterDriver(config, New)
op.RegisterDriver(func() driver.Driver {
return &Teambition{}
})
}

View File

@ -1,6 +1,7 @@
package teambition
import (
"context"
"errors"
"fmt"
"io"
@ -12,6 +13,7 @@ import (
"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/go-resty/resty/v2"
log "github.com/sirupsen/logrus"
)
@ -115,13 +117,15 @@ func (d *Teambition) getFiles(parentId string) ([]model.Obj, error) {
return files, nil
}
func (d *Teambition) upload(file model.FileStreamer, token string) (*FileUpload, error) {
func (d *Teambition) upload(ctx context.Context, file model.FileStreamer, token string) (*FileUpload, error) {
prefix := "tcs"
if d.isInternational() {
prefix = "us-tcs"
}
var newFile FileUpload
_, err := base.RestyClient.R().SetResult(&newFile).SetHeader("Authorization", token).
_, err := base.RestyClient.R().
SetContext(ctx).
SetResult(&newFile).SetHeader("Authorization", token).
SetMultipartFormData(map[string]string{
"name": file.GetName(),
"type": file.GetMimetype(),
@ -135,7 +139,7 @@ func (d *Teambition) upload(file model.FileStreamer, token string) (*FileUpload,
return &newFile, nil
}
func (d *Teambition) chunkUpload(file model.FileStreamer, token string, up driver.UpdateProgress) (*FileUpload, error) {
func (d *Teambition) chunkUpload(ctx context.Context, file model.FileStreamer, token string, up driver.UpdateProgress) (*FileUpload, error) {
prefix := "tcs"
referer := "https://www.teambition.com/"
if d.isInternational() {
@ -153,6 +157,9 @@ func (d *Teambition) chunkUpload(file model.FileStreamer, token string, up drive
return nil, err
}
for i := 0; i < newChunk.Chunks; i++ {
if utils.IsCanceled(ctx) {
return nil, ctx.Err()
}
chunkSize := newChunk.ChunkSize
if i == newChunk.Chunks-1 {
chunkSize = int(file.GetSize()) - i*chunkSize
@ -166,7 +173,9 @@ func (d *Teambition) chunkUpload(file model.FileStreamer, token string, up drive
u := fmt.Sprintf("https://%s.teambition.net/upload/chunk/%s?chunk=%d&chunks=%d",
prefix, newChunk.FileKey, i+1, newChunk.Chunks)
log.Debugf("url: %s", u)
_, err := base.RestyClient.R().SetHeaders(map[string]string{
_, err := base.RestyClient.R().
SetContext(ctx).
SetHeaders(map[string]string{
"Authorization": token,
"Content-Type": "application/octet-stream",
"Referer": referer,

View File

@ -6,7 +6,6 @@ import (
"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/utils"
)
type Template struct {
@ -19,18 +18,13 @@ func (d *Template) Config() driver.Config {
}
func (d *Template) GetAddition() driver.Additional {
return d.Addition
return &d.Addition
}
func (d *Template) Init(ctx context.Context, storage model.Storage) error {
d.Storage = storage
err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition)
if err != nil {
return err
}
func (d *Template) Init(ctx context.Context) error {
// TODO login / refresh token
//op.MustSaveDriverStorage(d)
return err
return nil
}
func (d *Template) Drop(ctx context.Context) error {
@ -42,11 +36,6 @@ func (d *Template) List(ctx context.Context, dir model.Obj, args model.ListArgs)
return nil, errs.NotImplement
}
//func (d *Template) Get(ctx context.Context, path string) (model.Obj, error) {
// // this is optional
// return nil, errs.NotImplement
//}
func (d *Template) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
// TODO return link of file
return nil, errs.NotImplement

View File

@ -25,7 +25,7 @@ var config = driver.Config{
}
func init() {
op.RegisterDriver(config, func() driver.Driver {
op.RegisterDriver(func() driver.Driver {
return &Template{}
})
}

214
drivers/terabox/driver.go Normal file
View File

@ -0,0 +1,214 @@
package terbox
import (
"bytes"
"context"
"crypto/md5"
"encoding/hex"
"fmt"
"github.com/alist-org/alist/v3/drivers/base"
"github.com/alist-org/alist/v3/pkg/utils"
log "github.com/sirupsen/logrus"
"io"
"math"
"net/http"
"os"
stdpath "path"
"strconv"
"strings"
"github.com/alist-org/alist/v3/internal/driver"
"github.com/alist-org/alist/v3/internal/model"
)
type Terabox struct {
model.Storage
Addition
}
func (d *Terabox) Config() driver.Config {
return config
}
func (d *Terabox) GetAddition() driver.Additional {
return &d.Addition
}
func (d *Terabox) Init(ctx context.Context) error {
_, err := d.request("https://www.terabox.com/api/check/login", http.MethodGet, nil, nil)
return err
}
func (d *Terabox) Drop(ctx context.Context) error {
return nil
}
func (d *Terabox) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
files, err := d.getFiles(dir.GetPath())
if err != nil {
return nil, err
}
return utils.SliceConvert(files, func(src File) (model.Obj, error) {
return fileToObj(src), nil
})
}
func (d *Terabox) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
if d.DownloadAPI == "crack" {
return d.linkCrack(file, args)
}
return d.linkOfficial(file, args)
}
func (d *Terabox) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
_, err := d.create(stdpath.Join(parentDir.GetPath(), dirName), 0, 1, "", "")
return err
}
func (d *Terabox) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
data := []base.Json{
{
"path": srcObj.GetPath(),
"dest": dstDir.GetPath(),
"newname": srcObj.GetName(),
},
}
_, err := d.manage("move", data)
return err
}
func (d *Terabox) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
data := []base.Json{
{
"path": srcObj.GetPath(),
"newname": newName,
},
}
_, err := d.manage("rename", data)
return err
}
func (d *Terabox) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
data := []base.Json{
{
"path": srcObj.GetPath(),
"dest": dstDir.GetPath(),
"newname": srcObj.GetName(),
},
}
_, err := d.manage("copy", data)
return err
}
func (d *Terabox) Remove(ctx context.Context, obj model.Obj) error {
data := []string{obj.GetPath()}
_, err := d.manage("delete", data)
return err
}
func (d *Terabox) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
tempFile, err := utils.CreateTempFile(stream.GetReadCloser())
if err != nil {
return err
}
defer func() {
_ = tempFile.Close()
_ = os.Remove(tempFile.Name())
}()
var Default int64 = 4 * 1024 * 1024
defaultByteData := make([]byte, Default)
count := int(math.Ceil(float64(stream.GetSize()) / float64(Default)))
// cal md5
h1 := md5.New()
h2 := md5.New()
block_list := make([]string, 0)
left := stream.GetSize()
for i := 0; i < count; i++ {
byteSize := Default
var byteData []byte
if left < Default {
byteSize = left
byteData = make([]byte, byteSize)
} else {
byteData = defaultByteData
}
left -= byteSize
_, err = io.ReadFull(tempFile, byteData)
if err != nil {
return err
}
h1.Write(byteData)
h2.Write(byteData)
block_list = append(block_list, fmt.Sprintf("\"%s\"", hex.EncodeToString(h2.Sum(nil))))
h2.Reset()
}
_, err = tempFile.Seek(0, io.SeekStart)
if err != nil {
return err
}
rawPath := stdpath.Join(dstDir.GetPath(), stream.GetName())
path := encodeURIComponent(rawPath)
block_list_str := fmt.Sprintf("[%s]", strings.Join(block_list, ","))
data := fmt.Sprintf("path=%s&size=%d&isdir=0&autoinit=1&block_list=%s",
path, stream.GetSize(),
block_list_str)
params := map[string]string{}
var precreateResp PrecreateResp
_, err = d.post("/api/precreate", params, data, &precreateResp)
if err != nil {
return err
}
log.Debugf("%+v", precreateResp)
if precreateResp.ReturnType == 2 {
return nil
}
params = map[string]string{
"method": "upload",
"path": path,
"uploadid": precreateResp.Uploadid,
"app_id": "250528",
"web": "1",
"channel": "dubox",
"clienttype": "0",
}
left = stream.GetSize()
for i, partseq := range precreateResp.BlockList {
if utils.IsCanceled(ctx) {
return ctx.Err()
}
byteSize := Default
var byteData []byte
if left < Default {
byteSize = left
byteData = make([]byte, byteSize)
} else {
byteData = defaultByteData
}
left -= byteSize
_, err = io.ReadFull(tempFile, byteData)
if err != nil {
return err
}
u := "https://c-jp.terabox.com/rest/2.0/pcs/superfile2"
params["partseq"] = strconv.Itoa(partseq)
res, err := base.RestyClient.R().
SetContext(ctx).
SetQueryParams(params).
SetFileReader("file", stream.GetName(), bytes.NewReader(byteData)).
SetHeader("Cookie", d.Cookie).
Post(u)
if err != nil {
return err
}
log.Debugln(res.String())
if len(precreateResp.BlockList) > 0 {
up(i * 100 / len(precreateResp.BlockList))
}
}
_, err = d.create(rawPath, stream.GetSize(), 0, precreateResp.Uploadid, block_list_str)
return err
}
var _ driver.Driver = (*Terabox)(nil)

25
drivers/terabox/meta.go Normal file
View File

@ -0,0 +1,25 @@
package terbox
import (
"github.com/alist-org/alist/v3/internal/driver"
"github.com/alist-org/alist/v3/internal/op"
)
type Addition struct {
driver.RootPath
Cookie string `json:"cookie" required:"true"`
DownloadAPI string `json:"download_api" type:"select" options:"official,crack" default:"official"`
OrderBy string `json:"order_by" type:"select" options:"name,time,size" default:"name"`
OrderDirection string `json:"order_direction" type:"select" options:"asc,desc" default:"asc"`
}
var config = driver.Config{
Name: "Terabox",
DefaultRoot: "/",
}
func init() {
op.RegisterDriver(func() driver.Driver {
return &Terabox{}
})
}

92
drivers/terabox/types.go Normal file
View File

@ -0,0 +1,92 @@
package terbox
import (
"github.com/alist-org/alist/v3/internal/model"
"strconv"
"time"
)
type File struct {
//TkbindId int `json:"tkbind_id"`
//OwnerType int `json:"owner_type"`
//Category int `json:"category"`
//RealCategory string `json:"real_category"`
FsId int64 `json:"fs_id"`
ServerMtime int64 `json:"server_mtime"`
//OperId int `json:"oper_id"`
//ServerCtime int `json:"server_ctime"`
Thumbs struct {
//Icon string `json:"icon"`
Url3 string `json:"url3"`
//Url2 string `json:"url2"`
//Url1 string `json:"url1"`
} `json:"thumbs"`
//Wpfile int `json:"wpfile"`
//LocalMtime int `json:"local_mtime"`
Size int64 `json:"size"`
//ExtentTinyint7 int `json:"extent_tinyint7"`
Path string `json:"path"`
//Share int `json:"share"`
//ServerAtime int `json:"server_atime"`
//Pl int `json:"pl"`
//LocalCtime int `json:"local_ctime"`
ServerFilename string `json:"server_filename"`
//Md5 string `json:"md5"`
//OwnerId int `json:"owner_id"`
//Unlist int `json:"unlist"`
Isdir int `json:"isdir"`
}
type ListResp struct {
Errno int `json:"errno"`
GuidInfo string `json:"guid_info"`
List []File `json:"list"`
RequestId int64 `json:"request_id"`
Guid int `json:"guid"`
}
func fileToObj(f File) *model.ObjThumb {
return &model.ObjThumb{
Object: model.Object{
ID: strconv.FormatInt(f.FsId, 10),
Name: f.ServerFilename,
Size: f.Size,
Modified: time.Unix(f.ServerMtime, 0),
IsFolder: f.Isdir == 1,
},
Thumbnail: model.Thumbnail{Thumbnail: f.Thumbs.Url3},
}
}
type DownloadResp struct {
Errno int `json:"errno"`
Dlink []struct {
Dlink string `json:"dlink"`
} `json:"dlink"`
}
type DownloadResp2 struct {
Errno int `json:"errno"`
Info []struct {
Dlink string `json:"dlink"`
} `json:"info"`
RequestID int64 `json:"request_id"`
}
type HomeInfoResp struct {
Errno int `json:"errno"`
Data struct {
Sign1 string `json:"sign1"`
Sign3 string `json:"sign3"`
Timestamp int `json:"timestamp"`
} `json:"data"`
}
type PrecreateResp struct {
Path string `json:"path"`
Uploadid string `json:"uploadid"`
ReturnType int `json:"return_type"`
BlockList []int `json:"block_list"`
Errno int `json:"errno"`
RequestId int64 `json:"request_id"`
}

192
drivers/terabox/util.go Normal file
View File

@ -0,0 +1,192 @@
package terbox
import (
"encoding/base64"
"fmt"
"github.com/alist-org/alist/v3/drivers/base"
"github.com/alist-org/alist/v3/internal/model"
"github.com/alist-org/alist/v3/pkg/utils"
"github.com/go-resty/resty/v2"
"net/http"
"net/url"
"strconv"
"strings"
"time"
)
func (d *Terabox) request(furl string, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) {
req := base.RestyClient.R()
req.SetHeaders(map[string]string{
"Cookie": d.Cookie,
"Accept": "application/json, text/plain, */*",
"Referer": "https://www.terabox.com/",
"User-Agent": base.UserAgent,
})
req.SetQueryParam("app_id", "250528")
req.SetQueryParam("web", "1")
req.SetQueryParam("channel", "dubox")
req.SetQueryParam("clienttype", "0")
if callback != nil {
callback(req)
}
if resp != nil {
req.SetResult(resp)
}
res, err := req.Execute(method, furl)
if err != nil {
return nil, err
}
return res.Body(), nil
}
func (d *Terabox) get(pathname string, params map[string]string, resp interface{}) ([]byte, error) {
return d.request("https://www.terabox.com"+pathname, http.MethodGet, func(req *resty.Request) {
req.SetQueryParams(params)
}, resp)
}
func (d *Terabox) post(pathname string, params map[string]string, data interface{}, resp interface{}) ([]byte, error) {
return d.request("https://www.terabox.com"+pathname, http.MethodPost, func(req *resty.Request) {
req.SetQueryParams(params)
req.SetBody(data)
}, resp)
}
func (d *Terabox) getFiles(dir string) ([]File, error) {
page := 1
num := 100
params := map[string]string{
"dir": dir,
}
if d.OrderBy != "" {
params["order"] = d.OrderBy
if d.OrderDirection == "desc" {
params["desc"] = "1"
}
}
res := make([]File, 0)
for {
params["page"] = strconv.Itoa(page)
params["num"] = strconv.Itoa(num)
var resp ListResp
_, err := d.get("/api/list", params, &resp)
if err != nil {
return nil, err
}
if len(resp.List) == 0 {
break
}
res = append(res, resp.List...)
page++
}
return res, nil
}
func sign(s1, s2 string) string {
var a = make([]int, 256)
var p = make([]int, 256)
var o []byte
var v = len(s1)
for q := 0; q < 256; q++ {
a[q] = int(s1[(q % v) : (q%v)+1][0])
p[q] = q
}
for u, q := 0, 0; q < 256; q++ {
u = (u + p[q] + a[q]) % 256
p[q], p[u] = p[u], p[q]
}
for i, u, q := 0, 0, 0; q < len(s2); q++ {
i = (i + 1) % 256
u = (u + p[i]) % 256
p[i], p[u] = p[u], p[i]
k := p[((p[i] + p[u]) % 256)]
o = append(o, byte(int(s2[q])^k))
}
return base64.StdEncoding.EncodeToString(o)
}
func (d *Terabox) genSign() (string, error) {
var resp HomeInfoResp
_, err := d.get("/api/home/info", map[string]string{}, &resp)
if err != nil {
return "", err
}
return sign(resp.Data.Sign3, resp.Data.Sign1), nil
}
func (d *Terabox) linkOfficial(file model.Obj, args model.LinkArgs) (*model.Link, error) {
var resp DownloadResp
signString, err := d.genSign()
if err != nil {
return nil, err
}
params := map[string]string{
"type": "dlink",
"fidlist": fmt.Sprintf("[%s]", file.GetID()),
"sign": signString,
"vip": "2",
"timestamp": strconv.FormatInt(time.Now().Unix(), 10),
}
_, err = d.get("/api/download", params, &resp)
if err != nil {
return nil, err
}
res, err := base.NoRedirectClient.R().SetHeader("Cookie", d.Cookie).SetHeader("User-Agent", base.UserAgent).Get(resp.Dlink[0].Dlink)
if err != nil {
return nil, err
}
u := res.Header().Get("location")
return &model.Link{
URL: u,
Header: http.Header{
"User-Agent": []string{base.UserAgent},
},
}, nil
}
func (d *Terabox) linkCrack(file model.Obj, args model.LinkArgs) (*model.Link, error) {
var resp DownloadResp2
param := map[string]string{
"target": fmt.Sprintf("[\"%s\"]", file.GetPath()),
"dlink": "1",
"origin": "dlna",
}
_, err := d.get("/api/filemetas", param, &resp)
if err != nil {
return nil, err
}
return &model.Link{
URL: resp.Info[0].Dlink,
Header: http.Header{
"User-Agent": []string{base.UserAgent},
},
}, nil
}
func (d *Terabox) manage(opera string, filelist interface{}) ([]byte, error) {
params := map[string]string{
"onnest": "fail",
"opera": opera,
}
marshal, err := utils.Json.Marshal(filelist)
if err != nil {
return nil, err
}
data := fmt.Sprintf("async=0&filelist=%s&ondup=newcopy", string(marshal))
return d.post("/api/filemanager", params, data, nil)
}
func (d *Terabox) create(path string, size int64, isdir int, uploadid, block_list string) ([]byte, error) {
params := map[string]string{}
data := fmt.Sprintf("path=%s&size=%d&isdir=%d", encodeURIComponent(path), size, isdir)
if uploadid != "" {
data += fmt.Sprintf("&uploadid=%s&block_list=%s", uploadid, block_list)
}
return d.post("/api/create", params, data, nil)
}
func encodeURIComponent(str string) string {
r := url.QueryEscape(str)
r = strings.ReplaceAll(r, "+", "%20")
return r
}

View File

@ -32,15 +32,10 @@ func (x *Thunder) Config() driver.Config {
}
func (x *Thunder) GetAddition() driver.Additional {
return x.Addition
return &x.Addition
}
func (x *Thunder) Init(ctx context.Context, storage model.Storage) (err error) {
x.Storage = storage
if err = utils.Json.UnmarshalFromString(x.Storage.Addition, &x.Addition); err != nil {
return err
}
func (x *Thunder) Init(ctx context.Context) (err error) {
// 初始化所需参数
if x.XunLeiCommon == nil {
x.XunLeiCommon = &XunLeiCommon{
@ -126,15 +121,10 @@ func (x *ThunderExpert) Config() driver.Config {
}
func (x *ThunderExpert) GetAddition() driver.Additional {
return x.ExpertAddition
return &x.ExpertAddition
}
func (x *ThunderExpert) Init(ctx context.Context, storage model.Storage) (err error) {
x.Storage = storage
if err = utils.Json.UnmarshalFromString(x.Storage.Addition, &x.ExpertAddition); err != nil {
return err
}
func (x *ThunderExpert) Init(ctx context.Context) (err error) {
// 防止重复登录
identity := x.GetIdentity()
if identity != x.identity || !x.IsLogin() {

View File

@ -93,10 +93,10 @@ var configExpert = driver.Config{
}
func init() {
op.RegisterDriver(config, func() driver.Driver {
op.RegisterDriver(func() driver.Driver {
return &Thunder{}
})
op.RegisterDriver(configExpert, func() driver.Driver {
op.RegisterDriver(func() driver.Driver {
return &ThunderExpert{}
})
}

View File

@ -25,15 +25,10 @@ func (d *USS) Config() driver.Config {
}
func (d *USS) GetAddition() driver.Additional {
return d.Addition
return &d.Addition
}
func (d *USS) Init(ctx context.Context, storage model.Storage) error {
d.Storage = storage
err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition)
if err != nil {
return err
}
func (d *USS) Init(ctx context.Context) error {
d.client = upyun.NewUpYun(&upyun.UpYunConfig{
Bucket: d.Bucket,
Operator: d.OperatorName,
@ -49,19 +44,17 @@ func (d *USS) Drop(ctx context.Context) error {
func (d *USS) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
prefix := getKey(dir.GetPath(), true)
objsChan := make(chan *upyun.FileInfo, 10)
var err error
defer close(objsChan)
go func() {
err = d.client.List(&upyun.GetObjectsConfig{
cfg := &upyun.GetObjectsConfig{
Path: prefix,
ObjectsChan: objsChan,
MaxListObjects: 0,
MaxListLevel: 1,
})
}()
if err != nil {
}
if err := d.client.List(cfg); err != nil {
return nil, err
}
res := make([]model.Obj, 0)
for obj := range objsChan {
t := obj.Time
@ -73,14 +66,9 @@ func (d *USS) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]m
}
res = append(res, &f)
}
return res, err
return res, nil
}
//func (d *USS) Get(ctx context.Context, path string) (model.Obj, error) {
// // this is optional
// return nil, errs.NotImplement
//}
func (d *USS) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
key := getKey(file.GetPath(), false)
host := d.CustomHost
@ -133,6 +121,7 @@ func (d *USS) Remove(ctx context.Context, obj model.Obj) error {
}
func (d *USS) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
// TODO not support cancel??
return d.client.Put(&upyun.PutObjectConfig{
Path: getKey(path.Join(dstDir.GetPath(), stream.GetName()), false),
Reader: stream,

View File

@ -18,12 +18,11 @@ type Addition struct {
var config = driver.Config{
Name: "USS",
LocalSort: true,
}
func New() driver.Driver {
return &USS{}
DefaultRoot: "/",
}
func init() {
op.RegisterDriver(config, New)
op.RegisterDriver(func() driver.Driver {
return &USS{}
})
}

View File

@ -7,7 +7,6 @@ import (
"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/alist-org/alist/v3/pkg/utils/random"
)
@ -20,12 +19,7 @@ func (d *Virtual) Config() driver.Config {
return config
}
func (d *Virtual) Init(ctx context.Context, storage model.Storage) error {
d.Storage = storage
err := utils.Json.UnmarshalFromString(storage.Addition, &d.Addition)
if err != nil {
return err
}
func (d *Virtual) Init(ctx context.Context) error {
return nil
}
@ -34,7 +28,7 @@ func (d *Virtual) Drop(ctx context.Context) error {
}
func (d *Virtual) GetAddition() driver.Additional {
return d.Addition
return &d.Addition
}
func (d *Virtual) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {

View File

@ -21,10 +21,8 @@ var config = driver.Config{
//NoCache: true,
}
func New() driver.Driver {
return &Virtual{}
}
func init() {
op.RegisterDriver(config, New)
op.RegisterDriver(func() driver.Driver {
return &Virtual{}
})
}

View File

@ -26,16 +26,11 @@ func (d *WebDav) Config() driver.Config {
}
func (d *WebDav) GetAddition() driver.Additional {
return d.Addition
return &d.Addition
}
func (d *WebDav) Init(ctx context.Context, storage model.Storage) error {
d.Storage = storage
err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition)
if err != nil {
return err
}
err = d.setClient()
func (d *WebDav) Init(ctx context.Context) error {
err := d.setClient()
if err == nil {
d.cron = cron.NewCron(time.Hour * 12)
d.cron.Do(func() {
@ -67,30 +62,15 @@ func (d *WebDav) List(ctx context.Context, dir model.Obj, args model.ListArgs) (
})
}
//func (d *WebDav) Get(ctx context.Context, path string) (model.Obj, error) {
// // this is optional
// return nil, errs.NotImplement
//}
func (d *WebDav) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
callback := func(r *http.Request) {
if args.Header.Get("Range") != "" {
r.Header.Set("Range", args.Header.Get("Range"))
}
if args.Header.Get("If-Range") != "" {
r.Header.Set("If-Range", args.Header.Get("If-Range"))
}
}
reader, header, err := d.client.ReadStream(file.GetPath(), callback)
url, header, err := d.client.Link(file.GetPath())
if err != nil {
return nil, err
}
link := &model.Link{Data: reader}
if header.Get("Content-Range") != "" {
link.Status = 206
link.Header = header
}
return link, nil
return &model.Link{
URL: url,
Header: header,
}, nil
}
func (d *WebDav) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
@ -118,6 +98,7 @@ func (d *WebDav) Put(ctx context.Context, dstDir model.Obj, stream model.FileStr
r.Header.Set("Content-Type", stream.GetMimetype())
r.ContentLength = stream.GetSize()
}
// TODO: support cancel
err := d.client.WriteStream(path.Join(dstDir.GetPath(), stream.GetName()), stream, 0644, callback)
return err
}

View File

@ -16,14 +16,12 @@ type Addition struct {
var config = driver.Config{
Name: "WebDav",
LocalSort: true,
OnlyLocal: true,
OnlyProxy: true,
DefaultRoot: "/",
}
func New() driver.Driver {
return &WebDav{}
}
func init() {
op.RegisterDriver(config, New)
op.RegisterDriver(func() driver.Driver {
return &WebDav{}
})
}

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